1 /**
2  * @file icesdp.c  SDP Attributes for ICE
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_sa.h>
14 #include <re_net.h>
15 #include <re_stun.h>
16 #include <re_ice.h>
17 #include "ice.h"
18 
19 
20 #define DEBUG_MODULE "icesdp"
21 #define DEBUG_LEVEL 5
22 #include <re_dbg.h>
23 
24 
25 const char ice_attr_cand[]        = "candidate";
26 const char ice_attr_remote_cand[] = "remote-candidates";
27 const char ice_attr_lite[]        = "ice-lite";
28 const char ice_attr_ufrag[]       = "ice-ufrag";
29 const char ice_attr_pwd[]         = "ice-pwd";
30 const char ice_attr_mismatch[]    = "ice-mismatch";
31 
32 
33 static const char rel_addr_str[] = "raddr";
34 static const char rel_port_str[] = "rport";
35 
36 
37 /* Encode SDP Attributes */
38 
39 
transp_name(enum ice_transp transp)40 static const char *transp_name(enum ice_transp transp)
41 {
42 	switch (transp) {
43 
44 	case ICE_TRANSP_UDP: return "UDP";
45 	default:             return "???";
46 	}
47 }
48 
49 
transp_resolve(const struct pl * transp)50 static enum ice_transp transp_resolve(const struct pl *transp)
51 {
52 	if (!pl_strcasecmp(transp, "UDP"))
53 		return ICE_TRANSP_UDP;
54 
55 	return ICE_TRANSP_NONE;
56 }
57 
58 
59 /**
60  * Encode SDP candidate attribute
61  *
62  * @param pf    Print function
63  * @param cand  Candidate to encode
64  *
65  * @return 0 if success, otherwise errorcode
66  */
ice_cand_encode(struct re_printf * pf,const struct ice_cand * cand)67 int ice_cand_encode(struct re_printf *pf, const struct ice_cand *cand)
68 {
69 	int err;
70 
71 	err = re_hprintf(pf, "%s %u %s %u %j %u typ %s",
72 			 cand->foundation, cand->compid,
73 			 transp_name(cand->transp), cand->prio,
74 			 &cand->addr, sa_port(&cand->addr),
75 			 ice_cand_type2name(cand->type));
76 
77 	if (sa_isset(&cand->rel, SA_ADDR))
78 		err |= re_hprintf(pf, " raddr %j", &cand->rel);
79 
80 	if (sa_isset(&cand->rel, SA_PORT))
81 		err |= re_hprintf(pf, " rport %u", sa_port(&cand->rel));
82 
83 	return err;
84 }
85 
86 
87 /**
88  * Check if remote candidates are available
89  *
90  * @param icem ICE Media object
91  *
92  * @return True if available, otherwise false
93  */
ice_remotecands_avail(const struct icem * icem)94 bool ice_remotecands_avail(const struct icem *icem)
95 {
96 	if (!icem)
97 		return false;
98 
99 	return icem->lrole == ICE_ROLE_CONTROLLING &&
100 		icem->state == ICE_CHECKLIST_COMPLETED;
101 }
102 
103 
104 /**
105  * Encode the SDP "remote-candidates" Attribute
106  *
107  * @param pf   Print function
108  * @param icem ICE Media object
109  *
110  * @return 0 if success, otherwise errorcode
111  */
ice_remotecands_encode(struct re_printf * pf,const struct icem * icem)112 int ice_remotecands_encode(struct re_printf *pf, const struct icem *icem)
113 {
114 	struct le *le;
115 	int err = 0;
116 
117 	if (!icem)
118 		return EINVAL;
119 
120 	for (le = icem->rcandl.head; le && !err; le = le->next) {
121 
122 		const struct ice_cand *rcand = le->data;
123 
124 		err = re_hprintf(pf, "%s%d %j %u",
125 				 icem->rcandl.head==le ? "" : " ",
126 				 rcand->compid,
127 				 &rcand->addr, sa_port(&rcand->addr));
128 	}
129 
130 	return err;
131 }
132 
133 
134 /* Decode SDP Attributes */
135 
136 
ufrag_decode(struct icem * icem,const char * value)137 static int ufrag_decode(struct icem *icem, const char *value)
138 {
139 	char *ufrag = NULL;
140 	int err;
141 
142 	err = str_dup(&ufrag, value);
143 	if (err)
144 		return err;
145 
146 	mem_deref(icem->rufrag);
147 	icem->rufrag = mem_ref(ufrag);
148 
149 	mem_deref(ufrag);
150 
151 	return 0;
152 }
153 
154 
pwd_decode(struct icem * icem,const char * value)155 static int pwd_decode(struct icem *icem, const char *value)
156 {
157 	char *pwd = NULL;
158 	int err;
159 
160 	err = str_dup(&pwd, value);
161 	if (err)
162 		return err;
163 
164 	mem_deref(icem->rpwd);
165 	icem->rpwd = mem_ref(pwd);
166 
167 	mem_deref(pwd);
168 
169 	return 0;
170 }
171 
172 
media_ufrag_decode(struct icem * icem,const char * value)173 static int media_ufrag_decode(struct icem *icem, const char *value)
174 {
175 	icem->rufrag = mem_deref(icem->rufrag);
176 
177 	return str_dup(&icem->rufrag, value);
178 }
179 
180 
media_pwd_decode(struct icem * icem,const char * value)181 static int media_pwd_decode(struct icem *icem, const char *value)
182 {
183 	icem->rpwd = mem_deref(icem->rpwd);
184 
185 	return str_dup(&icem->rpwd, value);
186 }
187 
188 
cand_decode(struct icem * icem,const char * val)189 static int cand_decode(struct icem *icem, const char *val)
190 {
191 	struct pl foundation, compid, transp, prio, addr, port, cand_type;
192 	struct pl extra = pl_null;
193 	struct sa caddr, rel_addr;
194 	char type[8];
195 	uint8_t cid;
196 	int err;
197 
198 	sa_init(&rel_addr, AF_INET);
199 
200 	err = re_regex(val, strlen(val),
201 		       "[^ ]+ [0-9]+ [^ ]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+[^]*",
202 		       &foundation, &compid, &transp, &prio,
203 		       &addr, &port, &cand_type, &extra);
204 	if (err)
205 		return err;
206 
207 	if (ICE_TRANSP_NONE == transp_resolve(&transp)) {
208 		DEBUG_NOTICE("<%s> ignoring candidate with"
209 			     " unknown transport=%r (%r:%r)\n",
210 			     icem->name, &transp, &cand_type, &addr);
211 		return 0;
212 	}
213 
214 	if (pl_isset(&extra)) {
215 
216 		struct pl name, value;
217 
218 		/* Loop through " SP attr SP value" pairs */
219 		while (!re_regex(extra.p, extra.l, " [^ ]+ [^ ]+",
220 				 &name, &value)) {
221 
222 			pl_advance(&extra, value.p + value.l - extra.p);
223 
224 			if (0 == pl_strcasecmp(&name, rel_addr_str)) {
225 				err = sa_set(&rel_addr, &value,
226 					     sa_port(&rel_addr));
227 				if (err)
228 					break;
229 			}
230 			else if (0 == pl_strcasecmp(&name, rel_port_str)) {
231 				sa_set_port(&rel_addr, pl_u32(&value));
232 			}
233 		}
234 	}
235 
236 	err = sa_set(&caddr, &addr, pl_u32(&port));
237 	if (err)
238 		return err;
239 
240 	cid = pl_u32(&compid);
241 
242 	/* add only if not exist */
243 	if (icem_cand_find(&icem->rcandl, cid, &caddr))
244 		return 0;
245 
246 	(void)pl_strcpy(&cand_type, type, sizeof(type));
247 
248 	return icem_rcand_add(icem, ice_cand_name2type(type), cid,
249 			      pl_u32(&prio), &caddr, &rel_addr, &foundation);
250 }
251 
252 
253 /**
254  * Decode SDP session attributes
255  *
256  * @param icem  ICE Media object
257  * @param name  Name of the SDP attribute
258  * @param value Value of the SDP attribute (optional)
259  *
260  * @return 0 if success, otherwise errorcode
261  */
ice_sdp_decode(struct icem * icem,const char * name,const char * value)262 int ice_sdp_decode(struct icem *icem, const char *name, const char *value)
263 {
264 	if (!icem)
265 		return EINVAL;
266 
267 	if (0 == str_casecmp(name, ice_attr_lite)) {
268 		if (ICE_MODE_LITE == icem->lmode) {
269 			DEBUG_WARNING("we are lite, peer is also lite!\n");
270 			return EPROTO;
271 		}
272 		icem->rmode = ICE_MODE_LITE;
273 		icem->lrole = ICE_ROLE_CONTROLLING;
274 	}
275 	else if (0 == str_casecmp(name, ice_attr_ufrag))
276 		return ufrag_decode(icem, value);
277 	else if (0 == str_casecmp(name, ice_attr_pwd))
278 		return pwd_decode(icem, value);
279 
280 	return 0;
281 }
282 
283 
284 /**
285  * Decode SDP media attributes
286  *
287  * @param icem  ICE Media object
288  * @param name  Name of the SDP attribute
289  * @param value Value of the SDP attribute (optional)
290  *
291  * @return 0 if success, otherwise errorcode
292  */
icem_sdp_decode(struct icem * icem,const char * name,const char * value)293 int icem_sdp_decode(struct icem *icem, const char *name, const char *value)
294 {
295 	if (!icem)
296 		return EINVAL;
297 
298 	if (0 == str_casecmp(name, ice_attr_cand))
299 		return cand_decode(icem, value);
300 	else if (0 == str_casecmp(name, ice_attr_mismatch))
301 		icem->mismatch = true;
302 	else if (0 == str_casecmp(name, ice_attr_ufrag))
303 		return media_ufrag_decode(icem, value);
304 	else if (0 == str_casecmp(name, ice_attr_pwd))
305 		return media_pwd_decode(icem, value);
306 
307 	return 0;
308 }
309 
310 
ice_tcptype_name(enum ice_tcptype tcptype)311 static const char *ice_tcptype_name(enum ice_tcptype tcptype)
312 {
313 	switch (tcptype) {
314 
315 	case ICE_TCP_ACTIVE:  return "active";
316 	case ICE_TCP_PASSIVE: return "passive";
317 	case ICE_TCP_SO:      return "so";
318 	default: return "???";
319 	}
320 }
321 
322 
ice_tcptype_resolve(const struct pl * pl)323 static enum ice_tcptype ice_tcptype_resolve(const struct pl *pl)
324 {
325 	if (0 == pl_strcasecmp(pl, "active"))  return ICE_TCP_ACTIVE;
326 	if (0 == pl_strcasecmp(pl, "passive")) return ICE_TCP_PASSIVE;
327 	if (0 == pl_strcasecmp(pl, "so"))      return ICE_TCP_SO;
328 
329 	return (enum ice_tcptype)-1;
330 }
331 
332 
ice_cand_attr_encode(struct re_printf * pf,const struct ice_cand_attr * cand)333 int ice_cand_attr_encode(struct re_printf *pf,
334 			 const struct ice_cand_attr *cand)
335 {
336 	int err = 0;
337 
338 	if (!cand)
339 		return 0;
340 
341 	err |= re_hprintf(pf, "%s %u %s %u %j %u typ %s",
342 			  cand->foundation, cand->compid,
343 			  net_proto2name(cand->proto), cand->prio,
344 			  &cand->addr, sa_port(&cand->addr),
345 			  ice_cand_type2name(cand->type));
346 
347 	if (sa_isset(&cand->rel_addr, SA_ADDR))
348 		err |= re_hprintf(pf, " raddr %j", &cand->rel_addr);
349 
350 	if (sa_isset(&cand->rel_addr, SA_PORT))
351 		err |= re_hprintf(pf, " rport %u", sa_port(&cand->rel_addr));
352 
353 	if (cand->proto == IPPROTO_TCP) {
354 		err |= re_hprintf(pf, " tcptype %s",
355 				  ice_tcptype_name(cand->tcptype));
356 	}
357 
358 	return err;
359 }
360 
361 
ice_cand_attr_decode(struct ice_cand_attr * cand,const char * val)362 int ice_cand_attr_decode(struct ice_cand_attr *cand, const char *val)
363 {
364 	struct pl pl_fnd, pl_compid, pl_transp, pl_prio, pl_addr, pl_port;
365 	struct pl pl_type, pl_raddr, pl_rport, pl_opt = PL_INIT;
366 	size_t len;
367 	char type[8];
368 	int err;
369 
370 	if (!cand || !val)
371 		return EINVAL;
372 
373 	memset(cand, 0, sizeof(*cand));
374 
375 	len = str_len(val);
376 
377 	err = re_regex(val, len,
378 		       "[^ ]+ [0-9]+ [a-z]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+"
379 		       "[^]*",
380 		       &pl_fnd, &pl_compid, &pl_transp, &pl_prio,
381 		       &pl_addr, &pl_port, &pl_type, &pl_opt);
382 	if (err)
383 		return err;
384 
385 	(void)pl_strcpy(&pl_fnd, cand->foundation, sizeof(cand->foundation));
386 
387 	if (0 == pl_strcasecmp(&pl_transp, "UDP"))
388 		cand->proto = IPPROTO_UDP;
389 	else if (0 == pl_strcasecmp(&pl_transp, "TCP"))
390 		cand->proto = IPPROTO_TCP;
391 	else
392 		cand->proto = 0;
393 
394 	err = sa_set(&cand->addr, &pl_addr, pl_u32(&pl_port));
395 	if (err)
396 		return err;
397 
398 	cand->compid = pl_u32(&pl_compid);
399 	cand->prio   = pl_u32(&pl_prio);
400 
401 	(void)pl_strcpy(&pl_type, type, sizeof(type));
402 
403 	cand->type = ice_cand_name2type(type);
404 
405 	/* optional */
406 
407 	if (0 == re_regex(pl_opt.p, pl_opt.l, "raddr [^ ]+ rport [0-9]+",
408 			  &pl_raddr, &pl_rport)) {
409 
410 		err = sa_set(&cand->rel_addr, &pl_raddr, pl_u32(&pl_rport));
411 		if (err)
412 			return err;
413 	}
414 
415 	if (cand->proto == IPPROTO_TCP) {
416 
417 		struct pl tcptype;
418 
419 		err = re_regex(pl_opt.p, pl_opt.l, "tcptype [^ ]+",
420 			       &tcptype);
421 		if (err)
422 			return err;
423 
424 		cand->tcptype = ice_tcptype_resolve(&tcptype);
425 	}
426 
427 	return 0;
428 }
429