1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2009-2010 Marcel Holtmann <marcel@holtmann.org>
6 * Copyright (C) 2009-2010 Nokia Corporation
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <signal.h>
30
31 #include <glib.h>
32
33 #include "btio.h"
34
35 #define DEFAULT_ACCEPT_TIMEOUT 2
36
37 struct io_data {
38 guint ref;
39 GIOChannel *io;
40 BtIOType type;
41 gint reject;
42 gint disconn;
43 gint accept;
44 };
45
io_data_unref(struct io_data * data)46 static void io_data_unref(struct io_data *data)
47 {
48 data->ref--;
49
50 if (data->ref)
51 return;
52
53 if (data->io)
54 g_io_channel_unref(data->io);
55
56 g_free(data);
57 }
58
io_data_ref(struct io_data * data)59 static struct io_data *io_data_ref(struct io_data *data)
60 {
61 data->ref++;
62 return data;
63 }
64
io_data_new(GIOChannel * io,BtIOType type,gint reject,gint disconn,gint accept)65 static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
66 gint disconn, gint accept)
67 {
68 struct io_data *data;
69
70 data = g_new0(struct io_data, 1);
71 data->io = io;
72 data->type = type;
73 data->reject = reject;
74 data->disconn = disconn;
75 data->accept = accept;
76
77 return io_data_ref(data);
78 }
79
io_watch(GIOChannel * io,GIOCondition cond,gpointer user_data)80 static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
81 {
82 printf("Disconnected\n");
83 return FALSE;
84 }
85
disconn_timeout(gpointer user_data)86 static gboolean disconn_timeout(gpointer user_data)
87 {
88 struct io_data *data = user_data;
89
90 printf("Disconnecting\n");
91
92 g_io_channel_shutdown(data->io, TRUE, NULL);
93
94 return FALSE;
95 }
96
connect_cb(GIOChannel * io,GError * err,gpointer user_data)97 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
98 {
99 struct io_data *data = user_data;
100 GIOCondition cond;
101 char addr[18];
102 uint16_t handle;
103 uint8_t cls[3];
104
105 if (err) {
106 printf("Connecting failed: %s\n", err->message);
107 return;
108 }
109
110 if (!bt_io_get(io, data->type, &err,
111 BT_IO_OPT_DEST, addr,
112 BT_IO_OPT_HANDLE, &handle,
113 BT_IO_OPT_CLASS, cls,
114 BT_IO_OPT_INVALID)) {
115 printf("Unable to get destination address: %s\n",
116 err->message);
117 g_clear_error(&err);
118 strcpy(addr, "(unknown)");
119 }
120
121 printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
122 addr, handle, cls[0], cls[1], cls[2]);
123
124 if (data->type == BT_IO_L2CAP || data->type == BT_IO_SCO) {
125 uint16_t omtu, imtu;
126
127 if (!bt_io_get(io, data->type, &err,
128 BT_IO_OPT_OMTU, &omtu,
129 BT_IO_OPT_IMTU, &imtu,
130 BT_IO_OPT_INVALID)) {
131 printf("Unable to get L2CAP MTU sizes: %s\n",
132 err->message);
133 g_clear_error(&err);
134 } else
135 printf("imtu=%u, omtu=%u\n", imtu, omtu);
136 }
137
138 if (data->disconn == 0) {
139 g_io_channel_shutdown(io, TRUE, NULL);
140 printf("Disconnected\n");
141 return;
142 }
143
144 if (data->io == NULL)
145 data->io = g_io_channel_ref(io);
146
147 if (data->disconn > 0) {
148 io_data_ref(data);
149 g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
150 disconn_timeout, data,
151 (GDestroyNotify) io_data_unref);
152 }
153
154
155 io_data_ref(data);
156 cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
157 g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
158 (GDestroyNotify) io_data_unref);
159 }
160
confirm_timeout(gpointer user_data)161 static gboolean confirm_timeout(gpointer user_data)
162 {
163 struct io_data *data = user_data;
164
165 if (data->reject >= 0) {
166 printf("Rejecting connection\n");
167 g_io_channel_shutdown(data->io, TRUE, NULL);
168 return FALSE;
169 }
170
171 printf("Accepting connection\n");
172
173 io_data_ref(data);
174
175 if (!bt_io_accept(data->io, connect_cb, data,
176 (GDestroyNotify) io_data_unref, NULL)) {
177 printf("bt_io_accept() failed\n");
178 io_data_unref(data);
179 }
180
181 return FALSE;
182 }
183
confirm_cb(GIOChannel * io,gpointer user_data)184 static void confirm_cb(GIOChannel *io, gpointer user_data)
185 {
186 char addr[18];
187 struct io_data *data = user_data;
188 GError *err = NULL;
189
190 if (!bt_io_get(io, data->type, &err, BT_IO_OPT_DEST, addr,
191 BT_IO_OPT_INVALID)) {
192 printf("bt_io_get(OPT_DEST): %s\n", err->message);
193 g_clear_error(&err);
194 } else
195 printf("Got confirmation request for %s\n", addr);
196
197 if (data->accept < 0 && data->reject < 0)
198 return;
199
200 if (data->reject == 0) {
201 printf("Rejecting connection\n");
202 g_io_channel_shutdown(io, TRUE, NULL);
203 return;
204 }
205
206 data->io = g_io_channel_ref(io);
207 io_data_ref(data);
208
209 if (data->accept == 0) {
210 if (!bt_io_accept(io, connect_cb, data,
211 (GDestroyNotify) io_data_unref,
212 &err)) {
213 printf("bt_io_accept() failed: %s\n", err->message);
214 g_clear_error(&err);
215 io_data_unref(data);
216 return;
217 }
218 } else {
219 gint seconds = (data->reject > 0) ?
220 data->reject : data->accept;
221 g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
222 confirm_timeout, data,
223 (GDestroyNotify) io_data_unref);
224 }
225 }
226
l2cap_connect(const char * src,const char * dst,uint16_t psm,gint disconn,gint sec)227 static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
228 gint disconn, gint sec)
229 {
230 struct io_data *data;
231 GError *err = NULL;
232
233 printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
234
235 data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
236
237 if (src)
238 data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
239 (GDestroyNotify) io_data_unref,
240 &err,
241 BT_IO_OPT_SOURCE, src,
242 BT_IO_OPT_DEST, dst,
243 BT_IO_OPT_PSM, psm,
244 BT_IO_OPT_SEC_LEVEL, sec,
245 BT_IO_OPT_INVALID);
246 else
247 data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
248 (GDestroyNotify) io_data_unref,
249 &err,
250 BT_IO_OPT_DEST, dst,
251 BT_IO_OPT_PSM, psm,
252 BT_IO_OPT_SEC_LEVEL, sec,
253 BT_IO_OPT_INVALID);
254
255 if (!data->io) {
256 printf("Connecting to %s failed: %s\n", dst, err->message);
257 g_error_free(err);
258 exit(EXIT_FAILURE);
259 }
260 }
261
l2cap_listen(const char * src,uint16_t psm,gint defer,gint reject,gint disconn,gint accept,gint sec,gboolean master)262 static void l2cap_listen(const char *src, uint16_t psm, gint defer,
263 gint reject, gint disconn, gint accept,
264 gint sec, gboolean master)
265 {
266 struct io_data *data;
267 BtIOConnect conn;
268 BtIOConfirm cfm;
269 GIOChannel *l2_srv;
270 GError *err = NULL;
271
272 if (defer) {
273 conn = NULL;
274 cfm = confirm_cb;
275 } else {
276 conn = connect_cb;
277 cfm = NULL;
278 }
279
280 printf("Listening on L2CAP PSM %u\n", psm);
281
282 data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
283
284 if (src)
285 l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
286 data, (GDestroyNotify) io_data_unref,
287 &err,
288 BT_IO_OPT_SOURCE, src,
289 BT_IO_OPT_PSM, psm,
290 BT_IO_OPT_SEC_LEVEL, sec,
291 BT_IO_OPT_MASTER, master,
292 BT_IO_OPT_INVALID);
293 else
294 l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
295 data, (GDestroyNotify) io_data_unref,
296 &err,
297 BT_IO_OPT_PSM, psm,
298 BT_IO_OPT_SEC_LEVEL, sec,
299 BT_IO_OPT_MASTER, master,
300 BT_IO_OPT_INVALID);
301
302 if (!l2_srv) {
303 printf("Listening failed: %s\n", err->message);
304 g_error_free(err);
305 exit(EXIT_FAILURE);
306 }
307
308 g_io_channel_unref(l2_srv);
309 }
310
rfcomm_connect(const char * src,const char * dst,uint8_t ch,gint disconn,gint sec)311 static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
312 gint disconn, gint sec)
313 {
314 struct io_data *data;
315 GError *err = NULL;
316
317 printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
318
319 data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
320
321 if (src)
322 data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
323 (GDestroyNotify) io_data_unref,
324 &err,
325 BT_IO_OPT_SOURCE, src,
326 BT_IO_OPT_DEST, dst,
327 BT_IO_OPT_CHANNEL, ch,
328 BT_IO_OPT_SEC_LEVEL, sec,
329 BT_IO_OPT_INVALID);
330 else
331 data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
332 (GDestroyNotify) io_data_unref,
333 &err,
334 BT_IO_OPT_DEST, dst,
335 BT_IO_OPT_CHANNEL, ch,
336 BT_IO_OPT_SEC_LEVEL, sec,
337 BT_IO_OPT_INVALID);
338
339 if (!data->io) {
340 printf("Connecting to %s failed: %s\n", dst, err->message);
341 g_error_free(err);
342 exit(EXIT_FAILURE);
343 }
344 }
345
rfcomm_listen(const char * src,uint8_t ch,gboolean defer,gint reject,gint disconn,gint accept,gint sec,gboolean master)346 static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
347 gint reject, gint disconn, gint accept,
348 gint sec, gboolean master)
349 {
350 struct io_data *data;
351 BtIOConnect conn;
352 BtIOConfirm cfm;
353 GIOChannel *rc_srv;
354 GError *err = NULL;
355
356 if (defer) {
357 conn = NULL;
358 cfm = confirm_cb;
359 } else {
360 conn = connect_cb;
361 cfm = NULL;
362 }
363
364 data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
365
366 if (src)
367 rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
368 data, (GDestroyNotify) io_data_unref,
369 &err,
370 BT_IO_OPT_SOURCE, src,
371 BT_IO_OPT_CHANNEL, ch,
372 BT_IO_OPT_SEC_LEVEL, sec,
373 BT_IO_OPT_MASTER, master,
374 BT_IO_OPT_INVALID);
375 else
376 rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
377 data, (GDestroyNotify) io_data_unref,
378 &err,
379 BT_IO_OPT_CHANNEL, ch,
380 BT_IO_OPT_SEC_LEVEL, sec,
381 BT_IO_OPT_MASTER, master,
382 BT_IO_OPT_INVALID);
383
384 if (!rc_srv) {
385 printf("Listening failed: %s\n", err->message);
386 g_error_free(err);
387 exit(EXIT_FAILURE);
388 }
389
390 bt_io_get(rc_srv, BT_IO_RFCOMM, &err,
391 BT_IO_OPT_CHANNEL, &ch,
392 BT_IO_OPT_INVALID);
393
394 printf("Listening on RFCOMM channel %u\n", ch);
395
396 g_io_channel_unref(rc_srv);
397 }
398
sco_connect(const char * src,const char * dst,gint disconn)399 static void sco_connect(const char *src, const char *dst, gint disconn)
400 {
401 struct io_data *data;
402 GError *err = NULL;
403
404 printf("Connecting SCO to %s\n", dst);
405
406 data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
407
408 if (src)
409 data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
410 (GDestroyNotify) io_data_unref,
411 &err,
412 BT_IO_OPT_SOURCE, src,
413 BT_IO_OPT_DEST, dst,
414 BT_IO_OPT_INVALID);
415 else
416 data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
417 (GDestroyNotify) io_data_unref,
418 &err,
419 BT_IO_OPT_DEST, dst,
420 BT_IO_OPT_INVALID);
421
422 if (!data->io) {
423 printf("Connecting to %s failed: %s\n", dst, err->message);
424 g_error_free(err);
425 exit(EXIT_FAILURE);
426 }
427 }
428
sco_listen(const char * src,gint disconn)429 static void sco_listen(const char *src, gint disconn)
430 {
431 struct io_data *data;
432 GIOChannel *sco_srv;
433 GError *err = NULL;
434
435 printf("Listening for SCO connections\n");
436
437 data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
438
439 if (src)
440 sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
441 data, (GDestroyNotify) io_data_unref,
442 &err,
443 BT_IO_OPT_SOURCE, src,
444 BT_IO_OPT_INVALID);
445 else
446 sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
447 data, (GDestroyNotify) io_data_unref,
448 &err, BT_IO_OPT_INVALID);
449
450 if (!sco_srv) {
451 printf("Listening failed: %s\n", err->message);
452 g_error_free(err);
453 exit(EXIT_FAILURE);
454 }
455
456 g_io_channel_unref(sco_srv);
457 }
458
459 static gint opt_channel = -1;
460 static gint opt_psm = 0;
461 static gboolean opt_sco = FALSE;
462 static gboolean opt_defer = FALSE;
463 static char *opt_dev = NULL;
464 static gint opt_reject = -1;
465 static gint opt_disconn = -1;
466 static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
467 static gint opt_sec = 0;
468 static gboolean opt_master = FALSE;
469
470 static GMainLoop *main_loop;
471
472 static GOptionEntry options[] = {
473 { "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
474 "RFCOMM channel" },
475 { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
476 "L2CAP PSM" },
477 { "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
478 "Use SCO" },
479 { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
480 "Use DEFER_SETUP for incoming connections" },
481 { "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
482 "Security level" },
483 { "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
484 "Which HCI device to use" },
485 { "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
486 "Reject connection after N seconds" },
487 { "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
488 "Disconnect connection after N seconds" },
489 { "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
490 "Accept connection after N seconds" },
491 { "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
492 "Master role switch (incoming connections)" },
493 { NULL },
494 };
495
sig_term(int sig)496 static void sig_term(int sig)
497 {
498 g_main_loop_quit(main_loop);
499 }
500
main(int argc,char * argv[])501 int main(int argc, char *argv[])
502 {
503 GOptionContext *context;
504
505 context = g_option_context_new(NULL);
506 g_option_context_add_main_entries(context, options, NULL);
507
508 if (!g_option_context_parse(context, &argc, &argv, NULL))
509 exit(EXIT_FAILURE);
510
511 g_option_context_free(context);
512
513 printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d\n",
514 opt_accept, opt_reject, opt_disconn, opt_defer, opt_sec);
515
516 if (opt_psm) {
517 if (argc > 1)
518 l2cap_connect(opt_dev, argv[1], opt_psm,
519 opt_disconn, opt_sec);
520 else
521 l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
522 opt_disconn, opt_accept, opt_sec,
523 opt_master);
524 }
525
526 if (opt_channel != -1) {
527 if (argc > 1)
528 rfcomm_connect(opt_dev, argv[1], opt_channel,
529 opt_disconn, opt_sec);
530 else
531 rfcomm_listen(opt_dev, opt_channel, opt_defer,
532 opt_reject, opt_disconn, opt_accept,
533 opt_sec, opt_master);
534 }
535
536 if (opt_sco) {
537 if (argc > 1)
538 sco_connect(opt_dev, argv[1], opt_disconn);
539 else
540 sco_listen(opt_dev, opt_disconn);
541 }
542
543 signal(SIGTERM, sig_term);
544 signal(SIGINT, sig_term);
545
546 main_loop = g_main_loop_new(NULL, FALSE);
547
548 g_main_loop_run(main_loop);
549
550 g_main_loop_unref(main_loop);
551
552 printf("Exiting\n");
553
554 exit(EXIT_SUCCESS);
555 }
556