1 /*
2 
3   $Id$
4 
5   G N O K I I
6 
7   A Linux/Unix toolset and driver for the mobile phones.
8 
9   This file is part of gnokii.
10 
11   Gnokii is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 2 of the License, or
14   (at your option) any later version.
15 
16   Gnokii is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   You should have received a copy of the GNU General Public License
22   along with gnokii; if not, write to the Free Software
23   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 
25   Copyright (C) 1999-2000  Hugh Blemings & Pavel Jan�k ml.
26   Copyright (C) 2002       Ladis Michl, Marcel Holtmann <marcel@holtmann.org>
27   Copyright (C) 2003       BORBELY Zoltan, Pawel Kot
28 
29   PhoneManager Utilities: rfcomm channel autodetection
30   Copyright (C) 2003-2004 Edd Dumbill <edd@usefulinc.com>
31   Copyright (C) 2005-2007 Bastien Nocera <hadess@hadess.net>
32 
33 */
34 
35 #include "config.h"
36 #include "compat.h"
37 #include "misc.h"
38 #include "gnokii.h"
39 #include "devices/unixbluetooth.h"
40 
41 #if defined(HAVE_BLUETOOTH_BLUEZ) || defined(HAVE_BLUETOOTH_NETGRAPH) || defined(HAVE_BLUETOOTH_NETBT)
42 
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <sys/time.h>
49 #include <sys/socket.h>
50 
51 #ifdef HAVE_BLUETOOTH_NETGRAPH	/* FreeBSD / netgraph */
52 
53 #include <bluetooth.h>
54 #include </usr/include/sdp.h>
55 
56 #define BTPROTO_RFCOMM BLUETOOTH_PROTO_RFCOMM
57 #define BDADDR_ANY NG_HCI_BDADDR_ANY
58 #define GNOKII_SERIAL_PORT_CLASS	SDP_SERVICE_CLASS_SERIAL_PORT
59 #define GNOKII_DIALUP_NETWORK_CLASS	SDP_SERVICE_CLASS_DIALUP_NETWORKING
60 #define sockaddr_rc sockaddr_rfcomm
61 #define rc_family rfcomm_family
62 #define rc_bdaddr rfcomm_bdaddr
63 #define rc_channel rfcomm_channel
64 
65 #else
66 #ifdef HAVE_BLUETOOTH_NETBT	/* FreeBSD / netbt */
67 
68 #include <bluetooth.h>
69 #include </usr/include/sdp.h>
70 
71 #define GNOKII_SERIAL_PORT_CLASS	SDP_SERVICE_CLASS_SERIAL_PORT
72 #define GNOKII_DIALUP_NETWORK_CLASS	SDP_SERVICE_CLASS_DIALUP_NETWORKING
73 #define sockaddr_rc sockaddr_bt
74 #define rc_family bt_family
75 #define rc_bdaddr bt_bdaddr
76 #define rc_channel bt_channel
77 
78 #else	/* Linux / BlueZ support */
79 
80 #include <bluetooth/bluetooth.h>
81 #include <bluetooth/rfcomm.h>
82 #include <bluetooth/sdp.h>
83 #include <bluetooth/sdp_lib.h>
84 
85 #define GNOKII_SERIAL_PORT_CLASS	SERIAL_PORT_SVCLASS_ID
86 #define GNOKII_DIALUP_NETWORK_CLASS	DIALUP_NET_SVCLASS_ID
87 
88 #endif /* HAVE_BLUETOOTH_NETBT */
89 #endif /* HAVE_BLUETOOTH_NETGRAPH */
90 
91 #if defined(HAVE_BLUETOOTH_NETGRAPH) || defined(HAVE_BLUETOOTH_NETBT) /* FreeBSD / NetBSD */
92 
93 /*
94 ** FreeBSD version of the find_service_channel function.
95 ** Written by Guido Falsi <mad@madpilot.net>.
96 ** Contains code taken from FreeBSD's sdpcontrol and rfcomm_sppd
97 ** programs, which are Copyright (c) 2001-2003 Maksim Yevmenkin
98 ** <m_evmenkin@yahoo.com>.
99 **
100 ** Also thanks to Iain Hibbert for his suggestions.
101 */
102 
103 #define attrs_len	(sizeof(attrs)/sizeof(attrs[0]))
104 #define NRECS   25      /* request this much records from the SDP server */
105 #define BSIZE   256     /* one attribute buffer size */
106 #define values_len      (sizeof(values)/sizeof(values[0]))
107 
find_service_channel(bdaddr_t * adapter,bdaddr_t * device,int only_gnapplet,uint16_t svclass_id)108 static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id)
109 {
110 	int i, channel = -1;
111 	char name[64];
112 	void *ss = NULL;
113 	uint32_t attrs[] = {
114 		SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
115 			SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
116 		SDP_ATTR_RANGE( SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
117 			SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET),
118 	};
119 	/* Buffer for the attributes */
120 	static uint8_t          buffer[NRECS * attrs_len][BSIZE];
121 	/* SDP attributes */
122 	static sdp_attr_t       values[NRECS * attrs_len];
123 
124 	/* Initialize attribute values array */
125 	for (i = 0; i < values_len; i ++) {
126 		values[i].flags = SDP_ATTR_INVALID;
127 		values[i].attr = 0;
128 		values[i].vlen = BSIZE;
129 		values[i].value = buffer[i];
130 	}
131 
132 	if ((ss = sdp_open(adapter, device)) == NULL)
133 		return -1;
134 
135 	if (sdp_error(ss) != 0)
136 		goto end;
137 
138 	if (sdp_search(ss, 1, &svclass_id, attrs_len, attrs, values_len, values) != 0)
139 		goto end;
140 
141 	for (i = 0; i < values_len; i++) {
142 		union {
143 			uint8_t		uint8;
144 			uint16_t	uint16;
145 			uint32_t	uint32;
146 			uint64_t	uint64;
147 			int128_t	int128;
148 		} value;
149 		uint8_t *start, *end;
150 		uint32_t type, len;
151 
152 		if (values[i].flags != SDP_ATTR_OK)
153 			break;
154 
155 		start = values[i].value;
156 		end = values[i].value + values[i].vlen;
157 
158 		switch (values[i].attr) {
159 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
160 			SDP_GET8(type, start);
161 			switch (type) {
162 			case SDP_DATA_SEQ8:
163 				SDP_GET8(len, start);
164 				break;
165 
166 			case SDP_DATA_SEQ16:
167 				SDP_GET16(len, start);
168 				break;
169 
170 			case SDP_DATA_SEQ32:
171 				SDP_GET32(len, start);
172 				break;
173 
174 			default:
175 				goto end;
176 				break;
177 			}
178 
179 			SDP_GET8(type, start);
180 			switch (type) {
181 			case SDP_DATA_SEQ8:
182 				SDP_GET8(len, start);
183 				break;
184 
185 			case SDP_DATA_SEQ16:
186 				SDP_GET16(len, start);
187 				break;
188 
189 			case SDP_DATA_SEQ32:
190 				SDP_GET32(len, start);
191 				break;
192 
193 			default:
194 				goto end;
195 			}
196 
197 			while (start < end) {
198 				SDP_GET8(type, start);
199 				switch (type) {
200 				case SDP_DATA_UUID16:
201 					SDP_GET16(value.uint16, start);
202 					break;
203 
204 				case SDP_DATA_UUID32:
205 					SDP_GET32(value.uint32, start);
206 					break;
207 
208 				case SDP_DATA_UUID128:
209 					SDP_GET_UUID128(&value.int128, start);
210 					break;
211 
212 				default:
213 					goto end;
214 				}
215 				if (value.uint16 == 3) {
216 					SDP_GET8(type, start);
217 					switch (type) {
218 					case SDP_DATA_UINT8:
219 					case SDP_DATA_INT8:
220 						SDP_GET8(value.uint8, start);
221 						channel = value.uint8;
222 						break;
223 
224 					case SDP_DATA_UINT16:
225 					case SDP_DATA_INT16:
226 						SDP_GET16(value.uint16, start);
227 						channel = value.uint16;
228 						break;
229 
230 					case SDP_DATA_UINT32:
231 					case SDP_DATA_INT32:
232 						SDP_GET32(value.uint32, start);
233 						channel = value.uint32;
234 						break;
235 
236 					default:
237 						goto end;
238 					}
239 				} else {
240 					SDP_GET8(type, start);
241 					switch (type) {
242 					case SDP_DATA_SEQ8:
243 					case SDP_DATA_UINT8:
244 					case SDP_DATA_INT8:
245 					case SDP_DATA_BOOL:
246 						SDP_GET8(value.uint8, start);
247 						break;
248 
249 					case SDP_DATA_SEQ16:
250 					case SDP_DATA_UINT16:
251 					case SDP_DATA_INT16:
252 					case SDP_DATA_UUID16:
253 						SDP_GET16(value.uint16, start);
254 						break;
255 
256 					case SDP_DATA_SEQ32:
257 					case SDP_DATA_UINT32:
258 					case SDP_DATA_INT32:
259 					case SDP_DATA_UUID32:
260 						SDP_GET32(value.uint32, start);
261 						break;
262 
263 					case SDP_DATA_UINT64:
264 					case SDP_DATA_INT64:
265 						SDP_GET64(value.uint64, start);
266 						break;
267 
268 					case SDP_DATA_UINT128:
269 					case SDP_DATA_INT128:
270 						SDP_GET128(&value.int128, start);
271 						break;
272 
273 					default:
274 						goto end;
275 					}
276 				}
277 			}
278 			start += len;
279 			break;
280 
281 		case SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET:
282 			if (channel == -1)
283 				break;
284 
285 			SDP_GET8(type, start);
286 			switch (type) {
287 				case SDP_DATA_STR8:
288 				case SDP_DATA_URL8:
289 					SDP_GET8(len, start);
290 					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
291 					start += len;
292 					break;
293 
294 				case SDP_DATA_STR16:
295 				case SDP_DATA_URL16:
296 					SDP_GET16(len, start);
297 					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
298 					start += len;
299 					break;
300 
301 				case SDP_DATA_STR32:
302 				case SDP_DATA_URL32:
303 					SDP_GET32(len, start);
304 					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
305 					start += len;
306 					break;
307 
308 				default:
309 					goto end;
310 			}
311 			if (name == NULL)
312 				break;
313 
314 			if (only_gnapplet != 0) {
315 				if (strcmp(name, "gnapplet") == 0)
316 					goto end;
317 				else {
318 					channel = -1;
319 					break;
320 				}
321 			}
322 
323 			if (strstr(name, "Nokia PC Suite") != NULL) {
324 				channel = -1;
325 				break;
326 			}
327 
328 			if (strstr(name, "Bluetooth Serial Port") != NULL) {
329 				channel = -1;
330 				break;
331 			}
332 
333 			if (strstr(name, "m-Router Connectivity") != NULL) {
334 				channel = -1;
335 				break;
336 			}
337 
338 			goto end;
339 		}
340 	}
341 
342 end:
343 	sdp_close(ss);
344 	return channel;
345 }
346 
347 #else
348 /*
349  * Taken from gnome-phone-manager
350  */
get_rfcomm_channel(sdp_record_t * rec,int only_gnapplet)351 static int get_rfcomm_channel(sdp_record_t *rec, int only_gnapplet)
352 {
353 	int channel = -1;
354 	sdp_list_t *protos = NULL;
355 	sdp_data_t *data;
356 	char name[64];
357 
358 	if (sdp_get_access_protos(rec, &protos) != 0)
359 		goto end;
360 
361 	data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
362 	if (data)
363 		snprintf(name, sizeof(name), "%.*s", data->unitSize, data->val.str);
364 
365 	if (name == NULL)
366 		goto end;
367 
368 	/*
369 	 * If we're only supposed to check for gnapplet, do it here
370 	 * We ignore it if we're not supposed to check for it.
371 	 */
372 	if (only_gnapplet != 0) {
373 		if (strcmp(name, "gnapplet") == 0)
374 			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
375 		goto end;
376 	}
377 
378 	/*
379 	 * We can't seem to connect to the PC Suite channel.
380 	 */
381 	if (strstr(name, "Nokia PC Suite") != NULL)
382 		goto end;
383 	/*
384 	 * And that type of channel on Nokia Symbian phones doesn't
385 	 * work either.
386 	 */
387 	if (strstr(name, "Bluetooth Serial Port") != NULL)
388 		goto end;
389 	/*
390 	 * Avoid the m-Router channel, same as the PC Suite on Sony Ericsson
391 	 * phones.
392 	 */
393 	if (strstr(name, "m-Router Connectivity") != NULL)
394 		goto end;
395 
396 	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
397 end:
398 	sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, 0);
399 	sdp_list_free(protos, 0);
400 	return channel;
401 }
402 
403 /*
404  * Determine whether the given device supports Serial or Dial-Up Networking,
405  * and if so what the RFCOMM channel number for the service is.
406  */
find_service_channel(bdaddr_t * adapter,bdaddr_t * device,int only_gnapplet,uint16_t svclass_id)407 static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id)
408 {
409 	sdp_session_t *sdp = NULL;
410 	sdp_list_t *search = NULL, *attrs = NULL, *recs = NULL, *tmp;
411 	uuid_t browse_uuid, service_id;
412 	uint32_t range = 0x0000ffff;
413 	int channel = -1;
414 
415 	sdp = sdp_connect(adapter, device, SDP_RETRY_IF_BUSY);
416 	if (!sdp)
417 		goto end;
418 
419 	sdp_uuid16_create(&browse_uuid, PUBLIC_BROWSE_GROUP);
420 	sdp_uuid16_create(&service_id, svclass_id);
421 	search = sdp_list_append(NULL, &browse_uuid);
422 	search = sdp_list_append(search, &service_id);
423 
424 	attrs = sdp_list_append(NULL, &range);
425 
426 	if (sdp_service_search_attr_req(sdp, search,
427 					 SDP_ATTR_REQ_RANGE, attrs,
428 					 &recs))
429 		goto end;
430 
431 	for (tmp = recs; tmp != NULL; tmp = tmp->next) {
432 		sdp_record_t *rec = tmp->data;
433 
434 		/*
435 		 * If this service is better than what we've
436 		 * previously seen, try and get the channel number.
437 		 */
438 		channel = get_rfcomm_channel(rec, only_gnapplet);
439 		if (channel > 0)
440 			goto end;
441 	}
442 
443 end:
444 	sdp_list_free(recs, (sdp_free_func_t)sdp_record_free);
445 	sdp_list_free(search, NULL);
446 	sdp_list_free(attrs, NULL);
447 	sdp_close(sdp);
448 
449 	return channel;
450 }
451 
452 #endif
453 
get_serial_channel(bdaddr_t * device,int only_gnapplet)454 static uint8_t get_serial_channel(bdaddr_t *device, int only_gnapplet)
455 {
456 	bdaddr_t src;
457 	int channel;
458 	uint8_t retval;
459 
460 	bacpy(&src, BDADDR_ANY);
461 
462 	channel = find_service_channel(&src, device, only_gnapplet, GNOKII_SERIAL_PORT_CLASS);
463 	if (channel < 0)
464 		channel = find_service_channel(&src, device, only_gnapplet, GNOKII_DIALUP_NETWORK_CLASS);
465 
466 	if (channel < 0)
467 		retval = 0;
468 	else
469 		retval = channel;
470 	return retval;
471 }
472 
473 /* From: http://www.kegel.com/dkftpbench/nonblocking.html */
setNonblocking(int fd)474 static int setNonblocking(int fd)
475 {
476 	int retcode, flags;
477 
478 #if defined(O_NONBLOCK)
479 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
480 		flags = 0;
481 	retcode = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
482 #else
483 	flags = 1;
484 	retcode = ioctl(fd, FIOASYNC, &flags);
485 #endif
486 
487 	return retcode;
488 }
489 
bluetooth_open(const char * addr,uint8_t channel,struct gn_statemachine * state)490 int bluetooth_open(const char *addr, uint8_t channel, struct gn_statemachine *state)
491 {
492 	bdaddr_t bdaddr;
493 	struct sockaddr_rc raddr;
494 	int fd;
495 
496 	if (str2ba((char *)addr, &bdaddr)) {
497 		fprintf(stderr, _("Invalid bluetooth address \"%s\"\n"), addr);
498 		return -1;
499 	}
500 
501 	if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
502 		perror(_("Can't create socket"));
503 		return -1;
504 	}
505 
506 	memset(&raddr, 0, sizeof(raddr));
507 	raddr.rc_family = AF_BLUETOOTH;
508 	bacpy(&raddr.rc_bdaddr, &bdaddr);
509 	dprintf("Channel: %d\n", channel);
510 	if (channel < 1) {
511 		if (!strcmp(state->config.model, "gnapplet") ||
512 		    !strcmp(state->config.model, "symbian"))
513 			channel = get_serial_channel(&bdaddr, 1);
514 		else
515 			channel = get_serial_channel(&bdaddr, 0);
516 	}
517 	dprintf("Channel: %d\n", channel);
518 
519 	/* If none channel found, fail. */
520 	if (channel < 1) {
521 		fprintf(stderr, _("Cannot find any appropriate rfcomm channel and none was specified in the config.\n"));
522 		close(fd);
523 		return -1;
524 	}
525 
526 	dprintf("Using channel: %d\n", channel);
527 	raddr.rc_channel = channel;
528 
529 	if (connect(fd, (struct sockaddr *)&raddr, sizeof(raddr)) < 0) {
530 		perror(_("Can't connect"));
531 		close(fd);
532 		return -1;
533 	}
534 
535 	/* Ignore errors. If the socket was not set in the async way,
536 	 * we can live with that.
537 	 */
538 	setNonblocking(fd);
539 
540 	return fd;
541 }
542 
bluetooth_close(int fd,struct gn_statemachine * state)543 int bluetooth_close(int fd, struct gn_statemachine *state)
544 {
545 	sleep(2);
546 	return close(fd);
547 }
548 
bluetooth_write(int fd,const __ptr_t bytes,int size,struct gn_statemachine * state)549 int bluetooth_write(int fd, const __ptr_t bytes, int size, struct gn_statemachine *state)
550 {
551 	return write(fd, bytes, size);
552 }
553 
bluetooth_read(int fd,__ptr_t bytes,int size,struct gn_statemachine * state)554 int bluetooth_read(int fd, __ptr_t bytes, int size, struct gn_statemachine *state)
555 {
556 	return read(fd, bytes, size);
557 }
558 
bluetooth_select(int fd,struct timeval * timeout,struct gn_statemachine * state)559 int bluetooth_select(int fd, struct timeval *timeout, struct gn_statemachine *state)
560 {
561 	fd_set readfds;
562 
563 	FD_ZERO(&readfds);
564 	FD_SET(fd, &readfds);
565 
566 	return select(fd + 1, &readfds, NULL, NULL, timeout);
567 }
568 
569 #endif	/* HAVE_BLUETOOTH_BLUEZ || HAVE_BLUETOOTH_NETGRAPH || HAVE_BLUETOOTH_NETBT */
570