• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 
34 #include <bluetooth/bluetooth.h>
35 #include <bluetooth/sdp.h>
36 #include <bluetooth/sdp_lib.h>
37 
38 #include <glib.h>
39 
40 #include "btio.h"
41 #include "sdpd.h"
42 #include "glib-helper.h"
43 
44 /* Number of seconds to keep a sdp_session_t in the cache */
45 #define CACHE_TIMEOUT 2
46 
47 struct cached_sdp_session {
48 	bdaddr_t src;
49 	bdaddr_t dst;
50 	sdp_session_t *session;
51 	guint timer;
52 };
53 
54 static GSList *cached_sdp_sessions = NULL;
55 
cached_session_expired(gpointer user_data)56 static gboolean cached_session_expired(gpointer user_data)
57 {
58 	struct cached_sdp_session *cached = user_data;
59 
60 	cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
61 
62 	sdp_close(cached->session);
63 
64 	g_free(cached);
65 
66 	return FALSE;
67 }
68 
get_sdp_session(const bdaddr_t * src,const bdaddr_t * dst)69 static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
70 {
71 	GSList *l;
72 
73 	for (l = cached_sdp_sessions; l != NULL; l = l->next) {
74 		struct cached_sdp_session *c = l->data;
75 		sdp_session_t *session;
76 
77 		if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
78 			continue;
79 
80 		g_source_remove(c->timer);
81 
82 		session = c->session;
83 
84 		cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
85 		g_free(c);
86 
87 		return session;
88 	}
89 
90 	return sdp_connect(src, dst, SDP_NON_BLOCKING);
91 }
92 
cache_sdp_session(bdaddr_t * src,bdaddr_t * dst,sdp_session_t * session)93 static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
94 						sdp_session_t *session)
95 {
96 	struct cached_sdp_session *cached;
97 
98 	cached = g_new0(struct cached_sdp_session, 1);
99 
100 	bacpy(&cached->src, src);
101 	bacpy(&cached->dst, dst);
102 
103 	cached->session = session;
104 
105 	cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
106 
107 	cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
108 						cached_session_expired,
109 						cached);
110 }
111 
112 struct search_context {
113 	bdaddr_t		src;
114 	bdaddr_t		dst;
115 	sdp_session_t		*session;
116 	bt_callback_t		cb;
117 	bt_destroy_t		destroy;
118 	gpointer		user_data;
119 	uuid_t			uuid;
120 	guint			io_id;
121 };
122 
123 static GSList *context_list = NULL;
124 
search_context_cleanup(struct search_context * ctxt)125 static void search_context_cleanup(struct search_context *ctxt)
126 {
127 	context_list = g_slist_remove(context_list, ctxt);
128 
129 	if (ctxt->destroy)
130 		ctxt->destroy(ctxt->user_data);
131 
132 	g_free(ctxt);
133 }
134 
search_completed_cb(uint8_t type,uint16_t status,uint8_t * rsp,size_t size,void * user_data)135 static void search_completed_cb(uint8_t type, uint16_t status,
136 			uint8_t *rsp, size_t size, void *user_data)
137 {
138 	struct search_context *ctxt = user_data;
139 	sdp_list_t *recs = NULL;
140 	int scanned, seqlen = 0, bytesleft = size;
141 	uint8_t dataType;
142 	int err = 0;
143 
144 	if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
145 		err = -EPROTO;
146 		goto done;
147 	}
148 
149 	scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
150 	if (!scanned || !seqlen)
151 		goto done;
152 
153 	rsp += scanned;
154 	bytesleft -= scanned;
155 	do {
156 		sdp_record_t *rec;
157 		int recsize;
158 
159 		recsize = 0;
160 		rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
161 		if (!rec)
162 			break;
163 
164 		if (!recsize) {
165 			sdp_record_free(rec);
166 			break;
167 		}
168 
169 		scanned += recsize;
170 		rsp += recsize;
171 		bytesleft -= recsize;
172 
173 		recs = sdp_list_append(recs, rec);
174 	} while (scanned < (ssize_t) size && bytesleft > 0);
175 
176 done:
177 	cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
178 
179 	if (ctxt->cb)
180 		ctxt->cb(recs, err, ctxt->user_data);
181 
182 	if (recs)
183 		sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
184 
185 	search_context_cleanup(ctxt);
186 }
187 
search_process_cb(GIOChannel * chan,GIOCondition cond,gpointer user_data)188 static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond,
189 							gpointer user_data)
190 {
191 	struct search_context *ctxt = user_data;
192 	int err = 0;
193 
194 	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
195 		err = EIO;
196 		goto failed;
197 	}
198 
199 	if (sdp_process(ctxt->session) < 0)
200 		goto failed;
201 
202 	return TRUE;
203 
204 failed:
205 	if (err) {
206 		sdp_close(ctxt->session);
207 		ctxt->session = NULL;
208 
209 		if (ctxt->cb)
210 			ctxt->cb(NULL, err, ctxt->user_data);
211 
212 		search_context_cleanup(ctxt);
213 	}
214 
215 	return FALSE;
216 }
217 
connect_watch(GIOChannel * chan,GIOCondition cond,gpointer user_data)218 static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
219 							gpointer user_data)
220 {
221 	struct search_context *ctxt = user_data;
222 	sdp_list_t *search, *attrids;
223 	uint32_t range = 0x0000ffff;
224 	socklen_t len;
225 	int sk, err = 0;
226 
227 	sk = g_io_channel_unix_get_fd(chan);
228 	ctxt->io_id = 0;
229 
230 	len = sizeof(err);
231 	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
232 		err = errno;
233 		goto failed;
234 	}
235 
236 	if (err != 0)
237 		goto failed;
238 
239 	if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
240 		err = EIO;
241 		goto failed;
242 	}
243 
244 	search = sdp_list_append(NULL, &ctxt->uuid);
245 	attrids = sdp_list_append(NULL, &range);
246 	if (sdp_service_search_attr_async(ctxt->session,
247 				search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
248 		sdp_list_free(attrids, NULL);
249 		sdp_list_free(search, NULL);
250 		err = EIO;
251 		goto failed;
252 	}
253 
254 	sdp_list_free(attrids, NULL);
255 	sdp_list_free(search, NULL);
256 
257 	/* Set callback responsible for update the internal SDP transaction */
258 	ctxt->io_id = g_io_add_watch(chan,
259 				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
260 				search_process_cb, ctxt);
261 	return FALSE;
262 
263 failed:
264 	sdp_close(ctxt->session);
265 	ctxt->session = NULL;
266 
267 	if (ctxt->cb)
268 		ctxt->cb(NULL, -err, ctxt->user_data);
269 
270 	search_context_cleanup(ctxt);
271 
272 	return FALSE;
273 }
274 
create_search_context(struct search_context ** ctxt,const bdaddr_t * src,const bdaddr_t * dst,uuid_t * uuid)275 static int create_search_context(struct search_context **ctxt,
276 					const bdaddr_t *src,
277 					const bdaddr_t *dst,
278 					uuid_t *uuid)
279 {
280 	sdp_session_t *s;
281 	GIOChannel *chan;
282 
283 	if (!ctxt)
284 		return -EINVAL;
285 
286 	s = get_sdp_session(src, dst);
287 	if (!s)
288 		return -errno;
289 
290 	*ctxt = g_try_malloc0(sizeof(struct search_context));
291 	if (!*ctxt) {
292 		sdp_close(s);
293 		return -ENOMEM;
294 	}
295 
296 	bacpy(&(*ctxt)->src, src);
297 	bacpy(&(*ctxt)->dst, dst);
298 	(*ctxt)->session = s;
299 	(*ctxt)->uuid = *uuid;
300 
301 	chan = g_io_channel_unix_new(sdp_get_socket(s));
302 	(*ctxt)->io_id = g_io_add_watch(chan,
303 				G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
304 				connect_watch, *ctxt);
305 	g_io_channel_unref(chan);
306 
307 	return 0;
308 }
309 
bt_search_service(const bdaddr_t * src,const bdaddr_t * dst,uuid_t * uuid,bt_callback_t cb,void * user_data,bt_destroy_t destroy)310 int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
311 			uuid_t *uuid, bt_callback_t cb, void *user_data,
312 			bt_destroy_t destroy)
313 {
314 	struct search_context *ctxt = NULL;
315 	int err;
316 
317 	if (!cb)
318 		return -EINVAL;
319 
320 	err = create_search_context(&ctxt, src, dst, uuid);
321 	if (err < 0)
322 		return err;
323 
324 	ctxt->cb	= cb;
325 	ctxt->destroy	= destroy;
326 	ctxt->user_data	= user_data;
327 
328 	context_list = g_slist_append(context_list, ctxt);
329 
330 	return 0;
331 }
332 
find_by_bdaddr(gconstpointer data,gconstpointer user_data)333 static gint find_by_bdaddr(gconstpointer data, gconstpointer user_data)
334 {
335 	const struct search_context *ctxt = data, *search = user_data;
336 
337 	return (bacmp(&ctxt->dst, &search->dst) &&
338 					bacmp(&ctxt->src, &search->src));
339 }
340 
bt_cancel_discovery(const bdaddr_t * src,const bdaddr_t * dst)341 int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
342 {
343 	struct search_context match, *ctxt;
344 	GSList *l;
345 
346 	memset(&match, 0, sizeof(match));
347 	bacpy(&match.src, src);
348 	bacpy(&match.dst, dst);
349 
350 	/* Ongoing SDP Discovery */
351 	l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
352 	if (l == NULL)
353 		return -ENOENT;
354 
355 	ctxt = l->data;
356 
357 	if (!ctxt->session)
358 		return -ENOTCONN;
359 
360 	if (ctxt->io_id)
361 		g_source_remove(ctxt->io_id);
362 
363 	if (ctxt->session)
364 		sdp_close(ctxt->session);
365 
366 	search_context_cleanup(ctxt);
367 
368 	return 0;
369 }
370 
bt_uuid2string(uuid_t * uuid)371 char *bt_uuid2string(uuid_t *uuid)
372 {
373 	gchar *str;
374 	uuid_t uuid128;
375 	unsigned int data0;
376 	unsigned short data1;
377 	unsigned short data2;
378 	unsigned short data3;
379 	unsigned int data4;
380 	unsigned short data5;
381 
382 	if (!uuid)
383 		return NULL;
384 
385 	switch (uuid->type) {
386 	case SDP_UUID16:
387 		sdp_uuid16_to_uuid128(&uuid128, uuid);
388 		break;
389 	case SDP_UUID32:
390 		sdp_uuid32_to_uuid128(&uuid128, uuid);
391 		break;
392 	case SDP_UUID128:
393 		memcpy(&uuid128, uuid, sizeof(uuid_t));
394 		break;
395 	default:
396 		/* Type of UUID unknown */
397 		return NULL;
398 	}
399 
400 	memcpy(&data0, &uuid128.value.uuid128.data[0], 4);
401 	memcpy(&data1, &uuid128.value.uuid128.data[4], 2);
402 	memcpy(&data2, &uuid128.value.uuid128.data[6], 2);
403 	memcpy(&data3, &uuid128.value.uuid128.data[8], 2);
404 	memcpy(&data4, &uuid128.value.uuid128.data[10], 4);
405 	memcpy(&data5, &uuid128.value.uuid128.data[14], 2);
406 
407 	str = g_try_malloc0(MAX_LEN_UUID_STR);
408 	if (!str)
409 		return NULL;
410 
411 	sprintf(str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
412 			g_ntohl(data0), g_ntohs(data1),
413 			g_ntohs(data2), g_ntohs(data3),
414 			g_ntohl(data4), g_ntohs(data5));
415 
416 	return str;
417 }
418 
419 static struct {
420 	const char	*name;
421 	uint16_t	class;
422 } bt_services[] = {
423 	{ "vcp",	VIDEO_CONF_SVCLASS_ID		},
424 	{ "pbap",	PBAP_SVCLASS_ID			},
425 	{ "sap",	SAP_SVCLASS_ID			},
426 	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	},
427 	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	},
428 	{ "bip",	IMAGING_SVCLASS_ID		},
429 	{ "synch",	IRMC_SYNC_SVCLASS_ID		},
430 	{ "dun",	DIALUP_NET_SVCLASS_ID		},
431 	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		},
432 	{ "fax",	FAX_SVCLASS_ID			},
433 	{ "spp",	SERIAL_PORT_SVCLASS_ID		},
434 	{ "hsp",	HEADSET_SVCLASS_ID		},
435 	{ "hfp",	HANDSFREE_SVCLASS_ID		},
436 	{ }
437 };
438 
name2class(const char * pattern)439 static uint16_t name2class(const char *pattern)
440 {
441 	int i;
442 
443 	for (i = 0; bt_services[i].name; i++) {
444 		if (strcasecmp(bt_services[i].name, pattern) == 0)
445 			return bt_services[i].class;
446 	}
447 
448 	return 0;
449 }
450 
is_uuid128(const char * string)451 static inline gboolean is_uuid128(const char *string)
452 {
453 	return (strlen(string) == 36 &&
454 			string[8] == '-' &&
455 			string[13] == '-' &&
456 			string[18] == '-' &&
457 			string[23] == '-');
458 }
459 
string2uuid16(uuid_t * uuid,const char * string)460 static int string2uuid16(uuid_t *uuid, const char *string)
461 {
462 	int length = strlen(string);
463 	char *endptr = NULL;
464 	uint16_t u16;
465 
466 	if (length != 4 && length != 6)
467 		return -EINVAL;
468 
469 	u16 = strtol(string, &endptr, 16);
470 	if (endptr && *endptr == '\0') {
471 		sdp_uuid16_create(uuid, u16);
472 		return 0;
473 	}
474 
475 	return -EINVAL;
476 }
477 
bt_name2string(const char * pattern)478 char *bt_name2string(const char *pattern)
479 {
480 	uuid_t uuid;
481 	uint16_t uuid16;
482 	int i;
483 
484 	/* UUID 128 string format */
485 	if (is_uuid128(pattern))
486 		return g_strdup(pattern);
487 
488 	/* Friendly service name format */
489 	uuid16 = name2class(pattern);
490 	if (uuid16)
491 		goto proceed;
492 
493 	/* HEX format */
494 	uuid16 = strtol(pattern, NULL, 16);
495 	for (i = 0; bt_services[i].class; i++) {
496 		if (bt_services[i].class == uuid16)
497 			goto proceed;
498 	}
499 
500 	return NULL;
501 
502 proceed:
503 	sdp_uuid16_create(&uuid, uuid16);
504 
505 	return bt_uuid2string(&uuid);
506 }
507 
bt_string2uuid(uuid_t * uuid,const char * string)508 int bt_string2uuid(uuid_t *uuid, const char *string)
509 {
510 	uint32_t data0, data4;
511 	uint16_t data1, data2, data3, data5;
512 
513 	if (is_uuid128(string) &&
514 			sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
515 				&data0, &data1, &data2, &data3, &data4, &data5) == 6) {
516 		uint8_t val[16];
517 
518 		data0 = g_htonl(data0);
519 		data1 = g_htons(data1);
520 		data2 = g_htons(data2);
521 		data3 = g_htons(data3);
522 		data4 = g_htonl(data4);
523 		data5 = g_htons(data5);
524 
525 		memcpy(&val[0], &data0, 4);
526 		memcpy(&val[4], &data1, 2);
527 		memcpy(&val[6], &data2, 2);
528 		memcpy(&val[8], &data3, 2);
529 		memcpy(&val[10], &data4, 4);
530 		memcpy(&val[14], &data5, 2);
531 
532 		sdp_uuid128_create(uuid, val);
533 
534 		return 0;
535 	} else {
536 		uint16_t class = name2class(string);
537 		if (class) {
538 			sdp_uuid16_create(uuid, class);
539 			return 0;
540 		}
541 
542 		return string2uuid16(uuid, string);
543 	}
544 }
545 
bt_list2string(GSList * list)546 gchar *bt_list2string(GSList *list)
547 {
548 	GSList *l;
549 	gchar *str, *tmp;
550 
551 	if (!list)
552 		return NULL;
553 
554 	str = g_strdup((const gchar *) list->data);
555 
556 	for (l = list->next; l; l = l->next) {
557 		tmp = g_strconcat(str, " " , (const gchar *) l->data, NULL);
558 		g_free(str);
559 		str = tmp;
560 	}
561 
562 	return str;
563 }
564 
bt_string2list(const gchar * str)565 GSList *bt_string2list(const gchar *str)
566 {
567 	GSList *l = NULL;
568 	gchar **uuids;
569 	int i = 0;
570 
571 	if (!str)
572 		return NULL;
573 
574 	/* FIXME: eglib doesn't support g_strsplit */
575 	uuids = g_strsplit(str, " ", 0);
576 	if (!uuids)
577 		return NULL;
578 
579 	while (uuids[i]) {
580 		l = g_slist_append(l, uuids[i]);
581 		i++;
582 	}
583 
584 	g_free(uuids);
585 
586 	return l;
587 }
588