From 965c347ab154fd36ca98cd1fd29b048068d70dab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 25 Apr 2004 21:54:11 +0000 Subject: Add the pand utility --- Makefile.am | 2 +- configure.in | 2 +- pand/Makefile.am | 15 ++ pand/bnep.c | 305 ++++++++++++++++++++++++++++ pand/main.c | 604 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ pand/pand.1 | 59 ++++++ pand/pand.h | 44 ++++ pand/sdp.c | 201 ++++++++++++++++++ 8 files changed, 1230 insertions(+), 2 deletions(-) create mode 100644 pand/Makefile.am create mode 100644 pand/bnep.c create mode 100644 pand/main.c create mode 100644 pand/pand.1 create mode 100644 pand/pand.h create mode 100644 pand/sdp.c diff --git a/Makefile.am b/Makefile.am index 1824ac2d..6a87b06c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ # $Id$ # -SUBDIRS := hcid tools rfcomm sdpd test scripts pcmcia +SUBDIRS := hcid tools rfcomm sdpd pand test scripts pcmcia DISTCLEANFILES = conftest.c conftest diff --git a/configure.in b/configure.in index c9c11be5..c2868a9d 100644 --- a/configure.in +++ b/configure.in @@ -25,4 +25,4 @@ AC_PATH_BLUEZ AC_PATH_DBUS -AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) +AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile pand/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) diff --git a/pand/Makefile.am b/pand/Makefile.am new file mode 100644 index 00000000..20b99270 --- /dev/null +++ b/pand/Makefile.am @@ -0,0 +1,15 @@ +# +# $Id$ +# + +bin_PROGRAMS = pand + +pand_SOURCES = main.c pand.h bnep.c sdp.c + +LDFLAGS = @BLUEZ_LIBS@ + +INCLUDES = @BLUEZ_INCLUDES@ + +man_MANS = pand.1 + +EXTRA_DIST = $(man_MANS) diff --git a/pand/bnep.c b/pand/bnep.c new file mode 100644 index 00000000..6833f941 --- /dev/null +++ b/pand/bnep.c @@ -0,0 +1,305 @@ +/* + pand - Bluetooth PAN daemon for BlueZ + Copyright (C) 2002 Maxim Krasnyansky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * $Id$ + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "pand.h" + +static int ctl; + +/* Compatibility with old ioctls */ +#define OLD_BNEPCONADD 1 +#define OLD_BNEPCONDEL 2 +#define OLD_BNEPGETCONLIST 3 +#define OLD_BNEPGETCONINFO 4 + +static unsigned long bnepconnadd; +static unsigned long bnepconndel; +static unsigned long bnepgetconnlist; +static unsigned long bnepgetconninfo; + +static struct { + char *str; + uint16_t uuid; +} __svc[] = { + { "PANU", BNEP_SVC_PANU }, + { "NAP", BNEP_SVC_NAP }, + { "GN", BNEP_SVC_GN }, + { NULL } +}; + +int bnep_str2svc(char *svc, uint16_t *uuid) +{ + int i; + for (i=0; __svc[i].str; i++) + if (!strcasecmp(svc, __svc[i].str)) { + *uuid = __svc[i].uuid; + return 0; + } + return -1; +} + +char *bnep_svc2str(uint16_t uuid) +{ + int i; + for (i=0; __svc[i].str; i++) + if (__svc[i].uuid == uuid) + return __svc[i].str; + return NULL; +} + +int bnep_init(void) +{ + ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); + if (ctl < 0) { + perror("Failed to open control socket"); + return 1; + } + + /* Temporary ioctl compatibility hack */ + { + struct bnep_connlist_req req; + struct bnep_conninfo ci[1]; + + req.cnum = 1; + req.ci = ci; + + if (!ioctl(ctl, BNEPGETCONNLIST, &req)) { + /* New ioctls */ + bnepconnadd = BNEPCONNADD; + bnepconndel = BNEPCONNDEL; + bnepgetconnlist = BNEPGETCONNLIST; + bnepgetconninfo = BNEPGETCONNINFO; + } else { + /* Old ioctls */ + bnepconnadd = OLD_BNEPCONADD; + bnepconndel = OLD_BNEPCONDEL; + bnepgetconnlist = OLD_BNEPGETCONLIST; + bnepgetconninfo = OLD_BNEPGETCONINFO; + } + } + + return 0; +} + +int bnep_cleanup(void) +{ + close(ctl); + return 0; +} + +int bnep_show_connections(void) +{ + struct bnep_connlist_req req; + struct bnep_conninfo ci[48]; + int i; + + req.cnum = 48; + req.ci = ci; + if (ioctl(ctl, bnepgetconnlist, &req)) { + perror("Failed to get connection list"); + return -1; + } + + for (i=0; i < req.cnum; i++) { + printf("%s %s %s\n", ci[i].device, + batostr((bdaddr_t *) ci[i].dst), + bnep_svc2str(ci[i].role)); + } + return 0; +} + +int bnep_kill_connection(uint8_t *dst) +{ + struct bnep_conndel_req req; + + memcpy(req.dst, dst, ETH_ALEN); + req.flags = 0; + if (ioctl(ctl, bnepconndel, &req)) { + perror("Failed to kill connection"); + return -1; + } + return 0; +} + +int bnep_kill_all_connections(void) +{ + struct bnep_connlist_req req; + struct bnep_conninfo ci[48]; + int i; + + req.cnum = 48; + req.ci = ci; + if (ioctl(ctl, bnepgetconnlist, &req)) { + perror("Failed to get connection list"); + return -1; + } + + for (i=0; i < req.cnum; i++) { + struct bnep_conndel_req req; + memcpy(req.dst, ci[i].dst, ETH_ALEN); + req.flags = 0; + ioctl(ctl, bnepconndel, &req); + } + return 0; +} + +static int bnep_connadd(int sk, uint16_t role, char *dev) +{ + struct bnep_connadd_req req; + + strcpy(req.device, dev); + req.sock = sk; + req.role = role; + if (ioctl(ctl, bnepconnadd, &req)) + return -1; + strcpy(dev, req.device); + return 0; +} + +struct __service_16 { + uint16_t dst; + uint16_t src; +} __attribute__ ((packed)); + +struct __service_32 { + uint16_t unused1; + uint16_t dst; + uint16_t unused2; + uint16_t src; +} __attribute__ ((packed)); + +struct __service_128 { + uint16_t unused1; + uint16_t dst; + uint16_t unused2[8]; + uint16_t src; + uint16_t unused3[7]; +} __attribute__ ((packed)); + +int bnep_accept_connection(int sk, uint16_t role, char *dev) +{ + struct bnep_setup_conn_req *req; + struct bnep_control_rsp *rsp; + unsigned char pkt[BNEP_MTU]; + int r; + + r = recv(sk, pkt, BNEP_MTU, 0); + if (r <= 0) + return -1; + + errno = EPROTO; + + if (r < sizeof(*req)) + return -1; + + req = (void *) pkt; + if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) + return -1; + + /* FIXME: Check role UUIDs */ + + rsp = (void *) pkt; + rsp->type = BNEP_CONTROL; + rsp->ctrl = BNEP_SETUP_CONN_RSP; + rsp->resp = htons(BNEP_SUCCESS); + if (send(sk, rsp, sizeof(*rsp), 0) < 0) + return -1; + + return bnep_connadd(sk, role, dev); +} + +/* Create BNEP connection + * sk - Connect L2CAP socket + * role - Local role + * service - Remote service + * dev - Network device (contains actual dev name on return) + */ +int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev) +{ + struct bnep_setup_conn_req *req; + struct bnep_control_rsp *rsp; + struct __service_16 *s; + unsigned char pkt[BNEP_MTU]; + int r; + + /* Send request */ + req = (void *) pkt; + req->type = BNEP_CONTROL; + req->ctrl = BNEP_SETUP_CONN_REQ; + req->uuid_size = 2; //16bit UUID + s = (void *) req->service; + s->dst = htons(svc); + s->src = htons(role); + + if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0) + return -1; + +receive: + /* Get response */ + r = recv(sk, pkt, BNEP_MTU, 0); + if (r <= 0) + return -1; + + errno = EPROTO; + + if (r < sizeof(*rsp)) + return -1; + + rsp = (void *) pkt; + if (rsp->type != BNEP_CONTROL) + return -1; + + if (rsp->ctrl != BNEP_SETUP_CONN_RSP) + goto receive; + + r = ntohs(rsp->resp); + + switch (r) { + case BNEP_SUCCESS: + break; + + case BNEP_CONN_INVALID_DST: + case BNEP_CONN_INVALID_SRC: + case BNEP_CONN_INVALID_SVC: + errno = EPROTO; + return -1; + + case BNEP_CONN_NOT_ALLOWED: + errno = EACCES; + return -1; + } + + return bnep_connadd(sk, role, dev); +} diff --git a/pand/main.c b/pand/main.c new file mode 100644 index 00000000..91a9a92c --- /dev/null +++ b/pand/main.c @@ -0,0 +1,604 @@ +/* + pand - Bluetooth PAN daemon for BlueZ + Copyright (C) 2002 Maxim Krasnyansky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "pand.h" + +static uint16_t role = BNEP_SVC_PANU; // Local role (ie service) +static uint16_t service = BNEP_SVC_NAP; // Remote service + +static int detach = 1; +static int persist; +static int use_sdp = 1; +static int use_cache; +static int encrypt; +static int master; +static int search_duration = 10; + +static struct { + int valid; + char dst[40]; + bdaddr_t bdaddr; +} cache; + +static char netdev[16] = "bnep%d"; + +static bdaddr_t src_addr = *BDADDR_ANY; +static int src_dev = -1; + +volatile int terminate; + +enum { + NONE, + SHOW, + LISTEN, + CONNECT, + KILL +} modes; + +static void run_devup(char *dev, char *dst) +{ + char *argv[4], prog[40]; + + sprintf(prog, "%s/%s", PAND_CONFIG_DIR, PAND_DEVUP_CMD); + + if (access(prog, R_OK | X_OK)) + return; + + if (fork()) + return; + + argv[0] = prog; + argv[1] = dev; + argv[2] = dst; + argv[3] = NULL; + execv(prog, argv); + exit(1); +} + +static int do_listen(void) +{ + struct l2cap_options l2o; + struct sockaddr_l2 l2a; + int sk, olen, lm; + + if (use_sdp) + bnep_sdp_register(role); + + // Create L2CAP socket and bind it to PSM BNEP + sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", + strerror(errno), errno); + return -1; + } + + l2a.l2_family = AF_BLUETOOTH; + l2a.l2_psm = htobs(BNEP_PSM); + l2a.l2_bdaddr = src_addr; + + if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) { + syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno); + return -1; + } + + /* Setup L2CAP options according to BNEP spec */ + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) { + syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)", + strerror(errno), errno); + return -1; + } + + l2o.imtu = l2o.omtu = BNEP_MTU; + if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { + syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)", + strerror(errno), errno); + return -1; + } + + /* Set link mode */ + lm = 0; + if (master) + lm |= L2CAP_LM_MASTER; + + if (encrypt) + lm |= L2CAP_LM_ENCRYPT; + + if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { + syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno); + return -1; + } + + listen(sk, 10); + + while (!terminate) { + int alen = sizeof(l2a); + int nsk; + nsk = accept(sk, (struct sockaddr *) &l2a, &alen); + if (nsk < 0) { + syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno); + continue; + } + + switch (fork()) { + case 0: + break; + case -1: + syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno); + default: + close(nsk); + continue; + } + + if (!bnep_accept_connection(nsk, role, netdev)) { + char str[40]; + ba2str(&l2a.l2_bdaddr, str); + + syslog(LOG_INFO, "New connection from %s %s", str, netdev); + + run_devup(netdev, str); + } else { + syslog(LOG_ERR, "Connection failed. %s(%d)", + strerror(errno), errno); + } + + close(nsk); + exit(0); + } + + if (use_sdp) + bnep_sdp_unregister(); + return 0; +} + +/* Wait for disconnect or error condition on the socket */ +static int w4_hup(int sk) +{ + struct pollfd pf; + int n; + + while (!terminate) { + pf.fd = sk; + pf.events = POLLERR | POLLHUP; + n = poll(&pf, 1, -1); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + syslog(LOG_ERR, "Poll failed. %s(%d)", + strerror(errno), errno); + return 1; + } + + if (n) { + int err = 0, olen = sizeof(err); + getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen); + syslog(LOG_INFO, "%s disconnected%s%s", netdev, + err ? " : " : "", err ? strerror(err) : ""); + + close(sk); + return 0; + } + } + return 0; +} + +/* Connect and initiate BNEP session + * Returns: + * -1 - critical error (exit persist mode) + * 1 - non critical error + * 0 - success + */ +static int create_connection(char *dst, bdaddr_t *bdaddr) +{ + struct l2cap_options l2o; + struct sockaddr_l2 l2a; + int sk, olen, r = 0; + + syslog(LOG_INFO, "Connecting to %s", dst); + + sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", + strerror(errno), errno); + return -1; + } + + /* Setup L2CAP options according to BNEP spec */ + getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen); + l2o.imtu = l2o.omtu = BNEP_MTU; + setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)); + + l2a.l2_family = AF_BLUETOOTH; + + /* Set local address */ + l2a.l2_psm = 0; + l2a.l2_bdaddr = src_addr; + + if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) + syslog(LOG_ERR, "Bind failed. %s(%d)", + strerror(errno), errno); + + l2a.l2_psm = htobs(BNEP_PSM); + l2a.l2_bdaddr = *bdaddr; + + if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) && + !bnep_create_connection(sk, role, service, netdev)) { + + syslog(LOG_INFO, "%s connected", netdev); + + run_devup(netdev, dst); + + if (persist) + w4_hup(sk); + + r = 0; + } else { + syslog(LOG_ERR, "Connect to %s failed. %s(%d)", + dst, strerror(errno), errno); + r = 1; + } + + close(sk); + + if (use_cache) { + if (!r) { + /* Succesesful connection, validate cache */ + strcpy(cache.dst, dst); + bacpy(&cache.bdaddr, bdaddr); + cache.valid = use_cache; + } else + cache.valid--; + } + + return r; +} + +/* Search and connect + * Returns: + * -1 - critical error (exit persist mode) + * 1 - non critical error + * 0 - success + */ +static int do_connect(void) +{ + inquiry_info *ii; + int reconnect = 0; + int i, n, r = 0; + + do { + if (reconnect) + sleep(persist); + reconnect = 1; + + if (cache.valid > 0) { + /* Use cached bdaddr */ + r = create_connection(cache.dst, &cache.bdaddr); + if (r < 0) { + terminate = 1; + break; + } + continue; + } + + syslog(LOG_INFO, "Inquiring"); + + /* FIXME: Should we use non general LAP here ? */ + + ii = NULL; + n = hci_inquiry(src_dev, search_duration, 10, NULL, &ii, 0); + if (n < 0) { + syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno); + continue; + } + + for (i = 0; i < n; i++) { + char dst[40]; + ba2str(&ii[i].bdaddr, dst); + + if (use_sdp) { + syslog(LOG_INFO, "Searching for %s on %s", + bnep_svc2str(service), dst); + + if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0) + continue; + } + + r = create_connection(dst, &ii[i].bdaddr); + if (r < 0) { + terminate = 1; + break; + } + } + free(ii); + } while (!terminate && persist); + + return r; +} + +static void do_show(void) +{ + bnep_show_connections(); +} + +static void do_kill(char *dst) +{ + if (dst) + bnep_kill_connection((void *) strtoba(dst)); + else + bnep_kill_all_connections(); +} + +void sig_hup(int sig) +{ + return; +} + +void sig_term(int sig) +{ + terminate = 1; +} + +static struct option main_lopts[] = { + { "help", 0, 0, 'h' }, + { "listen", 0, 0, 's' }, + { "connect", 1, 0, 'c' }, + { "search", 2, 0, 'Q' }, + { "kill", 1, 0, 'k' }, + { "killall", 0, 0, 'K' }, + { "role", 1, 0, 'r' }, + { "service", 1, 0, 'd' }, + { "device", 1, 0, 'i' }, + { "source", 1, 0, 'S' }, + { "nosdp", 0, 0, 'D' }, + { "list", 0, 0, 'l' }, + { "show", 0, 0, 'l' }, + { "nodetach", 0, 0, 'n' }, + { "persist", 2, 0, 'p' }, + { "encrypt", 0, 0, 'E' }, + { "master", 0, 0, 'M' }, + { "cache", 0, 0, 'C' }, + { 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:i:S:lnp::DQ::EMC::"; + +static char main_help[] = + "PAN daemon version " VERSION " \n" + "Usage:\n" + "\tpand \n" + "Options:\n" + "\t--show --list -l Show active PAN connections\n" + "\t--listen -s Listen for PAN connections\n" + "\t--connect -c Create PAN connection\n" + "\t--search -Q[duration] Search and connect\n" + "\t--kill -k Kill PAN connection\n" + "\t--killall -K Kill all PAN connections\n" + "\t--role -r Local PAN role (PANU, NAP, GN)\n" + "\t--service -d Remote PAN service (PANU, NAP, GN)\n" + "\t--device -i Network interface name\n" + "\t--source -S Source bdaddr\n" + "\t--nosdp -D Disable SDP\n" + "\t--encrypt -E Enable encryption\n" + "\t--master -M Become the master of a piconet\n" + "\t--nodetach -n Do not become a daemon\n" + "\t--persist -p[interval] Persist mode\n" + "\t--cache -C[valid] Cache addresses\n"; + +int main(int argc, char **argv) +{ + char *dst = NULL, *src = NULL; + struct sigaction sa; + int mode = NONE; + int opt; + + while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { + switch(opt) { + case 'l': + mode = SHOW; + detach = 0; + break; + + case 's': + mode = LISTEN; + break; + + case 'c': + mode = CONNECT; + dst = strdup(optarg); + break; + + case 'Q': + mode = CONNECT; + dst = NULL; + if (optarg) + search_duration = atoi(optarg); + break; + + case 'k': + mode = KILL; + detach = 0; + dst = strdup(optarg); + break; + + case 'K': + mode = KILL; + detach = 0; + dst = NULL; + break; + + case 'S': + src = strdup(optarg); + break; + + case 'r': + bnep_str2svc(optarg, &role); + break; + + case 'd': + bnep_str2svc(optarg, &service); + break; + + case 'D': + use_sdp = 0; + break; + + case 'E': + encrypt = 1; + break; + + case 'M': + master = 1; + break; + + case 'i': + strcpy(netdev, optarg); + break; + + case 'n': + detach = 0; + break; + + case 'p': + if (optarg) + persist = atoi(optarg); + else + persist = 5; + break; + + case 'C': + if (optarg) + use_cache = atoi(optarg); + else + use_cache = 2; + break; + + case 'h': + default: + printf(main_help); + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (bnep_init()) + return -1; + + /* Check non daemon modes first */ + switch (mode) { + case SHOW: + do_show(); + return 0; + + case KILL: + do_kill(dst); + return 0; + + case NONE: + printf(main_help); + return 0; + } + + /* Initialize signals */ + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + if (detach) { + if (fork()) exit(0); + + /* Direct stdin,stdout,stderr to '/dev/null' */ + { + int fd = open("/dev/null", O_RDWR); + dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); + close(fd); + } + + setsid(); + chdir("/"); + } + + openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); + syslog(LOG_INFO, "PAN daemon ver %s", VERSION); + + if (src) { + src_dev = hci_devid(src); + if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { + syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno); + return -1; + } + } + + if (dst) { + /* Disable cache invalidation */ + use_cache = 0; + + strncpy(cache.dst, dst, sizeof(cache.dst) - 1); + str2ba(dst, &cache.bdaddr); + cache.valid = 1; + } + + switch (mode) { + case CONNECT: + do_connect(); + break; + + case LISTEN: + do_listen(); + break; + } + + return 0; +} diff --git a/pand/pand.1 b/pand/pand.1 new file mode 100644 index 00000000..a11a33c0 --- /dev/null +++ b/pand/pand.1 @@ -0,0 +1,59 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. +.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands" +.SH NAME +pand \- BlueZ Bluetooth PAN daemon +.SH DESCRIPTION +The pand PAN daemon allows your computer to connect to ethernet +networks using Bluetooth. +.SH SYNPOSIS +pand +.SH OPTIONS +.TP +\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR +Show active PAN connections +.TP +\fB\-\-listen\fR \fB\-s\fR +Listen for PAN connections +.TP +\fB\-\-connect\fR \fB\-c\fR +Create PAN connection +.TP +\fB\-\-search\fR \fB\-Q[duration]\fR +Search and connect +.TP +\fB\-\-kill\fR \fB\-k\fR +Kill PAN connection +.TP +\fB\-\-killall\fR \fB\-K\fR +Kill all PAN connections +.TP +\fB\-\-role\fR \fB\-r\fR +Local PAN role (PANU, NAP, GN) +.TP +\fB\-\-service\fR \fB\-d\fR +Remote PAN service (PANU, NAP, GN) +.TP +\fB\-\-device\fR \fB\-i\fR +Network interface name +.TP +\fB\-\-source\fR \fB\-S\fR +Source bdaddr +.TP +\fB\-\-nosdp\fR \fB\-D\fR +Disable SDP +.TP +\fB\-\-encrypt\fR \fB\-E\fR +Enable encryption +.TP +\fB\-\-master\fR \fB\-M\fR +Become the master of a piconet +.TP +\fB\-\-nodetach\fR \fB\-n\fR +Do not become a daemon +.TP +\fB\-\-persist\fR \fB\-p[interval]\fR +Persist mode +.TP +\fB\-\-cache\fR \fB\-C[valid]\fR +Cache addresses + diff --git a/pand/pand.h b/pand/pand.h new file mode 100644 index 00000000..268316de --- /dev/null +++ b/pand/pand.h @@ -0,0 +1,44 @@ +/* + pand - Bluetooth PAN daemon for BlueZ + Copyright (C) 2002 Maxim Krasnyansky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * $Id$ + */ + +/* PAN scripts & commands */ +#define PAND_CONFIG_DIR "/etc/bluetooth/pan" +#define PAND_DEVUP_CMD "dev-up" + +/* BNEP functions */ +int bnep_init(void); +int bnep_cleanup(void); + +int bnep_str2svc(char *svc, uint16_t *uuid); +char *bnep_svc2str(uint16_t uuid); + +int bnep_show_connections(void); +int bnep_kill_connection(uint8_t *dst); +int bnep_kill_all_connections(void); + +int bnep_accept_connection(int sk, uint16_t role, char *dev); +int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev); + +/* SDP functions */ +int bnep_sdp_register(uint16_t role); +void bnep_sdp_unregister(void); +int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service); diff --git a/pand/sdp.c b/pand/sdp.c new file mode 100644 index 00000000..16f092d9 --- /dev/null +++ b/pand/sdp.c @@ -0,0 +1,201 @@ +/* + pand - Bluetooth PAN daemon for BlueZ + Copyright (C) 2002 Maxim Krasnyansky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pand.h" + +static sdp_record_t *record; +static sdp_session_t *session; + +void bnep_sdp_unregister(void) +{ + if (record && sdp_record_unregister(session, record)) + syslog(LOG_ERR, "Service record unregistration failed."); + + sdp_close(session); +} + +int bnep_sdp_register(uint16_t role) +{ + sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; + uuid_t root_uuid, pan, l2cap, bnep; + sdp_profile_desc_t profile[1]; + sdp_list_t *proto[2]; + uint16_t psm = 15, version = 0x0100; + int status; + + session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); + if (!session) { + syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)", + strerror(errno), errno); + return -1; + } + + record = sdp_record_alloc(); + if (!record) { + syslog(LOG_ERR, "Failed to allocate service record %s(%d)", + strerror(errno), errno); + sdp_close(session); + return -1; + } + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm)); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&bnep, BNEP_UUID); + proto[1] = sdp_list_append(NULL, &bnep); + proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT16, &version)); + + /* Supported protocols */ + { + uint16_t ptype[4] = { + 0x0800, /* IPv4 */ + 0x0806, /* ARP */ + }; + sdp_data_t *head, *pseq; + int p; + + for (p = 0, head = NULL; p < 2; p++) { + sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]); + if (head) + sdp_seq_append(head, data); + else + head = data; + } + pseq = sdp_data_alloc(SDP_SEQ16, head); + proto[1] = sdp_list_append(proto[1], pseq); + } + + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + switch (role) { + case BNEP_SVC_NAP: + sdp_uuid16_create(&pan, NAP_SVCLASS_ID); + svclass = sdp_list_append(NULL, &pan); + sdp_set_service_classes(record, svclass); + + sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + sdp_set_info_attr(record, "Network Access Point", NULL, NULL); + break; + + case BNEP_SVC_GN: + sdp_uuid16_create(&pan, GN_SVCLASS_ID); + svclass = sdp_list_append(NULL, &pan); + sdp_set_service_classes(record, svclass); + + sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + sdp_set_info_attr(record, "Group Network Service", NULL, NULL); + break; + + case BNEP_SVC_PANU: + sdp_uuid16_create(&pan, PANU_SVCLASS_ID); + svclass = sdp_list_append(NULL, &pan); + sdp_set_service_classes(record, svclass); + + sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + sdp_set_info_attr(record, "PAN User", NULL, NULL); + break; + } + + status = sdp_record_register(session, record, 0); + if (status) { + syslog(LOG_ERR, "SDP registration failed."); + sdp_record_free(record); record = NULL; + sdp_close(session); + return -1; + } + return 0; +} + +// Search for PAN service. +// Returns 1 if service is found and 0 otherwise. +int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service) +{ + sdp_list_t *srch, *rsp = NULL; + sdp_session_t *s; + uuid_t svclass; + int err; + + switch (service) { + case BNEP_SVC_PANU: + sdp_uuid16_create(&svclass, PANU_SVCLASS_ID); + break; + case BNEP_SVC_NAP: + sdp_uuid16_create(&svclass, NAP_SVCLASS_ID); + break; + case BNEP_SVC_GN: + sdp_uuid16_create(&svclass, GN_SVCLASS_ID); + break; + } + + srch = sdp_list_append(NULL, &svclass); + + s = sdp_connect(src, dst, 0); + if (!s) { + syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)", + strerror(errno), errno); + return 0; + } + + err = sdp_service_search_req(s, srch, 1, &rsp); + sdp_close(s); + + /* Assume that search is successeful + * if at least one record is found */ + if (!err && sdp_list_len(rsp)) + return 1; + + return 0; +} -- cgit