ciptool.c revision 1d3715db11f573060cb02a2b7b7c44f06607d337
1/*
2 *
3 *  Bluetooth Common ISDN Access Profile (CIP)
4 *
5 *  Copyright (C) 2002-2003  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <stdio.h>
29#include <errno.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <malloc.h>
33#include <getopt.h>
34#include <signal.h>
35#include <sys/poll.h>
36#include <sys/ioctl.h>
37#include <sys/socket.h>
38
39#include <bluetooth/bluetooth.h>
40#include <bluetooth/hci.h>
41#include <bluetooth/hci_lib.h>
42#include <bluetooth/l2cap.h>
43#include <bluetooth/sdp.h>
44#include <bluetooth/sdp_lib.h>
45#include <bluetooth/cmtp.h>
46
47
48static volatile sig_atomic_t __io_canceled = 0;
49
50static void sig_hup(int sig)
51{
52	return;
53}
54
55static void sig_term(int sig)
56{
57	__io_canceled = 1;
58}
59
60static char *cmtp_state[] = {
61	"unknown",
62	"connected",
63	"open",
64	"bound",
65	"listening",
66	"connecting",
67	"connecting",
68	"config",
69	"disconnecting",
70	"closed"
71};
72
73static char *cmtp_flagstostr(uint32_t flags)
74{
75	static char str[100] = "";
76
77	strcat(str, "[");
78
79	if (flags & (1 << CMTP_LOOPBACK))
80		strcat(str, "loopback");
81
82	strcat(str, "]");
83
84	return str;
85}
86
87static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm)
88{
89	sdp_session_t *s;
90	sdp_list_t *srch, *attrs, *rsp;
91	uuid_t svclass;
92	uint16_t attr;
93	int err;
94
95	if (!(s = sdp_connect(src, dst, 0)))
96		return -1;
97
98	sdp_uuid16_create(&svclass, CIP_SVCLASS_ID);
99	srch = sdp_list_append(NULL, &svclass);
100
101	attr = SDP_ATTR_PROTO_DESC_LIST;
102	attrs = sdp_list_append(NULL, &attr);
103
104	err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
105
106	sdp_close(s);
107
108	if (err)
109		return 0;
110
111	for (; rsp; rsp = rsp->next) {
112		sdp_record_t *rec = (sdp_record_t *) rsp->data;
113		sdp_list_t *protos;
114
115		if (!sdp_get_access_protos(rec, &protos)) {
116			unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID);
117			if (p > 0) {
118				*psm = p;
119				return 1;
120			}
121		}
122	}
123
124	return 0;
125}
126
127static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags)
128{
129	struct cmtp_connadd_req req;
130	struct hci_dev_info di;
131	struct sockaddr_l2 addr;
132	struct l2cap_options opts;
133	int sk, size;
134
135	hci_devinfo(dev_id, &di);
136	if (!(di.link_policy & HCI_LP_RSWITCH)) {
137		printf("Local device is not accepting role switch\n");
138	}
139
140	if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
141		perror("Can't create L2CAP socket");
142		exit(1);
143	}
144
145	addr.l2_family = AF_BLUETOOTH;
146	bacpy(&addr.l2_bdaddr, src);
147	addr.l2_psm = 0;
148
149	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
150		perror("Can't bind L2CAP socket");
151		close(sk);
152		exit(1);
153	}
154
155	size = sizeof(opts);
156	if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
157		perror("Can't get L2CAP options");
158		close(sk);
159		exit(1);
160	}
161
162	opts.imtu = CMTP_DEFAULT_MTU;
163	opts.omtu = CMTP_DEFAULT_MTU;
164	opts.flush_to = 0xffff;
165
166	if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
167		perror("Can't set L2CAP options");
168		close(sk);
169		exit(1);
170	}
171
172	addr.l2_family = AF_BLUETOOTH;
173	bacpy(&addr.l2_bdaddr, dst);
174	addr.l2_psm = htobs(psm);
175
176	if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
177		perror("Can't connect L2CAP socket");
178		close(sk);
179		exit(1);
180	}
181
182	req.sock = sk;
183	req.flags = flags;
184
185	if (ioctl(ctl, CMTPCONNADD, &req) < 0) {
186		perror("Can't create connection");
187		exit(1);
188	}
189
190	return sk;
191}
192
193static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
194{
195	struct cmtp_connlist_req req;
196	struct cmtp_conninfo ci[16];
197	char addr[18];
198	int i;
199
200	req.cnum = 16;
201	req.ci   = ci;
202
203	if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) {
204		perror("Can't get connection list");
205		exit(1);
206	}
207
208	for (i = 0; i < req.cnum; i++) {
209		ba2str(&ci[i].bdaddr, addr);
210		printf("%d %s %s %s\n", ci[i].num, addr,
211			cmtp_state[ci[i].state],
212			ci[i].flags ? cmtp_flagstostr(ci[i].flags) : "");
213	}
214}
215
216static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
217{
218	inquiry_info *info = NULL;
219	bdaddr_t src, dst;
220	unsigned short psm;
221	int i, dev_id, num_rsp, length, flags;
222	char addr[18];
223	uint8_t class[3];
224
225	ba2str(bdaddr, addr);
226	dev_id = hci_devid(addr);
227	if (dev_id < 0) {
228		dev_id = hci_get_route(NULL);
229		hci_devba(dev_id, &src);
230	} else
231		bacpy(&src, bdaddr);
232
233	length  = 8;	/* ~10 seconds */
234	num_rsp = 0;
235	flags   = 0;
236
237	printf("Searching ...\n");
238
239	num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
240
241	for (i = 0; i < num_rsp; i++) {
242		memcpy(class, (info+i)->dev_class, 3);
243		if ((class[1] == 2) && ((class[0] / 4) == 5)) {
244			bacpy(&dst, &(info+i)->bdaddr);
245			ba2str(&dst, addr);
246
247			printf("\tChecking service for %s\n", addr);
248			if (!get_psm(&src, &dst, &psm))
249				continue;
250
251			free(info);
252
253			printf("\tConnecting to device %s\n", addr);
254			do_connect(ctl, dev_id, &src, &dst, psm, 0);
255			return;
256		}
257	}
258
259	free(info);
260	fprintf(stderr, "\tNo devices in range or visible\n");
261	exit(1);
262}
263
264static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
265{
266	bdaddr_t src, dst;
267	unsigned short psm;
268	int dev_id;
269	char addr[18];
270
271	if (argc < 2)
272		return;
273
274	str2ba(argv[1], &dst);
275
276	ba2str(bdaddr, addr);
277	dev_id = hci_devid(addr);
278	if (dev_id < 0) {
279		dev_id = hci_get_route(&dst);
280		hci_devba(dev_id, &src);
281	} else
282		bacpy(&src, bdaddr);
283
284	if (argc < 3) {
285		if (!get_psm(&src, &dst, &psm))
286			psm = 4099;
287	} else
288		psm = atoi(argv[2]);
289
290	do_connect(ctl, dev_id, &src, &dst, psm, 0);
291}
292
293static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
294{
295	struct cmtp_conndel_req req;
296	struct cmtp_connlist_req cl;
297	struct cmtp_conninfo ci[16];
298
299	if (argc < 2) {
300		cl.cnum = 16;
301		cl.ci   = ci;
302
303		if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) {
304			perror("Can't get connection list");
305			exit(1);
306		}
307
308		if (cl.cnum == 0)
309			return;
310
311		if (cl.cnum != 1) {
312			fprintf(stderr, "You have to specifiy the device address.\n");
313			exit(1);
314		}
315
316		bacpy(&req.bdaddr, &ci[0].bdaddr);
317	} else
318		str2ba(argv[1], &req.bdaddr);
319
320	if (ioctl(ctl, CMTPCONNDEL, &req) < 0) {
321		perror("Can't release connection");
322		exit(1);
323	}
324}
325
326static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
327{
328	struct cmtp_conndel_req req;
329	struct sigaction sa;
330	struct pollfd p;
331	bdaddr_t src, dst;
332	unsigned short psm;
333	int dev_id, sk;
334	char addr[18];
335
336	if (argc < 2)
337		return;
338
339	str2ba(argv[1], &dst);
340
341	ba2str(bdaddr, addr);
342	dev_id = hci_devid(addr);
343	if (dev_id < 0) {
344		dev_id = hci_get_route(&dst);
345		hci_devba(dev_id, &src);
346	} else
347		bacpy(&src, bdaddr);
348
349	ba2str(&dst, addr);
350	printf("Connecting to %s in loopback mode\n", addr);
351
352	if (argc < 3) {
353		if (!get_psm(&src, &dst, &psm))
354			psm = 4099;
355	} else
356		psm = atoi(argv[2]);
357
358	sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
359
360	printf("Press CTRL-C for hangup\n");
361
362	memset(&sa, 0, sizeof(sa));
363	sa.sa_flags   = SA_NOCLDSTOP;
364	sa.sa_handler = SIG_IGN;
365	sigaction(SIGCHLD, &sa, NULL);
366	sigaction(SIGPIPE, &sa, NULL);
367
368	sa.sa_handler = sig_term;
369	sigaction(SIGTERM, &sa, NULL);
370	sigaction(SIGINT,  &sa, NULL);
371
372	sa.sa_handler = sig_hup;
373	sigaction(SIGHUP, &sa, NULL);
374
375	p.fd = sk;
376	p.events = POLLERR | POLLHUP;
377
378	while (!__io_canceled) {
379		p.revents = 0;
380		if (poll(&p, 1, 100))
381			break;
382	}
383
384	bacpy(&req.bdaddr, &dst);
385	ioctl(ctl, CMTPCONNDEL, &req);
386}
387
388
389static struct {
390	char *cmd;
391	char *alt;
392	void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv);
393	char *opt;
394	char *doc;
395} command[] = {
396	{ "show",     "list",       cmd_show,     0,          "Show remote connections"      },
397	{ "search",   "scan",       cmd_search,   0,          "Search for a remote device"   },
398	{ "connect",  "create",     cmd_create,   "<bdaddr>", "Connect a remote device"      },
399	{ "release",  "disconnect", cmd_release,  "[bdaddr]", "Disconnect the remote device" },
400	{ "loopback", "test",       cmd_loopback, "<bdaddr>", "Loopback test of a device"    },
401	{ NULL, NULL, NULL, 0, 0 }
402};
403
404static void usage(void)
405{
406	int i;
407
408	printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n");
409
410	printf("Usage:\n"
411		"\tciptool [options] [command]\n"
412		"\n");
413
414	printf("Options:\n"
415		"\t-i [hciX|bdaddr]   Local HCI device or BD Address\n"
416		"\t-h, --help         Display help\n"
417		"\n");
418
419	printf("Commands:\n");
420	for (i = 0; command[i].cmd; i++)
421		printf("\t%-8s %-10s\t%s\n", command[i].cmd,
422		command[i].opt ? command[i].opt : " ",
423		command[i].doc);
424	printf("\n");
425}
426
427static struct option main_options[] = {
428	{ "help",	0, 0, 'h' },
429	{ "device",	1, 0, 'i' },
430	{ 0, 0, 0, 0 }
431};
432
433int main(int argc, char *argv[])
434{
435	bdaddr_t bdaddr;
436	int i, opt, ctl;
437
438	bacpy(&bdaddr, BDADDR_ANY);
439
440	while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
441		switch(opt) {
442		case 'i':
443			if (!strncmp(optarg, "hci", 3))
444				hci_devba(atoi(optarg + 3), &bdaddr);
445			else
446				str2ba(optarg, &bdaddr);
447			break;
448		case 'h':
449			usage();
450			exit(0);
451		default:
452			exit(0);
453		}
454	}
455
456	argc -= optind;
457	argv += optind;
458	optind = 0;
459
460	if (argc < 1) {
461		usage();
462		return 0;
463	}
464
465	if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) {
466		perror("Can't open CMTP control socket");
467		exit(1);
468	}
469
470	for (i = 0; command[i].cmd; i++) {
471		if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
472			continue;
473		command[i].func(ctl, &bdaddr, argc, argv);
474		close(ctl);
475		exit(0);
476	}
477
478	usage();
479
480	close(ctl);
481
482	return 0;
483}
484