Index: head/lib/libbluetooth/bluetooth.c =================================================================== --- head/lib/libbluetooth/bluetooth.c (revision 281209) +++ head/lib/libbluetooth/bluetooth.c (revision 281210) @@ -1,371 +1,371 @@ /* * bluetooth.c */ /*- * Copyright (c) 2001-2009 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bluetooth.c,v 1.3 2003/05/20 23:04:30 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include #define _PATH_BT_HOSTS "/etc/bluetooth/hosts" #define _PATH_BT_PROTOCOLS "/etc/bluetooth/protocols" #define MAXALIASES 35 static FILE *hostf = NULL; static int host_stayopen = 0; static struct hostent host; static bdaddr_t host_addr; static char *host_addr_ptrs[2]; static char *host_aliases[MAXALIASES]; static FILE *protof = NULL; static int proto_stayopen = 0; static struct protoent proto; static char *proto_aliases[MAXALIASES]; static char buf[BUFSIZ + 1]; static int bt_hex_byte (char const *str); static int bt_hex_nibble (char nibble); struct hostent * bt_gethostbyname(char const *name) { struct hostent *p; char **cp; bt_sethostent(host_stayopen); while ((p = bt_gethostent()) != NULL) { if (strcasecmp(p->h_name, name) == 0) break; for (cp = p->h_aliases; *cp != 0; cp++) if (strcasecmp(*cp, name) == 0) goto found; } found: bt_endhostent(); return (p); } struct hostent * bt_gethostbyaddr(char const *addr, int len, int type) { struct hostent *p; if (type != AF_BLUETOOTH || len != sizeof(bdaddr_t)) { h_errno = NO_RECOVERY; return (NULL); } bt_sethostent(host_stayopen); while ((p = bt_gethostent()) != NULL) if (p->h_addrtype == type && bcmp(p->h_addr, addr, len) == 0) break; bt_endhostent(); return (p); } struct hostent * bt_gethostent(void) { char *p, *cp, **q; if (hostf == NULL) hostf = fopen(_PATH_BT_HOSTS, "r"); if (hostf == NULL) { h_errno = NETDB_INTERNAL; return (NULL); } again: if ((p = fgets(buf, sizeof(buf), hostf)) == NULL) { h_errno = HOST_NOT_FOUND; return (NULL); } if (*p == '#') goto again; if ((cp = strpbrk(p, "#\n")) == NULL) goto again; *cp = 0; if ((cp = strpbrk(p, " \t")) == NULL) goto again; *cp++ = 0; if (bt_aton(p, &host_addr) == 0) goto again; host_addr_ptrs[0] = (char *) &host_addr; host_addr_ptrs[1] = NULL; host.h_addr_list = host_addr_ptrs; host.h_length = sizeof(host_addr); host.h_addrtype = AF_BLUETOOTH; while (*cp == ' ' || *cp == '\t') cp++; host.h_name = cp; q = host.h_aliases = host_aliases; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = 0; while (cp != NULL && *cp != 0) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &host_aliases[MAXALIASES - 1]) *q++ = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = 0; } *q = NULL; h_errno = NETDB_SUCCESS; return (&host); } void bt_sethostent(int stayopen) { if (hostf == NULL) hostf = fopen(_PATH_BT_HOSTS, "r"); else rewind(hostf); host_stayopen = stayopen; } void bt_endhostent(void) { if (hostf != NULL && host_stayopen == 0) { (void) fclose(hostf); hostf = NULL; } } struct protoent * bt_getprotobyname(char const *name) { struct protoent *p; char **cp; bt_setprotoent(proto_stayopen); while ((p = bt_getprotoent()) != NULL) { if (strcmp(p->p_name, name) == 0) break; for (cp = p->p_aliases; *cp != 0; cp++) if (strcmp(*cp, name) == 0) goto found; } found: bt_endprotoent(); return (p); } struct protoent * bt_getprotobynumber(int proto) { struct protoent *p; bt_setprotoent(proto_stayopen); while ((p = bt_getprotoent()) != NULL) if (p->p_proto == proto) break; bt_endprotoent(); return (p); } struct protoent * bt_getprotoent(void) { char *p, *cp, **q; if (protof == NULL) protof = fopen(_PATH_BT_PROTOCOLS, "r"); if (protof == NULL) return (NULL); again: if ((p = fgets(buf, sizeof(buf), protof)) == NULL) return (NULL); if (*p == '#') goto again; if ((cp = strpbrk(p, "#\n")) == NULL) goto again; *cp = '\0'; proto.p_name = p; if ((cp = strpbrk(p, " \t")) == NULL) goto again; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; if ((p = strpbrk(cp, " \t")) != NULL) *p++ = '\0'; proto.p_proto = atoi(cp); q = proto.p_aliases = proto_aliases; if (p != NULL) { cp = p; while (cp != NULL && *cp != 0) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &proto_aliases[MAXALIASES - 1]) *q++ = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0'; } } *q = NULL; return (&proto); } void bt_setprotoent(int stayopen) { if (protof == NULL) protof = fopen(_PATH_BT_PROTOCOLS, "r"); else rewind(protof); proto_stayopen = stayopen; } void bt_endprotoent(void) { if (protof != NULL) { (void) fclose(protof); protof = NULL; } } char const * bt_ntoa(bdaddr_t const *ba, char *str) { static char buffer[24]; if (str == NULL) str = buffer; sprintf(str, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); return (str); } int bt_aton(char const *str, bdaddr_t *ba) { int i, b; char *end = NULL; memset(ba, 0, sizeof(*ba)); for (i = 5, end = strchr(str, ':'); i > 0 && *str != '\0' && end != NULL; i --, str = end + 1, end = strchr(str, ':')) { switch (end - str) { case 1: b = bt_hex_nibble(str[0]); break; case 2: b = bt_hex_byte(str); break; default: b = -1; break; } if (b < 0) return (0); ba->b[i] = b; } if (i != 0 || end != NULL || *str == 0) return (0); switch (strlen(str)) { case 1: b = bt_hex_nibble(str[0]); break; case 2: b = bt_hex_byte(str); break; default: b = -1; break; } if (b < 0) return (0); ba->b[i] = b; return (1); } static int bt_hex_byte(char const *str) { int n1, n2; if ((n1 = bt_hex_nibble(str[0])) < 0) return (-1); if ((n2 = bt_hex_nibble(str[1])) < 0) return (-1); return ((((n1 & 0x0f) << 4) | (n2 & 0x0f)) & 0xff); } static int bt_hex_nibble(char nibble) { if ('0' <= nibble && nibble <= '9') return (nibble - '0'); if ('a' <= nibble && nibble <= 'f') return (nibble - 'a' + 0xa); if ('A' <= nibble && nibble <= 'F') return (nibble - 'A' + 0xa); return (-1); } Index: head/lib/libbluetooth/dev.c =================================================================== --- head/lib/libbluetooth/dev.c (revision 281209) +++ head/lib/libbluetooth/dev.c (revision 281210) @@ -1,95 +1,96 @@ /* * dev.c */ /*- * Copyright (c) 2009 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include struct bt_devaddr_match_arg { char devname[HCI_DEVNAME_SIZE]; bdaddr_t const *bdaddr; }; static bt_devenum_cb_t bt_devaddr_match; int bt_devaddr(char const *devname, bdaddr_t *addr) { struct bt_devinfo di; strlcpy(di.devname, devname, sizeof(di.devname)); if (bt_devinfo(&di) < 0) return (0); if (addr != NULL) bdaddr_copy(addr, &di.bdaddr); return (1); } int bt_devname(char *devname, bdaddr_t const *addr) { struct bt_devaddr_match_arg arg; memset(&arg, 0, sizeof(arg)); arg.bdaddr = addr; if (bt_devenum(&bt_devaddr_match, &arg) < 0) return (0); if (arg.devname[0] == '\0') { errno = ENXIO; return (0); } if (devname != NULL) strlcpy(devname, arg.devname, HCI_DEVNAME_SIZE); return (1); } static int bt_devaddr_match(int s, struct bt_devinfo const *di, void *arg) { struct bt_devaddr_match_arg *m = (struct bt_devaddr_match_arg *)arg; if (!bdaddr_same(&di->bdaddr, m->bdaddr)) return (0); strlcpy(m->devname, di->devname, sizeof(m->devname)); return (1); } Index: head/lib/libbluetooth/hci.c =================================================================== --- head/lib/libbluetooth/hci.c (revision 281209) +++ head/lib/libbluetooth/hci.c (revision 281210) @@ -1,734 +1,735 @@ /* * hci.c */ /*- * Copyright (c) 2009 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #undef MIN #define MIN(a, b) (((a) < (b))? (a) : (b)) static int bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname); static char * bt_dev2node (char const *devname, char *nodename, int nnlen); int bt_devopen(char const *devname) { struct sockaddr_hci ha; bdaddr_t ba; int s; if (devname == NULL) { errno = EINVAL; return (-1); } memset(&ha, 0, sizeof(ha)); ha.hci_len = sizeof(ha); ha.hci_family = AF_BLUETOOTH; if (bt_aton(devname, &ba)) { if (!bt_devname(ha.hci_node, &ba)) return (-1); } else if (bt_dev2node(devname, ha.hci_node, sizeof(ha.hci_node)) == NULL) { errno = ENXIO; return (-1); } s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); if (s < 0) return (-1); if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0) { close(s); return (-1); } return (s); } int bt_devclose(int s) { return (close(s)); } int bt_devsend(int s, uint16_t opcode, void *param, size_t plen) { ng_hci_cmd_pkt_t h; struct iovec iv[2]; int ivn; if ((plen == 0 && param != NULL) || (plen > 0 && param == NULL) || plen > UINT8_MAX) { errno = EINVAL; return (-1); } iv[0].iov_base = &h; iv[0].iov_len = sizeof(h); ivn = 1; h.type = NG_HCI_CMD_PKT; h.opcode = htole16(opcode); if (plen > 0) { h.length = plen; iv[1].iov_base = param; iv[1].iov_len = plen; ivn = 2; } else h.length = 0; while (writev(s, iv, ivn) < 0) { if (errno == EAGAIN || errno == EINTR) continue; return (-1); } return (0); } ssize_t bt_devrecv(int s, void *buf, size_t size, time_t to) { ssize_t n; if (buf == NULL || size == 0) { errno = EINVAL; return (-1); } if (to >= 0) { fd_set rfd; struct timeval tv; FD_ZERO(&rfd); FD_SET(s, &rfd); tv.tv_sec = to; tv.tv_usec = 0; while ((n = select(s + 1, &rfd, NULL, NULL, &tv)) < 0) { if (errno == EAGAIN || errno == EINTR) continue; return (-1); } if (n == 0) { errno = ETIMEDOUT; return (-1); } assert(FD_ISSET(s, &rfd)); } while ((n = read(s, buf, size)) < 0) { if (errno == EAGAIN || errno == EINTR) continue; return (-1); } switch (*((uint8_t *) buf)) { case NG_HCI_CMD_PKT: { ng_hci_cmd_pkt_t *h = (ng_hci_cmd_pkt_t *) buf; if (n >= sizeof(*h) && n == (sizeof(*h) + h->length)) return (n); } break; case NG_HCI_ACL_DATA_PKT: { ng_hci_acldata_pkt_t *h = (ng_hci_acldata_pkt_t *) buf; if (n >= sizeof(*h) && n == (sizeof(*h) + le16toh(h->length))) return (n); } break; case NG_HCI_SCO_DATA_PKT: { ng_hci_scodata_pkt_t *h = (ng_hci_scodata_pkt_t *) buf; if (n >= sizeof(*h) && n == (sizeof(*h) + h->length)) return (n); } break; case NG_HCI_EVENT_PKT: { ng_hci_event_pkt_t *h = (ng_hci_event_pkt_t *) buf; if (n >= sizeof(*h) && n == (sizeof(*h) + h->length)) return (n); } break; } errno = EIO; return (-1); } int bt_devreq(int s, struct bt_devreq *r, time_t to) { uint8_t buf[320]; /* more than enough */ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) buf; ng_hci_command_compl_ep *cc = (ng_hci_command_compl_ep *)(e+1); ng_hci_command_status_ep *cs = (ng_hci_command_status_ep*)(e+1); struct bt_devfilter old, new; time_t t_end; uint16_t opcode; ssize_t n; int error; if (s < 0 || r == NULL || to < 0) { errno = EINVAL; return (-1); } if ((r->rlen == 0 && r->rparam != NULL) || (r->rlen > 0 && r->rparam == NULL)) { errno = EINVAL; return (-1); } memset(&new, 0, sizeof(new)); bt_devfilter_pkt_set(&new, NG_HCI_EVENT_PKT); bt_devfilter_evt_set(&new, NG_HCI_EVENT_COMMAND_COMPL); bt_devfilter_evt_set(&new, NG_HCI_EVENT_COMMAND_STATUS); if (r->event != 0) bt_devfilter_evt_set(&new, r->event); if (bt_devfilter(s, &new, &old) < 0) return (-1); error = 0; n = bt_devsend(s, r->opcode, r->cparam, r->clen); if (n < 0) { error = errno; goto out; } opcode = htole16(r->opcode); t_end = time(NULL) + to; do { to = t_end - time(NULL); if (to < 0) to = 0; n = bt_devrecv(s, buf, sizeof(buf), to); if (n < 0) { error = errno; goto out; } if (e->type != NG_HCI_EVENT_PKT) { error = EIO; goto out; } n -= sizeof(*e); switch (e->event) { case NG_HCI_EVENT_COMMAND_COMPL: if (cc->opcode == opcode) { n -= sizeof(*cc); if (r->rlen >= n) { r->rlen = n; memcpy(r->rparam, cc + 1, r->rlen); } goto out; } break; case NG_HCI_EVENT_COMMAND_STATUS: if (cs->opcode == opcode) { if (r->event != NG_HCI_EVENT_COMMAND_STATUS) { if (cs->status != 0) { error = EIO; goto out; } } else { if (r->rlen >= n) { r->rlen = n; memcpy(r->rparam, cs, r->rlen); } goto out; } } break; default: if (e->event == r->event) { if (r->rlen >= n) { r->rlen = n; memcpy(r->rparam, e + 1, r->rlen); } goto out; } break; } } while (to > 0); error = ETIMEDOUT; out: bt_devfilter(s, &old, NULL); if (error != 0) { errno = error; return (-1); } return (0); } int bt_devfilter(int s, struct bt_devfilter const *new, struct bt_devfilter *old) { struct ng_btsocket_hci_raw_filter f; socklen_t len; if (new == NULL && old == NULL) { errno = EINVAL; return (-1); } if (old != NULL) { len = sizeof(f); if (getsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, &f, &len) < 0) return (-1); memset(old, 0, sizeof(*old)); memcpy(old->packet_mask, &f.packet_mask, MIN(sizeof(old->packet_mask), sizeof(f.packet_mask))); memcpy(old->event_mask, &f.event_mask, MIN(sizeof(old->event_mask), sizeof(f.packet_mask))); } if (new != NULL) { memset(&f, 0, sizeof(f)); memcpy(&f.packet_mask, new->packet_mask, MIN(sizeof(f.packet_mask), sizeof(new->event_mask))); memcpy(&f.event_mask, new->event_mask, MIN(sizeof(f.event_mask), sizeof(new->event_mask))); len = sizeof(f); if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, &f, len) < 0) return (-1); } return (0); } void bt_devfilter_pkt_set(struct bt_devfilter *filter, uint8_t type) { bit_set(filter->packet_mask, type - 1); } void bt_devfilter_pkt_clr(struct bt_devfilter *filter, uint8_t type) { bit_clear(filter->packet_mask, type - 1); } int bt_devfilter_pkt_tst(struct bt_devfilter const *filter, uint8_t type) { return (bit_test(filter->packet_mask, type - 1)); } void bt_devfilter_evt_set(struct bt_devfilter *filter, uint8_t event) { bit_set(filter->event_mask, event - 1); } void bt_devfilter_evt_clr(struct bt_devfilter *filter, uint8_t event) { bit_clear(filter->event_mask, event - 1); } int bt_devfilter_evt_tst(struct bt_devfilter const *filter, uint8_t event) { return (bit_test(filter->event_mask, event - 1)); } int bt_devinquiry(char const *devname, time_t length, int num_rsp, struct bt_devinquiry **ii) { uint8_t buf[320]; char _devname[HCI_DEVNAME_SIZE]; struct bt_devfilter f; ng_hci_inquiry_cp *cp = (ng_hci_inquiry_cp *) buf; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) buf; ng_hci_inquiry_result_ep *ep = (ng_hci_inquiry_result_ep *)(e+1); ng_hci_inquiry_response *ir; struct bt_devinquiry *i; int s, n; if (ii == NULL) { errno = EINVAL; return (-1); } if (devname == NULL) { memset(_devname, 0, sizeof(_devname)); devname = _devname; n = bt_devenum(bt_devany_cb, _devname); if (n <= 0) { if (n == 0) *ii = NULL; return (n); } } s = bt_devopen(devname); if (s < 0) return (-1); if (bt_devfilter(s, NULL, &f) < 0) { bt_devclose(s); return (-1); } bt_devfilter_evt_set(&f, NG_HCI_EVENT_INQUIRY_COMPL); bt_devfilter_evt_set(&f, NG_HCI_EVENT_INQUIRY_RESULT); if (bt_devfilter(s, &f, NULL) < 0) { bt_devclose(s); return (-1); } /* Always use GIAC LAP */ cp->lap[0] = 0x33; cp->lap[1] = 0x8b; cp->lap[2] = 0x9e; /* * Calculate inquire length in 1.28 second units * v2.x specification says that 1.28 -> 61.44 seconds * range is acceptable */ if (length <= 0) length = 5; else if (length == 1) length = 2; else if (length > 62) length = 62; cp->inquiry_length = (uint8_t)((length * 100) / 128); if (num_rsp <= 0 || num_rsp > 255) num_rsp = 8; cp->num_responses = (uint8_t) num_rsp; i = *ii = calloc(num_rsp, sizeof(struct bt_devinquiry)); if (i == NULL) { bt_devclose(s); errno = ENOMEM; return (-1); } if (bt_devsend(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_INQUIRY), cp, sizeof(*cp)) < 0) { free(i); bt_devclose(s); return (-1); } wait_for_more: n = bt_devrecv(s, buf, sizeof(buf), length); if (n < 0) { free(i); bt_devclose(s); return (-1); } if (n < sizeof(ng_hci_event_pkt_t)) { free(i); bt_devclose(s); errno = EIO; return (-1); } switch (e->event) { case NG_HCI_EVENT_INQUIRY_COMPL: break; case NG_HCI_EVENT_INQUIRY_RESULT: ir = (ng_hci_inquiry_response *)(ep + 1); for (n = 0; n < MIN(ep->num_responses, num_rsp); n ++) { bdaddr_copy(&i->bdaddr, &ir->bdaddr); i->pscan_rep_mode = ir->page_scan_rep_mode; i->pscan_period_mode = ir->page_scan_period_mode; memcpy(i->dev_class, ir->uclass, sizeof(i->dev_class)); i->clock_offset = le16toh(ir->clock_offset); ir ++; i ++; num_rsp --; } /* FALLTHROUGH */ default: goto wait_for_more; /* NOT REACHED */ } bt_devclose(s); return (i - *ii); } int bt_devinfo(struct bt_devinfo *di) { union { struct ng_btsocket_hci_raw_node_state r0; struct ng_btsocket_hci_raw_node_bdaddr r1; struct ng_btsocket_hci_raw_node_features r2; struct ng_btsocket_hci_raw_node_buffer r3; struct ng_btsocket_hci_raw_node_stat r4; struct ng_btsocket_hci_raw_node_link_policy_mask r5; struct ng_btsocket_hci_raw_node_packet_mask r6; struct ng_btsocket_hci_raw_node_role_switch r7; struct ng_btsocket_hci_raw_node_debug r8; } rp; struct sockaddr_hci ha; socklen_t halen; int s, rval; if (di == NULL) { errno = EINVAL; return (-1); } s = bt_devopen(di->devname); if (s < 0) return (-1); rval = -1; halen = sizeof(ha); if (getsockname(s, (struct sockaddr *) &ha, &halen) < 0) goto bad; strlcpy(di->devname, ha.hci_node, sizeof(di->devname)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &rp.r0, sizeof(rp.r0)) < 0) goto bad; di->state = rp.r0.state; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &rp.r1, sizeof(rp.r1)) < 0) goto bad; bdaddr_copy(&di->bdaddr, &rp.r1.bdaddr); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &rp.r2, sizeof(rp.r2)) < 0) goto bad; memcpy(di->features, rp.r2.features, sizeof(di->features)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &rp.r3, sizeof(rp.r3)) < 0) goto bad; di->cmd_free = rp.r3.buffer.cmd_free; di->sco_size = rp.r3.buffer.sco_size; di->sco_pkts = rp.r3.buffer.sco_pkts; di->sco_free = rp.r3.buffer.sco_free; di->acl_size = rp.r3.buffer.acl_size; di->acl_pkts = rp.r3.buffer.acl_pkts; di->acl_free = rp.r3.buffer.acl_free; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &rp.r4, sizeof(rp.r4)) < 0) goto bad; di->cmd_sent = rp.r4.stat.cmd_sent; di->evnt_recv = rp.r4.stat.evnt_recv; di->acl_recv = rp.r4.stat.acl_recv; di->acl_sent = rp.r4.stat.acl_sent; di->sco_recv = rp.r4.stat.sco_recv; di->sco_sent = rp.r4.stat.sco_sent; di->bytes_recv = rp.r4.stat.bytes_recv; di->bytes_sent = rp.r4.stat.bytes_sent; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, &rp.r5, sizeof(rp.r5)) < 0) goto bad; di->link_policy_info = rp.r5.policy_mask; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, &rp.r6, sizeof(rp.r6)) < 0) goto bad; di->packet_type_info = rp.r6.packet_mask; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH, &rp.r7, sizeof(rp.r7)) < 0) goto bad; di->role_switch_info = rp.r7.role_switch; if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &rp.r8, sizeof(rp.r8)) < 0) goto bad; di->debug = rp.r8.debug; rval = 0; bad: bt_devclose(s); return (rval); } int bt_devenum(bt_devenum_cb_t cb, void *arg) { struct ng_btsocket_hci_raw_node_list_names rp; struct bt_devinfo di; struct sockaddr_hci ha; int s, i, count; rp.num_names = HCI_DEVMAX; rp.names = (struct nodeinfo *) calloc(rp.num_names, sizeof(struct nodeinfo)); if (rp.names == NULL) { errno = ENOMEM; return (-1); } memset(&ha, 0, sizeof(ha)); ha.hci_len = sizeof(ha); ha.hci_family = AF_BLUETOOTH; ha.hci_node[0] = 'x'; s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); if (s < 0) { free(rp.names); return (-1); } if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &rp, sizeof(rp)) < 0) { close(s); free(rp.names); return (-1); } for (count = 0, i = 0; i < rp.num_names; i ++) { strlcpy(di.devname, rp.names[i].name, sizeof(di.devname)); if (bt_devinfo(&di) < 0) continue; count ++; if (cb == NULL) continue; strlcpy(ha.hci_node, rp.names[i].name, sizeof(ha.hci_node)); if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0) continue; if ((*cb)(s, &di, arg) > 0) break; } close (s); free(rp.names); return (count); } static int bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname) { strlcpy((char *) xdevname, di->devname, HCI_DEVNAME_SIZE); return (1); } static char * bt_dev2node(char const *devname, char *nodename, int nnlen) { static char const * bt_dev_prefix[] = { "btccc", /* 3Com Bluetooth PC-CARD */ "h4", /* UART/serial Bluetooth devices */ "ubt", /* Bluetooth USB devices */ NULL /* should be last */ }; static char _nodename[HCI_DEVNAME_SIZE]; char const **p; char *ep; int plen, unit; if (nodename == NULL) { nodename = _nodename; nnlen = HCI_DEVNAME_SIZE; } for (p = bt_dev_prefix; *p != NULL; p ++) { plen = strlen(*p); if (strncmp(devname, *p, plen) != 0) continue; unit = strtoul(devname + plen, &ep, 10); if (*ep != '\0' && strcmp(ep, "hci") != 0 && strcmp(ep, "l2cap") != 0) return (NULL); /* can't make sense of device name */ snprintf(nodename, nnlen, "%s%uhci", *p, unit); return (nodename); } return (NULL); } Index: head/lib/libsdp/search.c =================================================================== --- head/lib/libsdp/search.c (revision 281209) +++ head/lib/libsdp/search.c (revision 281210) @@ -1,421 +1,422 @@ /* * search.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: search.c,v 1.2 2003/09/04 22:12:13 max Exp $ * $FreeBSD$ */ #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include int32_t sdp_search(void *xss, uint32_t plen, uint16_t const *pp, uint32_t alen, uint32_t const *ap, uint32_t vlen, sdp_attr_t *vp) { struct sdp_xpdu { sdp_pdu_t pdu; uint16_t len; } __attribute__ ((packed)) xpdu; sdp_session_p ss = (sdp_session_p) xss; uint8_t *req = NULL, *rsp = NULL, *rsp_tmp = NULL; int32_t t, len; uint16_t lo, hi; if (ss == NULL) return (-1); if (ss->req == NULL || ss->rsp == NULL || plen == 0 || pp == NULL || alen == 0 || ap == NULL) { ss->error = EINVAL; return (-1); } req = ss->req; /* Calculate ServiceSearchPattern length */ plen = plen * (sizeof(pp[0]) + 1); /* Calculate AttributeIDList length */ for (len = 0, t = 0; t < alen; t ++) { lo = (uint16_t) (ap[t] >> 16); hi = (uint16_t) (ap[t]); if (lo > hi) { ss->error = EINVAL; return (-1); } if (lo != hi) len += (sizeof(ap[t]) + 1); else len += (sizeof(lo) + 1); } alen = len; /* Calculate length of the request */ len = plen + sizeof(uint8_t) + sizeof(uint16_t) + /* ServiceSearchPattern */ sizeof(uint16_t) + /* MaximumAttributeByteCount */ alen + sizeof(uint8_t) + sizeof(uint16_t); /* AttributeIDList */ if (ss->req_e - req < len) { ss->error = ENOBUFS; return (-1); } /* Put ServiceSearchPattern */ SDP_PUT8(SDP_DATA_SEQ16, req); SDP_PUT16(plen, req); for (; plen > 0; pp ++, plen -= (sizeof(pp[0]) + 1)) { SDP_PUT8(SDP_DATA_UUID16, req); SDP_PUT16(*pp, req); } /* Put MaximumAttributeByteCount */ SDP_PUT16(0xffff, req); /* Put AttributeIDList */ SDP_PUT8(SDP_DATA_SEQ16, req); SDP_PUT16(alen, req); for (; alen > 0; ap ++) { lo = (uint16_t) (*ap >> 16); hi = (uint16_t) (*ap); if (lo != hi) { /* Put attribute range */ SDP_PUT8(SDP_DATA_UINT32, req); SDP_PUT32(*ap, req); alen -= (sizeof(ap[0]) + 1); } else { /* Put attribute */ SDP_PUT8(SDP_DATA_UINT16, req); SDP_PUT16(lo, req); alen -= (sizeof(lo) + 1); } } /* Submit ServiceSearchAttributeRequest and wait for response */ ss->cslen = 0; rsp = ss->rsp; do { struct iovec iov[2]; uint8_t *req_cs = req; /* Add continuation state (if any) */ if (ss->req_e - req_cs < ss->cslen + 1) { ss->error = ENOBUFS; return (-1); } SDP_PUT8(ss->cslen, req_cs); if (ss->cslen > 0) { memcpy(req_cs, ss->cs, ss->cslen); req_cs += ss->cslen; } /* Prepare SDP PDU header */ xpdu.pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST; xpdu.pdu.tid = htons(ss->tid); xpdu.pdu.len = htons(req_cs - ss->req); /* Submit request */ iov[0].iov_base = (void *) &xpdu; iov[0].iov_len = sizeof(xpdu.pdu); iov[1].iov_base = (void *) ss->req; iov[1].iov_len = req_cs - ss->req; do { len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0])); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } /* Read response */ iov[0].iov_base = (void *) &xpdu; iov[0].iov_len = sizeof(xpdu); iov[1].iov_base = (void *) rsp; iov[1].iov_len = ss->imtu; do { len = readv(ss->s, iov, sizeof(iov)/sizeof(iov[0])); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } if (len < sizeof(xpdu)) { ss->error = ENOMSG; return (-1); } xpdu.pdu.tid = ntohs(xpdu.pdu.tid); xpdu.pdu.len = ntohs(xpdu.pdu.len); xpdu.len = ntohs(xpdu.len); if (xpdu.pdu.pid == SDP_PDU_ERROR_RESPONSE || xpdu.pdu.tid != ss->tid || xpdu.pdu.len > len || xpdu.len > xpdu.pdu.len) { ss->error = EIO; return (-1); } rsp += xpdu.len; ss->tid ++; /* Save continuation state (if any) */ ss->cslen = rsp[0]; if (ss->cslen > 0) { if (ss->cslen > sizeof(ss->cs)) { ss->error = ENOBUFS; return (-1); } memcpy(ss->cs, rsp + 1, ss->cslen); /* * Ensure that we always have ss->imtu bytes * available in the ss->rsp buffer */ if (ss->rsp_e - rsp <= ss->imtu) { uint32_t size, offset; size = ss->rsp_e - ss->rsp + ss->imtu; offset = rsp - ss->rsp; rsp_tmp = realloc(ss->rsp, size); if (rsp_tmp == NULL) { ss->error = ENOMEM; return (-1); } ss->rsp = rsp_tmp; ss->rsp_e = ss->rsp + size; rsp = ss->rsp + offset; } } } while (ss->cslen > 0); /* * If we got here then we have completed SDP transaction and now * we must populate attribute values into vp array. At this point * ss->rsp points to the beginning of the response and rsp points * to the end of the response. * * From Bluetooth v1.1 spec page 364 * * The AttributeLists is a data element sequence where each element * in turn is a data element sequence representing an attribute list. * Each attribute list contains attribute IDs and attribute values * from one service record. The first element in each attribute list * contains the attribute ID of the first attribute to be returned for * that service record. The second element in each attribute list * contains the corresponding attribute value. Successive pairs of * elements in each attribute list contain additional attribute ID * and value pairs. Only attributes that have non-null values within * the service record and whose attribute IDs were specified in the * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists * Neither an attribute ID nor attribute value is placed in * AttributeLists for attributes in the service record that have no * value. Within each attribute list, the attributes are listed in * ascending order of attribute ID value. */ if (vp == NULL) goto done; rsp_tmp = ss->rsp; /* Skip the first SEQ */ SDP_GET8(t, rsp_tmp); switch (t) { case SDP_DATA_SEQ8: SDP_GET8(len, rsp_tmp); break; case SDP_DATA_SEQ16: SDP_GET16(len, rsp_tmp); break; case SDP_DATA_SEQ32: SDP_GET32(len, rsp_tmp); break; default: ss->error = ENOATTR; return (-1); /* NOT REACHED */ } for (; rsp_tmp < rsp && vlen > 0; ) { /* Get set of attributes for the next record */ SDP_GET8(t, rsp_tmp); switch (t) { case SDP_DATA_SEQ8: SDP_GET8(len, rsp_tmp); break; case SDP_DATA_SEQ16: SDP_GET16(len, rsp_tmp); break; case SDP_DATA_SEQ32: SDP_GET32(len, rsp_tmp); break; default: ss->error = ENOATTR; return (-1); /* NOT REACHED */ } /* Now rsp_tmp points to list of (attr,value) pairs */ for (; len > 0 && vlen > 0; vp ++, vlen --) { /* Attribute */ SDP_GET8(t, rsp_tmp); if (t != SDP_DATA_UINT16) { ss->error = ENOATTR; return (-1); } SDP_GET16(vp->attr, rsp_tmp); /* Attribute value */ switch (rsp_tmp[0]) { case SDP_DATA_NIL: alen = 0; break; case SDP_DATA_UINT8: case SDP_DATA_INT8: case SDP_DATA_BOOL: alen = sizeof(uint8_t); break; case SDP_DATA_UINT16: case SDP_DATA_INT16: case SDP_DATA_UUID16: alen = sizeof(uint16_t); break; case SDP_DATA_UINT32: case SDP_DATA_INT32: case SDP_DATA_UUID32: alen = sizeof(uint32_t); break; case SDP_DATA_UINT64: case SDP_DATA_INT64: alen = sizeof(uint64_t); break; case SDP_DATA_UINT128: case SDP_DATA_INT128: case SDP_DATA_UUID128: alen = sizeof(uint128_t); break; case SDP_DATA_STR8: case SDP_DATA_URL8: case SDP_DATA_SEQ8: case SDP_DATA_ALT8: alen = rsp_tmp[1] + sizeof(uint8_t); break; case SDP_DATA_STR16: case SDP_DATA_URL16: case SDP_DATA_SEQ16: case SDP_DATA_ALT16: alen = ((uint16_t)rsp_tmp[1] << 8) | ((uint16_t)rsp_tmp[2]); alen += sizeof(uint16_t); break; case SDP_DATA_STR32: case SDP_DATA_URL32: case SDP_DATA_SEQ32: case SDP_DATA_ALT32: alen = ((uint32_t)rsp_tmp[1] << 24) | ((uint32_t)rsp_tmp[2] << 16) | ((uint32_t)rsp_tmp[3] << 8) | ((uint32_t)rsp_tmp[4]); alen += sizeof(uint32_t); break; default: ss->error = ENOATTR; return (-1); /* NOT REACHED */ } alen += sizeof(uint8_t); if (vp->value != NULL) { if (alen <= vp->vlen) { vp->flags = SDP_ATTR_OK; vp->vlen = alen; } else vp->flags = SDP_ATTR_TRUNCATED; memcpy(vp->value, rsp_tmp, vp->vlen); } else vp->flags = SDP_ATTR_INVALID; len -= ( sizeof(uint8_t) + sizeof(uint16_t) + alen ); rsp_tmp += alen; } } done: ss->error = 0; return (0); } Index: head/lib/libsdp/service.c =================================================================== --- head/lib/libsdp/service.c (revision 281209) +++ head/lib/libsdp/service.c (revision 281210) @@ -1,237 +1,238 @@ /* * service.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: service.c,v 1.1 2004/01/13 19:32:36 max Exp $ * $FreeBSD$ */ #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include static int32_t sdp_receive_error_pdu(sdp_session_p ss); int32_t sdp_register_service(void *xss, uint16_t uuid, bdaddr_p const bdaddr, uint8_t const *data, uint32_t datalen, uint32_t *handle) { sdp_session_p ss = (sdp_session_p) xss; struct iovec iov[4]; sdp_pdu_t pdu; int32_t len; if (ss == NULL) return (-1); if (bdaddr == NULL || data == NULL || datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) { ss->error = EINVAL; return (-1); } if (sizeof(pdu)+sizeof(uuid)+sizeof(*bdaddr)+datalen > SDP_LOCAL_MTU) { ss->error = EMSGSIZE; return (-1); } pdu.pid = SDP_PDU_SERVICE_REGISTER_REQUEST; pdu.tid = htons(++ss->tid); pdu.len = htons(sizeof(uuid) + sizeof(*bdaddr) + datalen); uuid = htons(uuid); iov[0].iov_base = (void *) &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = (void *) &uuid; iov[1].iov_len = sizeof(uuid); iov[2].iov_base = (void *) bdaddr; iov[2].iov_len = sizeof(*bdaddr); iov[3].iov_base = (void *) data; iov[3].iov_len = datalen; do { len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0])); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } len = sdp_receive_error_pdu(ss); if (len < 0) return (-1); if (len != sizeof(pdu) + sizeof(uint16_t) + sizeof(uint32_t)) { ss->error = EIO; return (-1); } if (handle != NULL) { *handle = (uint32_t) ss->rsp[--len]; *handle |= (uint32_t) ss->rsp[--len] << 8; *handle |= (uint32_t) ss->rsp[--len] << 16; *handle |= (uint32_t) ss->rsp[--len] << 24; } return (0); } int32_t sdp_unregister_service(void *xss, uint32_t handle) { sdp_session_p ss = (sdp_session_p) xss; struct iovec iov[2]; sdp_pdu_t pdu; int32_t len; if (ss == NULL) return (-1); if (!(ss->flags & SDP_SESSION_LOCAL)) { ss->error = EINVAL; return (-1); } if (sizeof(pdu) + sizeof(handle) > SDP_LOCAL_MTU) { ss->error = EMSGSIZE; return (-1); } pdu.pid = SDP_PDU_SERVICE_UNREGISTER_REQUEST; pdu.tid = htons(++ss->tid); pdu.len = htons(sizeof(handle)); handle = htonl(handle); iov[0].iov_base = (void *) &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = (void *) &handle; iov[1].iov_len = sizeof(handle); do { len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0])); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0); } int32_t sdp_change_service(void *xss, uint32_t handle, uint8_t const *data, uint32_t datalen) { sdp_session_p ss = (sdp_session_p) xss; struct iovec iov[3]; sdp_pdu_t pdu; int32_t len; if (ss == NULL) return (-1); if (data == NULL || datalen == 0 || !(ss->flags & SDP_SESSION_LOCAL)) { ss->error = EINVAL; return (-1); } if (sizeof(pdu) + sizeof(handle) + datalen > SDP_LOCAL_MTU) { ss->error = EMSGSIZE; return (-1); } pdu.pid = SDP_PDU_SERVICE_CHANGE_REQUEST; pdu.tid = htons(++ss->tid); pdu.len = htons(sizeof(handle) + datalen); handle = htons(handle); iov[0].iov_base = (void *) &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = (void *) &handle; iov[1].iov_len = sizeof(handle); iov[2].iov_base = (void *) data; iov[2].iov_len = datalen; do { len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0])); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } return ((sdp_receive_error_pdu(ss) < 0)? -1 : 0); } static int32_t sdp_receive_error_pdu(sdp_session_p ss) { sdp_pdu_p pdu; int32_t len; uint16_t error; do { len = read(ss->s, ss->rsp, ss->rsp_e - ss->rsp); } while (len < 0 && errno == EINTR); if (len < 0) { ss->error = errno; return (-1); } pdu = (sdp_pdu_p) ss->rsp; pdu->tid = ntohs(pdu->tid); pdu->len = ntohs(pdu->len); if (pdu->pid != SDP_PDU_ERROR_RESPONSE || pdu->tid != ss->tid || pdu->len < 2 || pdu->len != len - sizeof(*pdu)) { ss->error = EIO; return (-1); } error = (uint16_t) ss->rsp[sizeof(pdu)] << 8; error |= (uint16_t) ss->rsp[sizeof(pdu) + 1]; if (error != 0) { ss->error = EIO; return (-1); } return (len); } Index: head/lib/libsdp/session.c =================================================================== --- head/lib/libsdp/session.c (revision 281209) +++ head/lib/libsdp/session.c (revision 281210) @@ -1,177 +1,180 @@ /* * session.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: session.c,v 1.2 2003/09/04 22:12:13 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include void * sdp_open(bdaddr_t const *l, bdaddr_t const *r) { sdp_session_p ss = NULL; struct sockaddr_l2cap sa; socklen_t size; if ((ss = calloc(1, sizeof(*ss))) == NULL) goto fail; if (l == NULL || r == NULL) { ss->error = EINVAL; goto fail; } ss->s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (ss->s < 0) { ss->error = errno; goto fail; } sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; sa.l2cap_psm = 0; + sa.l2cap_cid = 0; + sa.l2cap_bdaddr_type = BDADDR_BREDR; + memcpy(&sa.l2cap_bdaddr, l, sizeof(sa.l2cap_bdaddr)); if (bind(ss->s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { ss->error = errno; goto fail; } sa.l2cap_psm = htole16(NG_L2CAP_PSM_SDP); memcpy(&sa.l2cap_bdaddr, r, sizeof(sa.l2cap_bdaddr)); if (connect(ss->s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { ss->error = errno; goto fail; } size = sizeof(ss->omtu); if (getsockopt(ss->s, SOL_L2CAP, SO_L2CAP_OMTU, &ss->omtu, &size) < 0) { ss->error = errno; goto fail; } if ((ss->req = malloc(ss->omtu)) == NULL) { ss->error = ENOMEM; goto fail; } ss->req_e = ss->req + ss->omtu; size = sizeof(ss->imtu); if (getsockopt(ss->s, SOL_L2CAP, SO_L2CAP_IMTU, &ss->imtu, &size) < 0) { ss->error = errno; goto fail; } if ((ss->rsp = malloc(ss->imtu)) == NULL) { ss->error = ENOMEM; goto fail; } ss->rsp_e = ss->rsp + ss->imtu; ss->error = 0; fail: return ((void *) ss); } void * sdp_open_local(char const *control) { sdp_session_p ss = NULL; struct sockaddr_un sa; if ((ss = calloc(1, sizeof(*ss))) == NULL) goto fail; ss->s = socket(PF_UNIX, SOCK_STREAM, 0); if (ss->s < 0) { ss->error = errno; goto fail; } if (control == NULL) control = SDP_LOCAL_PATH; sa.sun_len = sizeof(sa); sa.sun_family = AF_UNIX; strlcpy(sa.sun_path, control, sizeof(sa.sun_path)); if (connect(ss->s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { ss->error = errno; goto fail; } ss->flags |= SDP_SESSION_LOCAL; ss->imtu = ss->omtu = SDP_LOCAL_MTU; if ((ss->req = malloc(ss->omtu)) == NULL) { ss->error = ENOMEM; goto fail; } ss->req_e = ss->req + ss->omtu; if ((ss->rsp = malloc(ss->imtu)) == NULL) { ss->error = ENOMEM; goto fail; } ss->rsp_e = ss->rsp + ss->imtu; ss->error = 0; fail: return ((void *) ss); } int32_t sdp_close(void *xss) { sdp_session_p ss = (sdp_session_p) xss; if (ss != NULL) { if (ss->s >= 0) close(ss->s); if (ss->req != NULL) free(ss->req); if (ss->rsp != NULL) free(ss->rsp); memset(ss, 0, sizeof(*ss)); free(ss); } return (0); } int32_t sdp_error(void *xss) { sdp_session_p ss = (sdp_session_p) xss; return ((ss != NULL)? ss->error : EINVAL); } Index: head/lib/libsdp/util.c =================================================================== --- head/lib/libsdp/util.c (revision 281209) +++ head/lib/libsdp/util.c (revision 281210) @@ -1,457 +1,458 @@ /* * util.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: util.c,v 1.5 2003/09/08 02:29:35 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include /* * SDP attribute description */ struct sdp_attr_desc { uint32_t attr; char const *desc; }; typedef struct sdp_attr_desc sdp_attr_desc_t; typedef struct sdp_attr_desc * sdp_attr_desc_p; static sdp_attr_desc_t sdp_uuids_desc[] = { { SDP_UUID_PROTOCOL_SDP, "SDP", }, { SDP_UUID_PROTOCOL_UDP, "UDP", }, { SDP_UUID_PROTOCOL_RFCOMM, "RFCOMM", }, { SDP_UUID_PROTOCOL_TCP, "TCP", }, { SDP_UUID_PROTOCOL_TCS_BIN, "TCS BIN", }, { SDP_UUID_PROTOCOL_TCS_AT, "TCS AT", }, { SDP_UUID_PROTOCOL_OBEX, "OBEX", }, { SDP_UUID_PROTOCOL_IP, "IP", }, { SDP_UUID_PROTOCOL_FTP, "FTP", }, { SDP_UUID_PROTOCOL_HTTP, "HTTP", }, { SDP_UUID_PROTOCOL_WSP, "WSP", }, { SDP_UUID_PROTOCOL_BNEP, "BNEP", }, { SDP_UUID_PROTOCOL_UPNP, "UPNP", }, { SDP_UUID_PROTOCOL_HIDP, "HIDP", }, { SDP_UUID_PROTOCOL_HARDCOPY_CONTROL_CHANNEL, "Hardcopy Control Channel", }, { SDP_UUID_PROTOCOL_HARDCOPY_DATA_CHANNEL, "Hardcopy Data Channel", }, { SDP_UUID_PROTOCOL_HARDCOPY_NOTIFICATION, "Hardcopy Notification", }, { SDP_UUID_PROTOCOL_AVCTP, "AVCTP", }, { SDP_UUID_PROTOCOL_AVDTP, "AVDTP", }, { SDP_UUID_PROTOCOL_CMPT, "CMPT", }, { SDP_UUID_PROTOCOL_UDI_C_PLANE, "UDI C-Plane", }, { SDP_UUID_PROTOCOL_L2CAP, "L2CAP", }, /* Service Class IDs/Bluetooth Profile IDs */ { SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER, "Service Discovery Server", }, { SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR, "Browse Group Descriptor", }, { SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP, "Public Browse Group", }, { SDP_SERVICE_CLASS_SERIAL_PORT, "Serial Port", }, { SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, "LAN Access Using PPP", }, { SDP_SERVICE_CLASS_DIALUP_NETWORKING, "Dial-Up Networking", }, { SDP_SERVICE_CLASS_IR_MC_SYNC, "IrMC Sync", }, { SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH, "OBEX Object Push", }, { SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER, "OBEX File Transfer", }, { SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND, "IrMC Sync Command", }, { SDP_SERVICE_CLASS_HEADSET, "Headset", }, { SDP_SERVICE_CLASS_CORDLESS_TELEPHONY, "Cordless Telephony", }, { SDP_SERVICE_CLASS_AUDIO_SOURCE, "Audio Source", }, { SDP_SERVICE_CLASS_AUDIO_SINK, "Audio Sink", }, { SDP_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET, "A/V Remote Control Target", }, { SDP_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION, "Advanced Audio Distribution", }, { SDP_SERVICE_CLASS_AV_REMOTE_CONTROL, "A/V Remote Control", }, { SDP_SERVICE_CLASS_VIDEO_CONFERENCING, "Video Conferencing", }, { SDP_SERVICE_CLASS_INTERCOM, "Intercom", }, { SDP_SERVICE_CLASS_FAX, "Fax", }, { SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY, "Headset Audio Gateway", }, { SDP_SERVICE_CLASS_WAP, "WAP", }, { SDP_SERVICE_CLASS_WAP_CLIENT, "WAP Client", }, { SDP_SERVICE_CLASS_PANU, "PANU", }, { SDP_SERVICE_CLASS_NAP, "Network Access Point", }, { SDP_SERVICE_CLASS_GN, "GN", }, { SDP_SERVICE_CLASS_DIRECT_PRINTING, "Direct Printing", }, { SDP_SERVICE_CLASS_REFERENCE_PRINTING, "Reference Printing", }, { SDP_SERVICE_CLASS_IMAGING, "Imaging", }, { SDP_SERVICE_CLASS_IMAGING_RESPONDER, "Imaging Responder", }, { SDP_SERVICE_CLASS_IMAGING_AUTOMATIC_ARCHIVE, "Imaging Automatic Archive", }, { SDP_SERVICE_CLASS_IMAGING_REFERENCED_OBJECTS, "Imaging Referenced Objects", }, { SDP_SERVICE_CLASS_HANDSFREE, "Handsfree", }, { SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY, "Handsfree Audio Gateway", }, { SDP_SERVICE_CLASS_DIRECT_PRINTING_REFERENCE_OBJECTS, "Direct Printing Reference Objects", }, { SDP_SERVICE_CLASS_REFLECTED_UI, "Reflected UI", }, { SDP_SERVICE_CLASS_BASIC_PRINTING, "Basic Printing", }, { SDP_SERVICE_CLASS_PRINTING_STATUS, "Printing Status", }, { SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE, "Human Interface Device", }, { SDP_SERVICE_CLASS_HARDCOPY_CABLE_REPLACEMENT, "Hardcopy Cable Replacement", }, { SDP_SERVICE_CLASS_HCR_PRINT, "HCR Print", }, { SDP_SERVICE_CLASS_HCR_SCAN, "HCR Scan", }, { SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS, "Common ISDN Access", }, { SDP_SERVICE_CLASS_VIDEO_CONFERENCING_GW, "Video Conferencing Gateway", }, { SDP_SERVICE_CLASS_UDI_MT, "UDI MT", }, { SDP_SERVICE_CLASS_UDI_TA, "UDI TA", }, { SDP_SERVICE_CLASS_AUDIO_VIDEO, "Audio/Video", }, { SDP_SERVICE_CLASS_SIM_ACCESS, "SIM Access", }, { SDP_SERVICE_CLASS_PHONEBOOK_ACCESS_PCE, "Phonebook Access - PCE", }, { SDP_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, "Phonebook Access - PSE", }, { SDP_SERVICE_CLASS_PHONEBOOK_ACCESS, "Phonebook Access", }, { SDP_SERVICE_CLASS_PNP_INFORMATION, "PNP Information", }, { SDP_SERVICE_CLASS_GENERIC_NETWORKING, "Generic Networking", }, { SDP_SERVICE_CLASS_GENERIC_FILE_TRANSFER, "Generic File Transfer", }, { SDP_SERVICE_CLASS_GENERIC_AUDIO, "Generic Audio", }, { SDP_SERVICE_CLASS_GENERIC_TELEPHONY, "Generic Telephony", }, { SDP_SERVICE_CLASS_UPNP, "UPNP", }, { SDP_SERVICE_CLASS_UPNP_IP, "UPNP IP", }, { SDP_SERVICE_CLASS_ESDP_UPNP_IP_PAN, "ESDP UPNP IP PAN", }, { SDP_SERVICE_CLASS_ESDP_UPNP_IP_LAP, "ESDP UPNP IP LAP", }, { SDP_SERVICE_CLASS_ESDP_UPNP_L2CAP, "ESDP UPNP L2CAP", }, { SDP_SERVICE_CLASS_VIDEO_SOURCE, "Video Source", }, { SDP_SERVICE_CLASS_VIDEO_SINK, "Video Sink", }, { SDP_SERVICE_CLASS_VIDEO_DISTRIBUTION, "Video Distribution", }, { 0xffff, NULL, } }; static sdp_attr_desc_t sdp_attrs_desc[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, "Record handle", }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, "Service Class ID list", }, { SDP_ATTR_SERVICE_RECORD_STATE, "Service Record State", }, { SDP_ATTR_SERVICE_ID, "Service ID", }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, "Protocol Descriptor List", }, { SDP_ATTR_BROWSE_GROUP_LIST, "Browse Group List", }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, "Language Base Attribute ID List", }, { SDP_ATTR_SERVICE_INFO_TIME_TO_LIVE, "Service Info Time-To-Live", }, { SDP_ATTR_SERVICE_AVAILABILITY, "Service Availability", }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, "Bluetooh Profile Descriptor List", }, { SDP_ATTR_DOCUMENTATION_URL, "Documentation URL", }, { SDP_ATTR_CLIENT_EXECUTABLE_URL, "Client Executable URL", }, { SDP_ATTR_ICON_URL, "Icon URL", }, { SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, "Additional Protocol Descriptor Lists" }, { SDP_ATTR_GROUP_ID, /*SDP_ATTR_IP_SUBNET, SDP_ATTR_VERSION_NUMBER_LIST*/ "Group ID/IP Subnet/Version Number List", }, { SDP_ATTR_SERVICE_DATABASE_STATE, "Service Database State", }, { SDP_ATTR_SERVICE_VERSION, "Service Version", }, { SDP_ATTR_EXTERNAL_NETWORK, /*SDP_ATTR_NETWORK, SDP_ATTR_SUPPORTED_DATA_STORES_LIST*/ "External Network/Network/Supported Data Stores List", }, { SDP_ATTR_FAX_CLASS1_SUPPORT, /*SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL*/ "Fax Class1 Support/Remote Audio Volume Control", }, { SDP_ATTR_FAX_CLASS20_SUPPORT, /*SDP_ATTR_SUPPORTED_FORMATS_LIST*/ "Fax Class20 Support/Supported Formats List", }, { SDP_ATTR_FAX_CLASS2_SUPPORT, "Fax Class2 Support", }, { SDP_ATTR_AUDIO_FEEDBACK_SUPPORT, "Audio Feedback Support", }, { SDP_ATTR_NETWORK_ADDRESS, "Network Address", }, { SDP_ATTR_WAP_GATEWAY, "WAP Gateway", }, { SDP_ATTR_HOME_PAGE_URL, "Home Page URL", }, { SDP_ATTR_WAP_STACK_TYPE, "WAP Stack Type", }, { SDP_ATTR_SECURITY_DESCRIPTION, "Security Description", }, { SDP_ATTR_NET_ACCESS_TYPE, "Net Access Type", }, { SDP_ATTR_MAX_NET_ACCESS_RATE, "Max Net Access Rate", }, { SDP_ATTR_IPV4_SUBNET, "IPv4 Subnet", }, { SDP_ATTR_IPV6_SUBNET, "IPv6 Subnet", }, { SDP_ATTR_SUPPORTED_CAPABALITIES, "Supported Capabalities", }, { SDP_ATTR_SUPPORTED_FEATURES, "Supported Features", }, { SDP_ATTR_SUPPORTED_FUNCTIONS, "Supported Functions", }, { SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY, "Total Imaging Data Capacity", }, { SDP_ATTR_SUPPORTED_REPOSITORIES, "Supported Repositories", }, { 0xffff, NULL, } }; char const * sdp_attr2desc(uint16_t attr) { register sdp_attr_desc_p a = sdp_attrs_desc; for (; a->desc != NULL; a++) if (attr == a->attr) break; return ((a->desc != NULL)? a->desc : "Unknown"); } char const * sdp_uuid2desc(uint16_t uuid) { register sdp_attr_desc_p a = sdp_uuids_desc; for (; a->desc != NULL; a++) if (uuid == a->attr) break; return ((a->desc != NULL)? a->desc : "Unknown"); } void sdp_print(uint32_t level, uint8_t const *start, uint8_t const *end) { union { int8_t int8; int16_t int16; int32_t int32; int64_t int64; int128_t int128; uint8_t uint8; uint16_t uint16; uint32_t uint32; uint64_t uint64; } value; uint8_t type; uint32_t i; if (start == NULL || end == NULL) return; while (start < end) { for (i = 0; i < level; i++) printf("\t"); SDP_GET8(type, start); switch (type) { case SDP_DATA_NIL: printf("nil\n"); break; case SDP_DATA_UINT8: SDP_GET8(value.uint8, start); printf("uint8 %u\n", value.uint8); break; case SDP_DATA_UINT16: SDP_GET16(value.uint16, start); printf("uint16 %u\n", value.uint16); break; case SDP_DATA_UINT32: SDP_GET32(value.uint32, start); printf("uint32 %u\n", value.uint32); break; case SDP_DATA_UINT64: SDP_GET64(value.uint64, start); printf("uint64 %ju\n", value.uint64); break; case SDP_DATA_UINT128: case SDP_DATA_INT128: SDP_GET128(&value.int128, start); printf("u/int128 %#8.8x%8.8x%8.8x%8.8x\n", *(uint32_t *)&value.int128.b[0], *(uint32_t *)&value.int128.b[4], *(uint32_t *)&value.int128.b[8], *(uint32_t *)&value.int128.b[12]); break; case SDP_DATA_UUID128: SDP_GET_UUID128(&value.int128, start); printf("uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", ntohl(*(uint32_t *)&value.int128.b[0]), ntohs(*(uint16_t *)&value.int128.b[4]), ntohs(*(uint16_t *)&value.int128.b[6]), ntohs(*(uint16_t *)&value.int128.b[8]), ntohs(*(uint16_t *)&value.int128.b[10]), ntohl(*(uint32_t *)&value.int128.b[12])); break; case SDP_DATA_INT8: SDP_GET8(value.int8, start); printf("int8 %d\n", value.int8); break; case SDP_DATA_INT16: SDP_GET16(value.int16, start); printf("int16 %d\n", value.int16); break; case SDP_DATA_INT32: SDP_GET32(value.int32, start); printf("int32 %d\n", value.int32); break; case SDP_DATA_INT64: SDP_GET64(value.int64, start); printf("int64 %ju\n", value.int64); break; case SDP_DATA_UUID16: SDP_GET16(value.uint16, start); printf("uuid16 %#4.4x - %s\n", value.uint16, sdp_uuid2desc(value.uint16)); break; case SDP_DATA_UUID32: SDP_GET32(value.uint32, start); printf("uuid32 %#8.8x\n", value.uint32); break; case SDP_DATA_STR8: SDP_GET8(value.uint8, start); printf("str8 %*.*s\n", value.uint8, value.uint8, start); start += value.uint8; break; case SDP_DATA_STR16: SDP_GET16(value.uint16, start); printf("str16 %*.*s\n", value.uint16, value.uint16, start); start += value.uint16; break; case SDP_DATA_STR32: SDP_GET32(value.uint32, start); printf("str32 %*.*s\n", value.uint32, value.uint32, start); start += value.uint32; break; case SDP_DATA_BOOL: SDP_GET8(value.uint8, start); printf("bool %d\n", value.uint8); break; case SDP_DATA_SEQ8: SDP_GET8(value.uint8, start); printf("seq8 %d\n", value.uint8); sdp_print(level + 1, start, start + value.uint8); start += value.uint8; break; case SDP_DATA_SEQ16: SDP_GET16(value.uint16, start); printf("seq16 %d\n", value.uint16); sdp_print(level + 1, start, start + value.uint16); start += value.uint16; break; case SDP_DATA_SEQ32: SDP_GET32(value.uint32, start); printf("seq32 %d\n", value.uint32); sdp_print(level + 1, start, start + value.uint32); start += value.uint32; break; case SDP_DATA_ALT8: SDP_GET8(value.uint8, start); printf("alt8 %d\n", value.uint8); sdp_print(level + 1, start, start + value.uint8); start += value.uint8; break; case SDP_DATA_ALT16: SDP_GET16(value.uint16, start); printf("alt16 %d\n", value.uint16); sdp_print(level + 1, start, start + value.uint16); start += value.uint16; break; case SDP_DATA_ALT32: SDP_GET32(value.uint32, start); printf("alt32 %d\n", value.uint32); sdp_print(level + 1, start, start + value.uint32); start += value.uint32; break; case SDP_DATA_URL8: SDP_GET8(value.uint8, start); printf("url8 %*.*s\n", value.uint8, value.uint8, start); start += value.uint8; break; case SDP_DATA_URL16: SDP_GET16(value.uint16, start); printf("url16 %*.*s\n", value.uint16, value.uint16, start); start += value.uint16; break; case SDP_DATA_URL32: SDP_GET32(value.uint32, start); printf("url32 %*.*s\n", value.uint32, value.uint32, start); start += value.uint32; break; default: printf("unknown data type: %#02x\n", *start ++); break; } } } Index: head/usr.bin/bluetooth/bthost/bthost.c =================================================================== --- head/usr.bin/bluetooth/bthost/bthost.c (revision 281209) +++ head/usr.bin/bluetooth/bthost/bthost.c (revision 281210) @@ -1,142 +1,143 @@ /* * bthost.c * * Copyright (c) 2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthost.c,v 1.5 2003/05/21 20:30:01 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include static int hostmode (char const *arg, int brief); static int protomode (char const *arg, int brief); static void usage (void); int main(int argc, char **argv) { int opt, brief = 0, proto = 0; while ((opt = getopt(argc, argv, "bhp")) != -1) { switch (opt) { case 'b': brief = 1; break; case 'p': proto = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; if (argc < 1) usage(); exit(proto? protomode(*argv, brief) : hostmode(*argv, brief)); } static int hostmode(char const *arg, int brief) { struct hostent *he = NULL; bdaddr_t ba; char bastr[32]; int reverse; if (bt_aton(arg, &ba) == 1) { reverse = 1; he = bt_gethostbyaddr((char const *) &ba, sizeof(ba), AF_BLUETOOTH); } else { reverse = 0; he = bt_gethostbyname(arg); } if (he == NULL) { herror(reverse? bt_ntoa(&ba, bastr) : arg); return (1); } if (brief) printf("%s", reverse? he->h_name : bt_ntoa((bdaddr_t *)(he->h_addr), bastr)); else printf("Host %s has %s %s\n", reverse? bt_ntoa(&ba, bastr) : arg, reverse? "name" : "address", reverse? he->h_name : bt_ntoa((bdaddr_t *)(he->h_addr), bastr)); return (0); } static int protomode(char const *arg, int brief) { struct protoent *pe = NULL; int proto; if ((proto = atoi(arg)) != 0) pe = bt_getprotobynumber(proto); else pe = bt_getprotobyname(arg); if (pe == NULL) { fprintf(stderr, "%s: Unknown Protocol/Service Multiplexor\n", arg); return (1); } if (brief) { if (proto) printf("%s", pe->p_name); else printf("%d", pe->p_proto); } else { printf("Protocol/Service Multiplexor %s has number %d\n", pe->p_name, pe->p_proto); } return (0); } static void usage(void) { fprintf(stdout, "Usage: bthost [-b -h -p] host_or_protocol\n"); exit(255); } Index: head/usr.bin/bluetooth/btsockstat/btsockstat.c =================================================================== --- head/usr.bin/bluetooth/btsockstat/btsockstat.c (revision 281209) +++ head/usr.bin/bluetooth/btsockstat/btsockstat.c (revision 281210) @@ -1,644 +1,645 @@ /* * btsockstat.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: btsockstat.c,v 1.8 2003/05/21 22:40:25 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void hcirawpr (kvm_t *kvmd, u_long addr); static void l2caprawpr (kvm_t *kvmd, u_long addr); static void l2cappr (kvm_t *kvmd, u_long addr); static void l2caprtpr (kvm_t *kvmd, u_long addr); static void rfcommpr (kvm_t *kvmd, u_long addr); static void rfcommpr_s (kvm_t *kvmd, u_long addr); static char * bdaddrpr (bdaddr_p const ba, char *str, int len); static kvm_t * kopen (char const *memf); static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size); static void usage (void); /* * List of symbols */ static struct nlist nl[] = { #define N_HCI_RAW 0 { "_ng_btsocket_hci_raw_sockets" }, #define N_L2CAP_RAW 1 { "_ng_btsocket_l2cap_raw_sockets" }, #define N_L2CAP 2 { "_ng_btsocket_l2cap_sockets" }, #define N_L2CAP_RAW_RT 3 { "_ng_btsocket_l2cap_raw_rt" }, #define N_L2CAP_RT 4 { "_ng_btsocket_l2cap_rt" }, #define N_RFCOMM 5 { "_ng_btsocket_rfcomm_sockets" }, #define N_RFCOMM_S 6 { "_ng_btsocket_rfcomm_sessions" }, { "" }, }; #define state2str(x) \ (((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)]) /* * Main */ static int numeric_bdaddr = 0; int main(int argc, char *argv[]) { int opt, proto = -1, route = 0; kvm_t *kvmd = NULL; char *memf = NULL; while ((opt = getopt(argc, argv, "hnM:p:r")) != -1) { switch (opt) { case 'n': numeric_bdaddr = 1; break; case 'M': memf = optarg; break; case 'p': if (strcasecmp(optarg, "hci_raw") == 0) proto = N_HCI_RAW; else if (strcasecmp(optarg, "l2cap_raw") == 0) proto = N_L2CAP_RAW; else if (strcasecmp(optarg, "l2cap") == 0) proto = N_L2CAP; else if (strcasecmp(optarg, "rfcomm") == 0) proto = N_RFCOMM; else if (strcasecmp(optarg, "rfcomm_s") == 0) proto = N_RFCOMM_S; else usage(); /* NOT REACHED */ break; case 'r': route = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } if ((proto == N_HCI_RAW || proto == N_RFCOMM || proto == N_RFCOMM_S) && route) usage(); /* NOT REACHED */ /* * Discard setgid privileges if not the running kernel so that * bad guys can't print interesting stuff from kernel memory. */ if (memf != NULL) setgid(getgid()); kvmd = kopen(memf); if (kvmd == NULL) return (1); switch (proto) { case N_HCI_RAW: hcirawpr(kvmd, nl[N_HCI_RAW].n_value); break; case N_L2CAP_RAW: if (route) l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); else l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); break; case N_L2CAP: if (route) l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); else l2cappr(kvmd, nl[N_L2CAP].n_value); break; case N_RFCOMM: rfcommpr(kvmd, nl[N_RFCOMM].n_value); break; case N_RFCOMM_S: rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value); break; default: if (route) { l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); } else { hcirawpr(kvmd, nl[N_HCI_RAW].n_value); l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); l2cappr(kvmd, nl[N_L2CAP].n_value); rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value); rfcommpr(kvmd, nl[N_RFCOMM].n_value); } break; } return (kvm_close(kvmd)); } /* main */ /* * Print raw HCI sockets */ static void hcirawpr(kvm_t *kvmd, u_long addr) { ng_btsocket_hci_raw_pcb_p this = NULL, next = NULL; ng_btsocket_hci_raw_pcb_t pcb; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active raw HCI sockets\n" \ "%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n", "Socket", "PCB", "Flags", "Recv-Q", "Send-Q", "Local address"); } if (pcb.addr.hci_node[0] == 0) { pcb.addr.hci_node[0] = '*'; pcb.addr.hci_node[1] = 0; } fprintf(stdout, "%-8lx %-8lx %-6.6x %6d %6d %-16.16s\n", (unsigned long) pcb.so, (unsigned long) this, pcb.flags, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, pcb.addr.hci_node); } } /* hcirawpr */ /* * Print raw L2CAP sockets */ static void l2caprawpr(kvm_t *kvmd, u_long addr) { ng_btsocket_l2cap_raw_pcb_p this = NULL, next = NULL; ng_btsocket_l2cap_raw_pcb_t pcb; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active raw L2CAP sockets\n" \ "%-8.8s %-8.8s %-6.6s %-6.6s %-17.17s\n", "Socket", "PCB", "Recv-Q", "Send-Q", "Local address"); } fprintf(stdout, "%-8lx %-8lx %6d %6d %-17.17s\n", (unsigned long) pcb.so, (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, NULL, 0)); } } /* l2caprawpr */ /* * Print L2CAP sockets */ static void l2cappr(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_L2CAP_CLOSED */ "CLOSED", /* NG_BTSOCKET_L2CAP_CONNECTING */ "CON", /* NG_BTSOCKET_L2CAP_CONFIGURING */ "CONFIG", /* NG_BTSOCKET_L2CAP_OPEN */ "OPEN", /* NG_BTSOCKET_L2CAP_DISCONNECTING */ "DISCON" }; ng_btsocket_l2cap_pcb_p this = NULL, next = NULL; ng_btsocket_l2cap_pcb_t pcb; struct socket so; int first = 1; char local[24], remote[24]; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active L2CAP sockets\n" \ "%-8.8s %-6.6s %-6.6s %-23.23s %-17.17s %-5.5s %s\n", "PCB", "Recv-Q", "Send-Q", "Local address/PSM", "Foreign address", "CID", "State"); } fprintf(stdout, "%-8lx %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n", (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, local, sizeof(local)), pcb.psm, bdaddrpr(&pcb.dst, remote, sizeof(remote)), pcb.cid, (so.so_options & SO_ACCEPTCONN)? "LISTEN" : state2str(pcb.state)); } } /* l2cappr */ /* * Print L2CAP routing table */ static void l2caprtpr(kvm_t *kvmd, u_long addr) { ng_btsocket_l2cap_rtentry_p this = NULL, next = NULL; ng_btsocket_l2cap_rtentry_t rt; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0) return; next = LIST_NEXT(&rt, next); if (first) { first = 0; fprintf(stdout, "Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)? "raw " : ""); fprintf(stdout, "%-8.8s %-8.8s %-17.17s\n", "RTentry", "Hook", "BD_ADDR"); } fprintf(stdout, "%-8lx %-8lx %-17.17s\n", (unsigned long) this, (unsigned long) rt.hook, bdaddrpr(&rt.src, NULL, 0)); } } /* l2caprtpr */ /* * Print RFCOMM sockets */ static void rfcommpr(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_RFCOMM_DLC_CLOSED */ "CLOSED", /* NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT */ "W4CON", /* NG_BTSOCKET_RFCOMM_DLC_CONFIGURING */ "CONFIG", /* NG_BTSOCKET_RFCOMM_DLC_CONNECTING */ "CONN", /* NG_BTSOCKET_RFCOMM_DLC_CONNECTED */ "OPEN", /* NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING */ "DISCON" }; ng_btsocket_rfcomm_pcb_p this = NULL, next = NULL; ng_btsocket_rfcomm_pcb_t pcb; struct socket so; int first = 1; char local[24], remote[24]; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active RFCOMM sockets\n" \ "%-8.8s %-6.6s %-6.6s %-17.17s %-17.17s %-4.4s %-4.4s %s\n", "PCB", "Recv-Q", "Send-Q", "Local address", "Foreign address", "Chan", "DLCI", "State"); } fprintf(stdout, "%-8lx %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n", (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, local, sizeof(local)), bdaddrpr(&pcb.dst, remote, sizeof(remote)), pcb.channel, pcb.dlci, (so.so_options & SO_ACCEPTCONN)? "LISTEN" : state2str(pcb.state)); } } /* rfcommpr */ /* * Print RFCOMM sessions */ static void rfcommpr_s(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_RFCOMM_SESSION_CLOSED */ "CLOSED", /* NG_BTSOCKET_RFCOMM_SESSION_LISTENING */ "LISTEN", /* NG_BTSOCKET_RFCOMM_SESSION_CONNECTING */ "CONNECTING", /* NG_BTSOCKET_RFCOMM_SESSION_CONNECTED */ "CONNECTED", /* NG_BTSOCKET_RFCOMM_SESSION_OPEN */ "OPEN", /* NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING */ "DISCONNECTING" }; ng_btsocket_rfcomm_session_p this = NULL, next = NULL; ng_btsocket_rfcomm_session_t s; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &s, sizeof(s)) < 0) return; if (kread(kvmd, (u_long) s.l2so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&s, next); if (first) { first = 0; fprintf(stdout, "Active RFCOMM sessions\n" \ "%-8.8s %-8.8s %-4.4s %-5.5s %-5.5s %-4.4s %s\n", "L2PCB", "PCB", "Flags", "MTU", "Out-Q", "DLCs", "State"); } fprintf(stdout, "%-8lx %-8lx %-4x %-5d %-5d %-4s %s\n", (unsigned long) so.so_pcb, (unsigned long) this, s.flags, s.mtu, s.outq.len, LIST_EMPTY(&s.dlcs)? "No" : "Yes", state2str(s.state)); } } /* rfcommpr_s */ /* * Return BD_ADDR as string */ static char * bdaddrpr(bdaddr_p const ba, char *str, int len) { static char buffer[MAXHOSTNAMELEN]; struct hostent *he = NULL; if (str == NULL) { str = buffer; len = sizeof(buffer); } if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) { str[0] = '*'; str[1] = 0; return (str); } if (!numeric_bdaddr && (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) { strlcpy(str, he->h_name, len); return (str); } bt_ntoa(ba, str); return (str); } /* bdaddrpr */ /* * Open kvm */ static kvm_t * kopen(char const *memf) { kvm_t *kvmd = NULL; char errbuf[_POSIX2_LINE_MAX]; /* * Discard setgid privileges if not the running kernel so that * bad guys can't print interesting stuff from kernel memory. */ if (memf != NULL) setgid(getgid()); kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf); if (kvmd == NULL) { warnx("kvm_openfiles: %s", errbuf); return (NULL); } if (kvm_nlist(kvmd, nl) < 0) { warnx("kvm_nlist: %s", kvm_geterr(kvmd)); goto fail; } if (nl[0].n_type == 0) { warnx("kvm_nlist: no namelist"); goto fail; } return (kvmd); fail: kvm_close(kvmd); return (NULL); } /* kopen */ /* * Read kvm */ static int kread(kvm_t *kvmd, u_long addr, char *buffer, int size) { if (kvmd == NULL || buffer == NULL) return (-1); if (kvm_read(kvmd, addr, buffer, size) != size) { warnx("kvm_read: %s", kvm_geterr(kvmd)); return (-1); } return (0); } /* kread */ /* * Print usage and exit */ static void usage(void) { fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n"); exit(255); } /* usage */ Index: head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c =================================================================== --- head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c (revision 281209) +++ head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c (revision 281210) @@ -1,266 +1,266 @@ /* * rfcomm_sdp.c * * Copyright (c) 2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rfcomm_sdp.c,v 1.1 2003/09/07 18:15:55 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include #undef PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE #define PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE 256 #undef PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE #define PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE 12 static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end, int *channel, int *error); /* * Lookup RFCOMM channel number in the Protocol Descriptor List */ #undef rfcomm_channel_lookup_exit #define rfcomm_channel_lookup_exit(e) { \ if (error != NULL) \ *error = (e); \ if (ss != NULL) { \ sdp_close(ss); \ ss = NULL; \ } \ return (((e) == 0)? 0 : -1); \ } int rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote, int service, int *channel, int *error) { uint8_t buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE]; void *ss = NULL; uint16_t serv = (uint16_t) service; uint32_t attr = SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST); sdp_attr_t proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer }; uint32_t type, len; if (local == NULL) local = NG_HCI_BDADDR_ANY; if (remote == NULL || channel == NULL) rfcomm_channel_lookup_exit(EINVAL); if ((ss = sdp_open(local, remote)) == NULL) rfcomm_channel_lookup_exit(ENOMEM); if (sdp_error(ss) != 0) rfcomm_channel_lookup_exit(sdp_error(ss)); if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0) rfcomm_channel_lookup_exit(sdp_error(ss)); if (proto.flags != SDP_ATTR_OK) rfcomm_channel_lookup_exit(ENOATTR); sdp_close(ss); ss = NULL; /* * If it is possible for more than one kind of protocol stack to be * used to gain access to the service, the ProtocolDescriptorList * takes the form of a data element alternative. We always use the * first protocol stack. * * A minimal Protocol Descriptor List for RFCOMM based service would * look like * * seq8 len8 - 2 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes L2CAP * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes RFCOMM * uint8 value8 - 2 bytes RFCOMM param #1 * ========= * 14 bytes * * Lets not count first [seq8 len8] wrapper, so the minimal size of * the Protocol Descriptor List (the data we are actually interested * in) for RFCOMM based service would be 12 bytes. */ if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE) rfcomm_channel_lookup_exit(EINVAL); SDP_GET8(type, proto.value); if (type == SDP_DATA_ALT8) { SDP_GET8(len, proto.value); } else if (type == SDP_DATA_ALT16) { SDP_GET16(len, proto.value); } else if (type == SDP_DATA_ALT32) { SDP_GET32(len, proto.value); } else len = 0; if (len > 0) SDP_GET8(type, proto.value); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, proto.value); break; case SDP_DATA_SEQ16: SDP_GET16(len, proto.value); break; case SDP_DATA_SEQ32: SDP_GET32(len, proto.value); break; default: rfcomm_channel_lookup_exit(ENOATTR); /* NOT REACHED */ } if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE) rfcomm_channel_lookup_exit(EINVAL); return (rfcomm_proto_list_parse(proto.value, buffer + proto.vlen, channel, error)); } /* * Parse protocol descriptor list * * The ProtocolDescriptorList attribute describes one or more protocol * stacks that may be used to gain access to the service described by * the service record. If the ProtocolDescriptorList describes a single * stack, it takes the form of a data element sequence in which each * element of the sequence is a protocol descriptor. */ #undef rfcomm_proto_list_parse_exit #define rfcomm_proto_list_parse_exit(e) { \ if (error != NULL) \ *error = (e); \ return (((e) == 0)? 0 : -1); \ } static int rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end, int *channel, int *error) { int type, len, value; while (start < end) { /* * Parse protocol descriptor * * A protocol descriptor identifies a communications protocol * and provides protocol specific parameters. A protocol * descriptor is represented as a data element sequence. The * first data element in the sequence must be the UUID that * identifies the protocol. Additional data elements optionally * provide protocol specific information, such as the L2CAP * protocol/service multiplexer (PSM) and the RFCOMM server * channel number (CN). */ /* We must have at least one byte (type) */ if (end - start < 1) rfcomm_proto_list_parse_exit(EINVAL) SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: rfcomm_proto_list_parse_exit(ENOATTR) /* NOT REACHED */ } /* We must have at least 3 bytes (type + UUID16) */ if (end - start < 3) rfcomm_proto_list_parse_exit(EINVAL); /* Get protocol UUID */ SDP_GET8(type, start); len -= sizeof(uint8_t); switch (type) { case SDP_DATA_UUID16: SDP_GET16(value, start); len -= sizeof(uint16_t); if (value != SDP_UUID_PROTOCOL_RFCOMM) goto next_protocol; break; case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ default: rfcomm_proto_list_parse_exit(ENOATTR); /* NOT REACHED */ } /* * First protocol specific parameter for RFCOMM procotol must * be uint8 that represents RFCOMM channel number. So we must * have at least two bytes. */ if (end - start < 2) rfcomm_proto_list_parse_exit(EINVAL); SDP_GET8(type, start); if (type != SDP_DATA_UINT8) rfcomm_proto_list_parse_exit(ENOATTR); SDP_GET8(*channel, start); rfcomm_proto_list_parse_exit(0); /* NOT REACHED */ next_protocol: start += len; } /* * If we got here then it means we could not find RFCOMM protocol * descriptor, but the reply format was actually valid. */ rfcomm_proto_list_parse_exit(ENOATTR); } Index: head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c =================================================================== --- head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 281209) +++ head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 281210) @@ -1,459 +1,460 @@ /* * rfcomm_sppd.c */ /*- * Copyright (c) 2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rfcomm_sppd.c,v 1.4 2003/09/07 18:15:55 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SPPD_IDENT "rfcomm_sppd" #define SPPD_BUFFER_SIZE 1024 #define max(a, b) (((a) > (b))? (a) : (b)) int rfcomm_channel_lookup (bdaddr_t const *local, bdaddr_t const *remote, int service, int *channel, int *error); static int sppd_ttys_open (char **tty, int *amaster, int *aslave); static int sppd_read (int fd, char *buffer, int size); static int sppd_write (int fd, char *buffer, int size); static void sppd_sighandler (int s); static void usage (void); static int done; /* are we done? */ /* Main */ int main(int argc, char *argv[]) { struct sigaction sa; struct sockaddr_rfcomm ra; bdaddr_t addr; int n, background, channel, service, s, amaster, aslave, fd, doserver, dopty; fd_set rfd; char *tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE]; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); background = channel = 0; service = SDP_SERVICE_CLASS_SERIAL_PORT; doserver = 0; dopty = 0; /* Parse command line options */ while ((n = getopt(argc, argv, "a:bc:thS")) != -1) { switch (n) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* RFCOMM channel */ channel = strtoul(optarg, &ep, 10); if (*ep != '\0') { channel = 0; switch (tolower(optarg[0])) { case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'f': /* Fax */ service = SDP_SERVICE_CLASS_FAX; break; case 'l': /* LAN */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; case 's': /* Serial Port */ service = SDP_SERVICE_CLASS_SERIAL_PORT; break; default: errx(1, "Unknown service name: %s", optarg); /* NOT REACHED */ } } break; case 'b': /* Run in background */ background = 1; break; case 't': /* Open pseudo TTY */ dopty = 1; break; case 'S': doserver = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we have everything we need */ if (!doserver && memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) usage(); /* NOT REACHED */ /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sppd_sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "Could not sigaction(SIGTERM)"); if (sigaction(SIGHUP, &sa, NULL) < 0) err(1, "Could not sigaction(SIGHUP)"); if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) err(1, "Could not sigaction(SIGCHLD)"); /* Open TTYs */ if (dopty) { if (sppd_ttys_open(&tty, &amaster, &aslave) < 0) exit(1); fd = amaster; } else { if (background) usage(); amaster = STDIN_FILENO; fd = STDOUT_FILENO; } /* Open RFCOMM connection */ if (doserver) { struct sockaddr_rfcomm ma; bdaddr_t bt_addr_any; sdp_sp_profile_t sp; void *ss; uint32_t sdp_handle; int acceptsock, aaddrlen; acceptsock = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (acceptsock < 0) err(1, "Could not create socket"); memcpy(&bt_addr_any, NG_HCI_BDADDR_ANY, sizeof(bt_addr_any)); memset(&ma, 0, sizeof(ma)); ma.rfcomm_len = sizeof(ma); ma.rfcomm_family = AF_BLUETOOTH; memcpy(&ma.rfcomm_bdaddr, &bt_addr_any, sizeof(bt_addr_any)); ma.rfcomm_channel = channel; if (bind(acceptsock, (struct sockaddr *)&ma, sizeof(ma)) < 0) err(1, "Could not bind socket on channel %d", channel); if (listen(acceptsock, 10) != 0) err(1, "Could not listen on socket"); aaddrlen = sizeof(ma); if (getsockname(acceptsock, (struct sockaddr *)&ma, &aaddrlen) < 0) err(1, "Could not get socket name"); channel = ma.rfcomm_channel; ss = sdp_open_local(NULL); if (ss == NULL) errx(1, "Unable to create local SDP session"); if (sdp_error(ss) != 0) errx(1, "Unable to open local SDP session. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); memset(&sp, 0, sizeof(sp)); sp.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_SERIAL_PORT, &bt_addr_any, (void *)&sp, sizeof(sp), &sdp_handle) != 0) { errx(1, "Unable to register LAN service with " "local SDP daemon. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); } s = -1; while (s < 0) { aaddrlen = sizeof(ra); s = accept(acceptsock, (struct sockaddr *)&ra, &aaddrlen); if (s < 0) err(1, "Unable to accept()"); if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) && memcmp(&addr, &ra.rfcomm_bdaddr, sizeof(addr))) { warnx("Connect from wrong client"); close(s); s = -1; } } sdp_unregister_service(ss, sdp_handle); sdp_close(ss); close(acceptsock); } else { /* Check channel, if was not set then obtain it via SDP */ if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, service, &channel, &n) != 0) errc(1, n, "Could not obtain RFCOMM channel"); if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) err(1, "Could not create socket"); memset(&ra, 0, sizeof(ra)); ra.rfcomm_len = sizeof(ra); ra.rfcomm_family = AF_BLUETOOTH; if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not bind socket"); memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); ra.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not connect socket"); } /* Became daemon if required */ if (background && daemon(0, 0) < 0) err(1, "Could not daemon()"); openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Starting on %s...", (tty != NULL)? tty : "stdin/stdout"); /* Print used tty on stdout for wrappers to pick up */ if (!background) fprintf(stdout, "%s\n", tty); for (done = 0; !done; ) { FD_ZERO(&rfd); FD_SET(amaster, &rfd); FD_SET(s, &rfd); n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "Could not select(). %s", strerror(errno)); exit(1); } if (n == 0) continue; if (FD_ISSET(amaster, &rfd)) { n = sppd_read(amaster, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read master pty, " \ "fd=%d. %s", amaster, strerror(errno)); exit(1); } if (n == 0) break; /* XXX */ if (sppd_write(s, buf, n) < 0) { syslog(LOG_ERR, "Could not write to socket, " \ "fd=%d, size=%d. %s", s, n, strerror(errno)); exit(1); } } if (FD_ISSET(s, &rfd)) { n = sppd_read(s, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read socket, " \ "fd=%d. %s", s, strerror(errno)); exit(1); } if (n == 0) break; if (sppd_write(fd, buf, n) < 0) { syslog(LOG_ERR, "Could not write to master " \ "pty, fd=%d, size=%d. %s", fd, n, strerror(errno)); exit(1); } } } syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout"); closelog(); close(s); if (tty != NULL) { close(aslave); close(amaster); } return (0); } /* Open TTYs */ static int sppd_ttys_open(char **tty, int *amaster, int *aslave) { char pty[PATH_MAX]; struct termios tio; cfmakeraw(&tio); if (openpty(amaster, aslave, pty, &tio, NULL) == -1) { syslog(LOG_ERR, "Could not openpty(). %s", strerror(errno)); return (-1); } if ((*tty = strdup(pty)) == NULL) { syslog(LOG_ERR, "Could not strdup(). %s", strerror(errno)); close(*aslave); close(*amaster); return (-1); } return (0); } /* sppd_ttys_open */ /* Read data */ static int sppd_read(int fd, char *buffer, int size) { int n; again: n = read(fd, buffer, size); if (n < 0) { if (errno == EINTR) goto again; return (-1); } return (n); } /* sppd_read */ /* Write data */ static int sppd_write(int fd, char *buffer, int size) { int n, wrote; for (wrote = 0; size > 0; ) { n = write(fd, buffer, size); switch (n) { case -1: if (errno != EINTR) return (-1); break; case 0: /* XXX can happen? */ break; default: wrote += n; buffer += n; size -= n; break; } } return (wrote); } /* sppd_write */ /* Signal handler */ static void sppd_sighandler(int s) { syslog(LOG_INFO, "Signal %d received. Total %d signals received\n", s, ++ done); } /* sppd_sighandler */ /* Display usage and exit */ static void usage(void) { fprintf(stdout, "Usage: %s options\n" \ "Where options are:\n" \ "\t-a address Peer address (required in client mode)\n" \ "\t-b Run in background\n" \ "\t-c channel RFCOMM channel to connect to or listen on\n" \ "\t-t use slave pseudo tty (required in background mode)\n" \ "\t-S Server mode\n" \ "\t-h Display this message\n", SPPD_IDENT); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c =================================================================== --- head/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c (revision 281210) @@ -1,215 +1,216 @@ /* * bthidcontrol.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthidcontrol.c,v 1.2 2004/02/13 21:44:41 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidcontrol.h" static int do_bthid_command(bdaddr_p bdaddr, int argc, char **argv); static struct bthid_command * find_bthid_command(char const *command, struct bthid_command *category); static void print_bthid_command(struct bthid_command *category); static void usage(void); int32_t hid_sdp_query(bdaddr_t const *local, bdaddr_t const *remote, int32_t *error); uint32_t verbose = 0; /* * bthidcontrol */ int main(int argc, char *argv[]) { bdaddr_t bdaddr; int opt; hid_init(NULL); memcpy(&bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr)); while ((opt = getopt(argc, argv, "a:c:H:hv")) != -1) { switch (opt) { case 'a': /* bdaddr */ if (!bt_aton(optarg, &bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&bdaddr, he->h_addr, sizeof(bdaddr)); } break; case 'c': /* config file */ config_file = optarg; break; case 'H': /* HIDs file */ hids_file = optarg; break; case 'v': /* verbose */ verbose++; break; case 'h': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; if (*argv == NULL) usage(); return (do_bthid_command(&bdaddr, argc, argv)); } /* main */ /* Execute commands */ static int do_bthid_command(bdaddr_p bdaddr, int argc, char **argv) { char *cmd = argv[0]; struct bthid_command *c = NULL; int e, help; help = 0; if (strcasecmp(cmd, "help") == 0) { argc --; argv ++; if (argc <= 0) { fprintf(stdout, "Supported commands:\n"); print_bthid_command(sdp_commands); print_bthid_command(hid_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); return (OK); } help = 1; cmd = argv[0]; } c = find_bthid_command(cmd, sdp_commands); if (c == NULL) c = find_bthid_command(cmd, hid_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); return (ERROR); } if (!help) e = (c->handler)(bdaddr, -- argc, ++ argv); else e = USAGE; switch (e) { case OK: case FAILED: break; case ERROR: fprintf(stdout, "Could not execute command \"%s\". %s\n", cmd, strerror(errno)); break; case USAGE: fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); break; default: assert(0); break; } return (e); } /* do_bthid_command */ /* Try to find command in specified category */ static struct bthid_command * find_bthid_command(char const *command, struct bthid_command *category) { struct bthid_command *c = NULL; for (c = category; c->command != NULL; c++) { char *c_end = strchr(c->command, ' '); if (c_end != NULL) { int len = c_end - c->command; if (strncasecmp(command, c->command, len) == 0) return (c); } else if (strcasecmp(command, c->command) == 0) return (c); } return (NULL); } /* find_bthid_command */ /* Print commands in specified category */ static void print_bthid_command(struct bthid_command *category) { struct bthid_command *c = NULL; for (c = category; c->command != NULL; c++) fprintf(stdout, "\t%s\n", c->command); } /* print_bthid_command */ /* Usage */ static void usage(void) { fprintf(stderr, "Usage: bthidcontrol options command\n" \ "Where options are:\n" " -a bdaddr specify bdaddr\n" \ " -c file specify path to the bthidd config file\n" \ " -H file specify path to the bthidd HIDs file\n" \ " -h display usage and quit\n" \ " -v be verbose\n" \ " command one of the supported commands\n"); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/bthidcontrol/hid.c =================================================================== --- head/usr.sbin/bluetooth/bthidcontrol/hid.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidcontrol/hid.c (revision 281210) @@ -1,214 +1,215 @@ /* * hid.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: hid.c,v 1.3 2004/02/17 22:14:57 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include "bthid_config.h" #include "bthidcontrol.h" extern uint32_t verbose; static void hid_dump_descriptor (report_desc_t r); static void hid_dump_item (char const *label, struct hid_item *h); static int hid_dump(bdaddr_t *bdaddr, int argc, char **argv) { struct hid_device *hd = NULL; int e = FAILED; if (read_config_file() == 0) { if ((hd = get_hid_device(bdaddr)) != NULL) { hid_dump_descriptor(hd->desc); e = OK; } clean_config(); } return (e); } static int hid_forget(bdaddr_t *bdaddr, int argc, char **argv) { struct hid_device *hd = NULL; int e = FAILED; if (read_config_file() == 0) { if (read_hids_file() == 0) { if ((hd = get_hid_device(bdaddr)) != NULL) { hd->new_device = 1; if (write_hids_file() == 0) e = OK; } } clean_config(); } return (e); } static int hid_known(bdaddr_t *bdaddr, int argc, char **argv) { struct hid_device *hd = NULL; struct hostent *he = NULL; int e = FAILED; if (read_config_file() == 0) { if (read_hids_file() == 0) { e = OK; for (hd = get_next_hid_device(hd); hd != NULL; hd = get_next_hid_device(hd)) { if (hd->new_device) continue; he = bt_gethostbyaddr((char *) &hd->bdaddr, sizeof(hd->bdaddr), AF_BLUETOOTH); fprintf(stdout, "%s %s\n", bt_ntoa(&hd->bdaddr, NULL), (he != NULL && he->h_name != NULL)? he->h_name : ""); } } clean_config(); } return (e); } static void hid_dump_descriptor(report_desc_t r) { struct hid_data *d = NULL; struct hid_item h; for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) { switch (h.kind) { case hid_collection: fprintf(stdout, "Collection page=%s usage=%s\n", hid_usage_page(HID_PAGE(h.usage)), hid_usage_in_page(h.usage)); break; case hid_endcollection: fprintf(stdout, "End collection\n"); break; case hid_input: hid_dump_item("Input ", &h); break; case hid_output: hid_dump_item("Output ", &h); break; case hid_feature: hid_dump_item("Feature", &h); break; } } hid_end_parse(d); } static void hid_dump_item(char const *label, struct hid_item *h) { if ((h->flags & HIO_CONST) && !verbose) return; fprintf(stdout, "%s id=%u size=%u count=%u page=%s usage=%s%s%s%s%s%s%s%s%s%s", label, (uint8_t) h->report_ID, h->report_size, h->report_count, hid_usage_page(HID_PAGE(h->usage)), hid_usage_in_page(h->usage), h->flags & HIO_CONST ? " Const" : "", h->flags & HIO_VARIABLE ? " Variable" : "", h->flags & HIO_RELATIVE ? " Relative" : "", h->flags & HIO_WRAP ? " Wrap" : "", h->flags & HIO_NONLINEAR ? " NonLinear" : "", h->flags & HIO_NOPREF ? " NoPref" : "", h->flags & HIO_NULLSTATE ? " NullState" : "", h->flags & HIO_VOLATILE ? " Volatile" : "", h->flags & HIO_BUFBYTES ? " BufBytes" : ""); fprintf(stdout, ", logical range %d..%d", h->logical_minimum, h->logical_maximum); if (h->physical_minimum != h->physical_maximum) fprintf(stdout, ", physical range %d..%d", h->physical_minimum, h->physical_maximum); if (h->unit) fprintf(stdout, ", unit=0x%02x exp=%d", h->unit, h->unit_exponent); fprintf(stdout, "\n"); } struct bthid_command hid_commands[] = { { "Dump", "Dump HID descriptor for the specified device in human readable form. The\n" \ "device must have an entry in the Bluetooth HID daemon configuration file.\n", hid_dump }, { "Known", "List all known to the Bluetooth HID daemon devices.\n", hid_known }, { "Forget", "Forget (mark as new) specified HID device. This command is useful when it\n" \ "is required to remove device from the known HIDs file. This should be done\n" \ "when reset button was pressed on the device or the battery was changed. The\n"\ "Bluetooth HID daemon should be restarted.\n", hid_forget }, { NULL, NULL, NULL } }; Index: head/usr.sbin/bluetooth/bthidcontrol/sdp.c =================================================================== --- head/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 281210) @@ -1,434 +1,435 @@ /* * sdp.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidcontrol.h" static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error); static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a); static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a); static int32_t hid_sdp_parse_boolean (sdp_attr_p a); static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; static uint32_t attrs[] = { SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS), SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */ 0x0205), SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */ 0x0206), SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */ 0x0209), SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */ 0x020d) }; #define nattrs (sizeof(attrs)/sizeof(attrs[0])) static sdp_attr_t values[8]; #define nvalues (sizeof(values)/sizeof(values[0])) static uint8_t buffer[nvalues][512]; /* * Query remote device */ #undef hid_sdp_query_exit #define hid_sdp_query_exit(e) { \ if (error != NULL) \ *error = (e); \ if (ss != NULL) { \ sdp_close(ss); \ ss = NULL; \ } \ return (((e) == 0)? 0 : -1); \ } static int32_t hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error) { void *ss = NULL; uint8_t *hid_descriptor = NULL; int32_t i, control_psm = -1, interrupt_psm = -1, reconnect_initiate = -1, normally_connectable = 0, battery_power = 0, hid_descriptor_length = -1; if (local == NULL) local = NG_HCI_BDADDR_ANY; if (hd == NULL) hid_sdp_query_exit(EINVAL); for (i = 0; i < nvalues; i ++) { values[i].flags = SDP_ATTR_INVALID; values[i].attr = 0; values[i].vlen = sizeof(buffer[i]); values[i].value = buffer[i]; } if ((ss = sdp_open(local, &hd->bdaddr)) == NULL) hid_sdp_query_exit(ENOMEM); if (sdp_error(ss) != 0) hid_sdp_query_exit(sdp_error(ss)); if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0) hid_sdp_query_exit(sdp_error(ss)); sdp_close(ss); ss = NULL; for (i = 0; i < nvalues; i ++) { if (values[i].flags != SDP_ATTR_OK) continue; switch (values[i].attr) { case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); break; case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); break; case 0x0205: /* HIDReconnectInitiate */ reconnect_initiate = hid_sdp_parse_boolean(&values[i]); break; case 0x0206: /* HIDDescriptorList */ if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) { hid_descriptor = values[i].value; hid_descriptor_length = values[i].vlen; } break; case 0x0209: /* HIDBatteryPower */ battery_power = hid_sdp_parse_boolean(&values[i]); break; case 0x020d: /* HIDNormallyConnectable */ normally_connectable = hid_sdp_parse_boolean(&values[i]); break; } } if (control_psm == -1 || interrupt_psm == -1 || reconnect_initiate == -1 || hid_descriptor == NULL || hid_descriptor_length == -1) hid_sdp_query_exit(ENOATTR); hd->control_psm = control_psm; hd->interrupt_psm = interrupt_psm; hd->reconnect_initiate = reconnect_initiate? 1 : 0; hd->battery_power = battery_power? 1 : 0; hd->normally_connectable = normally_connectable? 1 : 0; hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length); if (hd->desc == NULL) hid_sdp_query_exit(ENOMEM); return (0); } /* * seq len 2 * seq len 2 * uuid value 3 * uint16 value 3 * seq len 2 * uuid value 3 */ static int32_t hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a) { uint8_t *ptr = a->value; uint8_t *end = a->value + a->vlen; int32_t type, len, uuid, psm; if (end - ptr < 15) return (-1); if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); } SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); /* Protocol */ SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); /* UUID */ if (ptr + 3 > end) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_UUID16: SDP_GET16(uuid, ptr); if (uuid != SDP_UUID_PROTOCOL_L2CAP) return (-1); break; case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ default: return (-1); } /* PSM */ if (ptr + 3 > end) return (-1); SDP_GET8(type, ptr); if (type != SDP_DATA_UINT16) return (-1); SDP_GET16(psm, ptr); return (psm); } /* * seq len 2 * seq len 2 * uint8 value8 2 * str value 3 */ static int32_t hid_sdp_parse_hid_descriptor(sdp_attr_p a) { uint8_t *ptr = a->value; uint8_t *end = a->value + a->vlen; int32_t type, len, descriptor_type; if (end - ptr < 9) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); while (ptr < end) { /* Descriptor */ SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: if (ptr + 1 > end) return (-1); SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: if (ptr + 2 > end) return (-1); SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: if (ptr + 4 > end) return (-1); SDP_GET32(len, ptr); break; default: return (-1); } /* Descripor type */ if (ptr + 1 > end) return (-1); SDP_GET8(type, ptr); if (type != SDP_DATA_UINT8 || ptr + 1 > end) return (-1); SDP_GET8(descriptor_type, ptr); /* Descriptor value */ if (ptr + 1 > end) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_STR8: if (ptr + 1 > end) return (-1); SDP_GET8(len, ptr); break; case SDP_DATA_STR16: if (ptr + 2 > end) return (-1); SDP_GET16(len, ptr); break; case SDP_DATA_STR32: if (ptr + 4 > end) return (-1); SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); if (descriptor_type == UDESC_REPORT && len > 0) { a->value = ptr; a->vlen = len; return (0); } ptr += len; } return (-1); } /* bool8 int8 */ static int32_t hid_sdp_parse_boolean(sdp_attr_p a) { if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) return (-1); return (a->value[1]); } /* Perform SDP query */ static int32_t hid_query(bdaddr_t *bdaddr, int argc, char **argv) { struct hid_device hd; int e; memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr)); if (hid_sdp_query(NULL, &hd, &e) < 0) { fprintf(stderr, "Could not perform SDP query on the " \ "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL), strerror(e), e); return (FAILED); } print_hid_device(&hd, stdout); return (OK); } struct bthid_command sdp_commands[] = { { "Query", "Perform SDP query to the specified device and print HID configuration entry\n"\ "for the device. The configuration entry should be appended to the Bluetooth\n"\ "HID daemon configuration file and the daemon should be restarted.\n", hid_query }, { NULL, NULL, NULL } }; Index: head/usr.sbin/bluetooth/bthidd/bthidd.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/bthidd.c (revision 281210) @@ -1,266 +1,267 @@ /* * bthidd.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bthidd.c,v 1.8 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" static int32_t write_pid_file (char const *file); static int32_t remove_pid_file (char const *file); static int32_t elapsed (int32_t tval); static void sighandler (int32_t s); static void usage (void); /* * bthidd */ static int32_t done = 0; /* are we done? */ int32_t main(int32_t argc, char *argv[]) { struct bthid_server srv; struct sigaction sa; char const *pid_file = BTHIDD_PIDFILE; char *ep; int32_t opt, detach, tval; memset(&srv, 0, sizeof(srv)); memset(&srv.bdaddr, 0, sizeof(srv.bdaddr)); detach = 1; tval = 10; /* sec */ while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) { switch (opt) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &srv.bdaddr)) { struct hostent *he; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr)); } break; case 'c': /* config file */ config_file = optarg; break; case 'd': /* do not detach */ detach = 0; break; case 'H': /* hids file */ hids_file = optarg; break; case 'p': /* pid file */ pid_file = optarg; break; case 't': /* rescan interval */ tval = strtol(optarg, (char **) &ep, 10); if (*ep != '\0' || tval <= 0) usage(); break; case 'h': default: usage(); /* NOT REACHED */ } } openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER); /* Become daemon if required */ if (detach && daemon(0, 0) < 0) { syslog(LOG_CRIT, "Could not become daemon. %s (%d)", strerror(errno), errno); exit(1); } /* Install signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) { syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } if (read_config_file() < 0 || read_hids_file() < 0 || server_init(&srv) < 0 || write_pid_file(pid_file) < 0) exit(1); for (done = 0; !done; ) { if (elapsed(tval)) client_rescan(&srv); if (server_do(&srv) < 0) break; } server_shutdown(&srv); remove_pid_file(pid_file); clean_config(); closelog(); return (0); } /* * Write pid file */ static int32_t write_pid_file(char const *file) { FILE *pid; assert(file != NULL); if ((pid = fopen(file, "w")) == NULL) { syslog(LOG_ERR, "Could not open file %s. %s (%d)", file, strerror(errno), errno); return (-1); } fprintf(pid, "%d", getpid()); fclose(pid); return (0); } /* * Remote pid file */ static int32_t remove_pid_file(char const *file) { assert(file != NULL); if (unlink(file) < 0) { syslog(LOG_ERR, "Could not unlink file %s. %s (%d)", file, strerror(errno), errno); return (-1); } return (0); } /* * Returns true if desired time interval has elapsed */ static int32_t elapsed(int32_t tval) { static struct timeval last = { 0, 0 }; struct timeval now; gettimeofday(&now, NULL); if (now.tv_sec - last.tv_sec >= tval) { last = now; return (1); } return (0); } /* * Signal handler */ static void sighandler(int32_t s) { syslog(LOG_NOTICE, "Got signal %d, total number of signals %d", s, ++ done); } /* * Display usage and exit */ static void usage(void) { fprintf(stderr, "Usage: %s [options]\n" \ "Where options are:\n" \ " -a address specify address to listen on (default ANY)\n" \ " -c file specify config file name\n" \ " -d run in foreground\n" \ " -H file specify known HIDs file name\n" \ " -h display this message\n" \ " -p file specify PID file name\n" \ " -t tval specify client rescan interval (sec)\n" \ "", BTHIDD_IDENT); exit(255); } Index: head/usr.sbin/bluetooth/bthidd/client.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/client.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/client.c (revision 281210) @@ -1,256 +1,259 @@ /* * client.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" static int32_t client_socket(bdaddr_p bdaddr, uint16_t psm); /* * Get next config entry and create outbound connection (if required) * * XXX Do only one device at a time. At least one of my devices (3COM * Bluetooth PCCARD) rejects Create_Connection command if another * Create_Connection command is still pending. Weird... */ static int32_t connect_in_progress = 0; int32_t client_rescan(bthid_server_p srv) { static hid_device_p d; bthid_session_p s; assert(srv != NULL); if (connect_in_progress) return (0); /* another connect is still pending */ d = get_next_hid_device(d); if (d == NULL) return (0); /* XXX should not happen? empty config? */ if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) return (0); /* session already active */ if (!d->new_device) { if (d->reconnect_initiate) return (0); /* device will initiate reconnect */ } syslog(LOG_NOTICE, "Opening outbound session for %s " \ "(new_device=%d, reconnect_initiate=%d)", bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate); if ((s = session_open(srv, d)) == NULL) { syslog(LOG_CRIT, "Could not create outbound session for %s", bt_ntoa(&d->bdaddr, NULL)); return (-1); } /* Open control channel */ s->ctrl = client_socket(&s->bdaddr, d->control_psm); if (s->ctrl < 0) { syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); return (-1); } s->state = W4CTRL; FD_SET(s->ctrl, &srv->wfdset); if (s->ctrl > srv->maxfd) srv->maxfd = s->ctrl; connect_in_progress = 1; return (0); } /* * Process connect on the socket */ int32_t client_connect(bthid_server_p srv, int32_t fd) { bthid_session_p s; hid_device_p d; int32_t error; socklen_t len; assert(srv != NULL); assert(fd >= 0); s = session_by_fd(srv, fd); assert(s != NULL); d = get_hid_device(&s->bdaddr); assert(d != NULL); error = 0; len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); connect_in_progress = 0; return (-1); } if (error != 0) { syslog(LOG_ERR, "Could not connect to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(error), error); session_close(s); connect_in_progress = 0; return (0); } switch (s->state) { case W4CTRL: /* Control channel is open */ assert(s->ctrl == fd); assert(s->intr == -1); /* Open interrupt channel */ s->intr = client_socket(&s->bdaddr, d->interrupt_psm); if (s->intr < 0) { syslog(LOG_ERR, "Could not open interrupt channel " \ "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); session_close(s); connect_in_progress = 0; return (-1); } s->state = W4INTR; FD_SET(s->intr, &srv->wfdset); if (s->intr > srv->maxfd) srv->maxfd = s->intr; d->new_device = 0; /* reset new device flag */ write_hids_file(); break; case W4INTR: /* Interrupt channel is open */ assert(s->ctrl != -1); assert(s->intr == fd); s->state = OPEN; connect_in_progress = 0; /* Register session's vkbd descriptor (if any) for read */ if (s->state == OPEN && d->keyboard) { assert(s->vkbd != -1); FD_SET(s->vkbd, &srv->rfdset); if (s->vkbd > srv->maxfd) srv->maxfd = s->vkbd; } break; default: assert(0); break; } /* Move fd to from the write fd set into read fd set */ FD_CLR(fd, &srv->wfdset); FD_SET(fd, &srv->rfdset); return (0); } /* * Create bound non-blocking socket and initiate connect */ static int client_socket(bdaddr_p bdaddr, uint16_t psm) { struct sockaddr_l2cap l2addr; int32_t s, m; s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (s < 0) return (-1); m = fcntl(s, F_GETFL); if (m < 0) { close(s); return (-1); } if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { close(s); return (-1); } l2addr.l2cap_len = sizeof(l2addr); l2addr.l2cap_family = AF_BLUETOOTH; memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = 0; - + l2addr.l2cap_bdaddr_type = BDADDR_BREDR; + l2addr.l2cap_cid = 0; + if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { close(s); return (-1); } memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = htole16(psm); if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && errno != EINPROGRESS) { close(s); return (-1); } return (s); } Index: head/usr.sbin/bluetooth/bthidd/hid.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/hid.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/hid.c (revision 281210) @@ -1,414 +1,415 @@ /* * hid.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" #include "kbd.h" /* * Process data from control channel */ int32_t hid_control(bthid_session_p s, uint8_t *data, int32_t len) { assert(s != NULL); assert(data != NULL); assert(len > 0); switch (data[0] >> 4) { case 0: /* Handshake (response to command) */ if (data[0] & 0xf) syslog(LOG_ERR, "Got handshake message with error " \ "response 0x%x from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); break; case 1: /* HID Control */ switch (data[0] & 0xf) { case 0: /* NOP */ break; case 1: /* Hard reset */ case 2: /* Soft reset */ syslog(LOG_WARNING, "Device %s requested %s reset", bt_ntoa(&s->bdaddr, NULL), ((data[0] & 0xf) == 1)? "hard" : "soft"); break; case 3: /* Suspend */ syslog(LOG_NOTICE, "Device %s requested Suspend", bt_ntoa(&s->bdaddr, NULL)); break; case 4: /* Exit suspend */ syslog(LOG_NOTICE, "Device %s requested Exit Suspend", bt_ntoa(&s->bdaddr, NULL)); break; case 5: /* Virtual cable unplug */ syslog(LOG_NOTICE, "Device %s unplugged virtual cable", bt_ntoa(&s->bdaddr, NULL)); session_close(s); break; default: syslog(LOG_WARNING, "Device %s sent unknown " \ "HID_Control message 0x%x", bt_ntoa(&s->bdaddr, NULL), data[0]); break; } break; default: syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); break; } return (0); } /* * Process data from the interrupt channel */ int32_t hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len) { hid_device_p hid_device; hid_data_t d; hid_item_t h; int32_t report_id, usage, page, val, mouse_x, mouse_y, mouse_z, mouse_butt, mevents, kevents, i; assert(s != NULL); assert(s->srv != NULL); assert(data != NULL); if (len < 3) { syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); return (-1); } if (data[0] != 0xa1) { syslog(LOG_ERR, "Got unexpected message 0x%x on " \ "Interrupt channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); return (-1); } report_id = data[1]; data ++; len --; hid_device = get_hid_device(&s->bdaddr); assert(hid_device != NULL); mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0; for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); hid_get_item(d, &h) > 0; ) { if ((h.flags & HIO_CONST) || (h.report_ID != report_id) || (h.kind != hid_input)) continue; page = HID_PAGE(h.usage); val = hid_get_data(data, &h); /* * When the input field is an array and the usage is specified * with a range instead of an ID, we have to derive the actual * usage by using the item value as an index in the usage range * list. */ if ((h.flags & HIO_VARIABLE)) { usage = HID_USAGE(h.usage); } else { const uint32_t usage_offset = val - h.logical_minimum; usage = HID_USAGE(h.usage_minimum + usage_offset); } switch (page) { case HUP_GENERIC_DESKTOP: switch (usage) { case HUG_X: mouse_x = val; mevents ++; break; case HUG_Y: mouse_y = val; mevents ++; break; case HUG_WHEEL: mouse_z = -val; mevents ++; break; case HUG_SYSTEM_SLEEP: if (val) syslog(LOG_NOTICE, "Sleep button pressed"); break; } break; case HUP_KEYBOARD: kevents ++; if (h.flags & HIO_VARIABLE) { if (val && usage < kbd_maxkey()) bit_set(s->keys1, usage); } else { if (val && val < kbd_maxkey()) bit_set(s->keys1, val); for (i = 1; i < h.report_count; i++) { h.pos += h.report_size; val = hid_get_data(data, &h); if (val && val < kbd_maxkey()) bit_set(s->keys1, val); } } break; case HUP_BUTTON: if (usage != 0) { if (usage == 2) usage = 3; else if (usage == 3) usage = 2; mouse_butt |= (val << (usage - 1)); mevents ++; } break; case HUP_CONSUMER: if (!val) break; switch (usage) { case HUC_AC_PAN: /* Horizontal scroll */ if (val < 0) mouse_butt |= (1 << 5); else mouse_butt |= (1 << 6); mevents ++; val = 0; break; case 0xb5: /* Scan Next Track */ val = 0x19; break; case 0xb6: /* Scan Previous Track */ val = 0x10; break; case 0xb7: /* Stop */ val = 0x24; break; case 0xcd: /* Play/Pause */ val = 0x22; break; case 0xe2: /* Mute */ val = 0x20; break; case 0xe9: /* Volume Up */ val = 0x30; break; case 0xea: /* Volume Down */ val = 0x2E; break; case 0x183: /* Media Select */ val = 0x6D; break; case 0x018a: /* Mail */ val = 0x6C; break; case 0x192: /* Calculator */ val = 0x21; break; case 0x194: /* My Computer */ val = 0x6B; break; case 0x221: /* WWW Search */ val = 0x65; break; case 0x223: /* WWW Home */ val = 0x32; break; case 0x224: /* WWW Back */ val = 0x6A; break; case 0x225: /* WWW Forward */ val = 0x69; break; case 0x226: /* WWW Stop */ val = 0x68; break; case 0x227: /* WWW Refresh */ val = 0x67; break; case 0x22a: /* WWW Favorites */ val = 0x66; break; default: val = 0; break; } /* XXX FIXME - UGLY HACK */ if (val != 0) { if (hid_device->keyboard) { int32_t buf[4] = { 0xe0, val, 0xe0, val|0x80 }; assert(s->vkbd != -1); write(s->vkbd, buf, sizeof(buf)); } else syslog(LOG_ERR, "Keyboard events " \ "received from non-keyboard " \ "device %s. Please report", bt_ntoa(&s->bdaddr, NULL)); } break; case HUP_MICROSOFT: switch (usage) { case 0xfe01: if (!hid_device->battery_power) break; switch (val) { case 1: syslog(LOG_INFO, "Battery is OK on %s", bt_ntoa(&s->bdaddr, NULL)); break; case 2: syslog(LOG_NOTICE, "Low battery on %s", bt_ntoa(&s->bdaddr, NULL)); break; case 3: syslog(LOG_WARNING, "Very low battery "\ "on %s", bt_ntoa(&s->bdaddr, NULL)); break; } break; } break; } } hid_end_parse(d); /* * XXX FIXME Feed keyboard events into kernel. * The code below works, bit host also needs to track * and handle repeat. * * Key repeat currently works in X, but not in console. */ if (kevents > 0) { if (hid_device->keyboard) { assert(s->vkbd != -1); kbd_process_keys(s); } else syslog(LOG_ERR, "Keyboard events received from " \ "non-keyboard device %s. Please report", bt_ntoa(&s->bdaddr, NULL)); } /* * XXX FIXME Feed mouse events into kernel. * The code block below works, but it is not good enough. * Need to track double-clicks etc. * * Double click currently works in X, but not in console. */ if (mevents > 0) { struct mouse_info mi; mi.operation = MOUSE_ACTION; mi.u.data.x = mouse_x; mi.u.data.y = mouse_y; mi.u.data.z = mouse_z; mi.u.data.buttons = mouse_butt; if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) syslog(LOG_ERR, "Could not process mouse events from " \ "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); } return (0); } Index: head/usr.sbin/bluetooth/bthidd/kbd.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/kbd.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/kbd.c (revision 281210) @@ -1,580 +1,581 @@ /* * kbd.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" #include "kbd.h" static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob); /* * HID code to PS/2 set 1 code translation table. * * http://www.microsoft.com/whdc/device/input/Scancode.mspx * * The table only contains "make" (key pressed) codes. * The "break" (key released) code is generated as "make" | 0x80 */ #define E0PREFIX (1U << 31) #define NOBREAK (1 << 30) #define CODEMASK (~(E0PREFIX|NOBREAK)) static int32_t const x[] = { /*==================================================*/ /* Name HID code Make Break*/ /*==================================================*/ /* No Event 00 */ -1, /* None */ /* Overrun Error 01 */ NOBREAK|0xFF, /* None */ /* POST Fail 02 */ NOBREAK|0xFC, /* None */ /* ErrorUndefined 03 */ -1, /* Unassigned */ /* a A 04 */ 0x1E, /* 9E */ /* b B 05 */ 0x30, /* B0 */ /* c C 06 */ 0x2E, /* AE */ /* d D 07 */ 0x20, /* A0 */ /* e E 08 */ 0x12, /* 92 */ /* f F 09 */ 0x21, /* A1 */ /* g G 0A */ 0x22, /* A2 */ /* h H 0B */ 0x23, /* A3 */ /* i I 0C */ 0x17, /* 97 */ /* j J 0D */ 0x24, /* A4 */ /* k K 0E */ 0x25, /* A5 */ /* l L 0F */ 0x26, /* A6 */ /* m M 10 */ 0x32, /* B2 */ /* n N 11 */ 0x31, /* B1 */ /* o O 12 */ 0x18, /* 98 */ /* p P 13 */ 0x19, /* 99 */ /* q Q 14 */ 0x10, /* 90 */ /* r R 15 */ 0x13, /* 93 */ /* s S 16 */ 0x1F, /* 9F */ /* t T 17 */ 0x14, /* 94 */ /* u U 18 */ 0x16, /* 96 */ /* v V 19 */ 0x2F, /* AF */ /* w W 1A */ 0x11, /* 91 */ /* x X 1B */ 0x2D, /* AD */ /* y Y 1C */ 0x15, /* 95 */ /* z Z 1D */ 0x2C, /* AC */ /* 1 ! 1E */ 0x02, /* 82 */ /* 2 @ 1F */ 0x03, /* 83 */ /* 3 # 20 */ 0x04, /* 84 */ /* 4 $ 21 */ 0x05, /* 85 */ /* 5 % 22 */ 0x06, /* 86 */ /* 6 ^ 23 */ 0x07, /* 87 */ /* 7 & 24 */ 0x08, /* 88 */ /* 8 * 25 */ 0x09, /* 89 */ /* 9 ( 26 */ 0x0A, /* 8A */ /* 0 ) 27 */ 0x0B, /* 8B */ /* Return 28 */ 0x1C, /* 9C */ /* Escape 29 */ 0x01, /* 81 */ /* Backspace 2A */ 0x0E, /* 8E */ /* Tab 2B */ 0x0F, /* 8F */ /* Space 2C */ 0x39, /* B9 */ /* - _ 2D */ 0x0C, /* 8C */ /* = + 2E */ 0x0D, /* 8D */ /* [ { 2F */ 0x1A, /* 9A */ /* ] } 30 */ 0x1B, /* 9B */ /* \ | 31 */ 0x2B, /* AB */ /* Europe 1 32 */ 0x2B, /* AB */ /* ; : 33 */ 0x27, /* A7 */ /* " ' 34 */ 0x28, /* A8 */ /* ` ~ 35 */ 0x29, /* A9 */ /* comma < 36 */ 0x33, /* B3 */ /* . > 37 */ 0x34, /* B4 */ /* / ? 38 */ 0x35, /* B5 */ /* Caps Lock 39 */ 0x3A, /* BA */ /* F1 3A */ 0x3B, /* BB */ /* F2 3B */ 0x3C, /* BC */ /* F3 3C */ 0x3D, /* BD */ /* F4 3D */ 0x3E, /* BE */ /* F5 3E */ 0x3F, /* BF */ /* F6 3F */ 0x40, /* C0 */ /* F7 40 */ 0x41, /* C1 */ /* F8 41 */ 0x42, /* C2 */ /* F9 42 */ 0x43, /* C3 */ /* F10 43 */ 0x44, /* C4 */ /* F11 44 */ 0x57, /* D7 */ /* F12 45 */ 0x58, /* D8 */ /* Print Screen 46 */ E0PREFIX|0x37, /* E0 B7 */ /* Scroll Lock 47 */ 0x46, /* C6 */ #if 0 /* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */ /* Pause 48 */ E1 1D 45 E1 9D C5, /* None */ #else /* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */ #endif /* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */ /* Home 4A */ E0PREFIX|0x47, /* E0 C7 */ /* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */ /* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */ /* End 4D */ E0PREFIX|0x4F, /* E0 CF */ /* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */ /* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */ /* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */ /* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */ /* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */ /* Num Lock 53 */ 0x45, /* C5 */ /* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */ /* Keypad * 55 */ 0x37, /* B7 */ /* Keypad - 56 */ 0x4A, /* CA */ /* Keypad + 57 */ 0x4E, /* CE */ /* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */ /* Keypad 1 End 59 */ 0x4F, /* CF */ /* Keypad 2 Down 5A */ 0x50, /* D0 */ /* Keypad 3 PageDn 5B */ 0x51, /* D1 */ /* Keypad 4 Left 5C */ 0x4B, /* CB */ /* Keypad 5 5D */ 0x4C, /* CC */ /* Keypad 6 Right 5E */ 0x4D, /* CD */ /* Keypad 7 Home 5F */ 0x47, /* C7 */ /* Keypad 8 Up 60 */ 0x48, /* C8 */ /* Keypad 9 PageUp 61 */ 0x49, /* C9 */ /* Keypad 0 Insert 62 */ 0x52, /* D2 */ /* Keypad . Delete 63 */ 0x53, /* D3 */ /* Europe 2 64 */ 0x56, /* D6 */ /* App 65 */ E0PREFIX|0x5D, /* E0 DD */ /* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */ /* Keypad = 67 */ 0x59, /* D9 */ /* F13 68 */ 0x64, /* E4 */ /* F14 69 */ 0x65, /* E5 */ /* F15 6A */ 0x66, /* E6 */ /* F16 6B */ 0x67, /* E7 */ /* F17 6C */ 0x68, /* E8 */ /* F18 6D */ 0x69, /* E9 */ /* F19 6E */ 0x6A, /* EA */ /* F20 6F */ 0x6B, /* EB */ /* F21 70 */ 0x6C, /* EC */ /* F22 71 */ 0x6D, /* ED */ /* F23 72 */ 0x6E, /* EE */ /* F24 73 */ 0x76, /* F6 */ /* Keyboard Execute 74 */ -1, /* Unassigned */ /* Keyboard Help 75 */ -1, /* Unassigned */ /* Keyboard Menu 76 */ -1, /* Unassigned */ /* Keyboard Select 77 */ -1, /* Unassigned */ /* Keyboard Stop 78 */ -1, /* Unassigned */ /* Keyboard Again 79 */ -1, /* Unassigned */ /* Keyboard Undo 7A */ -1, /* Unassigned */ /* Keyboard Cut 7B */ -1, /* Unassigned */ /* Keyboard Copy 7C */ -1, /* Unassigned */ /* Keyboard Paste 7D */ -1, /* Unassigned */ /* Keyboard Find 7E */ -1, /* Unassigned */ /* Keyboard Mute 7F */ -1, /* Unassigned */ /* Keyboard Volume Up 80 */ -1, /* Unassigned */ /* Keyboard Volume Dn 81 */ -1, /* Unassigned */ /* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */ /* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */ /* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */ /* Keypad comma 85 */ 0x7E, /* FE */ /* Keyboard Equal Sign 86 */ -1, /* Unassigned */ /* Keyboard Int'l 1 87 */ 0x73, /* F3 */ /* Keyboard Int'l 2 88 */ 0x70, /* F0 */ /* Keyboard Int'l 2 89 */ 0x7D, /* FD */ /* Keyboard Int'l 4 8A */ 0x79, /* F9 */ /* Keyboard Int'l 5 8B */ 0x7B, /* FB */ /* Keyboard Int'l 6 8C */ 0x5C, /* DC */ /* Keyboard Int'l 7 8D */ -1, /* Unassigned */ /* Keyboard Int'l 8 8E */ -1, /* Unassigned */ /* Keyboard Int'l 9 8F */ -1, /* Unassigned */ /* Keyboard Lang 1 90 */ 0x71, /* eisu */ /* Keyboard Lang 2 91 */ 0x72, /* kana */ /* Keyboard Lang 3 92 */ 0x78, /* F8 */ /* Keyboard Lang 4 93 */ 0x77, /* F7 */ /* Keyboard Lang 5 94 */ 0x76, /* F6 */ /* Keyboard Lang 6 95 */ -1, /* Unassigned */ /* Keyboard Lang 7 96 */ -1, /* Unassigned */ /* Keyboard Lang 8 97 */ -1, /* Unassigned */ /* Keyboard Lang 9 98 */ -1, /* Unassigned */ /* Keyboard Alternate Erase 99 */ -1, /* Unassigned */ /* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */ /* Keyboard Cancel 9B */ -1, /* Unassigned */ /* Keyboard Clear 9C */ -1, /* Unassigned */ /* Keyboard Prior 9D */ -1, /* Unassigned */ /* Keyboard Return 9E */ -1, /* Unassigned */ /* Keyboard Separator 9F */ -1, /* Unassigned */ /* Keyboard Out A0 */ -1, /* Unassigned */ /* Keyboard Oper A1 */ -1, /* Unassigned */ /* Keyboard Clear/Again A2 */ -1, /* Unassigned */ /* Keyboard CrSel/Props A3 */ -1, /* Unassigned */ /* Keyboard ExSel A4 */ -1, /* Unassigned */ /* Reserved A5 */ -1, /* Reserved */ /* Reserved A6 */ -1, /* Reserved */ /* Reserved A7 */ -1, /* Reserved */ /* Reserved A8 */ -1, /* Reserved */ /* Reserved A9 */ -1, /* Reserved */ /* Reserved AA */ -1, /* Reserved */ /* Reserved AB */ -1, /* Reserved */ /* Reserved AC */ -1, /* Reserved */ /* Reserved AD */ -1, /* Reserved */ /* Reserved AE */ -1, /* Reserved */ /* Reserved AF */ -1, /* Reserved */ /* Reserved B0 */ -1, /* Reserved */ /* Reserved B1 */ -1, /* Reserved */ /* Reserved B2 */ -1, /* Reserved */ /* Reserved B3 */ -1, /* Reserved */ /* Reserved B4 */ -1, /* Reserved */ /* Reserved B5 */ -1, /* Reserved */ /* Reserved B6 */ -1, /* Reserved */ /* Reserved B7 */ -1, /* Reserved */ /* Reserved B8 */ -1, /* Reserved */ /* Reserved B9 */ -1, /* Reserved */ /* Reserved BA */ -1, /* Reserved */ /* Reserved BB */ -1, /* Reserved */ /* Reserved BC */ -1, /* Reserved */ /* Reserved BD */ -1, /* Reserved */ /* Reserved BE */ -1, /* Reserved */ /* Reserved BF */ -1, /* Reserved */ /* Reserved C0 */ -1, /* Reserved */ /* Reserved C1 */ -1, /* Reserved */ /* Reserved C2 */ -1, /* Reserved */ /* Reserved C3 */ -1, /* Reserved */ /* Reserved C4 */ -1, /* Reserved */ /* Reserved C5 */ -1, /* Reserved */ /* Reserved C6 */ -1, /* Reserved */ /* Reserved C7 */ -1, /* Reserved */ /* Reserved C8 */ -1, /* Reserved */ /* Reserved C9 */ -1, /* Reserved */ /* Reserved CA */ -1, /* Reserved */ /* Reserved CB */ -1, /* Reserved */ /* Reserved CC */ -1, /* Reserved */ /* Reserved CD */ -1, /* Reserved */ /* Reserved CE */ -1, /* Reserved */ /* Reserved CF */ -1, /* Reserved */ /* Reserved D0 */ -1, /* Reserved */ /* Reserved D1 */ -1, /* Reserved */ /* Reserved D2 */ -1, /* Reserved */ /* Reserved D3 */ -1, /* Reserved */ /* Reserved D4 */ -1, /* Reserved */ /* Reserved D5 */ -1, /* Reserved */ /* Reserved D6 */ -1, /* Reserved */ /* Reserved D7 */ -1, /* Reserved */ /* Reserved D8 */ -1, /* Reserved */ /* Reserved D9 */ -1, /* Reserved */ /* Reserved DA */ -1, /* Reserved */ /* Reserved DB */ -1, /* Reserved */ /* Reserved DC */ -1, /* Reserved */ /* Reserved DD */ -1, /* Reserved */ /* Reserved DE */ -1, /* Reserved */ /* Reserved DF */ -1, /* Reserved */ /* Left Control E0 */ 0x1D, /* 9D */ /* Left Shift E1 */ 0x2A, /* AA */ /* Left Alt E2 */ 0x38, /* B8 */ /* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */ /* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */ /* Right Shift E5 */ 0x36, /* B6 */ /* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */ /* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */ }; #define xsize ((int32_t)(sizeof(x)/sizeof(x[0]))) /* * Get a max HID keycode (aligned) */ int32_t kbd_maxkey(void) { return (xsize); } /* * Process keys */ int32_t kbd_process_keys(bthid_session_p s) { bitstr_t diff[bitstr_size(xsize)]; int32_t f1, f2, i; assert(s != NULL); assert(s->srv != NULL); /* Check if the new keys have been pressed */ bit_ffs(s->keys1, xsize, &f1); /* Check if old keys still pressed */ bit_ffs(s->keys2, xsize, &f2); if (f1 == -1) { /* no new key pressed */ if (f2 != -1) { /* release old keys */ kbd_write(s->keys2, f2, 0, s->vkbd); memset(s->keys2, 0, bitstr_size(xsize)); } return (0); } if (f2 == -1) { /* no old keys, but new keys pressed */ assert(f1 != -1); memcpy(s->keys2, s->keys1, bitstr_size(xsize)); kbd_write(s->keys1, f1, 1, s->vkbd); memset(s->keys1, 0, bitstr_size(xsize)); return (0); } /* new keys got pressed, old keys got released */ memset(diff, 0, bitstr_size(xsize)); for (i = f2; i < xsize; i ++) { if (bit_test(s->keys2, i)) { if (!bit_test(s->keys1, i)) { bit_clear(s->keys2, i); bit_set(diff, i); } } } for (i = f1; i < xsize; i++) { if (bit_test(s->keys1, i)) { if (!bit_test(s->keys2, i)) bit_set(s->keys2, i); else bit_clear(s->keys1, i); } } bit_ffs(diff, xsize, &f2); if (f2 > 0) kbd_write(diff, f2, 0, s->vkbd); bit_ffs(s->keys1, xsize, &f1); if (f1 > 0) { kbd_write(s->keys1, f1, 1, s->vkbd); memset(s->keys1, 0, bitstr_size(xsize)); } return (0); } /* * Translate given keymap and write keyscodes */ static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) { int32_t i, *b, *eob, n, buf[64]; b = buf; eob = b + sizeof(buf)/sizeof(buf[0]); i = fb; while (i < xsize) { if (bit_test(m, i)) { n = kbd_xlate(i, make, b, eob); if (n == -1) { write(fd, buf, (b - buf) * sizeof(buf[0])); b = buf; continue; } b += n; } i ++; } if (b != buf) write(fd, buf, (b - buf) * sizeof(buf[0])); } /* * Translate HID code into PS/2 code and put codes into buffer b. * Returns the number of codes put in b. Return -1 if buffer has not * enough space. */ #undef PUT #define PUT(c, n, b, eob) \ do { \ if ((b) >= (eob)) \ return (-1); \ *(b) = (c); \ (b) ++; \ (n) ++; \ } while (0) static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob) { int32_t c, n; n = 0; if (code >= xsize) return (0); /* HID code is not in the table */ /* Handle special case - Pause/Break */ if (code == 0x48) { if (!make) return (0); /* No break code */ #if 0 XXX FIXME if (ctrl_is_pressed) { /* Break (Ctrl-Pause) */ PUT(0xe0, n, b, eob); PUT(0x46, n, b, eob); PUT(0xe0, n, b, eob); PUT(0xc6, n, b, eob); } else { /* Pause */ PUT(0xe1, n, b, eob); PUT(0x1d, n, b, eob); PUT(0x45, n, b, eob); PUT(0xe1, n, b, eob); PUT(0x9d, n, b, eob); PUT(0xc5, n, b, eob); } #endif return (n); } if ((c = x[code]) == -1) return (0); /* HID code translation is not defined */ if (make) { if (c & E0PREFIX) PUT(0xe0, n, b, eob); PUT((c & CODEMASK), n, b, eob); } else if (!(c & NOBREAK)) { if (c & E0PREFIX) PUT(0xe0, n, b, eob); PUT((0x80|(c & CODEMASK)), n, b, eob); } return (n); } /* * Process status change from vkbd(4) */ int32_t kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len) { vkbd_status_t st; uint8_t leds, report_id; hid_device_p hid_device; hid_data_t d; hid_item_t h; assert(s != NULL); assert(len == sizeof(vkbd_status_t)); memcpy(&st, data, sizeof(st)); leds = 0; report_id = NO_REPORT_ID; hid_device = get_hid_device(&s->bdaddr); assert(hid_device != NULL); for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1); hid_get_item(d, &h) > 0; ) { if (HID_PAGE(h.usage) == HUP_LEDS) { if (report_id == NO_REPORT_ID) report_id = h.report_ID; else if (h.report_ID != report_id) syslog(LOG_WARNING, "Output HID report IDs " \ "for %s do not match: %d vs. %d. " \ "Please report", bt_ntoa(&s->bdaddr, NULL), h.report_ID, report_id); switch(HID_USAGE(h.usage)) { case 0x01: /* Num Lock LED */ if (st.leds & LED_NUM) hid_set_data(&leds, &h, 1); break; case 0x02: /* Caps Lock LED */ if (st.leds & LED_CAP) hid_set_data(&leds, &h, 1); break; case 0x03: /* Scroll Lock LED */ if (st.leds & LED_SCR) hid_set_data(&leds, &h, 1); break; /* XXX add other LEDs ? */ } } } hid_end_parse(d); data[0] = 0xa2; /* DATA output (HID output report) */ if (report_id != NO_REPORT_ID) { data[1] = report_id; data[2] = leds; len = 3; } else { data[1] = leds; len = 2; } write(s->intr, data, len); return (0); } Index: head/usr.sbin/bluetooth/bthidd/lexer.l =================================================================== --- head/usr.sbin/bluetooth/bthidd/lexer.l (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/lexer.l (revision 281210) @@ -1,106 +1,106 @@ %{ /* * lexer.l */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: lexer.l,v 1.3 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include "parser.h" int yylex (void); #define YY_DECL int yylex(void) %} %option yylineno noyywrap nounput noinput delim [ \t\n] ws {delim}+ empty {delim}* comment \#.* hexdigit [0-9a-fA-F] hexbyte {hexdigit}{hexdigit}? device_word device bdaddr_word bdaddr control_psm_word control_psm interrupt_psm_word interrupt_psm reconnect_initiate_word reconnect_initiate battery_power_word battery_power normally_connectable_word normally_connectable hid_descriptor_word hid_descriptor true_word true false_word false bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte} hexbytestring 0x{hexbyte} %% \; return (';'); \: return (':'); \{ return ('{'); \} return ('}'); {ws} ; {empty} ; {comment} ; {device_word} return (T_DEVICE); {bdaddr_word} return (T_BDADDR); {control_psm_word} return (T_CONTROL_PSM); {interrupt_psm_word} return (T_INTERRUPT_PSM); {reconnect_initiate_word} return (T_RECONNECT_INITIATE); {battery_power_word} return (T_BATTERY_POWER); {normally_connectable_word} return (T_NORMALLY_CONNECTABLE); {hid_descriptor_word} return (T_HID_DESCRIPTOR); {true_word} return (T_TRUE); {false_word} return (T_FALSE); {bdaddrstring} { return (bt_aton(yytext, &yylval.bdaddr)? T_BDADDRSTRING : T_ERROR); } {hexbytestring} { char *ep; yylval.num = strtoul(yytext, &ep, 16); return (*ep == '\0'? T_HEXBYTE : T_ERROR); } . return (T_ERROR); %% Index: head/usr.sbin/bluetooth/bthidd/parser.y =================================================================== --- head/usr.sbin/bluetooth/bthidd/parser.y (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/parser.y (revision 281210) @@ -1,475 +1,476 @@ %{ /* * parser.y */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #ifndef BTHIDCONTROL #include #include #define SYSLOG syslog #define LOGCRIT LOG_CRIT #define LOGERR LOG_ERR #define LOGWARNING LOG_WARNING #define EOL #else #define SYSLOG fprintf #define LOGCRIT stderr #define LOGERR stderr #define LOGWARNING stderr #define EOL "\n" #endif /* ndef BTHIDCONTROL */ #include "bthid_config.h" int yylex (void); void yyerror (char const *); static int32_t check_hid_device(hid_device_p hid_device); static void free_hid_device (hid_device_p hid_device); extern FILE *yyin; extern int yylineno; char const *config_file = BTHIDD_CONFFILE; char const *hids_file = BTHIDD_HIDSFILE; static char buffer[1024]; static int32_t hid_descriptor_size; static hid_device_t *hid_device = NULL; static LIST_HEAD(, hid_device) hid_devices; %} %union { bdaddr_t bdaddr; int32_t num; } %token T_BDADDRSTRING %token T_HEXBYTE %token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE %token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR %token T_TRUE T_FALSE T_ERROR %% config: line | config line ; line: T_DEVICE { hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); if (hid_device == NULL) { SYSLOG(LOGCRIT, "Could not allocate new " \ "config entry" EOL); YYABORT; } hid_device->new_device = 1; } '{' options '}' { if (check_hid_device(hid_device)) LIST_INSERT_HEAD(&hid_devices,hid_device,next); else free_hid_device(hid_device); hid_device = NULL; } ; options: option ';' | options option ';' ; option: bdaddr | control_psm | interrupt_psm | reconnect_initiate | battery_power | normally_connectable | hid_descriptor | parser_error ; bdaddr: T_BDADDR T_BDADDRSTRING { memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); } ; control_psm: T_CONTROL_PSM T_HEXBYTE { hid_device->control_psm = $2; } ; interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE { hid_device->interrupt_psm = $2; } ; reconnect_initiate: T_RECONNECT_INITIATE T_TRUE { hid_device->reconnect_initiate = 1; } | T_RECONNECT_INITIATE T_FALSE { hid_device->reconnect_initiate = 0; } ; battery_power: T_BATTERY_POWER T_TRUE { hid_device->battery_power = 1; } | T_BATTERY_POWER T_FALSE { hid_device->battery_power = 0; } ; normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE { hid_device->normally_connectable = 1; } | T_NORMALLY_CONNECTABLE T_FALSE { hid_device->normally_connectable = 0; } ; hid_descriptor: T_HID_DESCRIPTOR { hid_descriptor_size = 0; } '{' hid_descriptor_bytes '}' { if (hid_device->desc != NULL) hid_dispose_report_desc(hid_device->desc); hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size); if (hid_device->desc == NULL) { SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); YYABORT; } } ; hid_descriptor_bytes: hid_descriptor_byte | hid_descriptor_bytes hid_descriptor_byte ; hid_descriptor_byte: T_HEXBYTE { if (hid_descriptor_size >= (int32_t) sizeof(buffer)) { SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); YYABORT; } buffer[hid_descriptor_size ++] = $1; } ; parser_error: T_ERROR { YYABORT; } %% /* Display parser error message */ void yyerror(char const *message) { SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); } /* Re-read config file */ int32_t read_config_file(void) { int32_t e; if (config_file == NULL) { SYSLOG(LOGERR, "Unknown config file name!" EOL); return (-1); } if ((yyin = fopen(config_file, "r")) == NULL) { SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, config_file, strerror(errno), errno); return (-1); } clean_config(); if (yyparse() < 0) { SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, config_file); e = -1; } else e = 0; fclose(yyin); yyin = NULL; return (e); } /* Clean config */ void clean_config(void) { while (!LIST_EMPTY(&hid_devices)) { hid_device_p d = LIST_FIRST(&hid_devices); LIST_REMOVE(d, next); free_hid_device(d); } } /* Lookup config entry */ hid_device_p get_hid_device(bdaddr_p bdaddr) { hid_device_p d; LIST_FOREACH(d, &hid_devices, next) if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) break; return (d); } /* Get next config entry */ hid_device_p get_next_hid_device(hid_device_p d) { return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); } /* Print config entry */ void print_hid_device(hid_device_p d, FILE *f) { /* XXX FIXME hack! */ struct report_desc { unsigned int size; unsigned char data[1]; }; /* XXX FIXME hack! */ struct report_desc *desc = (struct report_desc *) d->desc; uint32_t i; fprintf(f, "device {\n" \ " bdaddr %s;\n" \ " control_psm 0x%x;\n" \ " interrupt_psm 0x%x;\n" \ " reconnect_initiate %s;\n" \ " battery_power %s;\n" \ " normally_connectable %s;\n" \ " hid_descriptor {", bt_ntoa(&d->bdaddr, NULL), d->control_psm, d->interrupt_psm, d->reconnect_initiate? "true" : "false", d->battery_power? "true" : "false", d->normally_connectable? "true" : "false"); for (i = 0; i < desc->size; i ++) { if ((i % 8) == 0) fprintf(f, "\n "); fprintf(f, "0x%2.2x ", desc->data[i]); } fprintf(f, "\n" \ " };\n" \ "}\n"); } /* Check config entry */ static int32_t check_hid_device(hid_device_p d) { hid_data_t hd; hid_item_t hi; int32_t page; if (get_hid_device(&d->bdaddr) != NULL) { SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, bt_ntoa(&d->bdaddr, NULL)); return (0); } if (d->control_psm == 0) { SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); return (0); } if (d->interrupt_psm == 0) { SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); return (0); } if (d->desc == NULL) { SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); return (0); } /* XXX somehow need to make sure descriptor is valid */ for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) { switch (hi.kind) { case hid_collection: case hid_endcollection: case hid_output: case hid_feature: break; case hid_input: /* Check if the device may send keystrokes */ page = HID_PAGE(hi.usage); if (page == HUP_KEYBOARD) d->keyboard = 1; break; } } hid_end_parse(hd); return (1); } /* Free config entry */ static void free_hid_device(hid_device_p d) { if (d->desc != NULL) hid_dispose_report_desc(d->desc); memset(d, 0, sizeof(*d)); free(d); } /* Re-read hids file */ int32_t read_hids_file(void) { FILE *f; hid_device_t *d; char *line; bdaddr_t bdaddr; int32_t lineno; if (hids_file == NULL) { SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); return (-1); } if ((f = fopen(hids_file, "r")) == NULL) { if (errno == ENOENT) return (0); SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, hids_file, strerror(errno), errno); return (-1); } for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { if ((line = strtok(buffer, "\r\n\t ")) == NULL) continue; /* ignore empty lines */ if (!bt_aton(line, &bdaddr)) { SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ "%s:%d" EOL, hids_file, lineno); continue; } if ((d = get_hid_device(&bdaddr)) != NULL) d->new_device = 0; } fclose(f); return (0); } /* Write hids file */ int32_t write_hids_file(void) { char path[PATH_MAX]; FILE *f; hid_device_t *d; if (hids_file == NULL) { SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); return (-1); } snprintf(path, sizeof(path), "%s.new", hids_file); if ((f = fopen(path, "w")) == NULL) { SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, path, strerror(errno), errno); return (-1); } LIST_FOREACH(d, &hid_devices, next) if (!d->new_device) fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL)); fclose(f); if (rename(path, hids_file) < 0) { SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ "%s (%d)" EOL, path, hids_file, strerror(errno), errno); unlink(path); return (-1); } return (0); } Index: head/usr.sbin/bluetooth/bthidd/server.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/server.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/server.c (revision 281210) @@ -1,349 +1,352 @@ /* * server.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: server.c,v 1.9 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" #include "kbd.h" #undef max #define max(x, y) (((x) > (y))? (x) : (y)) static int32_t server_accept (bthid_server_p srv, int32_t fd); static int32_t server_process(bthid_server_p srv, int32_t fd); /* * Initialize server */ int32_t server_init(bthid_server_p srv) { struct sockaddr_l2cap l2addr; assert(srv != NULL); srv->ctrl = srv->intr = -1; FD_ZERO(&srv->rfdset); FD_ZERO(&srv->wfdset); LIST_INIT(&srv->sessions); /* Open /dev/consolectl */ srv->cons = open("/dev/consolectl", O_RDWR); if (srv->cons < 0) { syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)", strerror(errno), errno); return (-1); } /* Create control socket */ srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (srv->ctrl < 0) { syslog(LOG_ERR, "Could not create control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->cons); return (-1); } l2addr.l2cap_len = sizeof(l2addr); l2addr.l2cap_family = AF_BLUETOOTH; memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr)); l2addr.l2cap_psm = htole16(0x11); - + l2addr.l2cap_bdaddr_type = BDADDR_BREDR; + l2addr.l2cap_cid = 0; + if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } if (listen(srv->ctrl, 10) < 0) { syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } /* Create intrrupt socket */ srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (srv->intr < 0) { syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); close(srv->cons); return (-1); } l2addr.l2cap_psm = htole16(0x13); if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->intr); close(srv->ctrl); close(srv->cons); return (-1); } if (listen(srv->intr, 10) < 0) { syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\ "%s (%d)", strerror(errno), errno); close(srv->intr); close(srv->ctrl); close(srv->cons); return (-1); } FD_SET(srv->ctrl, &srv->rfdset); FD_SET(srv->intr, &srv->rfdset); srv->maxfd = max(srv->ctrl, srv->intr); return (0); } /* * Shutdown server */ void server_shutdown(bthid_server_p srv) { assert(srv != NULL); close(srv->cons); close(srv->ctrl); close(srv->intr); while (!LIST_EMPTY(&srv->sessions)) session_close(LIST_FIRST(&srv->sessions)); memset(srv, 0, sizeof(*srv)); } /* * Do one server iteration */ int32_t server_do(bthid_server_p srv) { struct timeval tv; fd_set rfdset, wfdset; int32_t n, fd; assert(srv != NULL); tv.tv_sec = 1; tv.tv_usec = 0; /* Copy cached version of the fd sets and call select */ memcpy(&rfdset, &srv->rfdset, sizeof(rfdset)); memcpy(&wfdset, &srv->wfdset, sizeof(wfdset)); n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv); if (n < 0) { if (errno == EINTR) return (0); syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)", srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno); return (-1); } /* Process descriptors (if any) */ for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) { if (FD_ISSET(fd, &rfdset)) { n --; if (fd == srv->ctrl || fd == srv->intr) server_accept(srv, fd); else server_process(srv, fd); } else if (FD_ISSET(fd, &wfdset)) { n --; client_connect(srv, fd); } } return (0); } /* * Accept new connection */ static int32_t server_accept(bthid_server_p srv, int32_t fd) { bthid_session_p s; hid_device_p d; struct sockaddr_l2cap l2addr; int32_t new_fd; socklen_t len; len = sizeof(l2addr); if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) { syslog(LOG_ERR, "Could not accept %s connection. %s (%d)", (fd == srv->ctrl)? "control" : "interrupt", strerror(errno), errno); return (-1); } /* Is device configured? */ if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) { syslog(LOG_ERR, "Rejecting %s connection from %s. " \ "Device not configured", (fd == srv->ctrl)? "control" : "interrupt", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); close(new_fd); return (-1); } /* Check if we have session for the device */ if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) { d->new_device = 0; /* reset new device flag */ write_hids_file(); /* Create new inbound session */ if ((s = session_open(srv, d)) == NULL) { syslog(LOG_CRIT, "Could not open inbound session " "for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); close(new_fd); return (-1); } } /* Update descriptors */ if (fd == srv->ctrl) { assert(s->ctrl == -1); s->ctrl = new_fd; s->state = (s->intr == -1)? W4INTR : OPEN; } else { assert(s->intr == -1); s->intr = new_fd; s->state = (s->ctrl == -1)? W4CTRL : OPEN; } FD_SET(new_fd, &srv->rfdset); if (new_fd > srv->maxfd) srv->maxfd = new_fd; syslog(LOG_NOTICE, "Accepted %s connection from %s", (fd == srv->ctrl)? "control" : "interrupt", bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); /* Register session's vkbd descriptor (if needed) for read */ if (s->state == OPEN && d->keyboard) { assert(s->vkbd != -1); FD_SET(s->vkbd, &srv->rfdset); if (s->vkbd > srv->maxfd) srv->maxfd = s->vkbd; } return (0); } /* * Process data on the connection */ static int32_t server_process(bthid_server_p srv, int32_t fd) { bthid_session_p s = session_by_fd(srv, fd); int32_t len, to_read; int32_t (*cb)(bthid_session_p, uint8_t *, int32_t); union { uint8_t b[1024]; vkbd_status_t s; } data; if (s == NULL) return (0); /* can happen on device disconnect */ if (fd == s->ctrl) { cb = hid_control; to_read = sizeof(data.b); } else if (fd == s->intr) { cb = hid_interrupt; to_read = sizeof(data.b); } else { assert(fd == s->vkbd); cb = kbd_status_changed; to_read = sizeof(data.s); } do { len = read(fd, &data, to_read); } while (len < 0 && errno == EINTR); if (len < 0) { syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)", bt_ntoa(&s->bdaddr, NULL), (fd == s->ctrl)? "control" : "interrupt", strerror(errno), errno); session_close(s); return (0); } if (len == 0) { syslog(LOG_NOTICE, "Remote device %s has closed %s connection", bt_ntoa(&s->bdaddr, NULL), (fd == s->ctrl)? "control" : "interrupt"); session_close(s); return (0); } (*cb)(s, (uint8_t *) &data, len); return (0); } Index: head/usr.sbin/bluetooth/bthidd/session.c =================================================================== --- head/usr.sbin/bluetooth/bthidd/session.c (revision 281209) +++ head/usr.sbin/bluetooth/bthidd/session.c (revision 281210) @@ -1,184 +1,185 @@ /* * session.c */ /*- * Copyright (c) 2006 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: session.c,v 1.3 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidd.h" #include "kbd.h" /* * Create new session */ bthid_session_p session_open(bthid_server_p srv, hid_device_p const d) { bthid_session_p s; assert(srv != NULL); assert(d != NULL); if ((s = (bthid_session_p) malloc(sizeof(*s))) == NULL) return (NULL); s->srv = srv; memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr)); s->ctrl = -1; s->intr = -1; if (d->keyboard) { /* Open /dev/vkbdctl */ s->vkbd = open("/dev/vkbdctl", O_RDWR); if (s->vkbd < 0) { syslog(LOG_ERR, "Could not open /dev/vkbdctl " \ "for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); free(s); return (NULL); } } else s->vkbd = -1; s->state = CLOSED; s->keys1 = bit_alloc(kbd_maxkey()); if (s->keys1 == NULL) { free(s); return (NULL); } s->keys2 = bit_alloc(kbd_maxkey()); if (s->keys2 == NULL) { free(s->keys1); free(s); return (NULL); } LIST_INSERT_HEAD(&srv->sessions, s, next); return (s); } /* * Lookup session by bdaddr */ bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr) { bthid_session_p s; assert(srv != NULL); assert(bdaddr != NULL); LIST_FOREACH(s, &srv->sessions, next) if (memcmp(&s->bdaddr, bdaddr, sizeof(s->bdaddr)) == 0) break; return (s); } /* * Lookup session by fd */ bthid_session_p session_by_fd(bthid_server_p srv, int32_t fd) { bthid_session_p s; assert(srv != NULL); assert(fd >= 0); LIST_FOREACH(s, &srv->sessions, next) if (s->ctrl == fd || s->intr == fd || s->vkbd == fd) break; return (s); } /* * Close session */ void session_close(bthid_session_p s) { assert(s != NULL); assert(s->srv != NULL); LIST_REMOVE(s, next); if (s->intr != -1) { FD_CLR(s->intr, &s->srv->rfdset); FD_CLR(s->intr, &s->srv->wfdset); close(s->intr); if (s->srv->maxfd == s->intr) s->srv->maxfd --; } if (s->ctrl != -1) { FD_CLR(s->ctrl, &s->srv->rfdset); FD_CLR(s->ctrl, &s->srv->wfdset); close(s->ctrl); if (s->srv->maxfd == s->ctrl) s->srv->maxfd --; } if (s->vkbd != -1) { FD_CLR(s->vkbd, &s->srv->rfdset); close(s->vkbd); if (s->srv->maxfd == s->vkbd) s->srv->maxfd --; } free(s->keys1); free(s->keys2); memset(s, 0, sizeof(*s)); free(s); } Index: head/usr.sbin/bluetooth/btpand/bnep.c =================================================================== --- head/usr.sbin/bluetooth/btpand/bnep.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/bnep.c (revision 281210) @@ -1,755 +1,756 @@ /* $NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include "btpand.h" #include "bnep.h" static bool bnep_recv_extension(packet_t *); static size_t bnep_recv_control(channel_t *, uint8_t *, size_t, bool); static size_t bnep_recv_control_command_not_understood(channel_t *, uint8_t *, size_t); static size_t bnep_recv_setup_connection_req(channel_t *, uint8_t *, size_t); static size_t bnep_recv_setup_connection_rsp(channel_t *, uint8_t *, size_t); static size_t bnep_recv_filter_net_type_set(channel_t *, uint8_t *, size_t); static size_t bnep_recv_filter_net_type_rsp(channel_t *, uint8_t *, size_t); static size_t bnep_recv_filter_multi_addr_set(channel_t *, uint8_t *, size_t); static size_t bnep_recv_filter_multi_addr_rsp(channel_t *, uint8_t *, size_t); static bool bnep_pfilter(channel_t *, packet_t *); static bool bnep_mfilter(channel_t *, packet_t *); static uint8_t NAP_UUID[] = { 0x00, 0x00, 0x11, 0x16, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; static uint8_t GN_UUID[] = { 0x00, 0x00, 0x11, 0x17, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, }; static uint8_t PANU_UUID[] = { 0x00, 0x00, 0x11, 0x15, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; /* * receive BNEP packet * return true if packet is to be forwarded */ bool bnep_recv(packet_t *pkt) { size_t len; uint8_t type; if (pkt->len < 1) return false; type = pkt->ptr[0]; packet_adj(pkt, 1); switch (BNEP_TYPE(type)) { case BNEP_GENERAL_ETHERNET: if (pkt->len < (ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) { log_debug("dropped short packet (type 0x%2.2x)", type); return false; } pkt->dst = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->src = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->type = pkt->ptr; packet_adj(pkt, ETHER_TYPE_LEN); break; case BNEP_CONTROL: len = bnep_recv_control(pkt->chan, pkt->ptr, pkt->len, false); if (len == 0) return false; packet_adj(pkt, len); break; case BNEP_COMPRESSED_ETHERNET: if (pkt->len < ETHER_TYPE_LEN) { log_debug("dropped short packet (type 0x%2.2x)", type); return false; } pkt->dst = pkt->chan->laddr; pkt->src = pkt->chan->raddr; pkt->type = pkt->ptr; packet_adj(pkt, ETHER_TYPE_LEN); break; case BNEP_COMPRESSED_ETHERNET_SRC_ONLY: if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) { log_debug("dropped short packet (type 0x%2.2x)", type); return false; } pkt->dst = pkt->chan->laddr; pkt->src = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->type = pkt->ptr; packet_adj(pkt, ETHER_TYPE_LEN); break; case BNEP_COMPRESSED_ETHERNET_DST_ONLY: if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) { log_debug("dropped short packet (type 0x%2.2x)", type); return false; } pkt->dst = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->src = pkt->chan->raddr; pkt->type = pkt->ptr; packet_adj(pkt, ETHER_TYPE_LEN); break; default: /* * Any packet containing a reserved BNEP * header packet type SHALL be dropped. */ log_debug("dropped packet with reserved type 0x%2.2x", type); return false; } if (BNEP_TYPE_EXT(type) && !bnep_recv_extension(pkt)) return false; /* invalid extensions */ if (BNEP_TYPE(type) == BNEP_CONTROL || pkt->chan->state != CHANNEL_OPEN) return false; /* no forwarding */ return true; } static bool bnep_recv_extension(packet_t *pkt) { exthdr_t *eh; size_t len, size; uint8_t type; do { if (pkt->len < 2) return false; type = pkt->ptr[0]; size = pkt->ptr[1]; if (pkt->len < size + 2) return false; switch (type) { case BNEP_EXTENSION_CONTROL: len = bnep_recv_control(pkt->chan, pkt->ptr + 2, size, true); if (len != size) log_err("ignored spurious data in exthdr"); break; default: /* Unknown extension headers in data packets */ /* SHALL be forwarded irrespective of any */ /* network protocol or multicast filter settings */ /* and any local filtering policy. */ eh = malloc(sizeof(exthdr_t)); if (eh == NULL) { log_err("exthdr malloc() failed: %m"); break; } eh->ptr = pkt->ptr; eh->len = size; STAILQ_INSERT_TAIL(&pkt->extlist, eh, next); break; } packet_adj(pkt, size + 2); } while (BNEP_TYPE_EXT(type)); return true; } static size_t bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext) { uint8_t type; size_t len; if (size-- < 1) return 0; type = *ptr++; switch (type) { case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: len = bnep_recv_control_command_not_understood(chan, ptr, size); break; case BNEP_SETUP_CONNECTION_REQUEST: if (isext) return 0; /* not allowed in extension headers */ len = bnep_recv_setup_connection_req(chan, ptr, size); break; case BNEP_SETUP_CONNECTION_RESPONSE: if (isext) return 0; /* not allowed in extension headers */ len = bnep_recv_setup_connection_rsp(chan, ptr, size); break; case BNEP_FILTER_NET_TYPE_SET: len = bnep_recv_filter_net_type_set(chan, ptr, size); break; case BNEP_FILTER_NET_TYPE_RESPONSE: len = bnep_recv_filter_net_type_rsp(chan, ptr, size); break; case BNEP_FILTER_MULTI_ADDR_SET: len = bnep_recv_filter_multi_addr_set(chan, ptr, size); break; case BNEP_FILTER_MULTI_ADDR_RESPONSE: len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size); break; default: len = 0; break; } if (len == 0) bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type); return len; } static size_t bnep_recv_control_command_not_understood(channel_t *chan, uint8_t *ptr, size_t size) { uint8_t type; if (size < 1) return 0; type = *ptr++; log_err("received Control Command Not Understood (0x%2.2x)", type); /* we didn't send any reserved commands, just cut them off */ channel_close(chan); return 1; } static size_t bnep_recv_setup_connection_req(channel_t *chan, uint8_t *ptr, size_t size) { uint8_t off; int src, dst, rsp; size_t len; if (size < 1) return 0; len = *ptr++; if (size < (len * 2 + 1)) return 0; if (chan->state != CHANNEL_WAIT_CONNECT_REQ && chan->state != CHANNEL_OPEN) { log_debug("ignored"); return (len * 2 + 1); } if (len == 2) off = 2; else if (len == 4) off = 0; else if (len == 16) off = 0; else { rsp = BNEP_SETUP_INVALID_UUID_SIZE; goto done; } if (memcmp(ptr, NAP_UUID + off, len) == 0) dst = SDP_SERVICE_CLASS_NAP; else if (memcmp(ptr, GN_UUID + off, len) == 0) dst = SDP_SERVICE_CLASS_GN; else if (memcmp(ptr, PANU_UUID + off, len) == 0) dst = SDP_SERVICE_CLASS_PANU; else dst = 0; if (dst != service_class) { rsp = BNEP_SETUP_INVALID_DST_UUID; goto done; } ptr += len; if (memcmp(ptr, NAP_UUID + off, len) == 0) src = SDP_SERVICE_CLASS_NAP; else if (memcmp(ptr, GN_UUID + off, len) == 0) src = SDP_SERVICE_CLASS_GN; else if (memcmp(ptr, PANU_UUID + off, len) == 0) src = SDP_SERVICE_CLASS_PANU; else src = 0; if ((dst != SDP_SERVICE_CLASS_PANU && src != SDP_SERVICE_CLASS_PANU) || src == 0) { rsp = BNEP_SETUP_INVALID_SRC_UUID; goto done; } rsp = BNEP_SETUP_SUCCESS; chan->state = CHANNEL_OPEN; channel_timeout(chan, 0); done: log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); bnep_send_control(chan, BNEP_SETUP_CONNECTION_RESPONSE, rsp); return (len * 2 + 1); } static size_t bnep_recv_setup_connection_rsp(channel_t *chan, uint8_t *ptr, size_t size) { int rsp; if (size < 2) return 0; rsp = be16dec(ptr); if (chan->state != CHANNEL_WAIT_CONNECT_RSP) { log_debug("ignored"); return 2; } log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); if (rsp == BNEP_SETUP_SUCCESS) { chan->state = CHANNEL_OPEN; channel_timeout(chan, 0); } else { channel_close(chan); } return 2; } static size_t bnep_recv_filter_net_type_set(channel_t *chan, uint8_t *ptr, size_t size) { pfilter_t *pf; int i, nf, rsp; size_t len; if (size < 2) return 0; len = be16dec(ptr); ptr += 2; if (size < (len + 2)) return 0; if (chan->state != CHANNEL_OPEN) { log_debug("ignored"); return (len + 2); } nf = len / 4; pf = malloc(nf * sizeof(pfilter_t)); if (pf == NULL) { rsp = BNEP_FILTER_TOO_MANY_FILTERS; goto done; } log_debug("nf = %d", nf); for (i = 0; i < nf; i++) { pf[i].start = be16dec(ptr); ptr += 2; pf[i].end = be16dec(ptr); ptr += 2; if (pf[i].start > pf[i].end) { free(pf); rsp = BNEP_FILTER_INVALID_RANGE; goto done; } log_debug("pf[%d] = %#4.4x, %#4.4x", i, pf[i].start, pf[i].end); } if (chan->pfilter) free(chan->pfilter); chan->pfilter = pf; chan->npfilter = nf; rsp = BNEP_FILTER_SUCCESS; done: log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); bnep_send_control(chan, BNEP_FILTER_NET_TYPE_RESPONSE, rsp); return (len + 2); } static size_t bnep_recv_filter_net_type_rsp(channel_t *chan, uint8_t *ptr, size_t size) { int rsp; if (size < 2) return 0; if (chan->state != CHANNEL_OPEN) { log_debug("ignored"); return 2; } rsp = be16dec(ptr); log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); /* we did not send any filter_net_type_set message */ return 2; } static size_t bnep_recv_filter_multi_addr_set(channel_t *chan, uint8_t *ptr, size_t size) { mfilter_t *mf; int i, nf, rsp; size_t len; if (size < 2) return 0; len = be16dec(ptr); ptr += 2; if (size < (len + 2)) return 0; if (chan->state != CHANNEL_OPEN) { log_debug("ignored"); return (len + 2); } nf = len / (ETHER_ADDR_LEN * 2); mf = malloc(nf * sizeof(mfilter_t)); if (mf == NULL) { rsp = BNEP_FILTER_TOO_MANY_FILTERS; goto done; } log_debug("nf = %d", nf); for (i = 0; i < nf; i++) { memcpy(mf[i].start, ptr, ETHER_ADDR_LEN); ptr += ETHER_ADDR_LEN; memcpy(mf[i].end, ptr, ETHER_ADDR_LEN); ptr += ETHER_ADDR_LEN; if (memcmp(mf[i].start, mf[i].end, ETHER_ADDR_LEN) > 0) { free(mf); rsp = BNEP_FILTER_INVALID_RANGE; goto done; } log_debug("pf[%d] = " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", i, mf[i].start[0], mf[i].start[1], mf[i].start[2], mf[i].start[3], mf[i].start[4], mf[i].start[5], mf[i].end[0], mf[i].end[1], mf[i].end[2], mf[i].end[3], mf[i].end[4], mf[i].end[5]); } if (chan->mfilter) free(chan->mfilter); chan->mfilter = mf; chan->nmfilter = nf; rsp = BNEP_FILTER_SUCCESS; done: log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); bnep_send_control(chan, BNEP_FILTER_MULTI_ADDR_RESPONSE, rsp); return (len + 2); } static size_t bnep_recv_filter_multi_addr_rsp(channel_t *chan, uint8_t *ptr, size_t size) { int rsp; if (size < 2) return false; if (chan->state != CHANNEL_OPEN) { log_debug("ignored"); return 2; } rsp = be16dec(ptr); log_debug("addr %s response 0x%2.2x", ether_ntoa((struct ether_addr *)chan->raddr), rsp); /* we did not send any filter_multi_addr_set message */ return 2; } void bnep_send_control(channel_t *chan, uint8_t type, ...) { packet_t *pkt; uint8_t *p; va_list ap; assert(chan->state != CHANNEL_CLOSED); pkt = packet_alloc(chan); if (pkt == NULL) return; p = pkt->ptr; va_start(ap, type); *p++ = BNEP_CONTROL; *p++ = type; switch(type) { case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: *p++ = va_arg(ap, int); break; case BNEP_SETUP_CONNECTION_REQUEST: *p++ = va_arg(ap, int); be16enc(p, va_arg(ap, int)); p += 2; be16enc(p, va_arg(ap, int)); p += 2; break; case BNEP_SETUP_CONNECTION_RESPONSE: case BNEP_FILTER_NET_TYPE_RESPONSE: case BNEP_FILTER_MULTI_ADDR_RESPONSE: be16enc(p, va_arg(ap, int)); p += 2; break; case BNEP_FILTER_NET_TYPE_SET: /* TODO */ case BNEP_FILTER_MULTI_ADDR_SET: /* TODO */ default: log_err("Can't send control type 0x%2.2x", type); break; } va_end(ap); pkt->len = p - pkt->ptr; channel_put(chan, pkt); packet_free(pkt); } /* * BNEP send packet routine * return true if packet can be removed from queue */ bool bnep_send(channel_t *chan, packet_t *pkt) { struct iovec iov[2]; uint8_t *p, *type, *proto; exthdr_t *eh; bool src, dst; size_t nw; if (pkt->type == NULL) { iov[0].iov_base = pkt->ptr; iov[0].iov_len = pkt->len; iov[1].iov_base = NULL; iov[1].iov_len = 0; } else { p = chan->sendbuf; dst = (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) != 0); src = (memcmp(pkt->src, chan->laddr, ETHER_ADDR_LEN) != 0); type = p; p += 1; if (dst && src) *type = BNEP_GENERAL_ETHERNET; else if (dst && !src) *type = BNEP_COMPRESSED_ETHERNET_DST_ONLY; else if (!dst && src) *type = BNEP_COMPRESSED_ETHERNET_SRC_ONLY; else /* (!dst && !src) */ *type = BNEP_COMPRESSED_ETHERNET; if (dst) { memcpy(p, pkt->dst, ETHER_ADDR_LEN); p += ETHER_ADDR_LEN; } if (src) { memcpy(p, pkt->src, ETHER_ADDR_LEN); p += ETHER_ADDR_LEN; } proto = p; memcpy(p, pkt->type, ETHER_TYPE_LEN); p += ETHER_TYPE_LEN; STAILQ_FOREACH(eh, &pkt->extlist, next) { if (p + eh->len > chan->sendbuf + chan->mtu) break; *type |= BNEP_EXT; type = p; memcpy(p, eh->ptr, eh->len); p += eh->len; } *type &= ~BNEP_EXT; iov[0].iov_base = chan->sendbuf; iov[0].iov_len = (p - chan->sendbuf); if ((chan->npfilter == 0 || bnep_pfilter(chan, pkt)) && (chan->nmfilter == 0 || bnep_mfilter(chan, pkt))) { iov[1].iov_base = pkt->ptr; iov[1].iov_len = pkt->len; } else if (be16dec(proto) == ETHERTYPE_VLAN && pkt->len >= ETHER_VLAN_ENCAP_LEN) { iov[1].iov_base = pkt->ptr; iov[1].iov_len = ETHER_VLAN_ENCAP_LEN; } else { iov[1].iov_base = NULL; iov[1].iov_len = 0; memset(proto, 0, ETHER_TYPE_LEN); } } if (iov[0].iov_len + iov[1].iov_len > chan->mtu) { log_err("packet exceeded MTU (dropped)"); return false; } nw = writev(chan->fd, iov, __arraycount(iov)); return (nw > 0); } static bool bnep_pfilter(channel_t *chan, packet_t *pkt) { int proto, i; proto = be16dec(pkt->type); if (proto == ETHERTYPE_VLAN) { /* IEEE 802.1Q tag header */ if (pkt->len < 4) return false; proto = be16dec(pkt->ptr + 2); } for (i = 0; i < chan->npfilter; i++) { if (chan->pfilter[i].start <= proto && chan->pfilter[i].end >=proto) return true; } return false; } static bool bnep_mfilter(channel_t *chan, packet_t *pkt) { int i; if (!ETHER_IS_MULTICAST(pkt->dst)) return true; for (i = 0; i < chan->nmfilter; i++) { if (memcmp(pkt->dst, chan->mfilter[i].start, ETHER_ADDR_LEN) >= 0 && memcmp(pkt->dst, chan->mfilter[i].end, ETHER_ADDR_LEN) <= 0) return true; } return false; } Index: head/usr.sbin/bluetooth/btpand/btpand.c =================================================================== --- head/usr.sbin/bluetooth/btpand/btpand.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/btpand.c (revision 281210) @@ -1,293 +1,294 @@ /* $NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert. All rights reserved."); __RCSID("$NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "btpand.h" /* global variables */ const char * control_path; /* -c */ const char * interface_name; /* -i */ const char * service_name; /* -s */ uint16_t service_class; bdaddr_t local_bdaddr; /* -d */ bdaddr_t remote_bdaddr; /* -a */ uint16_t l2cap_psm; /* -p */ int l2cap_mode; /* -m */ int server_limit; /* -n */ static const struct { const char * name; uint16_t class; const char * desc; } services[] = { { "PANU", SDP_SERVICE_CLASS_PANU, "Personal Area Networking User" }, { "NAP", SDP_SERVICE_CLASS_NAP, "Network Acess Point" }, { "GN", SDP_SERVICE_CLASS_GN, "Group Network" }, }; static void main_exit(int); static void main_detach(void); static void usage(void); int main(int argc, char *argv[]) { unsigned long ul; char * ep; int ch, status; while ((ch = getopt(argc, argv, "a:c:d:i:l:m:p:S:s:")) != -1) { switch (ch) { case 'a': /* remote address */ if (!bt_aton(optarg, &remote_bdaddr)) { struct hostent *he; if ((he = bt_gethostbyname(optarg)) == NULL) errx(EXIT_FAILURE, "%s: %s", optarg, hstrerror(h_errno)); bdaddr_copy(&remote_bdaddr, (bdaddr_t *)he->h_addr); } break; case 'c': /* control socket path */ control_path = optarg; break; case 'd': /* local address */ if (!bt_devaddr(optarg, &local_bdaddr)) { struct hostent *he; if ((he = bt_gethostbyname(optarg)) == NULL) errx(EXIT_FAILURE, "%s: %s", optarg, hstrerror(h_errno)); bdaddr_copy(&local_bdaddr, (bdaddr_t *)he->h_addr); } break; case 'i': /* tap interface name */ if (strchr(optarg, '/') == NULL) { asprintf(&ep, "/dev/%s", optarg); interface_name = ep; } else interface_name = optarg; break; case 'l': /* limit server sessions */ ul = strtoul(optarg, &ep, 10); if (*optarg == '\0' || *ep != '\0' || ul == 0) errx(EXIT_FAILURE, "%s: invalid session limit", optarg); server_limit = ul; break; case 'm': /* link mode */ warnx("Setting link mode is not yet supported"); break; case 'p': /* protocol/service multiplexer */ ul = strtoul(optarg, &ep, 0); if (*optarg == '\0' || *ep != '\0' || ul > 0xffff || L2CAP_PSM_INVALID(ul)) errx(EXIT_FAILURE, "%s: invalid PSM", optarg); l2cap_psm = ul; break; case 's': /* service */ case 'S': /* service (no SDP) */ for (ul = 0; strcasecmp(optarg, services[ul].name); ul++) { if (ul == __arraycount(services)) errx(EXIT_FAILURE, "%s: unknown service", optarg); } if (ch == 's') service_name = services[ul].name; service_class = services[ul].class; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; /* validate options */ if (bdaddr_any(&local_bdaddr) || service_class == 0) usage(); if (!bdaddr_any(&remote_bdaddr) && (server_limit != 0 || control_path != 0 || (service_name != NULL && l2cap_psm != 0))) usage(); /* default options */ if (interface_name == NULL) interface_name = "/dev/tap"; if (l2cap_psm == 0) l2cap_psm = L2CAP_PSM_BNEP; if (bdaddr_any(&remote_bdaddr) && server_limit == 0) { if (service_class == SDP_SERVICE_CLASS_PANU) server_limit = 1; else server_limit = 7; } #ifdef L2CAP_LM_MASTER if (server_limit > 1 && service_class != SDP_SERVICE_CLASS_PANU) l2cap_mode |= L2CAP_LM_MASTER; #endif /* * fork() now so that the setup can be done in the child process * (as kqueue is not inherited) but block in the parent until the * setup is finished so we can return an error if necessary. */ switch(fork()) { case -1: /* bad */ err(EXIT_FAILURE, "fork() failed"); case 0: /* child */ signal(SIGPIPE, SIG_IGN); openlog(getprogname(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_DAEMON); channel_init(); server_init(); event_init(); client_init(); tap_init(); main_detach(); event_dispatch(); break; default: /* parent */ signal(SIGUSR1, main_exit); wait(&status); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); break; } err(EXIT_FAILURE, "exiting"); } static void main_exit(int s) { /* child is all grown up */ _exit(EXIT_SUCCESS); } static void main_detach(void) { int fd; if (kill(getppid(), SIGUSR1) == -1) log_err("Could not signal main process: %m"); if (setsid() == -1) log_err("setsid() failed"); fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd == -1) { log_err("Could not open %s", _PATH_DEVNULL); } else { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); close(fd); } } static void usage(void) { const char *p = getprogname(); int n = strlen(p); fprintf(stderr, "usage: %s [-i ifname] [-m mode] -a address -d device\n" " %*s {-s service | -S service [-p psm]}\n" " %s [-c path] [-i ifname] [-l limit] [-m mode] [-p psm] -d device\n" " %*s {-s service | -S service}\n" "\n" "Where:\n" "\t-a address remote bluetooth device\n" "\t-c path SDP server socket\n" "\t-d device local bluetooth device\n" "\t-i ifname tap interface\n" "\t-l limit limit server sessions\n" "\t-m mode L2CAP link mode (NOT YET SUPPORTED)\n" "\t-p psm L2CAP PSM\n" "\t-S service service name (no SDP)\n" "\t-s service service name\n" "\n" "Known services:\n" "", p, n, "", p, n, ""); for (n = 0; n < __arraycount(services); n++) fprintf(stderr, "\t%s\t%s\n", services[n].name, services[n].desc); exit(EXIT_FAILURE); } Index: head/usr.sbin/bluetooth/btpand/channel.c =================================================================== --- head/usr.sbin/bluetooth/btpand/channel.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/channel.c (revision 281210) @@ -1,335 +1,335 @@ /* $NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); #include #include #include #include - +#define L2CAP_SOCKET_CHECKED #include "btpand.h" static struct chlist channel_list; static int channel_count; static int channel_tick; static void channel_start(int, short, void *); static void channel_read(int, short, void *); static void channel_dispatch(packet_t *); static void channel_watchdog(int, short, void *); void channel_init(void) { LIST_INIT(&channel_list); } channel_t * channel_alloc(void) { channel_t *chan; chan = malloc(sizeof(channel_t)); if (chan == NULL) { log_err("%s() failed: %m", __func__); return NULL; } memset(chan, 0, sizeof(channel_t)); STAILQ_INIT(&chan->pktlist); chan->state = CHANNEL_CLOSED; LIST_INSERT_HEAD(&channel_list, chan, next); server_update(++channel_count); return chan; } bool channel_open(channel_t *chan, int fd) { int n; assert(chan->refcnt == 0); assert(chan->state != CHANNEL_CLOSED); if (chan->mtu > 0) { chan->sendbuf = malloc(chan->mtu); if (chan->sendbuf == NULL) { log_err("Could not malloc channel sendbuf: %m"); return false; } } n = 1; if (ioctl(fd, FIONBIO, &n) == -1) { log_err("Could not set non-blocking IO: %m"); return false; } event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan); if (event_add(&chan->rd_ev, NULL) == -1) { log_err("Could not add channel read event: %m"); return false; } event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan); chan->refcnt++; chan->fd = fd; log_debug("(fd#%d)", chan->fd); return true; } void channel_close(channel_t *chan) { pkthdr_t *ph; assert(chan->state != CHANNEL_CLOSED); log_debug("(fd#%d)", chan->fd); chan->state = CHANNEL_CLOSED; event_del(&chan->rd_ev); event_del(&chan->wr_ev); close(chan->fd); chan->refcnt--; chan->tick = 0; while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) { STAILQ_REMOVE_HEAD(&chan->pktlist, next); pkthdr_free(ph); chan->qlen--; } if (chan->pfh != NULL) { pidfile_remove(chan->pfh); chan->pfh = NULL; } if (chan->refcnt == 0) channel_free(chan); } void channel_free(channel_t *chan) { assert(chan->refcnt == 0); assert(chan->state == CHANNEL_CLOSED); assert(chan->qlen == 0); assert(STAILQ_EMPTY(&chan->pktlist)); LIST_REMOVE(chan, next); free(chan->pfilter); free(chan->mfilter); free(chan->sendbuf); free(chan); server_update(--channel_count); if (server_limit == 0) { log_info("connection closed, exiting"); exit(EXIT_SUCCESS); } } static void channel_start(int fd, short ev, void *arg) { channel_t *chan = arg; pkthdr_t *ph; chan->oactive = true; while (chan->qlen > 0) { ph = STAILQ_FIRST(&chan->pktlist); channel_timeout(chan, 10); if (chan->send(chan, ph->data) == false) { if (event_add(&chan->wr_ev, NULL) == -1) { log_err("Could not add channel write event: %m"); channel_close(chan); } return; } STAILQ_REMOVE_HEAD(&chan->pktlist, next); pkthdr_free(ph); chan->qlen--; } channel_timeout(chan, 0); chan->oactive = false; } static void channel_read(int fd, short ev, void *arg) { channel_t *chan = arg; packet_t *pkt; ssize_t nr; pkt = packet_alloc(chan); if (pkt == NULL) { channel_close(chan); return; } nr = read(fd, pkt->buf, chan->mru); if (nr == -1) { log_err("channel read error: %m"); packet_free(pkt); channel_close(chan); return; } if (nr == 0) { /* EOF */ log_debug("(fd#%d) EOF", fd); packet_free(pkt); channel_close(chan); return; } pkt->len = nr; if (chan->recv(pkt) == true) channel_dispatch(pkt); packet_free(pkt); } static void channel_dispatch(packet_t *pkt) { channel_t *chan; /* * This is simple routing. I'm not sure if its allowed by * the PAN or BNEP specifications, but it seems logical * to send unicast packets to connected destinations where * possible. */ if (!ETHER_IS_MULTICAST(pkt->dst)) { LIST_FOREACH(chan, &channel_list, next) { if (chan == pkt->chan || chan->state != CHANNEL_OPEN) continue; if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) { if (chan->qlen > CHANNEL_MAXQLEN) log_notice("Queue overflow"); else channel_put(chan, pkt); return; } } } LIST_FOREACH(chan, &channel_list, next) { if (chan == pkt->chan || chan->state != CHANNEL_OPEN) continue; if (chan->qlen > CHANNEL_MAXQLEN) { log_notice("Queue overflow"); continue; } channel_put(chan, pkt); } } void channel_put(channel_t *chan, packet_t *pkt) { pkthdr_t *ph; ph = pkthdr_alloc(pkt); if (ph == NULL) return; chan->qlen++; STAILQ_INSERT_TAIL(&chan->pktlist, ph, next); if (!chan->oactive) channel_start(chan->fd, EV_WRITE, chan); } /* * Simple watchdog timer, only ticks when it is required and * closes the channel down if it times out. */ void channel_timeout(channel_t *chan, int to) { static struct event ev; if (to == 0) chan->tick = 0; else chan->tick = (channel_tick + to) % 60; if (channel_tick == 0) { evtimer_set(&ev, channel_watchdog, &ev); channel_watchdog(0, 0, &ev); } } static void channel_watchdog(int fd, short ev, void *arg) { static struct timeval tv = { .tv_sec = 1 }; channel_t *chan, *next; int tick; tick = (channel_tick % 60) + 1; channel_tick = 0; next = LIST_FIRST(&channel_list); while ((chan = next) != NULL) { next = LIST_NEXT(chan, next); if (chan->tick == tick) channel_close(chan); else if (chan->tick != 0) channel_tick = tick; } if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) { log_err("Could not add watchdog event: %m"); exit(EXIT_FAILURE); } } Index: head/usr.sbin/bluetooth/btpand/client.c =================================================================== --- head/usr.sbin/bluetooth/btpand/client.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/client.c (revision 281210) @@ -1,224 +1,228 @@ /* $NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $"); +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "btpand.h" #include "bnep.h" #include "sdp.h" static void client_query(void); void client_init(void) { struct sockaddr_l2cap sa; channel_t *chan; socklen_t len; int fd, n; uint16_t mru, mtu; if (bdaddr_any(&remote_bdaddr)) return; if (service_name) client_query(); fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (fd == -1) { log_err("Could not open L2CAP socket: %m"); exit(EXIT_FAILURE); } memset(&sa, 0, sizeof(sa)); sa.l2cap_family = AF_BLUETOOTH; sa.l2cap_len = sizeof(sa); + sa.l2cap_bdaddr_type = BDADDR_BREDR; + sa.l2cap_cid = 0; + bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not bind client socket: %m"); exit(EXIT_FAILURE); } mru = BNEP_MTU_MIN; if (setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { log_err("Could not set L2CAP IMTU (%d): %m", mru); exit(EXIT_FAILURE); } log_info("Opening connection to service 0x%4.4x at %s", service_class, bt_ntoa(&remote_bdaddr, NULL)); sa.l2cap_psm = htole16(l2cap_psm); bdaddr_copy(&sa.l2cap_bdaddr, &remote_bdaddr); if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not connect: %m"); exit(EXIT_FAILURE); } len = sizeof(mru); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { log_err("Could not get IMTU: %m"); exit(EXIT_FAILURE); } if (mru < BNEP_MTU_MIN) { log_err("L2CAP IMTU too small (%d)", mru); exit(EXIT_FAILURE); } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { log_err("Could not read SO_RCVBUF"); exit(EXIT_FAILURE); } if (n < (mru * 10)) { n = mru * 10; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) log_info("Could not increase SO_RCVBUF (from %d)", n); } len = sizeof(mtu); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { log_err("Could not get L2CAP OMTU: %m"); exit(EXIT_FAILURE); } if (mtu < BNEP_MTU_MIN) { log_err("L2CAP OMTU too small (%d)", mtu); exit(EXIT_FAILURE); } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { log_err("Could not get socket send buffer size: %m"); close(fd); return; } if (n < (mtu * 2)) { n = mtu * 2; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { log_err("Could not set socket send buffer size (%d): %m", n); close(fd); return; } } n = mtu; if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { log_err("Could not set socket low water mark (%d): %m", n); close(fd); return; } chan = channel_alloc(); if (chan == NULL) exit(EXIT_FAILURE); chan->send = bnep_send; chan->recv = bnep_recv; chan->mru = mru; chan->mtu = mtu; b2eaddr(chan->raddr, &remote_bdaddr); b2eaddr(chan->laddr, &local_bdaddr); chan->state = CHANNEL_WAIT_CONNECT_RSP; channel_timeout(chan, 10); if (!channel_open(chan, fd)) exit(EXIT_FAILURE); bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST, 2, service_class, SDP_SERVICE_CLASS_PANU); } static void client_query(void) { uint8_t buffer[512]; sdp_attr_t attr; uint32_t range; void *ss; int rv; uint8_t *seq0, *seq1; attr.flags = SDP_ATTR_INVALID; attr.attr = 0; attr.vlen = sizeof(buffer); attr.value = buffer; range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST); ss = sdp_open(&local_bdaddr, &remote_bdaddr); if (ss == NULL || (errno = sdp_error(ss)) != 0) { log_err("%s: %m", service_name); exit(EXIT_FAILURE); } log_info("Searching for %s service at %s", service_name, bt_ntoa(&remote_bdaddr, NULL)); rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr); if (rv != 0) { log_err("%s: %s", service_name, strerror(sdp_error(ss))); exit(EXIT_FAILURE); } sdp_close(ss); if (attr.flags != SDP_ATTR_OK || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) { log_err("%s service not found", service_name); exit(EXIT_FAILURE); } /* * we expect the following protocol descriptor list * * seq len * seq len * uuid value == L2CAP * uint16 value16 => PSM * seq len * uuid value == BNEP */ if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0) && _sdp_get_seq(&seq0, attr.value, &seq1) && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP) && _sdp_get_uint16(&seq1, seq0, &l2cap_psm) && _sdp_get_seq(&seq0, attr.value, &seq1) && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) { log_info("Found PSM %d for service %s", l2cap_psm, service_name); return; } log_err("%s query failed", service_name); exit(EXIT_FAILURE); } Index: head/usr.sbin/bluetooth/btpand/event.c =================================================================== --- head/usr.sbin/bluetooth/btpand/event.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/event.c (revision 281210) @@ -1,309 +1,310 @@ /* * event.h */ /*- * Copyright (c) 2009 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $FreeBSD$ */ /* * Hack to provide libevent (see devel/libevent port) like API. * Should be removed if FreeBSD ever decides to import libevent into base. */ #include #include #include #include #include #include #include #include #include "event.h" +#define L2CAP_SOCKET_CHECKED #include "btpand.h" #define __event_link(ev) \ do { \ TAILQ_INSERT_TAIL(&pending, ev, next); \ ev->flags |= EV_PENDING; \ } while (0) static void tv_add(struct timeval *, struct timeval const *); static void tv_sub(struct timeval *, struct timeval const *); static int tv_cmp(struct timeval const *, struct timeval const *); static int __event_dispatch(void); static void __event_add_current(struct event *); static void __event_del_current(struct event *); static TAILQ_HEAD(, event) pending; static TAILQ_HEAD(, event) current; void event_init(void) { TAILQ_INIT(&pending); } int event_dispatch(void) { while (__event_dispatch() == 0) ; return (-1); } static int __event_dispatch(void) { fd_set r, w; int nfd; struct event *ev; struct timeval now, timeout, t; FD_ZERO(&r); FD_ZERO(&w); nfd = 0; gettimeofday(&now, NULL); timeout.tv_sec = 10; /* arbitrary */ timeout.tv_usec = 0; TAILQ_INIT(¤t); /* * Build fd_set's */ event_log_debug("%s: building fd set...", __func__); while (!TAILQ_EMPTY(&pending)) { ev = TAILQ_FIRST(&pending); event_del(ev); if (ev->flags & EV_HAS_TIMEOUT) { if (tv_cmp(&now, &ev->expire) >= 0) t.tv_sec = t.tv_usec = 0; else { t = ev->expire; tv_sub(&t, &now); } if (tv_cmp(&t, &timeout) < 0) timeout = t; } if (ev->fd >= 0) { if (ev->flags & EV_READ) { FD_SET(ev->fd, &r); nfd = (nfd > ev->fd) ? nfd : ev->fd; } if (ev->flags & EV_WRITE) { FD_SET(ev->fd, &w); nfd = (nfd > ev->fd) ? nfd : ev->fd; } } __event_add_current(ev); } event_log_debug("%s: waiting for events...", __func__); nfd = select(nfd + 1, &r, &w, NULL, &timeout); if (nfd < 0) return (-1); /* * Process current pending */ event_log_debug("%s: processing events...", __func__); gettimeofday(&now, NULL); while (!TAILQ_EMPTY(¤t)) { ev = TAILQ_FIRST(¤t); __event_del_current(ev); /* check if fd is ready for reading/writing */ if (nfd > 0 && ev->fd >= 0) { if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) { if (ev->flags & EV_PERSIST) { if (ev->flags & EV_HAS_TIMEOUT) event_add(ev, &ev->timeout); else event_add(ev, NULL); } nfd --; event_log_debug("%s: calling %p(%d, %p), " \ "ev=%p", __func__, ev->cb, ev->fd, ev->cbarg, ev); (ev->cb)(ev->fd, (ev->flags & (EV_READ|EV_WRITE)), ev->cbarg); continue; } } /* if event has no timeout - just requeue */ if ((ev->flags & EV_HAS_TIMEOUT) == 0) { event_add(ev, NULL); continue; } /* check if event has expired */ if (tv_cmp(&now, &ev->expire) >= 0) { if (ev->flags & EV_PERSIST) event_add(ev, &ev->timeout); event_log_debug("%s: calling %p(%d, %p), ev=%p", __func__, ev->cb, ev->fd, ev->cbarg, ev); (ev->cb)(ev->fd, (ev->flags & (EV_READ|EV_WRITE)), ev->cbarg); continue; } assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); __event_link(ev); } return (0); } void __event_set(struct event *ev, int fd, short flags, void (*cb)(int, short, void *), void *cbarg) { ev->fd = fd; ev->flags = flags; ev->cb = cb; ev->cbarg = cbarg; } int __event_add(struct event *ev, const struct timeval *timeout) { assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); if (timeout != NULL) { gettimeofday(&ev->expire, NULL); tv_add(&ev->expire, timeout); ev->timeout = *timeout; ev->flags |= EV_HAS_TIMEOUT; } else ev->flags &= ~EV_HAS_TIMEOUT; __event_link(ev); return (0); } int __event_del(struct event *ev) { assert((ev->flags & EV_CURRENT) == 0); if ((ev->flags & EV_PENDING) != 0) { TAILQ_REMOVE(&pending, ev, next); ev->flags &= ~EV_PENDING; } return (0); } static void __event_add_current(struct event *ev) { assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); TAILQ_INSERT_TAIL(¤t, ev, next); ev->flags |= EV_CURRENT; } static void __event_del_current(struct event *ev) { assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT); TAILQ_REMOVE(¤t, ev, next); ev->flags &= ~EV_CURRENT; } static void tv_add(struct timeval *a, struct timeval const *b) { a->tv_sec += b->tv_sec; a->tv_usec += b->tv_usec; if(a->tv_usec >= 1000000) { a->tv_usec -= 1000000; a->tv_sec += 1; } } static void tv_sub(struct timeval *a, struct timeval const *b) { if (a->tv_usec < b->tv_usec) { a->tv_usec += 1000000; a->tv_sec -= 1; } a->tv_usec -= b->tv_usec; a->tv_sec -= b->tv_sec; } static int tv_cmp(struct timeval const *a, struct timeval const *b) { if (a->tv_sec > b->tv_sec) return (1); if (a->tv_sec < b->tv_sec) return (-1); if (a->tv_usec > b->tv_usec) return (1); if (a->tv_usec < b->tv_usec) return (-1); return (0); } Index: head/usr.sbin/bluetooth/btpand/packet.c =================================================================== --- head/usr.sbin/bluetooth/btpand/packet.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/packet.c (revision 281210) @@ -1,110 +1,111 @@ /* $NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); +#define L2CAP_SOCKET_CHECKED #include "btpand.h" packet_t * packet_alloc(channel_t *chan) { packet_t *pkt; pkt = malloc(sizeof(packet_t) + chan->mru); if (pkt == NULL) { log_err("%s() failed: %m", __func__); return NULL; } memset(pkt, 0, sizeof(packet_t)); STAILQ_INIT(&pkt->extlist); pkt->ptr = pkt->buf; pkt->chan = chan; chan->refcnt++; return pkt; } void packet_free(packet_t *pkt) { exthdr_t *eh; if (pkt->refcnt-- > 0) return; while ((eh = STAILQ_FIRST(&pkt->extlist)) != NULL) { STAILQ_REMOVE_HEAD(&pkt->extlist, next); free(eh); } pkt->chan->refcnt--; if (pkt->chan->refcnt == 0) channel_free(pkt->chan); free(pkt); } void packet_adj(packet_t *pkt, size_t size) { assert(pkt->refcnt == 0); assert(pkt->len >= size); pkt->ptr += size; pkt->len -= size; } pkthdr_t * pkthdr_alloc(packet_t *pkt) { pkthdr_t *ph; ph = malloc(sizeof(pkthdr_t)); if (ph == NULL) { log_err("%s() failed: %m", __func__); return NULL; } ph->data = pkt; pkt->refcnt++; return ph; } void pkthdr_free(pkthdr_t *ph) { packet_free(ph->data); free(ph); } Index: head/usr.sbin/bluetooth/btpand/sdp.c =================================================================== --- head/usr.sbin/bluetooth/btpand/sdp.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/sdp.c (revision 281210) @@ -1,209 +1,210 @@ /* $NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $"); #include +#define L2CAP_SOCKET_CHECKED #include "sdp.h" /* * SDP data stream manipulation routines */ /* Bluetooth Base UUID */ static const uuid_t BASE_UUID = { 0x00000000, 0x0000, 0x1000, 0x80, 0x00, { 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } }; /* * _sdp_match_uuid16(ptr, limit, uuid) * * examine SDP data stream at ptr for a UUID, and return * true if it matches the supplied short alias bluetooth UUID. * limit is the first address past the end of valid data. */ bool _sdp_match_uuid16(uint8_t **ptr, uint8_t *limit, uint16_t uuid) { uint8_t *p = *ptr; uuid_t u1, u2; memcpy(&u1, &BASE_UUID, sizeof(uuid_t)); u1.time_low = uuid; if (!_sdp_get_uuid(&p, limit, &u2) || !uuid_equal(&u1, &u2, NULL)) return false; *ptr = p; return true; } /* * _sdp_get_uuid(ptr, limit, uuid) * * examine SDP data stream at ptr for a UUID, and extract * to given storage, advancing ptr. * limit is the first address past the end of valid data. */ bool _sdp_get_uuid(uint8_t **ptr, uint8_t *limit, uuid_t *uuid) { uint8_t *p = *ptr; if (p + 1 > limit) return false; switch (*p++) { case SDP_DATA_UUID16: if (p + 2 > limit) return false; memcpy(uuid, &BASE_UUID, sizeof(uuid_t)); uuid->time_low = be16dec(p); p += 2; break; case SDP_DATA_UUID32: if (p + 4 > limit) return false; memcpy(uuid, &BASE_UUID, sizeof(uuid_t)); uuid->time_low = be32dec(p); p += 4; break; case SDP_DATA_UUID128: if (p + 16 > limit) return false; uuid_dec_be(p, uuid); p += 16; break; default: return false; } *ptr = p; return true; } /* * _sdp_get_seq(ptr, limit, seq) * * examine SDP data stream at ptr for a sequence. return * seq pointer if found and advance ptr to next object. * limit is the first address past the end of valid data. */ bool _sdp_get_seq(uint8_t **ptr, uint8_t *limit, uint8_t **seq) { uint8_t *p = *ptr; int32_t l; if (p + 1 > limit) return false; switch (*p++) { case SDP_DATA_SEQ8: if (p + 1 > limit) return false; l = *p; p += 1; break; case SDP_DATA_SEQ16: if (p + 2 > limit) return false; l = be16dec(p); p += 2; break; case SDP_DATA_SEQ32: if (p + 4 > limit) return false; l = be32dec(p); p += 4; break; default: return false; } if (p + l > limit) return false; *seq = p; *ptr = p + l; return true; } /* * _sdp_get_uint16(ptr, limit, value) * * examine SDP data stream at ptr for a uint16_t, and * extract to given storage, advancing ptr. * limit is the first address past the end of valid data. */ bool _sdp_get_uint16(uint8_t **ptr, uint8_t *limit, uint16_t *value) { uint8_t *p = *ptr; uint16_t v; if (p + 1 > limit) return false; switch (*p++) { case SDP_DATA_UINT16: if (p + 2 > limit) return false; v = be16dec(p); p += 2; break; default: return false; } *value = v; *ptr = p; return true; } Index: head/usr.sbin/bluetooth/btpand/server.c =================================================================== --- head/usr.sbin/bluetooth/btpand/server.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/server.c (revision 281210) @@ -1,289 +1,293 @@ /* $NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $"); #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include "btpand.h" #include "bnep.h" static struct event server_ev; static int server_fd; static int server_avail; static void * server_ss; static uint32_t server_handle; static void server_open(void); static void server_close(void); static void server_read(int, short, void *); static void server_register(void); void server_init(void) { server_fd = -1; } /* * The server_update() function is called whenever the channel count is * changed. We maintain the SDP record and open or close the server socket * as required. */ void server_update(int count) { if (server_limit == 0) return; log_debug("count %d", count); server_avail = UINT8_MAX - (count - 1) * UINT8_MAX / server_limit; log_info("Service Availability: %d/%d", server_avail, UINT8_MAX); if (server_avail == 0 && server_fd != -1) server_close(); if (server_avail > 0 && server_fd == -1) server_open(); if (service_name) server_register(); } static void server_open(void) { struct sockaddr_l2cap sa; uint16_t mru; server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (server_fd == -1) { log_err("Could not open L2CAP socket: %m"); exit(EXIT_FAILURE); } memset(&sa, 0, sizeof(sa)); sa.l2cap_family = AF_BLUETOOTH; sa.l2cap_len = sizeof(sa); sa.l2cap_psm = htole16(l2cap_psm); + sa.l2cap_bdaddr_type = BDADDR_BREDR; + sa.l2cap_cid = 0; + bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not bind server socket: %m"); exit(EXIT_FAILURE); } mru = BNEP_MTU_MIN; if (setsockopt(server_fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { log_err("Could not set L2CAP IMTU (%d): %m", mru); exit(EXIT_FAILURE); } if (listen(server_fd, 0) == -1) { log_err("Could not listen on server socket: %m"); exit(EXIT_FAILURE); } event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL); if (event_add(&server_ev, NULL) == -1) { log_err("Could not add server event: %m"); exit(EXIT_FAILURE); } log_info("server socket open"); } static void server_close(void) { event_del(&server_ev); close(server_fd); server_fd = -1; log_info("server socket closed"); } /* * handle connection request */ static void server_read(int s, short ev, void *arg) { struct sockaddr_l2cap ra, la; channel_t *chan; socklen_t len; int fd, n; uint16_t mru, mtu; len = sizeof(ra); fd = accept(s, (struct sockaddr *)&ra, &len); if (fd == -1) return; n = 1; if (ioctl(fd, FIONBIO, &n) == -1) { log_err("Could not set NonBlocking IO: %m"); close(fd); return; } len = sizeof(mru); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { log_err("Could not get L2CAP IMTU: %m"); close(fd); return; } if(mru < BNEP_MTU_MIN) { log_err("L2CAP IMTU too small (%d)", mru); close(fd); return; } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { log_err("Could not read SO_RCVBUF"); close(fd); return; } if (n < (mru * 10)) { n = mru * 10; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) log_info("Could not increase SO_RCVBUF (from %d)", n); } len = sizeof(mtu); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { log_err("Could not get L2CAP OMTU: %m"); close(fd); return; } if (mtu < BNEP_MTU_MIN) { log_err("L2CAP OMTU too small (%d)", mtu); close(fd); return; } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { log_err("Could not get socket send buffer size: %m"); close(fd); return; } if (n < (mtu * 2)) { n = mtu * 2; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { log_err("Could not set socket send buffer size (%d): %m", n); close(fd); return; } } n = mtu; if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { log_err("Could not set socket low water mark (%d): %m", n); close(fd); return; } len = sizeof(la); if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) { log_err("Could not get socket address: %m"); close(fd); return; } log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL)); chan = channel_alloc(); if (chan == NULL) { close(fd); return; } chan->send = bnep_send; chan->recv = bnep_recv; chan->mru = mru; chan->mtu = mtu; b2eaddr(chan->raddr, &ra.l2cap_bdaddr); b2eaddr(chan->laddr, &la.l2cap_bdaddr); chan->state = CHANNEL_WAIT_CONNECT_REQ; channel_timeout(chan, 10); if (!channel_open(chan, fd)) { chan->state = CHANNEL_CLOSED; channel_free(chan); close(fd); return; } } static void server_register(void) { sdp_nap_profile_t p; int rv; if (server_ss == NULL) { server_ss = sdp_open_local(control_path); if (server_ss == NULL || sdp_error(server_ss) != 0) { log_err("failed to contact SDP server"); return; } } memset(&p, 0, sizeof(p)); p.psm = l2cap_psm; p.load_factor = server_avail; p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001); if (server_handle) rv = sdp_change_service(server_ss, server_handle, (uint8_t *)&p, sizeof(p)); else rv = sdp_register_service(server_ss, service_class, &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle); if (rv != 0) { errno = sdp_error(server_ss); log_err("%s: %m", service_name); exit(EXIT_FAILURE); } } Index: head/usr.sbin/bluetooth/btpand/tap.c =================================================================== --- head/usr.sbin/bluetooth/btpand/tap.c (revision 281209) +++ head/usr.sbin/bluetooth/btpand/tap.c (revision 281210) @@ -1,167 +1,168 @@ /* $NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */ /*- * Copyright (c) 2008 Iain Hibbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $FreeBSD$ */ #include __RCSID("$NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); #include #include #include #include #include #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include "btpand.h" static bool tap_send(channel_t *, packet_t *); static bool tap_recv(packet_t *); void tap_init(void) { channel_t *chan; struct ifreq ifr; int fd, s; char pidfile[PATH_MAX]; fd = open(interface_name, O_RDWR); if (fd == -1) { log_err("Could not open \"%s\": %m", interface_name); exit(EXIT_FAILURE); } memset(&ifr, 0, sizeof(ifr)); if (ioctl(fd, TAPGIFNAME, &ifr) == -1) { log_err("Could not get interface name: %m"); exit(EXIT_FAILURE); } s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) { log_err("Could not open PF_LINK socket: %m"); exit(EXIT_FAILURE); } ifr.ifr_addr.sa_family = AF_LINK; ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; b2eaddr(ifr.ifr_addr.sa_data, &local_bdaddr); if (ioctl(s, SIOCSIFLLADDR, &ifr) == -1) { log_err("Could not set %s physical address: %m", ifr.ifr_name); exit(EXIT_FAILURE); } if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { log_err("Could not get interface flags: %m"); exit(EXIT_FAILURE); } if ((ifr.ifr_flags & IFF_UP) == 0) { ifr.ifr_flags |= IFF_UP; if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) { log_err("Could not set IFF_UP: %m"); exit(EXIT_FAILURE); } } close(s); log_info("Using interface %s with addr %s", ifr.ifr_name, ether_ntoa((struct ether_addr *)&ifr.ifr_addr.sa_data)); chan = channel_alloc(); if (chan == NULL) exit(EXIT_FAILURE); chan->send = tap_send; chan->recv = tap_recv; chan->mru = ETHER_HDR_LEN + ETHER_MAX_LEN; memcpy(chan->raddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); memcpy(chan->laddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); chan->state = CHANNEL_OPEN; if (!channel_open(chan, fd)) exit(EXIT_FAILURE); snprintf(pidfile, sizeof(pidfile), "%s/%s.pid", _PATH_VARRUN, ifr.ifr_name); chan->pfh = pidfile_open(pidfile, 0600, NULL); if (chan->pfh == NULL) log_err("can't create pidfile"); else if (pidfile_write(chan->pfh) < 0) { log_err("can't write pidfile"); pidfile_remove(chan->pfh); chan->pfh = NULL; } } static bool tap_send(channel_t *chan, packet_t *pkt) { struct iovec iov[4]; ssize_t nw; iov[0].iov_base = pkt->dst; iov[0].iov_len = ETHER_ADDR_LEN; iov[1].iov_base = pkt->src; iov[1].iov_len = ETHER_ADDR_LEN; iov[2].iov_base = pkt->type; iov[2].iov_len = ETHER_TYPE_LEN; iov[3].iov_base = pkt->ptr; iov[3].iov_len = pkt->len; /* tap device write never fails */ nw = writev(chan->fd, iov, __arraycount(iov)); assert(nw > 0); return true; } static bool tap_recv(packet_t *pkt) { if (pkt->len < ETHER_HDR_LEN) return false; pkt->dst = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->src = pkt->ptr; packet_adj(pkt, ETHER_ADDR_LEN); pkt->type = pkt->ptr; packet_adj(pkt, ETHER_TYPE_LEN); return true; } Index: head/usr.sbin/bluetooth/hccontrol/hccontrol.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/hccontrol.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/hccontrol.c (revision 281210) @@ -1,322 +1,323 @@ /* * hccontrol.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include "hccontrol.h" /* Prototypes */ static int do_hci_command (char const *, int, char **); static struct hci_command * find_hci_command (char const *, struct hci_command *); static int find_hci_nodes (struct nodeinfo **); static void print_hci_command (struct hci_command *); static void usage (void); /* Globals */ int verbose = 0; int timeout; int numeric_bdaddr = 0; /* Main */ int main(int argc, char *argv[]) { char *node = NULL; int n; /* Process command line arguments */ while ((n = getopt(argc, argv, "n:Nvh")) != -1) { switch (n) { case 'n': node = optarg; break; case 'N': numeric_bdaddr = 1; break; case 'v': verbose = 1; break; case 'h': default: usage(); } } argc -= optind; argv += optind; if (*argv == NULL) usage(); n = do_hci_command(node, argc, argv); return (n); } /* main */ /* Create socket and bind it */ static int socket_open(char const *node) { struct sockaddr_hci addr; struct ng_btsocket_hci_raw_filter filter; int s, mib[4], num; size_t size; struct nodeinfo *nodes; num = find_hci_nodes(&nodes); if (num == 0) errx(7, "Could not find HCI nodes"); if (node == NULL) { node = strdup(nodes[0].name); if (num > 1) fprintf(stdout, "Using HCI node: %s\n", node); } free(nodes); s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); if (s < 0) err(1, "Could not create socket"); memset(&addr, 0, sizeof(addr)); addr.hci_len = sizeof(addr); addr.hci_family = AF_BLUETOOTH; strncpy(addr.hci_node, node, sizeof(addr.hci_node)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) err(2, "Could not bind socket, node=%s", node); if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) err(3, "Could not connect socket, node=%s", node); memset(&filter, 0, sizeof(filter)); bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1); bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1); bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1); bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1); bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1); if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, (void * const) &filter, sizeof(filter)) < 0) err(4, "Could not setsockopt()"); size = (sizeof(mib)/sizeof(mib[0])); if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0) err(5, "Could not sysctlnametomib()"); if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), (void *) &timeout, &size, NULL, 0) < 0) err(6, "Could not sysctl()"); timeout ++; return (s); } /* socket_open */ /* Execute commands */ static int do_hci_command(char const *node, int argc, char **argv) { char *cmd = argv[0]; struct hci_command *c = NULL; int s, e, help; help = 0; if (strcasecmp(cmd, "help") == 0) { argc --; argv ++; if (argc <= 0) { fprintf(stdout, "Supported commands:\n"); print_hci_command(link_control_commands); print_hci_command(link_policy_commands); print_hci_command(host_controller_baseband_commands); print_hci_command(info_commands); print_hci_command(status_commands); print_hci_command(node_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); return (OK); } help = 1; cmd = argv[0]; } c = find_hci_command(cmd, link_control_commands); if (c != NULL) goto execute; c = find_hci_command(cmd, link_policy_commands); if (c != NULL) goto execute; c = find_hci_command(cmd, host_controller_baseband_commands); if (c != NULL) goto execute; c = find_hci_command(cmd, info_commands); if (c != NULL) goto execute; c = find_hci_command(cmd, status_commands); if (c != NULL) goto execute; c = find_hci_command(cmd, node_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); return (ERROR); } execute: if (!help) { s = socket_open(node); e = (c->handler)(s, -- argc, ++ argv); close(s); } else e = USAGE; switch (e) { case OK: case FAILED: break; case ERROR: fprintf(stdout, "Could not execute command \"%s\". %s\n", cmd, strerror(errno)); break; case USAGE: fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); break; default: assert(0); break; } return (e); } /* do_hci_command */ /* Try to find command in specified category */ static struct hci_command * find_hci_command(char const *command, struct hci_command *category) { struct hci_command *c = NULL; for (c = category; c->command != NULL; c++) { char *c_end = strchr(c->command, ' '); if (c_end != NULL) { int len = c_end - c->command; if (strncasecmp(command, c->command, len) == 0) return (c); } else if (strcasecmp(command, c->command) == 0) return (c); } return (NULL); } /* find_hci_command */ /* Find all HCI nodes */ static int find_hci_nodes(struct nodeinfo** nodes) { struct ng_btsocket_hci_raw_node_list_names r; struct sockaddr_hci addr; int s; const char * node = "ubt0hci"; r.num_names = MAX_NODE_NUM; r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo)); if (r.names == NULL) err(8, "Could not allocate memory"); s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); if (s < 0) err(9, "Could not create socket"); memset(&addr, 0, sizeof(addr)); addr.hci_len = sizeof(addr); addr.hci_family = AF_BLUETOOTH; strncpy(addr.hci_node, node, sizeof(addr.hci_node)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) err(10, "Could not bind socket"); if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0) err(11, "Could not get list of HCI nodes"); close(s); *nodes = r.names; return (r.num_names); } /* find_hci_nodes */ /* Print commands in specified category */ static void print_hci_command(struct hci_command *category) { struct hci_command *c = NULL; for (c = category; c->command != NULL; c++) fprintf(stdout, "\t%s\n", c->command); } /* print_hci_command */ /* Usage */ static void usage(void) { fprintf(stdout, "Usage: hccontrol [-hN] [-n HCI_node_name] cmd [p1] [..]\n"); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c (revision 281210) @@ -1,1877 +1,1878 @@ /* * host_controller_baseband.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: host_controller_baseband.c,v 1.4 2003/08/18 19:19:53 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "hccontrol.h" /* Convert hex ASCII to int4 */ static int hci_hexa2int4(const char *a) { if ('0' <= *a && *a <= '9') return (*a - '0'); if ('A' <= *a && *a <= 'F') return (*a - 'A' + 0xa); if ('a' <= *a && *a <= 'f') return (*a - 'a' + 0xa); return (-1); } /* Convert hex ASCII to int8 */ static int hci_hexa2int8(const char *a) { int hi = hci_hexa2int4(a); int lo = hci_hexa2int4(a + 1); if (hi < 0 || lo < 0) return (-1); return ((hi << 4) | lo); } /* Convert ascii hex string to the uint8_t[] */ static int hci_hexstring2array(char const *s, uint8_t *a, int asize) { int i, l, b; l = strlen(s) / 2; if (l > asize) l = asize; for (i = 0; i < l; i++) { b = hci_hexa2int8(s + i * 2); if (b < 0) return (-1); a[i] = (b & 0xff); } return (0); } /* Send RESET to the unit */ static int hci_reset(int s, int argc, char **argv) { ng_hci_status_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_RESET), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_reset */ /* Send Read_PIN_Type command to the unit */ static int hci_read_pin_type(int s, int argc, char **argv) { ng_hci_read_pin_type_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_PIN_TYPE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "PIN type: %s [%#02x]\n", hci_pin2str(rp.pin_type), rp.pin_type); return (OK); } /* hci_read_pin_type */ /* Send Write_PIN_Type command to the unit */ static int hci_write_pin_type(int s, int argc, char **argv) { ng_hci_write_pin_type_cp cp; ng_hci_write_pin_type_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) return (USAGE); cp.pin_type = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_PIN_TYPE), (char const *) &cp, sizeof(cp), (char *) &rp , &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_pin_type */ /* Send Read_Stored_Link_Key command to the unit */ static int hci_read_stored_link_key(int s, int argc, char **argv) { struct { ng_hci_cmd_pkt_t hdr; ng_hci_read_stored_link_key_cp cp; } __attribute__ ((packed)) cmd; struct { ng_hci_event_pkt_t hdr; union { ng_hci_command_compl_ep cc; ng_hci_return_link_keys_ep key; uint8_t b[NG_HCI_EVENT_PKT_SIZE]; } ep; } __attribute__ ((packed)) event; int n, n1; /* Send command */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.type = NG_HCI_CMD_PKT; cmd.hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_STORED_LINK_KEY)); cmd.hdr.length = sizeof(cmd.cp); switch (argc) { case 1: /* parse BD_ADDR */ if (!bt_aton(argv[0], &cmd.cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cmd.cp.bdaddr, he->h_addr, sizeof(cmd.cp.bdaddr)); } break; default: cmd.cp.read_all = 1; break; } if (hci_send(s, (char const *) &cmd, sizeof(cmd)) != OK) return (ERROR); /* Receive events */ again: memset(&event, 0, sizeof(event)); n = sizeof(event); if (hci_recv(s, (char *) &event, &n) != OK) return (ERROR); if (n <= sizeof(event.hdr)) { errno = EMSGSIZE; return (ERROR); } if (event.hdr.type != NG_HCI_EVENT_PKT) { errno = EIO; return (ERROR); } /* Parse event */ switch (event.hdr.event) { case NG_HCI_EVENT_COMMAND_COMPL: { ng_hci_read_stored_link_key_rp *rp = NULL; if (event.ep.cc.opcode == 0x0000 || event.ep.cc.opcode != cmd.hdr.opcode) goto again; rp = (ng_hci_read_stored_link_key_rp *)(event.ep.b + sizeof(event.ep.cc)); fprintf(stdout, "Complete: Status: %s [%#x]\n", hci_status2str(rp->status), rp->status); fprintf(stdout, "Maximum Number of keys: %d\n", le16toh(rp->max_num_keys)); fprintf(stdout, "Number of keys read: %d\n", le16toh(rp->num_keys_read)); } break; case NG_HCI_EVENT_RETURN_LINK_KEYS: { struct _key { bdaddr_t bdaddr; uint8_t key[NG_HCI_KEY_SIZE]; } __attribute__ ((packed)) *k = NULL; fprintf(stdout, "Event: Number of keys: %d\n", event.ep.key.num_keys); k = (struct _key *)(event.ep.b + sizeof(event.ep.key)); for (n = 0; n < event.ep.key.num_keys; n++) { fprintf(stdout, "\t%d: %s ", n + 1, hci_bdaddr2str(&k->bdaddr)); for (n1 = 0; n1 < sizeof(k->key); n1++) fprintf(stdout, "%02x", k->key[n1]); fprintf(stdout, "\n"); k ++; } goto again; } break; default: goto again; } return (OK); } /* hci_read_store_link_key */ /* Send Write_Stored_Link_Key command to the unit */ static int hci_write_stored_link_key(int s, int argc, char **argv) { struct { ng_hci_write_stored_link_key_cp p; bdaddr_t bdaddr; uint8_t key[NG_HCI_KEY_SIZE]; } cp; ng_hci_write_stored_link_key_rp rp; int32_t n; memset(&cp, 0, sizeof(cp)); switch (argc) { case 2: cp.p.num_keys_write = 1; /* parse BD_ADDR */ if (!bt_aton(argv[0], &cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr)); } /* parse key */ if (hci_hexstring2array(argv[1], cp.key, sizeof(cp.key)) < 0) return (USAGE); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_STORED_LINK_KEY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Number of keys written: %d\n", rp.num_keys_written); return (OK); } /* hci_write_stored_link_key */ /* Send Delete_Stored_Link_Key command to the unit */ static int hci_delete_stored_link_key(int s, int argc, char **argv) { ng_hci_delete_stored_link_key_cp cp; ng_hci_delete_stored_link_key_rp rp; int32_t n; memset(&cp, 0, sizeof(cp)); switch (argc) { case 1: /* parse BD_ADDR */ if (!bt_aton(argv[0], &cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr)); } break; default: cp.delete_all = 1; break; } /* send command */ n = sizeof(cp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_DELETE_STORED_LINK_KEY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Number of keys deleted: %d\n", rp.num_keys_deleted); return (OK); } /* hci_delete_stored_link_key */ /* Send Change_Local_Name command to the unit */ static int hci_change_local_name(int s, int argc, char **argv) { ng_hci_change_local_name_cp cp; ng_hci_change_local_name_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: snprintf(cp.name, sizeof(cp.name), "%s", argv[0]); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_CHANGE_LOCAL_NAME), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_change_local_name */ /* Send Read_Local_Name command to the unit */ static int hci_read_local_name(int s, int argc, char **argv) { ng_hci_read_local_name_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_LOCAL_NAME), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Local name: %s\n", rp.name); return (OK); } /* hci_read_local_name */ /* Send Read_Connection_Accept_Timeout to the unit */ static int hci_read_connection_accept_timeout(int s, int argc, char **argv) { ng_hci_read_con_accept_timo_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_CON_ACCEPT_TIMO), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.timeout = le16toh(rp.timeout); fprintf(stdout, "Connection accept timeout: %.2f msec [%d slots]\n", rp.timeout * 0.625, rp.timeout); return (OK); } /* hci_read_connection_accept_timeout */ /* Send Write_Connection_Accept_Timeout to the unit */ static int hci_write_connection_accept_timeout(int s, int argc, char **argv) { ng_hci_write_con_accept_timo_cp cp; ng_hci_write_con_accept_timo_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xb540) return (USAGE); cp.timeout = (uint16_t) n; cp.timeout = htole16(cp.timeout); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_connection_accept_timeout */ /* Send Read_Page_Timeout command to the unit */ static int hci_read_page_timeout(int s, int argc, char **argv) { ng_hci_read_page_timo_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_PAGE_TIMO), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.timeout = le16toh(rp.timeout); fprintf(stdout, "Page timeout: %.2f msec [%d slots]\n", rp.timeout * 0.625, rp.timeout); return (OK); } /* hci_read_page_timeoout */ /* Send Write_Page_Timeout command to the unit */ static int hci_write_page_timeout(int s, int argc, char **argv) { ng_hci_write_page_timo_cp cp; ng_hci_write_page_timo_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xffff) return (USAGE); cp.timeout = (uint16_t) n; cp.timeout = htole16(cp.timeout); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_PAGE_TIMO), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_page_timeout */ /* Send Read_Scan_Enable command to the unit */ static int hci_read_scan_enable(int s, int argc, char **argv) { ng_hci_read_scan_enable_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_SCAN_ENABLE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Scan enable: %s [%#02x]\n", hci_scan2str(rp.scan_enable), rp.scan_enable); return (OK); } /* hci_read_scan_enable */ /* Send Write_Scan_Enable command to the unit */ static int hci_write_scan_enable(int s, int argc, char **argv) { ng_hci_write_scan_enable_cp cp; ng_hci_write_scan_enable_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3) return (USAGE); cp.scan_enable = (uint8_t) n; break; default: return (USAGE); } n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_SCAN_ENABLE), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_scan_enable */ /* Send Read_Page_Scan_Activity command to the unit */ static int hci_read_page_scan_activity(int s, int argc, char **argv) { ng_hci_read_page_scan_activity_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.page_scan_interval = le16toh(rp.page_scan_interval); rp.page_scan_window = le16toh(rp.page_scan_window); fprintf(stdout, "Page Scan Interval: %.2f msec [%d slots]\n", rp.page_scan_interval * 0.625, rp.page_scan_interval); fprintf(stdout, "Page Scan Window: %.2f msec [%d slots]\n", rp.page_scan_window * 0.625, rp.page_scan_window); return (OK); } /* hci_read_page_scan_activity */ /* Send Write_Page_Scan_Activity command to the unit */ static int hci_write_page_scan_activity(int s, int argc, char **argv) { ng_hci_write_page_scan_activity_cp cp; ng_hci_write_page_scan_activity_rp rp; int n; /* parse command parameters */ switch (argc) { case 2: /* page scan interval */ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) return (USAGE); cp.page_scan_interval = (uint16_t) n; /* page scan window */ if (sscanf(argv[1], "%d", &n) != 1 || n < 0x12 || n > 0x1000) return (USAGE); cp.page_scan_window = (uint16_t) n; if (cp.page_scan_window > cp.page_scan_interval) return (USAGE); cp.page_scan_interval = htole16(cp.page_scan_interval); cp.page_scan_window = htole16(cp.page_scan_window); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_page_scan_activity */ /* Send Read_Inquiry_Scan_Activity command to the unit */ static int hci_read_inquiry_scan_activity(int s, int argc, char **argv) { ng_hci_read_inquiry_scan_activity_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.inquiry_scan_interval = le16toh(rp.inquiry_scan_interval); rp.inquiry_scan_window = le16toh(rp.inquiry_scan_window); fprintf(stdout, "Inquiry Scan Interval: %.2f msec [%d slots]\n", rp.inquiry_scan_interval * 0.625, rp.inquiry_scan_interval); fprintf(stdout, "Inquiry Scan Window: %.2f msec [%d slots]\n", rp.inquiry_scan_window * 0.625, rp.inquiry_scan_interval); return (OK); } /* hci_read_inquiry_scan_activity */ /* Send Write_Inquiry_Scan_Activity command to the unit */ static int hci_write_inquiry_scan_activity(int s, int argc, char **argv) { ng_hci_write_inquiry_scan_activity_cp cp; ng_hci_write_inquiry_scan_activity_rp rp; int n; /* parse command parameters */ switch (argc) { case 2: /* inquiry scan interval */ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) return (USAGE); cp.inquiry_scan_interval = (uint16_t) n; /* inquiry scan window */ if (sscanf(argv[1], "%d", &n) != 1 || n < 0x12 || n > 0x1000) return (USAGE); cp.inquiry_scan_window = (uint16_t) n; if (cp.inquiry_scan_window > cp.inquiry_scan_interval) return (USAGE); cp.inquiry_scan_interval = htole16(cp.inquiry_scan_interval); cp.inquiry_scan_window = htole16(cp.inquiry_scan_window); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_inquiry_scan_activity */ /* Send Read_Authentication_Enable command to the unit */ static int hci_read_authentication_enable(int s, int argc, char **argv) { ng_hci_read_auth_enable_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_AUTH_ENABLE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Authentication Enable: %s [%d]\n", rp.auth_enable? "Enabled" : "Disabled", rp.auth_enable); return (OK); } /* hci_read_authentication_enable */ /* Send Write_Authentication_Enable command to the unit */ static int hci_write_authentication_enable(int s, int argc, char **argv) { ng_hci_write_auth_enable_cp cp; ng_hci_write_auth_enable_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) return (USAGE); cp.auth_enable = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_AUTH_ENABLE), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_authentication_enable */ /* Send Read_Encryption_Mode command to the unit */ static int hci_read_encryption_mode(int s, int argc, char **argv) { ng_hci_read_encryption_mode_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_ENCRYPTION_MODE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Encryption mode: %s [%#02x]\n", hci_encrypt2str(rp.encryption_mode, 0), rp.encryption_mode); return (OK); } /* hci_read_encryption_mode */ /* Send Write_Encryption_Mode command to the unit */ static int hci_write_encryption_mode(int s, int argc, char **argv) { ng_hci_write_encryption_mode_cp cp; ng_hci_write_encryption_mode_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2) return (USAGE); cp.encryption_mode = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_ENCRYPTION_MODE), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_encryption_mode */ /* Send Read_Class_Of_Device command to the unit */ static int hci_read_class_of_device(int s, int argc, char **argv) { ng_hci_read_unit_class_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_UNIT_CLASS), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Class: %02x:%02x:%02x\n", rp.uclass[2], rp.uclass[1], rp.uclass[0]); return (0); } /* hci_read_class_of_device */ /* Send Write_Class_Of_Device command to the unit */ static int hci_write_class_of_device(int s, int argc, char **argv) { ng_hci_write_unit_class_cp cp; ng_hci_write_unit_class_rp rp; int n0, n1, n2; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3) return (USAGE); cp.uclass[0] = (n0 & 0xff); cp.uclass[1] = (n1 & 0xff); cp.uclass[2] = (n2 & 0xff); break; default: return (USAGE); } /* send command */ n0 = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_UNIT_CLASS), (char const *) &cp, sizeof(cp), (char *) &rp, &n0) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_class_of_device */ /* Send Read_Voice_Settings command to the unit */ static int hci_read_voice_settings(int s, int argc, char **argv) { ng_hci_read_voice_settings_rp rp; int n, input_coding, input_data_format, input_sample_size; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_VOICE_SETTINGS), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.settings = le16toh(rp.settings); input_coding = (rp.settings & 0x0300) >> 8; input_data_format = (rp.settings & 0x00c0) >> 6; input_sample_size = (rp.settings & 0x0020) >> 5; fprintf(stdout, "Voice settings: %#04x\n", rp.settings); fprintf(stdout, "Input coding: %s [%d]\n", hci_coding2str(input_coding), input_coding); fprintf(stdout, "Input data format: %s [%d]\n", hci_vdata2str(input_data_format), input_data_format); if (input_coding == 0x00) /* Only for Linear PCM */ fprintf(stdout, "Input sample size: %d bit [%d]\n", input_sample_size? 16 : 8, input_sample_size); return (OK); } /* hci_read_voice_settings */ /* Send Write_Voice_Settings command to the unit */ static int hci_write_voice_settings(int s, int argc, char **argv) { ng_hci_write_voice_settings_cp cp; ng_hci_write_voice_settings_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%x", &n) != 1) return (USAGE); cp.settings = (uint16_t) n; cp.settings = htole16(cp.settings); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_VOICE_SETTINGS), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_voice_settings */ /* Send Read_Number_Broadcast_Restransmissions */ static int hci_read_number_broadcast_retransmissions(int s, int argc, char **argv) { ng_hci_read_num_broadcast_retrans_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Number of broadcast retransmissions: %d\n", rp.counter); return (OK); } /* hci_read_number_broadcast_retransmissions */ /* Send Write_Number_Broadcast_Restransmissions */ static int hci_write_number_broadcast_retransmissions(int s, int argc, char **argv) { ng_hci_write_num_broadcast_retrans_cp cp; ng_hci_write_num_broadcast_retrans_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0xff) return (USAGE); cp.counter = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_number_broadcast_retransmissions */ /* Send Read_Hold_Mode_Activity command to the unit */ static int hci_read_hold_mode_activity(int s, int argc, char **argv) { ng_hci_read_hold_mode_activity_rp rp; int n; char buffer[1024]; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Hold Mode Activities: %#02x\n", rp.hold_mode_activity); if (rp.hold_mode_activity == 0) fprintf(stdout, "Maintain current Power State"); else fprintf(stdout, "%s", hci_hmode2str(rp.hold_mode_activity, buffer, sizeof(buffer))); fprintf(stdout, "\n"); return (OK); } /* hci_read_hold_mode_activity */ /* Send Write_Hold_Mode_Activity command to the unit */ static int hci_write_hold_mode_activity(int s, int argc, char **argv) { ng_hci_write_hold_mode_activity_cp cp; ng_hci_write_hold_mode_activity_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 4) return (USAGE); cp.hold_mode_activity = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_hold_mode_activity */ /* Send Read_SCO_Flow_Control_Enable command to the unit */ static int hci_read_sco_flow_control_enable(int s, int argc, char **argv) { ng_hci_read_sco_flow_control_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_SCO_FLOW_CONTROL), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "SCO flow control %s [%d]\n", rp.flow_control? "enabled" : "disabled", rp.flow_control); return (OK); } /* hci_read_sco_flow_control_enable */ /* Send Write_SCO_Flow_Control_Enable command to the unit */ static int hci_write_sco_flow_control_enable(int s, int argc, char **argv) { ng_hci_write_sco_flow_control_cp cp; ng_hci_write_sco_flow_control_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) return (USAGE); cp.flow_control = (uint8_t) n; break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_sco_flow_control_enable */ /* Send Read_Link_Supervision_Timeout command to the unit */ static int hci_read_link_supervision_timeout(int s, int argc, char **argv) { ng_hci_read_link_supervision_timo_cp cp; ng_hci_read_link_supervision_timo_rp rp; int n; switch (argc) { case 1: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.timeout = le16toh(rp.timeout); fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); fprintf(stdout, "Link supervision timeout: %.2f msec [%d slots]\n", rp.timeout * 0.625, rp.timeout); return (OK); } /* hci_read_link_supervision_timeout */ /* Send Write_Link_Supervision_Timeout command to the unit */ static int hci_write_link_supervision_timeout(int s, int argc, char **argv) { ng_hci_write_link_supervision_timo_cp cp; ng_hci_write_link_supervision_timo_rp rp; int n; switch (argc) { case 2: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); /* link supervision timeout */ if (sscanf(argv[1], "%d", &n) != 1 || n < 0 || n > 0xffff) return (USAGE); cp.timeout = (uint16_t) (n & 0x0fff); cp.timeout = htole16(cp.timeout); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_link_supervision_timeout */ /* Send Read_Page_Scan_Period_Mode command to the unit */ static int hci_read_page_scan_period_mode(int s, int argc, char **argv) { ng_hci_read_page_scan_period_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_PAGE_SCAN_PERIOD), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Page scan period mode: %#02x\n", rp.page_scan_period_mode); return (OK); } /* hci_read_page_scan_period_mode */ /* Send Write_Page_Scan_Period_Mode command to the unit */ static int hci_write_page_scan_period_mode(int s, int argc, char **argv) { ng_hci_write_page_scan_period_cp cp; ng_hci_write_page_scan_period_rp rp; int n; /* parse command arguments */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2) return (USAGE); cp.page_scan_period_mode = (n & 0xff); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_page_scan_period_mode */ /* Send Read_Page_Scan_Mode command to the unit */ static int hci_read_page_scan_mode(int s, int argc, char **argv) { ng_hci_read_page_scan_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_READ_PAGE_SCAN), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Page scan mode: %#02x\n", rp.page_scan_mode); return (OK); } /* hci_read_page_scan_mode */ /* Send Write_Page_Scan_Mode command to the unit */ static int hci_write_page_scan_mode(int s, int argc, char **argv) { ng_hci_write_page_scan_cp cp; ng_hci_write_page_scan_rp rp; int n; /* parse command arguments */ switch (argc) { case 1: if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3) return (USAGE); cp.page_scan_mode = (n & 0xff); break; default: return (USAGE); } /* send command */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, NG_HCI_OCF_WRITE_PAGE_SCAN), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_page_scan_mode */ struct hci_command host_controller_baseband_commands[] = { { "reset", "\nThe Reset command will reset the Host Controller and the Link Manager.\n" \ "After the reset is completed, the current operational state will be lost,\n" \ "the Bluetooth unit will enter standby mode and the Host Controller will\n" \ "automatically revert to the default values for the parameters for which\n" \ "default values are defined in the specification.", &hci_reset }, { "read_pin_type", "\nThe Read_PIN_Type command is used for the Host to read whether the Link\n" \ "Manager assumes that the Host supports variable PIN codes only a fixed PIN\n" \ "code.", &hci_read_pin_type }, { "write_pin_type ", "\nThe Write_PIN_Type command is used for the Host to write to the Host\n" \ "Controller whether the Host supports variable PIN codes or only a fixed PIN\n"\ "code.\n\n" \ "\t - dd; 0 - Variable; 1 - Fixed", &hci_write_pin_type }, { "read_stored_link_key []", "\nThe Read_Stored_Link_Key command provides the ability to read one or\n" \ "more link keys stored in the Bluetooth Host Controller. The Bluetooth Host\n" \ "Controller can store a limited number of link keys for other Bluetooth\n" \ "devices.\n\n" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name", &hci_read_stored_link_key }, { "write_stored_link_key ", "\nThe Write_Stored_Link_Key command provides the ability to write one\n" \ "or more link keys to be stored in the Bluetooth Host Controller. The\n" \ "Bluetooth Host Controller can store a limited number of link keys for other\n"\ "Bluetooth devices. If no additional space is available in the Bluetooth\n"\ "Host Controller then no additional link keys will be stored.\n\n" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \ "\t - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx up to 16 bytes link key", &hci_write_stored_link_key }, { "delete_stored_link_key []", "\nThe Delete_Stored_Link_Key command provides the ability to remove one\n" \ "or more of the link keys stored in the Bluetooth Host Controller. The\n" \ "Bluetooth Host Controller can store a limited number of link keys for other\n"\ "Bluetooth devices.\n\n" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name", &hci_delete_stored_link_key }, { "change_local_name ", "\nThe Change_Local_Name command provides the ability to modify the user\n" \ "friendly name for the Bluetooth unit.\n\n" \ "\t - string", &hci_change_local_name }, { "read_local_name", "\nThe Read_Local_Name command provides the ability to read the\n" \ "stored user-friendly name for the Bluetooth unit.", &hci_read_local_name }, { "read_connection_accept_timeout", "\nThis command will read the value for the Connection_Accept_Timeout\n" \ "configuration parameter. The Connection_Accept_Timeout configuration\n" \ "parameter allows the Bluetooth hardware to automatically deny a\n" \ "connection request after a specified time period has occurred and\n" \ "the new connection is not accepted. Connection Accept Timeout\n" \ "measured in Number of Baseband slots.", &hci_read_connection_accept_timeout }, { "write_connection_accept_timeout ", "\nThis command will write the value for the Connection_Accept_Timeout\n" \ "configuration parameter.\n\n" \ "\t - dddd; measured in number of baseband slots.", &hci_write_connection_accept_timeout }, { "read_page_timeout", "\nThis command will read the value for the Page_Timeout configuration\n" \ "parameter. The Page_Timeout configuration parameter defines the\n" \ "maximum time the local Link Manager will wait for a baseband page\n" \ "response from the remote unit at a locally initiated connection\n" \ "attempt. Page Timeout measured in Number of Baseband slots.", &hci_read_page_timeout }, { "write_page_timeout ", "\nThis command will write the value for the Page_Timeout configuration\n" \ "parameter.\n\n" \ "\t - dddd; measured in number of baseband slots.", &hci_write_page_timeout }, { "read_scan_enable", "\nThis command will read the value for the Scan_Enable parameter. The\n" \ "Scan_Enable parameter controls whether or not the Bluetooth uint\n" \ "will periodically scan for page attempts and/or inquiry requests\n" \ "from other Bluetooth unit.\n\n" \ "\t0x00 - No Scans enabled.\n" \ "\t0x01 - Inquiry Scan enabled. Page Scan disabled.\n" \ "\t0x02 - Inquiry Scan disabled. Page Scan enabled.\n" \ "\t0x03 - Inquiry Scan enabled. Page Scan enabled.", &hci_read_scan_enable }, { "write_scan_enable ", "\nThis command will write the value for the Scan_Enable parameter.\n" \ "The Scan_Enable parameter controls whether or not the Bluetooth\n" \ "unit will periodically scan for page attempts and/or inquiry\n" \ "requests from other Bluetooth unit.\n\n" \ "\t - dd;\n" \ "\t0 - No Scans enabled.\n" \ "\t1 - Inquiry Scan enabled. Page Scan disabled.\n" \ "\t2 - Inquiry Scan disabled. Page Scan enabled.\n" \ "\t3 - Inquiry Scan enabled. Page Scan enabled.", &hci_write_scan_enable }, { "read_page_scan_activity", "\nThis command will read the value for Page_Scan_Activity configuration\n" \ "parameters. The Page_Scan_Interval configuration parameter defines the\n" \ "amount of time between consecutive page scans. This time interval is \n" \ "defined from when the Host Controller started its last page scan until\n" \ "it begins the next page scan. The Page_Scan_Window configuration parameter\n" \ "defines the amount of time for the duration of the page scan. The\n" \ "Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.", &hci_read_page_scan_activity }, { "write_page_scan_activity interval(dddd) window(dddd)", "\nThis command will write the value for Page_Scan_Activity configuration\n" \ "parameter. The Page_Scan_Interval configuration parameter defines the\n" \ "amount of time between consecutive page scans. This is defined as the time\n" \ "interval from when the Host Controller started its last page scan until it\n" \ "begins the next page scan. The Page_Scan_Window configuration parameter\n" \ "defines the amount of time for the duration of the page scan. \n" \ "The Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.\n\n" \ "\t - Range: 0x0012 -- 0x100, Time = N * 0.625 msec\n" \ "\t - Range: 0x0012 -- 0x100, Time = N * 0.625 msec", &hci_write_page_scan_activity }, { "read_inquiry_scan_activity", "\nThis command will read the value for Inquiry_Scan_Activity configuration\n" \ "parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \ "amount of time between consecutive inquiry scans. This is defined as the\n" \ "time interval from when the Host Controller started its last inquiry scan\n" \ "until it begins the next inquiry scan.", &hci_read_inquiry_scan_activity }, { "write_inquiry_scan_activity interval(dddd) window(dddd)", "\nThis command will write the value for Inquiry_Scan_Activity configuration\n"\ "parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \ "amount of time between consecutive inquiry scans. This is defined as the\n" \ "time interval from when the Host Controller started its last inquiry scan\n" \ "until it begins the next inquiry scan. The Inquiry_Scan_Window configuration\n" \ "parameter defines the amount of time for the duration of the inquiry scan.\n" \ "The Inquiry_Scan_Window can only be less than or equal to the Inquiry_Scan_Interval.\n\n" \ "\t - Range: 0x0012 -- 0x100, Time = N * 0.625 msec\n" \ "\t - Range: 0x0012 -- 0x100, Time = N * 0.625 msec", &hci_write_inquiry_scan_activity }, { "read_authentication_enable", "\nThis command will read the value for the Authentication_Enable parameter.\n"\ "The Authentication_Enable parameter controls if the local unit requires\n"\ "to authenticate the remote unit at connection setup (between the\n" \ "Create_Connection command or acceptance of an incoming ACL connection\n"\ "and the corresponding Connection Complete event). At connection setup, only\n"\ "the unit(s) with the Authentication_Enable parameter enabled will try to\n"\ "authenticate the other unit.", &hci_read_authentication_enable }, { "write_authentication_enable enable(0|1)", "\nThis command will write the value for the Authentication_Enable parameter.\n"\ "The Authentication_Enable parameter controls if the local unit requires to\n"\ "authenticate the remote unit at connection setup (between the\n" \ "Create_Connection command or acceptance of an incoming ACL connection\n" \ "and the corresponding Connection Complete event). At connection setup, only\n"\ "the unit(s) with the Authentication_Enable parameter enabled will try to\n"\ "authenticate the other unit.", &hci_write_authentication_enable }, { "read_encryption_mode", "\nThis command will read the value for the Encryption_Mode parameter. The\n" \ "Encryption_Mode parameter controls if the local unit requires encryption\n" \ "to the remote unit at connection setup (between the Create_Connection\n" \ "command or acceptance of an incoming ACL connection and the corresponding\n" \ "Connection Complete event). At connection setup, only the unit(s) with\n" \ "the Authentication_Enable parameter enabled and Encryption_Mode parameter\n" \ "enabled will try to encrypt the connection to the other unit.\n\n" \ "\t:\n" \ "\t0x00 - Encryption disabled.\n" \ "\t0x01 - Encryption only for point-to-point packets.\n" \ "\t0x02 - Encryption for both point-to-point and broadcast packets.", &hci_read_encryption_mode }, { "write_encryption_mode mode(0|1|2)", "\tThis command will write the value for the Encryption_Mode parameter.\n" \ "The Encryption_Mode parameter controls if the local unit requires\n" \ "encryption to the remote unit at connection setup (between the\n" \ "Create_Connection command or acceptance of an incoming ACL connection\n" \ "and the corresponding Connection Complete event). At connection setup,\n" \ "only the unit(s) with the Authentication_Enable parameter enabled and\n" \ "Encryption_Mode parameter enabled will try to encrypt the connection to\n" \ "the other unit.\n\n" \ "\t (dd)\n" \ "\t0 - Encryption disabled.\n" \ "\t1 - Encryption only for point-to-point packets.\n" \ "\t2 - Encryption for both point-to-point and broadcast packets.", &hci_write_encryption_mode }, { "read_class_of_device", "\nThis command will read the value for the Class_of_Device parameter.\n" \ "The Class_of_Device parameter is used to indicate the capabilities of\n" \ "the local unit to other units.", &hci_read_class_of_device }, { "write_class_of_device class(xx:xx:xx)", "\nThis command will write the value for the Class_of_Device parameter.\n" \ "The Class_of_Device parameter is used to indicate the capabilities of \n" \ "the local unit to other units.\n\n" \ "\t (xx:xx:xx) - class of device", &hci_write_class_of_device }, { "read_voice_settings", "\nThis command will read the values for the Voice_Setting parameter.\n" \ "The Voice_Setting parameter controls all the various settings for voice\n" \ "connections. These settings apply to all voice connections, and cannot be\n" \ "set for individual voice connections. The Voice_Setting parameter controls\n" \ "the configuration for voice connections: Input Coding, Air coding format,\n" \ "input data format, Input sample size, and linear PCM parameter.", &hci_read_voice_settings }, { "write_voice_settings settings(xxxx)", "\nThis command will write the values for the Voice_Setting parameter.\n" \ "The Voice_Setting parameter controls all the various settings for voice\n" \ "connections. These settings apply to all voice connections, and cannot be\n" \ "set for individual voice connections. The Voice_Setting parameter controls\n" \ "the configuration for voice connections: Input Coding, Air coding format,\n" \ "input data format, Input sample size, and linear PCM parameter.\n\n" \ "\t (xxxx) - voice settings", &hci_write_voice_settings }, { "read_number_broadcast_retransmissions", "\nThis command will read the unit's parameter value for the Number of\n" \ "Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \ "unreliable.", &hci_read_number_broadcast_retransmissions }, { "write_number_broadcast_retransmissions count(dd)", "\nThis command will write the unit's parameter value for the Number of\n" \ "Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \ "unreliable.\n\n" \ "\t (dd) - number of broadcast retransimissions", &hci_write_number_broadcast_retransmissions }, { "read_hold_mode_activity", "\nThis command will read the value for the Hold_Mode_Activity parameter.\n" \ "The Hold_Mode_Activity value is used to determine what activities should\n" \ "be suspended when the unit is in hold mode.", &hci_read_hold_mode_activity }, { "write_hold_mode_activity settings(0|1|2|4)", "\nThis command will write the value for the Hold_Mode_Activity parameter.\n" \ "The Hold_Mode_Activity value is used to determine what activities should\n" \ "be suspended when the unit is in hold mode.\n\n" \ "\t (dd) - bit mask:\n" \ "\t0 - Maintain current Power State. Default\n" \ "\t1 - Suspend Page Scan.\n" \ "\t2 - Suspend Inquiry Scan.\n" \ "\t4 - Suspend Periodic Inquiries.", &hci_write_hold_mode_activity }, { "read_sco_flow_control_enable", "\nThe Read_SCO_Flow_Control_Enable command provides the ability to read\n" \ "the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \ "decide if the Host Controller will send Number Of Completed Packets events\n" \ "for SCO Connection Handles. This setting allows the Host to enable and\n" \ "disable SCO flow control.", &hci_read_sco_flow_control_enable }, { "write_sco_flow_control_enable enable(0|1)", "\nThe Write_SCO_Flow_Control_Enable command provides the ability to write\n" \ "the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \ "decide if the Host Controller will send Number Of Completed Packets events\n" \ "for SCO Connection Handles. This setting allows the Host to enable and\n" \ "disable SCO flow control. The SCO_Flow_Control_Enable setting can only be\n" \ "changed if no connections exist.", &hci_write_sco_flow_control_enable }, { "read_link_supervision_timeout ", "\nThis command will read the value for the Link_Supervision_Timeout\n" \ "parameter for the device. The Link_Supervision_Timeout parameter is used\n" \ "by the master or slave Bluetooth device to monitor link loss. If, for any\n" \ "reason, no Baseband packets are received from that Connection Handle for a\n" \ "duration longer than the Link_Supervision_Timeout, the connection is\n" "disconnected.\n\n" \ "\t - dddd; connection handle\n", &hci_read_link_supervision_timeout }, { "write_link_supervision_timeout ", "\nThis command will write the value for the Link_Supervision_Timeout\n" \ "parameter for the device. The Link_Supervision_Timeout parameter is used\n" \ "by the master or slave Bluetooth device to monitor link loss. If, for any\n" \ "reason, no Baseband packets are received from that connection handle for a\n" \ "duration longer than the Link_Supervision_Timeout, the connection is\n" \ "disconnected.\n\n" \ "\t - dddd; connection handle\n" \ "\t - dddd; timeout measured in number of baseband slots\n", &hci_write_link_supervision_timeout }, { "read_page_scan_period_mode", "\nThis command is used to read the mandatory Page_Scan_Period_Mode of the\n" \ "local Bluetooth device. Every time an inquiry response message is sent, the\n"\ "Bluetooth device will start a timer (T_mandatory_pscan), the value of which\n"\ "is dependent on the Page_Scan_Period_Mode. As long as this timer has not\n" \ "expired, the Bluetooth device will use the Page_Scan_Period_Mode for all\n" \ "following page scans.", &hci_read_page_scan_period_mode }, { "write_page_scan_period_mode ", "\nThis command is used to write the mandatory Page_Scan_Period_Mode of the\n" \ "local Bluetooth device. Every time an inquiry response message is sent, the\n"\ "Bluetooth device will start a timer (T_mandatory_pscan), the value of which\n"\ "is dependent on the Page_Scan_Period_Mode. As long as this timer has not\n" \ "expired, the Bluetooth device will use the Page_Scan_Period_Mode for all\n" \ "following page scans.\n\n" \ "\t - dd; page scan period mode:\n" \ "\t0x00 - P0 (Default)\n" \ "\t0x01 - P1\n" \ "\t0x02 - P2", &hci_write_page_scan_period_mode }, { "read_page_scan_mode", "\nThis command is used to read the default page scan mode of the local\n" \ "Bluetooth device. The Page_Scan_Mode parameter indicates the page scan mode\n"\ "that is used for the default page scan. Currently one mandatory page scan\n"\ "mode and three optional page scan modes are defined. Following an inquiry\n" \ "response, if the Baseband timer T_mandatory_pscan has not expired, the\n" \ "mandatory page scan mode must be applied.", &hci_read_page_scan_mode }, { "write_page_scan_mode ", "\nThis command is used to write the default page scan mode of the local\n" \ "Bluetooth device. The Page_Scan_Mode parameter indicates the page scan mode\n"\ "that is used for the default page scan. Currently, one mandatory page scan\n"\ "mode and three optional page scan modes are defined. Following an inquiry\n"\ "response, if the Baseband timer T_mandatory_pscan has not expired, the\n" \ "mandatory page scan mode must be applied.\n\n" \ "\t - dd; page scan mode:\n" \ "\t0x00 - Mandatory Page Scan Mode (Default)\n" \ "\t0x01 - Optional Page Scan Mode I\n" \ "\t0x02 - Optional Page Scan Mode II\n" \ "\t0x03 - Optional Page Scan Mode III", &hci_write_page_scan_mode }, { NULL, } }; Index: head/usr.sbin/bluetooth/hccontrol/info.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/info.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/info.c (revision 281210) @@ -1,216 +1,217 @@ /* * info.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: info.c,v 1.3 2003/08/18 19:19:54 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "hccontrol.h" /* Send Read_Local_Version_Information command to the unit */ static int hci_read_local_version_information(int s, int argc, char **argv) { ng_hci_read_local_ver_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_LOCAL_VER), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } rp.manufacturer = le16toh(rp.manufacturer); fprintf(stdout, "HCI version: %s [%#02x]\n", hci_ver2str(rp.hci_version), rp.hci_version); fprintf(stdout, "HCI revision: %#04x\n", le16toh(rp.hci_revision)); fprintf(stdout, "LMP version: %s [%#02x]\n", hci_lmpver2str(rp.lmp_version), rp.lmp_version); fprintf(stdout, "LMP sub-version: %#04x\n", le16toh(rp.lmp_subversion)); fprintf(stdout, "Manufacturer: %s [%#04x]\n", hci_manufacturer2str(rp.manufacturer), rp.manufacturer); return (OK); } /* hci_read_local_version_information */ /* Send Read_Local_Supported_Features command to the unit */ static int hci_read_local_supported_features(int s, int argc, char **argv) { ng_hci_read_local_features_rp rp; int n; char buffer[1024]; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_LOCAL_FEATURES), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Features: "); for (n = 0; n < sizeof(rp.features); n++) fprintf(stdout, "%#02x ", rp.features[n]); fprintf(stdout, "\n%s\n", hci_features2str(rp.features, buffer, sizeof(buffer))); return (OK); } /* hci_read_local_supported_features */ /* Sent Read_Buffer_Size command to the unit */ static int hci_read_buffer_size(int s, int argc, char **argv) { ng_hci_read_buffer_size_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_BUFFER_SIZE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Max. ACL packet size: %d bytes\n", le16toh(rp.max_acl_size)); fprintf(stdout, "Number of ACL packets: %d\n", le16toh(rp.num_acl_pkt)); fprintf(stdout, "Max. SCO packet size: %d bytes\n", rp.max_sco_size); fprintf(stdout, "Number of SCO packets: %d\n", le16toh(rp.num_sco_pkt)); return (OK); } /* hci_read_buffer_size */ /* Send Read_Country_Code command to the unit */ static int hci_read_country_code(int s, int argc, char **argv) { ng_hci_read_country_code_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_COUNTRY_CODE), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Country code: %s [%#02x]\n", hci_cc2str(rp.country_code), rp.country_code); return (OK); } /* hci_read_country_code */ /* Send Read_BD_ADDR command to the unit */ static int hci_read_bd_addr(int s, int argc, char **argv) { ng_hci_read_bdaddr_rp rp; int n; n = sizeof(rp); if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_BDADDR), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "BD_ADDR: %s\n", bt_ntoa(&rp.bdaddr, NULL)); return (OK); } /* hci_read_bd_addr */ struct hci_command info_commands[] = { { "read_local_version_information", "\nThis command will read the values for the version information for the\n" \ "local Bluetooth unit.", &hci_read_local_version_information }, { "read_local_supported_features", "\nThis command requests a list of the supported features for the local\n" \ "unit. This command will return a list of the LMP features.", &hci_read_local_supported_features }, { "read_buffer_size", "\nThe Read_Buffer_Size command is used to read the maximum size of the\n" \ "data portion of HCI ACL and SCO Data Packets sent from the Host to the\n" \ "Host Controller.", &hci_read_buffer_size }, { "read_country_code", "\nThis command will read the value for the Country_Code return parameter.\n" \ "The Country_Code defines which range of frequency band of the ISM 2.4 GHz\n" \ "band will be used by the unit.", &hci_read_country_code }, { "read_bd_addr", "\nThis command will read the value for the BD_ADDR parameter. The BD_ADDR\n" \ "is a 48-bit unique identifier for a Bluetooth unit.", &hci_read_bd_addr }, { NULL, }}; Index: head/usr.sbin/bluetooth/hccontrol/link_control.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/link_control.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/link_control.c (revision 281210) @@ -1,960 +1,961 @@ /* * link_control.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: link_control.c,v 1.4 2003/08/18 19:19:54 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "hccontrol.h" static void hci_inquiry_response (int n, uint8_t **b); /* Send Inquiry command to the unit */ static int hci_inquiry(int s, int argc, char **argv) { int n0, n1, n2, timo; char b[512]; ng_hci_inquiry_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* set defaults */ cp.lap[2] = 0x9e; cp.lap[1] = 0x8b; cp.lap[0] = 0x33; cp.inquiry_length = 5; cp.num_responses = 8; /* parse command parameters */ switch (argc) { case 3: /* number of responses, range 0x00 - 0xff */ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 0xff) return (USAGE); cp.num_responses = (n0 & 0xff); case 2: /* inquiry length (N * 1.28) sec, range 0x01 - 0x30 */ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x1 || n0 > 0x30) return (USAGE); cp.inquiry_length = (n0 & 0xff); case 1: /* LAP */ if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3) return (USAGE); cp.lap[0] = (n0 & 0xff); cp.lap[1] = (n1 & 0xff); cp.lap[2] = (n2 & 0xff); case 0: /* use defaults */ break; default: return (USAGE); } /* send request and expect status back */ n0 = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_INQUIRY), (char const *) &cp, sizeof(cp), b, &n0) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); timo = timeout; timeout = cp.inquiry_length * 1.28 + 1; wait_for_more: /* wait for inquiry events */ n0 = sizeof(b); if (hci_recv(s, b, &n0) == ERROR) { timeout = timo; return (ERROR); } if (n0 < sizeof(*e)) { timeout = timo; errno = EIO; return (ERROR); } switch (e->event) { case NG_HCI_EVENT_INQUIRY_RESULT: { ng_hci_inquiry_result_ep *ir = (ng_hci_inquiry_result_ep *)(e + 1); uint8_t *r = (uint8_t *)(ir + 1); fprintf(stdout, "Inquiry result, num_responses=%d\n", ir->num_responses); for (n0 = 0; n0 < ir->num_responses; n0++) hci_inquiry_response(n0, &r); goto wait_for_more; } case NG_HCI_EVENT_INQUIRY_COMPL: fprintf(stdout, "Inquiry complete. Status: %s [%#02x]\n", hci_status2str(*(b + sizeof(*e))), *(b + sizeof(*e))); break; default: goto wait_for_more; } timeout = timo; return (OK); } /* hci_inquiry */ /* Print Inquiry_Result event */ static void hci_inquiry_response(int n, uint8_t **b) { ng_hci_inquiry_response *ir = (ng_hci_inquiry_response *)(*b); fprintf(stdout, "Inquiry result #%d\n", n); fprintf(stdout, "\tBD_ADDR: %s\n", hci_bdaddr2str(&ir->bdaddr)); fprintf(stdout, "\tPage Scan Rep. Mode: %#02x\n", ir->page_scan_rep_mode); fprintf(stdout, "\tPage Scan Period Mode: %#02x\n", ir->page_scan_period_mode); fprintf(stdout, "\tPage Scan Mode: %#02x\n", ir->page_scan_mode); fprintf(stdout, "\tClass: %02x:%02x:%02x\n", ir->uclass[2], ir->uclass[1], ir->uclass[0]); fprintf(stdout, "\tClock offset: %#04x\n", le16toh(ir->clock_offset)); *b += sizeof(*ir); } /* hci_inquiry_response */ /* Send Create_Connection command to the unit */ static int hci_create_connection(int s, int argc, char **argv) { int n0; char b[512]; ng_hci_create_con_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* Set defaults */ memset(&cp, 0, sizeof(cp)); cp.pkt_type = htole16( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 | NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 | NG_HCI_PKT_DM5); cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0; cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE; cp.clock_offset = 0; cp.accept_role_switch = 1; /* parse command parameters */ switch (argc) { case 6: /* accept role switch */ if (sscanf(argv[5], "%d", &n0) != 1) return (USAGE); cp.accept_role_switch = n0 ? 1 : 0; case 5: /* clock offset */ if (sscanf(argv[4], "%d", &n0) != 1) return (USAGE); cp.clock_offset = (n0 & 0xffff); cp.clock_offset = htole16(cp.clock_offset); case 4: /* page scan mode */ if (sscanf(argv[3], "%d", &n0) != 1 || n0 < 0 || n0 > 3) return (USAGE); cp.page_scan_mode = (n0 & 0xff); case 3: /* page scan rep mode */ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 2) return (USAGE); cp.page_scan_rep_mode = (n0 & 0xff); case 2: /* packet type */ if (sscanf(argv[1], "%x", &n0) != 1) return (USAGE); n0 &= ( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 | NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 | NG_HCI_PKT_DM5); if (n0 == 0) return (USAGE); cp.pkt_type = (n0 & 0xffff); cp.pkt_type = htole16(cp.pkt_type); case 1: /* BD_ADDR */ if (!bt_aton(argv[0], &cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr)); } break; default: return (USAGE); } /* send request and expect status response */ n0 = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_CREATE_CON), (char const *) &cp, sizeof(cp), b, &n0) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n0 = sizeof(b); if (hci_recv(s, b, &n0) == ERROR) return (ERROR); if (n0 < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_CON_COMPL) { ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr)); fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Encryption mode: %s [%d]\n", hci_encrypt2str(ep->encryption_mode, 0), ep->encryption_mode); } else goto again; return (OK); } /* hci_create_connection */ /* Send Disconnect command to the unit */ static int hci_disconnect(int s, int argc, char **argv) { int n; char b[512]; ng_hci_discon_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* Set defaults */ memset(&cp, 0, sizeof(cp)); cp.reason = 0x13; /* parse command parameters */ switch (argc) { case 2: /* reason */ if (sscanf(argv[1], "%d", &n) != 1 || n <= 0x00 || n > 0xff) return (USAGE); cp.reason = (uint8_t) (n & 0xff); case 1: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_DISCON), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_DISCON_COMPL) { ng_hci_discon_compl_ep *ep = (ng_hci_discon_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Reason: %s [%#02x]\n", hci_status2str(ep->reason), ep->reason); } else goto again; return (OK); } /* hci_disconnect */ /* Send Add_SCO_Connection command to the unit */ static int hci_add_sco_connection(int s, int argc, char **argv) { int n; char b[512]; ng_hci_add_sco_con_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* Set defaults */ memset(&cp, 0, sizeof(cp)); cp.pkt_type = htole16(NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3); /* parse command parameters */ switch (argc) { case 2: /* packet type */ if (sscanf(argv[1], "%x", &n) != 1) return (USAGE); n &= (NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3); if (n == 0) return (USAGE); cp.pkt_type = (uint16_t) (n & 0x0fff); cp.pkt_type = htole16(cp.pkt_type); case 1: /* acl connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_ADD_SCO_CON), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_CON_COMPL) { ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr)); fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Encryption mode: %s [%d]\n", hci_encrypt2str(ep->encryption_mode, 0), ep->encryption_mode); } else goto again; return (OK); } /* Add_SCO_Connection */ /* Send Change_Connection_Packet_Type command to the unit */ static int hci_change_connection_packet_type(int s, int argc, char **argv) { int n; char b[512]; ng_hci_change_con_pkt_type_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; switch (argc) { case 2: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); /* packet type */ if (sscanf(argv[1], "%x", &n) != 1) return (USAGE); cp.pkt_type = (uint16_t) (n & 0xffff); cp.pkt_type = htole16(cp.pkt_type); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_CHANGE_CON_PKT_TYPE), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_CON_PKT_TYPE_CHANGED) { ng_hci_con_pkt_type_changed_ep *ep = (ng_hci_con_pkt_type_changed_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Packet type: %#04x\n", le16toh(ep->pkt_type)); } else goto again; return (OK); } /* hci_change_connection_packet_type */ /* Send Remote_Name_Request command to the unit */ static int hci_remote_name_request(int s, int argc, char **argv) { int n0; char b[512]; ng_hci_remote_name_req_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; memset(&cp, 0, sizeof(cp)); cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0; cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE; /* parse command parameters */ switch (argc) { case 4: /* clock_offset */ if (sscanf(argv[3], "%x", &n0) != 1) return (USAGE); cp.clock_offset = (n0 & 0xffff); cp.clock_offset = htole16(cp.clock_offset); case 3: /* page_scan_mode */ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x03) return (USAGE); cp.page_scan_mode = (n0 & 0xff); case 2: /* page_scan_rep_mode */ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x02) return (USAGE); cp.page_scan_rep_mode = (n0 & 0xff); case 1: /* BD_ADDR */ if (!bt_aton(argv[0], &cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr)); } break; default: return (USAGE); } /* send request and expect status response */ n0 = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_REMOTE_NAME_REQ), (char const *) &cp, sizeof(cp), b, &n0) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n0 = sizeof(b); if (hci_recv(s, b, &n0) == ERROR) return (ERROR); if (n0 < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL) { ng_hci_remote_name_req_compl_ep *ep = (ng_hci_remote_name_req_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr)); fprintf(stdout, "Name: %s\n", ep->name); } else goto again; return (OK); } /* hci_remote_name_request */ /* Send Read_Remote_Supported_Features command to the unit */ static int hci_read_remote_supported_features(int s, int argc, char **argv) { int n; char b[512]; ng_hci_read_remote_features_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; char buffer[1024]; /* parse command parameters */ switch (argc) { case 1: /* connecton handle */ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) return (USAGE); cp.con_handle = (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_READ_REMOTE_FEATURES), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL) { ng_hci_read_remote_features_compl_ep *ep = (ng_hci_read_remote_features_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Features: "); for (n = 0; n < sizeof(ep->features); n++) fprintf(stdout, "%#02x ", ep->features[n]); fprintf(stdout, "\n%s\n", hci_features2str(ep->features, buffer, sizeof(buffer))); } else goto again; return (OK); } /* hci_read_remote_supported_features */ /* Send Read_Remote_Version_Information command to the unit */ static int hci_read_remote_version_information(int s, int argc, char **argv) { int n; char b[512]; ng_hci_read_remote_ver_info_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* parse command parameters */ switch (argc) { case 1: /* connecton handle */ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) return (USAGE); cp.con_handle = (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_READ_REMOTE_VER_INFO), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL) { ng_hci_read_remote_ver_info_compl_ep *ep = (ng_hci_read_remote_ver_info_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } ep->manufacturer = le16toh(ep->manufacturer); fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "LMP version: %s [%#02x]\n", hci_lmpver2str(ep->lmp_version), ep->lmp_version); fprintf(stdout, "LMP sub-version: %#04x\n", le16toh(ep->lmp_subversion)); fprintf(stdout, "Manufacturer: %s [%#04x]\n", hci_manufacturer2str(ep->manufacturer), ep->manufacturer); } else goto again; return (OK); } /* hci_read_remote_version_information */ /* Send Read_Clock_Offset command to the unit */ static int hci_read_clock_offset(int s, int argc, char **argv) { int n; char b[512]; ng_hci_read_clock_offset_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* parse command parameters */ switch (argc) { case 1: /* connecton handle */ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) return (USAGE); cp.con_handle = (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request and expect status response */ n = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_READ_CLOCK_OFFSET), (char const *) &cp, sizeof(cp), b, &n) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n = sizeof(b); if (hci_recv(s, b, &n) == ERROR) return (ERROR); if (n < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL) { ng_hci_read_clock_offset_compl_ep *ep = (ng_hci_read_clock_offset_compl_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(ep->con_handle)); fprintf(stdout, "Clock offset: %#04x\n", le16toh(ep->clock_offset)); } else goto again; return (OK); } /* hci_read_clock_offset */ struct hci_command link_control_commands[] = { { "inquiry ", "\nThis command will cause the Bluetooth unit to enter Inquiry Mode.\n" \ "Inquiry Mode is used to discover other nearby Bluetooth units. The LAP\n" \ "input parameter contains the LAP from which the inquiry access code shall\n" \ "be derived when the inquiry procedure is made. The Inquiry_Length parameter\n"\ "specifies the total duration of the Inquiry Mode and, when this time\n" \ "expires, Inquiry will be halted. The Num_Responses parameter specifies the\n" \ "number of responses that can be received before the Inquiry is halted.\n\n" \ "\t - xx:xx:xx; 9e:8b:33 (GIAC), 93:8b:00 (LDIAC)\n" \ "\t - dd; total length == dd * 1.28 sec\n" \ "\t - dd", &hci_inquiry }, { "create_connection ", "" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name\n\n" \ "\t - xxxx; packet type\n" \ "" \ "\t\tACL packets\n" \ "\t\t-----------\n" \ "\t\t0x0008 DM1\n" \ "\t\t0x0010 DH1\n" \ "\t\t0x0400 DM3\n" \ "\t\t0x0800 DH3\n" \ "\t\t0x4000 DM5\n" \ "\t\t0x8000 DH5\n\n" \ "" \ "\trep_mode - d; page scan repetition mode\n" \ "" \ "\t\tPage scan repetition modes\n" \ "\t\t--------------------------\n" \ "\t\t0 Page scan repetition mode 0\n" \ "\t\t1 Page scan repetition mode 1\n" \ "\t\t2 Page scan repetition mode 2\n" \ "\n" \ "\tps_mode - d; Page scan mode\n" \ "" \ "\t\tPage scan modes\n" \ "\t\t---------------\n" \ "\t\t0 Mandatory page scan mode\n" \ "\t\t1 Optional page scan mode1\n" \ "\t\t2 Optional page scan mode2\n" \ "\t\t3 Optional page scan mode3\n" \ "\n" \ "\tclck_off - dddd; clock offset. Use 0 if unknown\n\n" \ "\trole_sw - d; allow (1) or deny role switch\n", &hci_create_connection }, { "disconnect ", "\nThe Disconnection command is used to terminate an existing connection.\n" \ "The connection handle command parameter indicates which connection is to\n" \ "be disconnected. The Reason command parameter indicates the reason for\n" \ "ending the connection.\n\n" \ "\t - dddd; connection handle\n" \ "\t - dd; reason; usually 19 (0x13) - user ended;\n" \ "\t also 0x05, 0x13-0x15, 0x1A, 0x29", &hci_disconnect }, { "add_sco_connection ", "This command will cause the link manager to create a SCO connection using\n" \ "the ACL connection specified by the connection handle command parameter.\n" \ "The Link Manager will determine how the new connection is established. This\n"\ "connection is determined by the current state of the device, its piconet,\n" \ "and the state of the device to be connected. The packet type command parameter\n" \ "specifies which packet types the Link Manager should use for the connection.\n"\ "The Link Manager must only use the packet type(s) specified by the packet\n" \ "type command parameter for sending HCI SCO data packets. Multiple packet\n" \ "types may be specified for the packet type command parameter by performing\n" \ "a bitwise OR operation of the different packet types. Note: An SCO connection\n" \ "can only be created when an ACL connection already exists and when it is\n" \ "not put in park mode.\n\n" \ "\t - dddd; ACL connection handle\n" \ "\t - xxxx; packet type\n" \ "" \ "\t\tSCO packets\n" \ "\t\t-----------\n" \ "\t\t0x0020 HV1\n" \ "\t\t0x0040 HV2\n" \ "\t\t0x0080 HV3\n", &hci_add_sco_connection }, { "change_connection_packet_type ", "The Change_Connection_Packet_Type command is used to change which packet\n" \ "types can be used for a connection that is currently established. This\n" \ "allows current connections to be dynamically modified to support different\n" \ "types of user data. The Packet_Type command parameter specifies which\n" \ "packet types the Link Manager can use for the connection. Multiple packet\n" \ "types may be specified for the Packet_Type command parameter by bitwise OR\n" \ "operation of the different packet types.\n\n" \ "\t - dddd; connection handle\n" \ "\t - xxxx; packet type mask\n" \ "" \ "\t\tACL packets\n" \ "\t\t-----------\n" \ "\t\t0x0008 DM1\n" \ "\t\t0x0010 DH1\n" \ "\t\t0x0400 DM3\n" \ "\t\t0x0800 DH3\n" \ "\t\t0x4000 DM5\n" \ "\t\t0x8000 DH5\n\n" \ "" \ "\t\tSCO packets\n" \ "\t\t-----------\n" \ "\t\t0x0020 HV1\n" \ "\t\t0x0040 HV2\n" \ "\t\t0x0080 HV3\n" \ "", &hci_change_connection_packet_type }, { "remote_name_request ", "\nThe Remote_Name_Request command is used to obtain the user-friendly\n" \ "name of another Bluetooth unit.\n\n" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \ "\t - dd; page scan repetition mode [0-2]\n" \ "\t - dd; page scan mode [0-3]\n" \ "\t - xxxx; clock offset [0 - 0xffff]", &hci_remote_name_request }, { "read_remote_supported_features ", "\nThis command requests a list of the supported features for the remote\n" \ "unit identified by the connection handle parameter. The connection handle\n" \ "must be a connection handle for an ACL connection.\n\n" \ "\t - dddd; connection handle", &hci_read_remote_supported_features }, { "read_remote_version_information ", "\nThis command will obtain the values for the version information for the\n" \ "remote Bluetooth unit identified by the connection handle parameter. The\n" \ "connection handle must be a connection handle for an ACL connection.\n\n" \ "\t - dddd; connection handle", &hci_read_remote_version_information }, { "read_clock_offset ", "\nThis command allows the Host to read the clock offset from the remote unit.\n" \ "\t - dddd; connection handle", &hci_read_clock_offset }, { NULL, }}; Index: head/usr.sbin/bluetooth/hccontrol/link_policy.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/link_policy.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/link_policy.c (revision 281210) @@ -1,305 +1,306 @@ /* * link_policy.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: link_policy.c,v 1.3 2003/08/18 19:19:54 max Exp $ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "hccontrol.h" /* Send Role Discovery to the unit */ static int hci_role_discovery(int s, int argc, char **argv) { ng_hci_role_discovery_cp cp; ng_hci_role_discovery_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_ROLE_DISCOVERY), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); fprintf(stdout, "Role: %s [%#x]\n", (rp.role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", rp.role); return (OK); } /* hci_role_discovery */ /* Send Swith Role to the unit */ static int hci_switch_role(int s, int argc, char **argv) { int n0; char b[512]; ng_hci_switch_role_cp cp; ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; /* parse command parameters */ switch (argc) { case 2: /* bdaddr */ if (!bt_aton(argv[0], &cp.bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(argv[0])) == NULL) return (USAGE); memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr)); } /* role */ if (sscanf(argv[1], "%d", &n0) != 1) return (USAGE); cp.role = n0? NG_HCI_ROLE_SLAVE : NG_HCI_ROLE_MASTER; break; default: return (USAGE); } /* send request and expect status response */ n0 = sizeof(b); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_SWITCH_ROLE), (char const *) &cp, sizeof(cp), b, &n0) == ERROR) return (ERROR); if (*b != 0x00) return (FAILED); /* wait for event */ again: n0 = sizeof(b); if (hci_recv(s, b, &n0) == ERROR) return (ERROR); if (n0 < sizeof(*e)) { errno = EIO; return (ERROR); } if (e->event == NG_HCI_EVENT_ROLE_CHANGE) { ng_hci_role_change_ep *ep = (ng_hci_role_change_ep *)(e + 1); if (ep->status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(ep->status), ep->status); return (FAILED); } fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr)); fprintf(stdout, "Role: %s [%#x]\n", (ep->role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", ep->role); } else goto again; return (OK); } /* hci_switch_role */ /* Send Read_Link_Policy_Settings command to the unit */ static int hci_read_link_policy_settings(int s, int argc, char **argv) { ng_hci_read_link_policy_settings_cp cp; ng_hci_read_link_policy_settings_rp rp; int n; /* parse command parameters */ switch (argc) { case 1: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); break; default: return (USAGE); } /* send request */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_READ_LINK_POLICY_SETTINGS), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); fprintf(stdout, "Link policy settings: %#x\n", le16toh(rp.settings)); return (OK); } /* hci_read_link_policy_settings */ /* Send Write_Link_Policy_Settings command to the unit */ static int hci_write_link_policy_settings(int s, int argc, char **argv) { ng_hci_write_link_policy_settings_cp cp; ng_hci_write_link_policy_settings_rp rp; int n; /* parse command parameters */ switch (argc) { case 2: /* connection handle */ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) return (USAGE); cp.con_handle = (uint16_t) (n & 0x0fff); cp.con_handle = htole16(cp.con_handle); /* link policy settings */ if (sscanf(argv[1], "%x", &n) != 1) return (USAGE); cp.settings = (uint16_t) (n & 0x0ffff); cp.settings = htole16(cp.settings); break; default: return (USAGE); } /* send request */ n = sizeof(rp); if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS), (char const *) &cp, sizeof(cp), (char *) &rp, &n) == ERROR) return (ERROR); if (rp.status != 0x00) { fprintf(stdout, "Status: %s [%#02x]\n", hci_status2str(rp.status), rp.status); return (FAILED); } return (OK); } /* hci_write_link_policy_settings */ struct hci_command link_policy_commands[] = { { "role_discovery ", "\nThe Role_Discovery command is used for a Bluetooth device to determine\n" \ "which role the device is performing for a particular Connection Handle.\n" \ "The connection handle must be a connection handle for an ACL connection.\n\n" \ "\t - dddd; connection handle", &hci_role_discovery }, { "switch_role ", "\nThe Switch_Role command is used for a Bluetooth device to switch the\n" \ "current role the device is performing for a particular connection with\n" \ "another specified Bluetooth device. The BD_ADDR command parameter indicates\n"\ "for which connection the role switch is to be performed. The Role indicates\n"\ "the requested new role that the local device performs. Note: the BD_ADDR\n" \ "command parameter must specify a Bluetooth device for which a connection\n" "already exists.\n\n" \ "\t - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \ "\t - dd; role; 0 - Master, 1 - Slave", &hci_switch_role }, { "read_link_policy_settings ", "\nThis command will read the Link Policy setting for the specified connection\n"\ "handle. The link policy settings parameter determines the behavior of the\n" \ "local Link Manager when it receives a request from a remote device or it\n" \ "determines itself to change the master-slave role or to enter the hold,\n" \ "sniff, or park mode. The local Link Manager will automatically accept or\n" \ "reject such a request from the remote device, and may even autonomously\n" \ "request itself, depending on the value of the link policy settings parameter\n"\ "for the corresponding connection handle. The connection handle must be a\n" \ "connection handle for an ACL connection.\n\n" \ "\t - dddd; connection handle", &hci_read_link_policy_settings }, { "write_link_policy_settings ", "\nThis command will write the Link Policy setting for the specified connection\n"\ "handle. The link policy settings parameter determines the behavior of the\n" \ "local Link Manager when it receives a request from a remote device or it\n" \ "determines itself to change the master-slave role or to enter the hold,\n" \ "sniff, or park mode. The local Link Manager will automatically accept or\n" \ "reject such a request from the remote device, and may even autonomously\n" \ "request itself, depending on the value of the link policy settings parameter\n"\ "for the corresponding connection handle. The connection handle must be a\n" \ "connection handle for an ACL connection. Multiple Link Manager policies may\n"\ "be specified for the link policy settings parameter by performing a bitwise\n"\ "OR operation of the different activity types.\n\n" \ "\t - dddd; connection handle\n" \ "\t - xxxx; settings\n" \ "\t\t0x0000 - Disable All LM Modes (Default)\n" \ "\t\t0x0001 - Enable Master Slave Switch\n" \ "\t\t0x0002 - Enable Hold Mode\n" \ "\t\t0x0004 - Enable Sniff Mode\n" \ "\t\t0x0008 - Enable Park Mode\n", &hci_write_link_policy_settings }, { NULL, }}; Index: head/usr.sbin/bluetooth/hccontrol/node.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/node.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/node.c (revision 281210) @@ -1,607 +1,608 @@ /* * node.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: node.c,v 1.6 2003/07/22 21:14:02 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include "hccontrol.h" /* Send Read_Node_State command to the node */ static int hci_read_node_state(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_state r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "State: %#x\n", r.state); return (OK); } /* hci_read_node_state */ /* Send Intitialize command to the node */ static int hci_node_initialize(int s, int argc, char **argv) { if (ioctl(s, SIOC_HCI_RAW_NODE_INIT) < 0) return (ERROR); return (OK); } /* hci_node_initialize */ /* Send Read_Debug_Level command to the node */ static int hci_read_debug_level(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_debug r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Debug level: %d\n", r.debug); return (OK); } /* hci_read_debug_level */ /* Send Write_Debug_Level command to the node */ static int hci_write_debug_level(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_debug r; memset(&r, 0, sizeof(r)); switch (argc) { case 1: r.debug = atoi(argv[0]); break; default: return (USAGE); } if (ioctl(s, SIOC_HCI_RAW_NODE_SET_DEBUG, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* hci_write_debug_level */ /* Send Read_Node_Buffer_Size command to the node */ static int hci_read_node_buffer_size(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_buffer r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Number of free command buffers: %d\n", r.buffer.cmd_free); fprintf(stdout, "Max. ACL packet size: %d\n", r.buffer.acl_size); fprintf(stdout, "Numbef of free ACL buffers: %d\n", r.buffer.acl_free); fprintf(stdout, "Total number of ACL buffers: %d\n", r.buffer.acl_pkts); fprintf(stdout, "Max. SCO packet size: %d\n", r.buffer.sco_size); fprintf(stdout, "Numbef of free SCO buffers: %d\n", r.buffer.sco_free); fprintf(stdout, "Total number of SCO buffers: %d\n", r.buffer.sco_pkts); return (OK); } /* hci_read_node_buffer_size */ /* Send Read_Node_BD_ADDR command to the node */ static int hci_read_node_bd_addr(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_bdaddr r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "BD_ADDR: %s\n", bt_ntoa(&r.bdaddr, NULL)); return (OK); } /* hci_read_node_bd_addr */ /* Send Read_Node_Features command to the node */ static int hci_read_node_features(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_features r; int n; char buffer[1024]; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Features: "); for (n = 0; n < sizeof(r.features)/sizeof(r.features[0]); n++) fprintf(stdout, "%#02x ", r.features[n]); fprintf(stdout, "\n%s\n", hci_features2str(r.features, buffer, sizeof(buffer))); return (OK); } /* hci_read_node_features */ /* Send Read_Node_Stat command to the node */ static int hci_read_node_stat(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_stat r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Commands sent: %d\n", r.stat.cmd_sent); fprintf(stdout, "Events received: %d\n", r.stat.evnt_recv); fprintf(stdout, "ACL packets received: %d\n", r.stat.acl_recv); fprintf(stdout, "ACL packets sent: %d\n", r.stat.acl_sent); fprintf(stdout, "SCO packets received: %d\n", r.stat.sco_recv); fprintf(stdout, "SCO packets sent: %d\n", r.stat.sco_sent); fprintf(stdout, "Bytes received: %d\n", r.stat.bytes_recv); fprintf(stdout, "Bytes sent: %d\n", r.stat.bytes_sent); return (OK); } /* hci_read_node_stat */ /* Send Reset_Node_Stat command to the node */ static int hci_reset_node_stat(int s, int argc, char **argv) { if (ioctl(s, SIOC_HCI_RAW_NODE_RESET_STAT) < 0) return (ERROR); return (OK); } /* hci_reset_node_stat */ /* Send Flush_Neighbor_Cache command to the node */ static int hci_flush_neighbor_cache(int s, int argc, char **argv) { if (ioctl(s, SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE) < 0) return (ERROR); return (OK); } /* hci_flush_neighbor_cache */ /* Send Read_Neighbor_Cache command to the node */ static int hci_read_neighbor_cache(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_neighbor_cache r; int n, error = OK; memset(&r, 0, sizeof(r)); r.num_entries = NG_HCI_MAX_NEIGHBOR_NUM; r.entries = calloc(NG_HCI_MAX_NEIGHBOR_NUM, sizeof(ng_hci_node_neighbor_cache_entry_ep)); if (r.entries == NULL) { errno = ENOMEM; return (ERROR); } if (ioctl(s, SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE, &r, sizeof(r)) < 0) { error = ERROR; goto out; } fprintf(stdout, "BD_ADDR " \ "Features " \ "Clock offset " \ "Page scan " \ "Rep. scan\n"); for (n = 0; n < r.num_entries; n++) { fprintf(stdout, "%-17.17s " \ "%02x %02x %02x %02x %02x %02x %02x %02x " \ "%#12x " \ "%#9x " \ "%#9x\n", hci_bdaddr2str(&r.entries[n].bdaddr), r.entries[n].features[0], r.entries[n].features[1], r.entries[n].features[2], r.entries[n].features[3], r.entries[n].features[4], r.entries[n].features[5], r.entries[n].features[6], r.entries[n].features[7], r.entries[n].clock_offset, r.entries[n].page_scan_mode, r.entries[n].page_scan_rep_mode); } out: free(r.entries); return (error); } /* hci_read_neightbor_cache */ /* Send Read_Connection_List command to the node */ static int hci_read_connection_list(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_con_list r; int n, error = OK; memset(&r, 0, sizeof(r)); r.num_connections = NG_HCI_MAX_CON_NUM; r.connections = calloc(NG_HCI_MAX_CON_NUM, sizeof(ng_hci_node_con_ep)); if (r.connections == NULL) { errno = ENOMEM; return (ERROR); } if (ioctl(s, SIOC_HCI_RAW_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) { error = ERROR; goto out; } fprintf(stdout, "Remote BD_ADDR " \ "Handle " \ "Type " \ "Mode " \ "Role " \ "Encrypt " \ "Pending " \ "Queue " \ "State\n"); for (n = 0; n < r.num_connections; n++) { fprintf(stdout, "%-17.17s " \ "%6d " \ "%4.4s " \ "%4d " \ "%4.4s " \ "%7.7s " \ "%7d " \ "%5d " \ "%s\n", hci_bdaddr2str(&r.connections[n].bdaddr), r.connections[n].con_handle, (r.connections[n].link_type == NG_HCI_LINK_ACL)? "ACL" : "SCO", r.connections[n].mode, (r.connections[n].role == NG_HCI_ROLE_MASTER)? "MAST" : "SLAV", hci_encrypt2str(r.connections[n].encryption_mode, 1), r.connections[n].pending, r.connections[n].queue_len, hci_con_state2str(r.connections[n].state)); } out: free(r.connections); return (error); } /* hci_read_connection_list */ /* Send Read_Node_Link_Policy_Settings_Mask command to the node */ int hci_read_node_link_policy_settings_mask(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_link_policy_mask r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Link Policy Settings mask: %#04x\n", r.policy_mask); return (OK); } /* hci_read_node_link_policy_settings_mask */ /* Send Write_Node_Link_Policy_Settings_Mask command to the node */ int hci_write_node_link_policy_settings_mask(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_link_policy_mask r; int m; memset(&r, 0, sizeof(r)); switch (argc) { case 1: if (sscanf(argv[0], "%x", &m) != 1) return (USAGE); r.policy_mask = (m & 0xffff); break; default: return (USAGE); } if (ioctl(s, SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* hci_write_node_link_policy_settings_mask */ /* Send Read_Node_Packet_Mask command to the node */ int hci_read_node_packet_mask(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_packet_mask r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Packet mask: %#04x\n", r.packet_mask); return (OK); } /* hci_read_node_packet_mask */ /* Send Write_Node_Packet_Mask command to the node */ int hci_write_node_packet_mask(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_packet_mask r; int m; memset(&r, 0, sizeof(r)); switch (argc) { case 1: if (sscanf(argv[0], "%x", &m) != 1) return (USAGE); r.packet_mask = (m & 0xffff); break; default: return (USAGE); } if (ioctl(s, SIOC_HCI_RAW_NODE_SET_PACKET_MASK, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* hci_write_node_packet_mask */ /* Send Read_Node_Role_Switch command to the node */ int hci_read_node_role_switch(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_role_switch r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Role switch: %d\n", r.role_switch); return (OK); } /* hci_read_node_role_switch */ /* Send Write_Node_Role_Switch command to the node */ int hci_write_node_role_switch(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_role_switch r; int m; memset(&r, 0, sizeof(r)); switch (argc) { case 1: if (sscanf(argv[0], "%d", &m) != 1) return (USAGE); r.role_switch = m? 1 : 0; break; default: return (USAGE); } if (ioctl(s, SIOC_HCI_RAW_NODE_SET_ROLE_SWITCH, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* hci_write_node_role_switch */ /* Send Read_Node_List command to the node */ int hci_read_node_list(int s, int argc, char **argv) { struct ng_btsocket_hci_raw_node_list_names r; int i; r.num_names = MAX_NODE_NUM; r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo)); if (r.names == NULL) return (ERROR); if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0) { free(r.names); return (ERROR); } fprintf(stdout, "Name ID Num hooks\n"); for (i = 0; i < r.num_names; ++i) fprintf(stdout, "%-15s %08x %9d\n", r.names[i].name, r.names[i].id, r.names[i].hooks); free(r.names); return (OK); } /* hci_read_node_list */ struct hci_command node_commands[] = { { "read_node_state", "Get the HCI node state", &hci_read_node_state }, { "initialize", "Initialize the HCI node", &hci_node_initialize }, { "read_debug_level", "Read the HCI node debug level", &hci_read_debug_level }, { "write_debug_level ", "Write the HCI node debug level", &hci_write_debug_level }, { "read_node_buffer_size", "Read the HCI node buffer information. This will return current state of the\n"\ "HCI buffer for the HCI node", &hci_read_node_buffer_size }, { "read_node_bd_addr", "Read the HCI node BD_ADDR. Returns device BD_ADDR as cached by the HCI node", &hci_read_node_bd_addr }, { "read_node_features", "Read the HCI node features. This will return list of supported features as\n" \ "cached by the HCI node", &hci_read_node_features }, { "read_node_stat", "Read packets and bytes counters for the HCI node", &hci_read_node_stat }, { "reset_node_stat", "Reset packets and bytes counters for the HCI node", &hci_reset_node_stat }, { "flush_neighbor_cache", "Flush content of the HCI node neighbor cache", &hci_flush_neighbor_cache }, { "read_neighbor_cache", "Read content of the HCI node neighbor cache", &hci_read_neighbor_cache }, { "read_connection_list", "Read the baseband connection descriptors list for the HCI node", &hci_read_connection_list }, { "read_node_link_policy_settings_mask", "Read the value of the Link Policy Settinngs mask for the HCI node", &hci_read_node_link_policy_settings_mask }, { "write_node_link_policy_settings_mask ", "Write the value of the Link Policy Settings mask for the HCI node. By default\n" \ "all supported Link Policy modes (as reported by the local device features) are\n"\ "enabled. The particular Link Policy mode is enabled if local device supports\n"\ "it and correspinding bit in the mask was set\n\n" \ "\t - xxxx; Link Policy mask\n" \ "\t\t0x0000 - Disable All LM Modes\n" \ "\t\t0x0001 - Enable Master Slave Switch\n" \ "\t\t0x0002 - Enable Hold Mode\n" \ "\t\t0x0004 - Enable Sniff Mode\n" \ "\t\t0x0008 - Enable Park Mode\n", &hci_write_node_link_policy_settings_mask }, { "read_node_packet_mask", "Read the value of the Packet mask for the HCI node", &hci_read_node_packet_mask }, { "write_node_packet_mask ", "Write the value of the Packet mask for the HCI node. By default all supported\n" \ "packet types (as reported by the local device features) are enabled. The\n" \ "particular packet type is enabled if local device supports it and corresponding\n" \ "bit in the mask was set\n\n" \ "\t - xxxx; packet type mask\n" \ "" \ "\t\tACL packets\n" \ "\t\t-----------\n" \ "\t\t0x0008 DM1\n" \ "\t\t0x0010 DH1\n" \ "\t\t0x0400 DM3\n" \ "\t\t0x0800 DH3\n" \ "\t\t0x4000 DM5\n" \ "\t\t0x8000 DH5\n" \ "\n" \ "\t\tSCO packets\n" \ "\t\t-----------\n" \ "\t\t0x0020 HV1\n" \ "\t\t0x0040 HV2\n" \ "\t\t0x0080 HV3\n", &hci_write_node_packet_mask }, { "read_node_role_switch", "Read the value of the Role Switch parameter for the HCI node", &hci_read_node_role_switch }, { "write_node_role_switch {0|1}", "Write the value of the Role Switch parameter for the HCI node. By default,\n" \ "if Role Switch is supported, local device will try to perform Role Switch\n" \ "and become Master on incoming connection. Some devices do not support Role\n" \ "Switch and thus incomming connections from such devices will fail. Setting\n" \ "this parameter to zero will prevent Role Switch and thus accepting device\n" \ "will remain Slave", &hci_write_node_role_switch }, { "read_node_list", "Get a list of HCI nodes, their Netgraph IDs and connected hooks.", &hci_read_node_list }, { NULL, }}; Index: head/usr.sbin/bluetooth/hccontrol/util.c =================================================================== --- head/usr.sbin/bluetooth/hccontrol/util.c (revision 281209) +++ head/usr.sbin/bluetooth/hccontrol/util.c (revision 281210) @@ -1,410 +1,411 @@ /* * util.c * * Copyright (c) 2001 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: util.c,v 1.2 2003/05/19 17:29:29 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #define SIZE(x) (sizeof((x))/sizeof((x)[0])) char const * hci_link2str(int link_type) { static char const * const t[] = { /* NG_HCI_LINK_SCO */ "SCO", /* NG_HCI_LINK_ACL */ "ACL" }; return (link_type >= SIZE(t)? "?" : t[link_type]); } /* hci_link2str */ char const * hci_pin2str(int type) { static char const * const t[] = { /* 0x00 */ "Variable PIN", /* 0x01 */ "Fixed PIN" }; return (type >= SIZE(t)? "?" : t[type]); } /* hci_pin2str */ char const * hci_scan2str(int scan) { static char const * const t[] = { /* 0x00 */ "No Scan enabled", /* 0x01 */ "Inquiry Scan enabled. Page Scan disabled", /* 0x02 */ "Inquiry Scan disabled. Page Scan enabled", /* 0x03 */ "Inquiry Scan enabled. Page Scan enabled" }; return (scan >= SIZE(t)? "?" : t[scan]); } /* hci_scan2str */ char const * hci_encrypt2str(int encrypt, int brief) { static char const * const t[] = { /* 0x00 */ "Disabled", /* 0x01 */ "Only for point-to-point packets", /* 0x02 */ "Both point-to-point and broadcast packets" }; static char const * const t1[] = { /* NG_HCI_ENCRYPTION_MODE_NONE */ "NONE", /* NG_HCI_ENCRYPTION_MODE_P2P */ "P2P", /* NG_HCI_ENCRYPTION_MODE_ALL */ "ALL", }; if (brief) return (encrypt >= SIZE(t1)? "?" : t1[encrypt]); return (encrypt >= SIZE(t)? "?" : t[encrypt]); } /* hci_encrypt2str */ char const * hci_coding2str(int coding) { static char const * const t[] = { /* 0x00 */ "Linear", /* 0x01 */ "u-law", /* 0x02 */ "A-law", /* 0x03 */ "Reserved" }; return (coding >= SIZE(t)? "?" : t[coding]); } /* hci_coding2str */ char const * hci_vdata2str(int data) { static char const * const t[] = { /* 0x00 */ "1's complement", /* 0x01 */ "2's complement", /* 0x02 */ "Sign-Magnitude", /* 0x03 */ "Reserved" }; return (data >= SIZE(t)? "?" : t[data]); } /* hci_vdata2str */ char const * hci_hmode2str(int mode, char *buffer, int size) { static char const * const t[] = { /* 0x01 */ "Suspend Page Scan ", /* 0x02 */ "Suspend Inquiry Scan ", /* 0x04 */ "Suspend Periodic Inquiries " }; if (buffer != NULL && size > 0) { int n; memset(buffer, 0, size); for (n = 0; n < SIZE(t); n++) { int len = strlen(buffer); if (len >= size) break; if (mode & (1 << n)) strncat(buffer, t[n], size - len); } } return (buffer); } /* hci_hmode2str */ char const * hci_ver2str(int ver) { static char const * const t[] = { /* 0x00 */ "Bluetooth HCI Specification 1.0B", /* 0x01 */ "Bluetooth HCI Specification 1.1", /* 0x02 */ "Bluetooth HCI Specification 1.2", /* 0x03 */ "Bluetooth HCI Specification 2.0" }; return (ver >= SIZE(t)? "?" : t[ver]); } /* hci_ver2str */ char const * hci_lmpver2str(int ver) { static char const * const t[] = { /* 0x00 */ "Bluetooth LMP 1.0", /* 0x01 */ "Bluetooth LMP 1.1", /* 0x02 */ "Bluetooth LMP 1.2", /* 0x03 */ "Bluetooth LMP 2.0" }; return (ver >= SIZE(t)? "?" : t[ver]); } /* hci_lmpver2str */ char const * hci_manufacturer2str(int m) { static char const * const t[] = { /* 0000 */ "Ericsson Technology Licensing", /* 0001 */ "Nokia Mobile Phones", /* 0002 */ "Intel Corp.", /* 0003 */ "IBM Corp.", /* 0004 */ "Toshiba Corp.", /* 0005 */ "3Com", /* 0006 */ "Microsoft", /* 0007 */ "Lucent", /* 0008 */ "Motorola", /* 0009 */ "Infineon Technologies AG", /* 0010 */ "Cambridge Silicon Radio", /* 0011 */ "Silicon Wave", /* 0012 */ "Digianswer A/S", /* 0013 */ "Texas Instruments Inc.", /* 0014 */ "Parthus Technologies Inc.", /* 0015 */ "Broadcom Corporation", /* 0016 */ "Mitel Semiconductor", /* 0017 */ "Widcomm, Inc.", /* 0018 */ "Zeevo, Inc.", /* 0019 */ "Atmel Corporation", /* 0020 */ "Mitsubishi Electric Corporation", /* 0021 */ "RTX Telecom A/S", /* 0022 */ "KC Technology Inc.", /* 0023 */ "Newlogic", /* 0024 */ "Transilica, Inc.", /* 0025 */ "Rohde & Schwartz GmbH & Co. KG", /* 0026 */ "TTPCom Limited", /* 0027 */ "Signia Technologies, Inc.", /* 0028 */ "Conexant Systems Inc.", /* 0029 */ "Qualcomm", /* 0030 */ "Inventel", /* 0031 */ "AVM Berlin", /* 0032 */ "BandSpeed, Inc.", /* 0033 */ "Mansella Ltd", /* 0034 */ "NEC Corporation", /* 0035 */ "WavePlus Technology Co., Ltd.", /* 0036 */ "Alcatel", /* 0037 */ "Philips Semiconductors", /* 0038 */ "C Technologies", /* 0039 */ "Open Interface", /* 0040 */ "R F Micro Devices", /* 0041 */ "Hitachi Ltd", /* 0042 */ "Symbol Technologies, Inc.", /* 0043 */ "Tenovis", /* 0044 */ "Macronix International Co. Ltd.", /* 0045 */ "GCT Semiconductor", /* 0046 */ "Norwood Systems", /* 0047 */ "MewTel Technology Inc.", /* 0048 */ "ST Microelectronics", /* 0049 */ "Synopsys", /* 0050 */ "Red-M (Communications) Ltd", /* 0051 */ "Commil Ltd", /* 0052 */ "Computer Access Technology Corporation (CATC)", /* 0053 */ "Eclipse (HQ Espana) S.L.", /* 0054 */ "Renesas Technology Corp.", /* 0055 */ "Mobilian Corporation", /* 0056 */ "Terax", /* 0057 */ "Integrated System Solution Corp.", /* 0058 */ "Matsushita Electric Industrial Co., Ltd.", /* 0059 */ "Gennum Corporation", /* 0060 */ "Research In Motion", /* 0061 */ "IPextreme, Inc.", /* 0062 */ "Systems and Chips, Inc", /* 0063 */ "Bluetooth SIG, Inc", /* 0064 */ "Seiko Epson Corporation" }; return (m >= SIZE(t)? "?" : t[m]); } /* hci_manufacturer2str */ char const * hci_features2str(uint8_t *features, char *buffer, int size) { static char const * const t[][8] = { { /* byte 0 */ /* 0 */ "<3-Slot> ", /* 1 */ "<5-Slot> ", /* 2 */ " ", /* 3 */ " ", /* 4 */ " ", /* 5 */ " ", /* 6 */ " ", /* 7 */ " " }, { /* byte 1 */ /* 0 */ " ", /* 1 */ " ", /* 2 */ " ", /* 3 */ " ", /* 4 */ " ", /* 5 */ " ", /* 6 */ " ", /* 7 */ " " }, { /* byte 2 */ /* 0 */ " ", /* 1 */ " ", /* 2 */ " ", /* 3 */ " ", /* 4 */ " ", /* 5 */ " ", /* 6 */ " ", /* 7 */ " " }}; if (buffer != NULL && size > 0) { int n, i, len0, len1; memset(buffer, 0, size); len1 = 0; for (n = 0; n < SIZE(t); n++) { for (i = 0; i < SIZE(t[n]); i++) { len0 = strlen(buffer); if (len0 >= size) goto done; if (features[n] & (1 << i)) { if (len1 + strlen(t[n][i]) > 60) { len1 = 0; buffer[len0 - 1] = '\n'; } len1 += strlen(t[n][i]); strncat(buffer, t[n][i], size - len0); } } } } done: return (buffer); } /* hci_features2str */ char const * hci_cc2str(int cc) { static char const * const t[] = { /* 0x00 */ "North America, Europe, Japan", /* 0x01 */ "France" }; return (cc >= SIZE(t)? "?" : t[cc]); } /* hci_cc2str */ char const * hci_con_state2str(int state) { static char const * const t[] = { /* NG_HCI_CON_CLOSED */ "CLOSED", /* NG_HCI_CON_W4_LP_CON_RSP */ "W4_LP_CON_RSP", /* NG_HCI_CON_W4_CONN_COMPLETE */ "W4_CONN_COMPLETE", /* NG_HCI_CON_OPEN */ "OPEN" }; return (state >= SIZE(t)? "UNKNOWN" : t[state]); } /* hci_con_state2str */ char const * hci_status2str(int status) { static char const * const t[] = { /* 0x00 */ "No error", /* 0x01 */ "Unknown HCI command", /* 0x02 */ "No connection", /* 0x03 */ "Hardware failure", /* 0x04 */ "Page timeout", /* 0x05 */ "Authentication failure", /* 0x06 */ "Key missing", /* 0x07 */ "Memory full", /* 0x08 */ "Connection timeout", /* 0x09 */ "Max number of connections", /* 0x0a */ "Max number of SCO connections to a unit", /* 0x0b */ "ACL connection already exists", /* 0x0c */ "Command disallowed", /* 0x0d */ "Host rejected due to limited resources", /* 0x0e */ "Host rejected due to security reasons", /* 0x0f */ "Host rejected due to remote unit is a personal unit", /* 0x10 */ "Host timeout", /* 0x11 */ "Unsupported feature or parameter value", /* 0x12 */ "Invalid HCI command parameter", /* 0x13 */ "Other end terminated connection: User ended connection", /* 0x14 */ "Other end terminated connection: Low resources", /* 0x15 */ "Other end terminated connection: About to power off", /* 0x16 */ "Connection terminated by local host", /* 0x17 */ "Repeated attempts", /* 0x18 */ "Pairing not allowed", /* 0x19 */ "Unknown LMP PDU", /* 0x1a */ "Unsupported remote feature", /* 0x1b */ "SCO offset rejected", /* 0x1c */ "SCO interval rejected", /* 0x1d */ "SCO air mode rejected", /* 0x1e */ "Invalid LMP parameters", /* 0x1f */ "Unspecified error", /* 0x20 */ "Unsupported LMP parameter value", /* 0x21 */ "Role change not allowed", /* 0x22 */ "LMP response timeout", /* 0x23 */ "LMP error transaction collision", /* 0x24 */ "LMP PSU not allowed", /* 0x25 */ "Encryption mode not acceptable", /* 0x26 */ "Unit key used", /* 0x27 */ "QoS is not supported", /* 0x28 */ "Instant passed", /* 0x29 */ "Pairing with unit key not supported" }; return (status >= SIZE(t)? "Unknown error" : t[status]); } /* hci_status2str */ char const * hci_bdaddr2str(bdaddr_t const *ba) { extern int numeric_bdaddr; static char buffer[MAXHOSTNAMELEN]; struct hostent *he = NULL; if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) { buffer[0] = '*'; buffer[1] = 0; return (buffer); } if (!numeric_bdaddr && (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) { strlcpy(buffer, he->h_name, sizeof(buffer)); return (buffer); } bt_ntoa(ba, buffer); return (buffer); } /* hci_bdaddr2str */ Index: head/usr.sbin/bluetooth/hcsecd/hcsecd.c =================================================================== --- head/usr.sbin/bluetooth/hcsecd/hcsecd.c (revision 281209) +++ head/usr.sbin/bluetooth/hcsecd/hcsecd.c (revision 281210) @@ -1,446 +1,447 @@ /* * hcsecd.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: hcsecd.c,v 1.6 2003/08/18 19:19:55 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "hcsecd.h" static int done = 0; static int process_pin_code_request_event (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); static int process_link_key_request_event (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); static int send_pin_code_reply (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin); static int send_link_key_reply (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, uint8_t *key); static int process_link_key_notification_event (int sock, struct sockaddr_hci *addr, ng_hci_link_key_notification_ep *ep); static void sighup (int s); static void sigint (int s); static void usage (void); /* Main */ int main(int argc, char *argv[]) { int n, detach, sock; socklen_t size; struct sigaction sa; struct sockaddr_hci addr; struct ng_btsocket_hci_raw_filter filter; char buffer[HCSECD_BUFFER_SIZE]; ng_hci_event_pkt_t *event = NULL; detach = 1; while ((n = getopt(argc, argv, "df:h")) != -1) { switch (n) { case 'd': detach = 0; break; case 'f': config_file = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } if (config_file == NULL) usage(); /* NOT REACHED */ if (getuid() != 0) errx(1, "** ERROR: You should run %s as privileged user!", HCSECD_IDENT); /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigint; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighup; if (sigaction(SIGHUP, &sa, NULL) < 0) err(1, "Could not sigaction(SIGHUP)"); /* Open socket and set filter */ sock = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); if (sock < 0) err(1, "Could not create HCI socket"); memset(&filter, 0, sizeof(filter)); bit_set(filter.event_mask, NG_HCI_EVENT_PIN_CODE_REQ - 1); bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_REQ - 1); bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_NOTIFICATION - 1); if (setsockopt(sock, SOL_HCI_RAW, SO_HCI_RAW_FILTER, (void * const) &filter, sizeof(filter)) < 0) err(1, "Could not set HCI socket filter"); if (detach && daemon(0, 0) < 0) err(1, "Could not daemon()ize"); openlog(HCSECD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); read_config_file(); read_keys_file(); if (detach) { FILE *pid = NULL; if ((pid = fopen(HCSECD_PIDFILE, "w")) == NULL) { syslog(LOG_ERR, "Could not create PID file %s. %s (%d)", HCSECD_PIDFILE, strerror(errno), errno); exit(1); } fprintf(pid, "%d", getpid()); fclose(pid); } event = (ng_hci_event_pkt_t *) buffer; while (!done) { size = sizeof(addr); n = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &size); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "Could not receive from HCI socket. " \ "%s (%d)", strerror(errno), errno); exit(1); } if (event->type != NG_HCI_EVENT_PKT) { syslog(LOG_ERR, "Received unexpected HCI packet, " \ "type=%#x", event->type); continue; } switch (event->event) { case NG_HCI_EVENT_PIN_CODE_REQ: process_pin_code_request_event(sock, &addr, (bdaddr_p)(event + 1)); break; case NG_HCI_EVENT_LINK_KEY_REQ: process_link_key_request_event(sock, &addr, (bdaddr_p)(event + 1)); break; case NG_HCI_EVENT_LINK_KEY_NOTIFICATION: process_link_key_notification_event(sock, &addr, (ng_hci_link_key_notification_ep *)(event + 1)); break; default: syslog(LOG_ERR, "Received unexpected HCI event, " \ "event=%#x", event->event); break; } } if (detach) if (remove(HCSECD_PIDFILE) < 0) syslog(LOG_ERR, "Could not remove PID file %s. %s (%d)", HCSECD_PIDFILE, strerror(errno), errno); dump_keys_file(); clean_config(); closelog(); close(sock); return (0); } /* Process PIN_Code_Request event */ static int process_pin_code_request_event(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr) { link_key_p key = NULL; syslog(LOG_DEBUG, "Got PIN_Code_Request event from '%s', " \ "remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); if ((key = get_key(bdaddr, 0)) != NULL) { syslog(LOG_DEBUG, "Found matching entry, " \ "remote bdaddr %s, name '%s', PIN code %s", bt_ntoa(&key->bdaddr, NULL), (key->name != NULL)? key->name : "No name", (key->pin != NULL)? "exists" : "doesn't exist"); return (send_pin_code_reply(sock, addr, bdaddr, key->pin)); } syslog(LOG_DEBUG, "Could not PIN code for remote bdaddr %s", bt_ntoa(bdaddr, NULL)); return (send_pin_code_reply(sock, addr, bdaddr, NULL)); } /* Process Link_Key_Request event */ static int process_link_key_request_event(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr) { link_key_p key = NULL; syslog(LOG_DEBUG, "Got Link_Key_Request event from '%s', " \ "remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); if ((key = get_key(bdaddr, 0)) != NULL) { syslog(LOG_DEBUG, "Found matching entry, " \ "remote bdaddr %s, name '%s', link key %s", bt_ntoa(&key->bdaddr, NULL), (key->name != NULL)? key->name : "No name", (key->key != NULL)? "exists" : "doesn't exist"); return (send_link_key_reply(sock, addr, bdaddr, key->key)); } syslog(LOG_DEBUG, "Could not find link key for remote bdaddr %s", bt_ntoa(bdaddr, NULL)); return (send_link_key_reply(sock, addr, bdaddr, NULL)); } /* Send PIN_Code_[Negative]_Reply */ static int send_pin_code_reply(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin) { uint8_t buffer[HCSECD_BUFFER_SIZE]; ng_hci_cmd_pkt_t *cmd = NULL; memset(buffer, 0, sizeof(buffer)); cmd = (ng_hci_cmd_pkt_t *) buffer; cmd->type = NG_HCI_CMD_PKT; if (pin != NULL) { ng_hci_pin_code_rep_cp *cp = NULL; cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_PIN_CODE_REP)); cmd->length = sizeof(*cp); cp = (ng_hci_pin_code_rep_cp *)(cmd + 1); memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); strncpy((char *) cp->pin, pin, sizeof(cp->pin)); cp->pin_size = strlen((char const *) cp->pin); syslog(LOG_DEBUG, "Sending PIN_Code_Reply to '%s' " \ "for remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); } else { ng_hci_pin_code_neg_rep_cp *cp = NULL; cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_PIN_CODE_NEG_REP)); cmd->length = sizeof(*cp); cp = (ng_hci_pin_code_neg_rep_cp *)(cmd + 1); memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); syslog(LOG_DEBUG, "Sending PIN_Code_Negative_Reply to '%s' " \ "for remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); } again: if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0, (struct sockaddr *) addr, sizeof(*addr)) < 0) { if (errno == EINTR) goto again; syslog(LOG_ERR, "Could not send PIN code reply to '%s' " \ "for remote bdaddr %s. %s (%d)", addr->hci_node, bt_ntoa(bdaddr, NULL), strerror(errno), errno); return (-1); } return (0); } /* Send Link_Key_[Negative]_Reply */ static int send_link_key_reply(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, uint8_t *key) { uint8_t buffer[HCSECD_BUFFER_SIZE]; ng_hci_cmd_pkt_t *cmd = NULL; memset(buffer, 0, sizeof(buffer)); cmd = (ng_hci_cmd_pkt_t *) buffer; cmd->type = NG_HCI_CMD_PKT; if (key != NULL) { ng_hci_link_key_rep_cp *cp = NULL; cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_LINK_KEY_REP)); cmd->length = sizeof(*cp); cp = (ng_hci_link_key_rep_cp *)(cmd + 1); memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); memcpy(&cp->key, key, sizeof(cp->key)); syslog(LOG_DEBUG, "Sending Link_Key_Reply to '%s' " \ "for remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); } else { ng_hci_link_key_neg_rep_cp *cp = NULL; cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_LINK_KEY_NEG_REP)); cmd->length = sizeof(*cp); cp = (ng_hci_link_key_neg_rep_cp *)(cmd + 1); memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); syslog(LOG_DEBUG, "Sending Link_Key_Negative_Reply to '%s' " \ "for remote bdaddr %s", addr->hci_node, bt_ntoa(bdaddr, NULL)); } again: if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0, (struct sockaddr *) addr, sizeof(*addr)) < 0) { if (errno == EINTR) goto again; syslog(LOG_ERR, "Could not send link key reply to '%s' " \ "for remote bdaddr %s. %s (%d)", addr->hci_node, bt_ntoa(bdaddr, NULL), strerror(errno), errno); return (-1); } return (0); } /* Process Link_Key_Notification event */ static int process_link_key_notification_event(int sock, struct sockaddr_hci *addr, ng_hci_link_key_notification_ep *ep) { link_key_p key = NULL; syslog(LOG_DEBUG, "Got Link_Key_Notification event from '%s', " \ "remote bdaddr %s", addr->hci_node, bt_ntoa(&ep->bdaddr, NULL)); if ((key = get_key(&ep->bdaddr, 1)) == NULL) { syslog(LOG_ERR, "Could not find entry for remote bdaddr %s", bt_ntoa(&ep->bdaddr, NULL)); return (-1); } syslog(LOG_DEBUG, "Updating link key for the entry, " \ "remote bdaddr %s, name '%s', link key %s", bt_ntoa(&key->bdaddr, NULL), (key->name != NULL)? key->name : "No name", (key->key != NULL)? "exists" : "doesn't exist"); if (key->key == NULL) { key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE); if (key->key == NULL) { syslog(LOG_ERR, "Could not allocate link key"); exit(1); } } memcpy(key->key, &ep->key, NG_HCI_KEY_SIZE); return (0); } /* Signal handlers */ static void sighup(int s) { syslog(LOG_DEBUG, "Got SIGHUP (%d)", s); dump_keys_file(); read_config_file(); read_keys_file(); } static void sigint(int s) { syslog(LOG_DEBUG, "Got signal %d, total number of signals %d", s, ++ done); } /* Display usage and exit */ static void usage(void) { fprintf(stderr, "Usage: %s [-d] -f config_file [-h]\n" \ "Where:\n" \ "\t-d do not detach from terminal\n" \ "\t-f config_file use \n" \ "\t-h display this message\n", HCSECD_IDENT); exit(255); } Index: head/usr.sbin/bluetooth/hcsecd/parser.y =================================================================== --- head/usr.sbin/bluetooth/hcsecd/parser.y (revision 281209) +++ head/usr.sbin/bluetooth/hcsecd/parser.y (revision 281210) @@ -1,434 +1,435 @@ %{ /* * parser.y * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: parser.y,v 1.5 2003/06/07 21:22:30 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include "hcsecd.h" int yyparse (void); int yylex (void); static void free_key (link_key_p key); static int hexa2int4(char *a); static int hexa2int8(char *a); extern int yylineno; static LIST_HEAD(, link_key) link_keys; char *config_file = "/etc/bluetooth/hcsecd.conf"; static link_key_p key = NULL; %} %union { char *string; } %token T_BDADDRSTRING T_HEXSTRING T_STRING %token T_DEVICE T_BDADDR T_NAME T_KEY T_PIN T_NOKEY T_NOPIN T_JUNK %% config: line | config line ; line: T_DEVICE { key = (link_key_p) malloc(sizeof(*key)); if (key == NULL) { syslog(LOG_ERR, "Could not allocate new " \ "config entry"); exit(1); } memset(key, 0, sizeof(*key)); } '{' options '}' { if (get_key(&key->bdaddr, 1) != NULL) { syslog(LOG_ERR, "Ignoring duplicated entry " \ "for bdaddr %s", bt_ntoa(&key->bdaddr, NULL)); free_key(key); } else LIST_INSERT_HEAD(&link_keys, key, next); key = NULL; } ; options: option ';' | options option ';' ; option: bdaddr | name | key | pin ; bdaddr: T_BDADDR T_BDADDRSTRING { if (!bt_aton($2, &key->bdaddr)) { syslog(LOG_ERR, "Cound not parse BD_ADDR " \ "'%s'", $2); exit(1); } } ; name: T_NAME T_STRING { if (key->name != NULL) free(key->name); key->name = strdup($2); if (key->name == NULL) { syslog(LOG_ERR, "Could not allocate new " \ "device name"); exit(1); } } ; key: T_KEY T_HEXSTRING { int i, len; if (key->key != NULL) free(key->key); key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE); if (key->key == NULL) { syslog(LOG_ERR, "Could not allocate new " \ "link key"); exit(1); } memset(key->key, 0, NG_HCI_KEY_SIZE); len = strlen($2) / 2; if (len > NG_HCI_KEY_SIZE) len = NG_HCI_KEY_SIZE; for (i = 0; i < len; i ++) key->key[i] = hexa2int8((char *)($2) + 2*i); } | T_KEY T_NOKEY { if (key->key != NULL) free(key->key); key->key = NULL; } ; pin: T_PIN T_STRING { if (key->pin != NULL) free(key->pin); key->pin = strdup($2); if (key->pin == NULL) { syslog(LOG_ERR, "Could not allocate new " \ "PIN code"); exit(1); } } | T_PIN T_NOPIN { if (key->pin != NULL) free(key->pin); key->pin = NULL; } ; %% /* Display parser error message */ void yyerror(char const *message) { syslog(LOG_ERR, "%s in line %d", message, yylineno); } /* Re-read config file */ void read_config_file(void) { extern FILE *yyin; if (config_file == NULL) { syslog(LOG_ERR, "Unknown config file name!"); exit(1); } if ((yyin = fopen(config_file, "r")) == NULL) { syslog(LOG_ERR, "Could not open config file '%s'. %s (%d)", config_file, strerror(errno), errno); exit(1); } clean_config(); if (yyparse() < 0) { syslog(LOG_ERR, "Could not parse config file '%s'",config_file); exit(1); } fclose(yyin); yyin = NULL; #if __config_debug__ dump_config(); #endif } /* Clean config */ void clean_config(void) { link_key_p key = NULL; while ((key = LIST_FIRST(&link_keys)) != NULL) { LIST_REMOVE(key, next); free_key(key); } } /* Find link key entry in the list. Return exact or default match */ link_key_p get_key(bdaddr_p bdaddr, int exact_match) { link_key_p key = NULL, defkey = NULL; LIST_FOREACH(key, &link_keys, next) { if (memcmp(bdaddr, &key->bdaddr, sizeof(key->bdaddr)) == 0) break; if (!exact_match) if (memcmp(NG_HCI_BDADDR_ANY, &key->bdaddr, sizeof(key->bdaddr)) == 0) defkey = key; } return ((key != NULL)? key : defkey); } #if __config_debug__ /* Dump config */ void dump_config(void) { link_key_p key = NULL; char buffer[64]; LIST_FOREACH(key, &link_keys, next) { if (key->key != NULL) snprintf(buffer, sizeof(buffer), "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", key->key[0], key->key[1], key->key[2], key->key[3], key->key[4], key->key[5], key->key[6], key->key[7], key->key[8], key->key[9], key->key[10], key->key[11], key->key[12], key->key[13], key->key[14], key->key[15]); syslog(LOG_DEBUG, "device %s " \ "bdaddr %s " \ "pin %s " \ "key %s", (key->name != NULL)? key->name : "noname", bt_ntoa(&key->bdaddr, NULL), (key->pin != NULL)? key->pin : "nopin", (key->key != NULL)? buffer : "nokey"); } } #endif /* Read keys file */ int read_keys_file(void) { FILE *f = NULL; link_key_t *key = NULL; char buf[HCSECD_BUFFER_SIZE], *p = NULL, *cp = NULL; bdaddr_t bdaddr; int i, len; if ((f = fopen(HCSECD_KEYSFILE, "r")) == NULL) { if (errno == ENOENT) return (0); syslog(LOG_ERR, "Could not open keys file %s. %s (%d)\n", HCSECD_KEYSFILE, strerror(errno), errno); return (-1); } while ((p = fgets(buf, sizeof(buf), f)) != NULL) { if (*p == '#') continue; if ((cp = strpbrk(p, " ")) == NULL) continue; *cp++ = '\0'; if (!bt_aton(p, &bdaddr)) continue; if ((key = get_key(&bdaddr, 1)) == NULL) continue; if (key->key == NULL) { key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE); if (key->key == NULL) { syslog(LOG_ERR, "Could not allocate link key"); exit(1); } } memset(key->key, 0, NG_HCI_KEY_SIZE); len = strlen(cp) / 2; if (len > NG_HCI_KEY_SIZE) len = NG_HCI_KEY_SIZE; for (i = 0; i < len; i ++) key->key[i] = hexa2int8(cp + 2*i); syslog(LOG_DEBUG, "Restored link key for the entry, " \ "remote bdaddr %s, name '%s'", bt_ntoa(&key->bdaddr, NULL), (key->name != NULL)? key->name : "No name"); } fclose(f); return (0); } /* Dump keys file */ int dump_keys_file(void) { link_key_p key = NULL; char tmp[PATH_MAX], buf[HCSECD_BUFFER_SIZE]; int f; snprintf(tmp, sizeof(tmp), "%s.tmp", HCSECD_KEYSFILE); if ((f = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) < 0) { syslog(LOG_ERR, "Could not create temp keys file %s. %s (%d)\n", tmp, strerror(errno), errno); return (-1); } LIST_FOREACH(key, &link_keys, next) { if (key->key == NULL) continue; snprintf(buf, sizeof(buf), "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", bt_ntoa(&key->bdaddr, NULL), key->key[0], key->key[1], key->key[2], key->key[3], key->key[4], key->key[5], key->key[6], key->key[7], key->key[8], key->key[9], key->key[10], key->key[11], key->key[12], key->key[13], key->key[14], key->key[15]); if (write(f, buf, strlen(buf)) < 0) { syslog(LOG_ERR, "Could not write temp keys file. " \ "%s (%d)\n", strerror(errno), errno); break; } } close(f); if (rename(tmp, HCSECD_KEYSFILE) < 0) { syslog(LOG_ERR, "Could not rename(%s, %s). %s (%d)\n", tmp, HCSECD_KEYSFILE, strerror(errno), errno); unlink(tmp); return (-1); } return (0); } /* Free key entry */ static void free_key(link_key_p key) { if (key->name != NULL) free(key->name); if (key->key != NULL) free(key->key); if (key->pin != NULL) free(key->pin); memset(key, 0, sizeof(*key)); free(key); } /* Convert hex ASCII to int4 */ static int hexa2int4(char *a) { if ('0' <= *a && *a <= '9') return (*a - '0'); if ('A' <= *a && *a <= 'F') return (*a - 'A' + 0xa); if ('a' <= *a && *a <= 'f') return (*a - 'a' + 0xa); syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a); exit(1); } /* Convert hex ASCII to int8 */ static int hexa2int8(char *a) { return ((hexa2int4(a) << 4) | hexa2int4(a + 1)); } Index: head/usr.sbin/bluetooth/l2control/l2cap.c =================================================================== --- head/usr.sbin/bluetooth/l2control/l2cap.c (revision 281209) +++ head/usr.sbin/bluetooth/l2control/l2cap.c (revision 281210) @@ -1,313 +1,314 @@ /* * l2cap.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: l2cap.c,v 1.5 2003/05/16 19:52:37 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include "l2control.h" #define SIZE(x) (sizeof((x))/sizeof((x)[0])) /* Print BDADDR */ static char * bdaddrpr(bdaddr_t const *ba) { extern int numeric_bdaddr; static char str[24]; struct hostent *he = NULL; if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) { str[0] = '*'; str[1] = 0; return (str); } if (!numeric_bdaddr && (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) { strlcpy(str, he->h_name, sizeof(str)); return (str); } bt_ntoa(ba, str); return (str); } /* bdaddrpr */ /* Send read_node_flags command to the node */ static int l2cap_read_node_flags(int s, int argc, char **argv) { struct ng_btsocket_l2cap_raw_node_flags r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Connectionless traffic flags:\n"); fprintf(stdout, "\tSDP: %s\n", (r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled"); fprintf(stdout, "\tRFCOMM: %s\n", (r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled"); fprintf(stdout, "\tTCP: %s\n", (r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled"); return (OK); } /* l2cap_read_node_flags */ /* Send read_debug_level command to the node */ static int l2cap_read_debug_level(int s, int argc, char **argv) { struct ng_btsocket_l2cap_raw_node_debug r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0) return (ERROR); fprintf(stdout, "Debug level: %d\n", r.debug); return (OK); } /* l2cap_read_debug_level */ /* Send write_debug_level command to the node */ static int l2cap_write_debug_level(int s, int argc, char **argv) { struct ng_btsocket_l2cap_raw_node_debug r; memset(&r, 0, sizeof(r)); switch (argc) { case 1: r.debug = atoi(argv[0]); break; default: return (USAGE); } if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* l2cap_write_debug_level */ /* Send read_connection_list command to the node */ static int l2cap_read_connection_list(int s, int argc, char **argv) { static char const * const state[] = { /* NG_L2CAP_CON_CLOSED */ "CLOSED", /* NG_L2CAP_W4_LP_CON_CFM */ "W4_LP_CON_CFM", /* NG_L2CAP_CON_OPEN */ "OPEN" }; #define con_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) struct ng_btsocket_l2cap_raw_con_list r; int n, error = OK; memset(&r, 0, sizeof(r)); r.num_connections = NG_L2CAP_MAX_CON_NUM; r.connections = calloc(NG_L2CAP_MAX_CON_NUM, sizeof(ng_l2cap_node_con_ep)); if (r.connections == NULL) { errno = ENOMEM; return (ERROR); } if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) { error = ERROR; goto out; } fprintf(stdout, "L2CAP connections:\n"); fprintf(stdout, "Remote BD_ADDR Handle Flags Pending State\n"); for (n = 0; n < r.num_connections; n++) { fprintf(stdout, "%-17.17s " \ "%6d " \ "%c%c%c%c%c " \ "%7d " \ "%s\n", bdaddrpr(&r.connections[n].remote), r.connections[n].con_handle, ((r.connections[n].flags & NG_L2CAP_CON_OUTGOING)? 'O' : 'I'), ((r.connections[n].flags & NG_L2CAP_CON_LP_TIMO)? 'L' : ' '), ((r.connections[n].flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)? 'D' : ' '), ((r.connections[n].flags & NG_L2CAP_CON_TX)? 'T' : ' '), ((r.connections[n].flags & NG_L2CAP_CON_RX)? 'R' : ' '), r.connections[n].pending, con_state2str(r.connections[n].state)); } out: free(r.connections); return (error); } /* l2cap_read_connection_list */ /* Send read_channel_list command to the node */ static int l2cap_read_channel_list(int s, int argc, char **argv) { static char const * const state[] = { /* NG_L2CAP_CLOSED */ "CLOSED", /* NG_L2CAP_W4_L2CAP_CON_RSP */ "W4_L2CAP_CON_RSP", /* NG_L2CAP_W4_L2CA_CON_RSP */ "W4_L2CA_CON_RSP", /* NG_L2CAP_CONFIG */ "CONFIG", /* NG_L2CAP_OPEN */ "OPEN", /* NG_L2CAP_W4_L2CAP_DISCON_RSP */ "W4_L2CAP_DISCON_RSP", /* NG_L2CAP_W4_L2CA_DISCON_RSP */ "W4_L2CA_DISCON_RSP" }; #define ch_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) struct ng_btsocket_l2cap_raw_chan_list r; int n, error = OK; memset(&r, 0, sizeof(r)); r.num_channels = NG_L2CAP_MAX_CHAN_NUM; r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM, sizeof(ng_l2cap_node_chan_ep)); if (r.channels == NULL) { errno = ENOMEM; return (ERROR); } if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) { error = ERROR; goto out; } fprintf(stdout, "L2CAP channels:\n"); fprintf(stdout, "Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State\n"); for (n = 0; n < r.num_channels; n++) { fprintf(stdout, "%-17.17s " \ "%5d/%5d %5d " \ "%5d/%5d " \ "%s\n", bdaddrpr(&r.channels[n].remote), r.channels[n].scid, r.channels[n].dcid, r.channels[n].psm, r.channels[n].imtu, r.channels[n].omtu, ch_state2str(r.channels[n].state)); } out: free(r.channels); return (error); } /* l2cap_read_channel_list */ /* Send read_auto_disconnect_timeout command to the node */ static int l2cap_read_auto_disconnect_timeout(int s, int argc, char **argv) { struct ng_btsocket_l2cap_raw_auto_discon_timo r; memset(&r, 0, sizeof(r)); if (ioctl(s, SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0) return (ERROR); if (r.timeout != 0) fprintf(stdout, "Auto disconnect timeout: %d sec\n", r.timeout); else fprintf(stdout, "Auto disconnect disabled\n"); return (OK); } /* l2cap_read_auto_disconnect_timeout */ /* Send write_auto_disconnect_timeout command to the node */ static int l2cap_write_auto_disconnect_timeout(int s, int argc, char **argv) { struct ng_btsocket_l2cap_raw_auto_discon_timo r; memset(&r, 0, sizeof(r)); switch (argc) { case 1: r.timeout = atoi(argv[0]); break; default: return (USAGE); } if (ioctl(s, SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0) return (ERROR); return (OK); } /* l2cap_write_auto_disconnect_timeout */ struct l2cap_command l2cap_commands[] = { { "read_node_flags", "Get L2CAP node flags", &l2cap_read_node_flags }, { "read_debug_level", "Get L2CAP node debug level", &l2cap_read_debug_level }, { "write_debug_level ", "Set L2CAP node debug level", &l2cap_write_debug_level }, { "read_connection_list", "Read list of the L2CAP connections", &l2cap_read_connection_list }, { "read_channel_list", "Read list of the L2CAP channels", &l2cap_read_channel_list }, { "read_auto_disconnect_timeout", "Get L2CAP node auto disconnect timeout (in sec)", &l2cap_read_auto_disconnect_timeout }, { "write_auto_disconnect_timeout ", "Set L2CAP node auto disconnect timeout (in sec)", &l2cap_write_auto_disconnect_timeout }, { NULL, }}; Index: head/usr.sbin/bluetooth/l2control/l2control.c =================================================================== --- head/usr.sbin/bluetooth/l2control/l2control.c (revision 281209) +++ head/usr.sbin/bluetooth/l2control/l2control.c (revision 281210) @@ -1,220 +1,221 @@ /* * l2control.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: l2control.c,v 1.6 2003/09/05 00:38:25 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include "l2control.h" /* Prototypes */ static int do_l2cap_command (bdaddr_p, int, char **); static struct l2cap_command * find_l2cap_command (char const *, struct l2cap_command *); static void print_l2cap_command (struct l2cap_command *); static void usage (void); /* Main */ int numeric_bdaddr = 0; int main(int argc, char *argv[]) { int n; bdaddr_t bdaddr; memset(&bdaddr, 0, sizeof(bdaddr)); /* Process command line arguments */ while ((n = getopt(argc, argv, "a:nh")) != -1) { switch (n) { case 'a': if (!bt_aton(optarg, &bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&bdaddr, he->h_addr, sizeof(bdaddr)); } break; case 'n': numeric_bdaddr = 1; break; case 'h': default: usage(); break; } } argc -= optind; argv += optind; if (*argv == NULL) usage(); return (do_l2cap_command(&bdaddr, argc, argv)); } /* main */ /* Execute commands */ static int do_l2cap_command(bdaddr_p bdaddr, int argc, char **argv) { char *cmd = argv[0]; struct l2cap_command *c = NULL; struct sockaddr_l2cap sa; int s, e, help; help = 0; if (strcasecmp(cmd, "help") == 0) { argc --; argv ++; if (argc <= 0) { fprintf(stdout, "Supported commands:\n"); print_l2cap_command(l2cap_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); return (OK); } help = 1; cmd = argv[0]; } c = find_l2cap_command(cmd, l2cap_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); return (ERROR); } if (!help) { if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0) usage(); memset(&sa, 0, sizeof(sa)); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; memcpy(&sa.l2cap_bdaddr, bdaddr, sizeof(sa.l2cap_bdaddr)); s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP); if (s < 0) err(1, "Could not create socket"); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) err(2, "Could not bind socket, bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL)); e = 0x0ffff; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &e, sizeof(e)) < 0) err(3, "Coult not setsockopt(RCVBUF, %d)", e); e = (c->handler)(s, -- argc, ++ argv); close(s); } else e = USAGE; switch (e) { case OK: case FAILED: break; case ERROR: fprintf(stdout, "Could not execute command \"%s\". %s\n", cmd, strerror(errno)); break; case USAGE: fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); break; default: assert(0); break; } return (e); } /* do_l2cap_command */ /* Try to find command in specified category */ static struct l2cap_command * find_l2cap_command(char const *command, struct l2cap_command *category) { struct l2cap_command *c = NULL; for (c = category; c->command != NULL; c++) { char *c_end = strchr(c->command, ' '); if (c_end != NULL) { int len = c_end - c->command; if (strncasecmp(command, c->command, len) == 0) return (c); } else if (strcasecmp(command, c->command) == 0) return (c); } return (NULL); } /* find_l2cap_command */ /* Print commands in specified category */ static void print_l2cap_command(struct l2cap_command *category) { struct l2cap_command *c = NULL; for (c = category; c->command != NULL; c++) fprintf(stdout, "\t%s\n", c->command); } /* print_l2cap_command */ /* Usage */ static void usage(void) { fprintf(stderr, "Usage: l2control [-hn] -a local cmd [params ..]\n"); fprintf(stderr, "Where:\n"); fprintf(stderr, " -a local Specify local device to connect to\n"); fprintf(stderr, " -h Display this message\n"); fprintf(stderr, " -n Show addresses as numbers\n"); fprintf(stderr, " cmd Supported command " \ "(see l2control help)\n"); fprintf(stderr, " params Optional command parameters\n"); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/l2ping/l2ping.c =================================================================== --- head/usr.sbin/bluetooth/l2ping/l2ping.c (revision 281209) +++ head/usr.sbin/bluetooth/l2ping/l2ping.c (revision 281210) @@ -1,292 +1,293 @@ /* * l2ping.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: l2ping.c,v 1.5 2003/05/16 19:54:40 max Exp $ * $FreeBSD$ */ #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include static void usage (void); static void tv_sub (struct timeval *, struct timeval const *); static double tv2msec (struct timeval const *); #undef min #define min(x, y) (((x) > (y))? (y) : (x)) static char const pattern[] = "1234567890-"; #define PATTERN_SIZE (sizeof(pattern) - 1) /* * Main */ int main(int argc, char *argv[]) { bdaddr_t src, dst; struct hostent *he; uint8_t *echo_data; struct sockaddr_l2cap sa; int32_t n, s, count, wait, flood, echo_size, numeric; char *endp, *rname; /* Set defaults */ memcpy(&src, NG_HCI_BDADDR_ANY, sizeof(src)); memcpy(&dst, NG_HCI_BDADDR_ANY, sizeof(dst)); echo_data = (uint8_t *) calloc(NG_L2CAP_MAX_ECHO_SIZE, sizeof(uint8_t)); if (echo_data == NULL) { fprintf(stderr, "Failed to allocate echo data buffer"); exit(1); } /* * Set default echo size to the NG_L2CAP_MTU_MINIMUM minus * the size of the L2CAP signalling command header. */ echo_size = NG_L2CAP_MTU_MINIMUM - sizeof(ng_l2cap_cmd_hdr_t); count = -1; /* unimited */ wait = 1; /* sec */ flood = 0; numeric = 0; /* Parse command line arguments */ while ((n = getopt(argc, argv, "a:c:fi:nS:s:h")) != -1) { switch (n) { case 'a': if (!bt_aton(optarg, &dst)) { if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&dst, he->h_addr, sizeof(dst)); } break; case 'c': count = strtol(optarg, &endp, 10); if (count <= 0 || *endp != '\0') usage(); break; case 'f': flood = 1; break; case 'i': wait = strtol(optarg, &endp, 10); if (wait <= 0 || *endp != '\0') usage(); break; case 'n': numeric = 1; break; case 'S': if (!bt_aton(optarg, &src)) { if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&src, he->h_addr, sizeof(src)); } break; case 's': echo_size = strtol(optarg, &endp, 10); if (echo_size < sizeof(int32_t) || echo_size > NG_L2CAP_MAX_ECHO_SIZE || *endp != '\0') usage(); break; case 'h': default: usage(); break; } } if (memcmp(&dst, NG_HCI_BDADDR_ANY, sizeof(dst)) == 0) usage(); he = bt_gethostbyaddr((const char *)&dst, sizeof(dst), AF_BLUETOOTH); if (he == NULL || he->h_name == NULL || he->h_name[0] == '\0' || numeric) asprintf(&rname, "%s", bt_ntoa(&dst, NULL)); else rname = strdup(he->h_name); if (rname == NULL) errx(1, "Failed to create remote hostname"); s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP); if (s < 0) err(2, "Could not create socket"); memset(&sa, 0, sizeof(sa)); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; memcpy(&sa.l2cap_bdaddr, &src, sizeof(sa.l2cap_bdaddr)); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) err(3, "Could not bind socket, src bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL)); memset(&sa, 0, sizeof(sa)); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; memcpy(&sa.l2cap_bdaddr, &dst, sizeof(sa.l2cap_bdaddr)); if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) err(4, "Could not connect socket, dst bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL)); /* Fill pattern */ for (n = 0; n < echo_size; ) { int32_t avail = min(echo_size - n, PATTERN_SIZE); memcpy(echo_data + n, pattern, avail); n += avail; } /* Start ping'ing */ for (n = 0; count == -1 || count > 0; n ++) { struct ng_btsocket_l2cap_raw_ping r; struct timeval a, b; int32_t fail; if (gettimeofday(&a, NULL) < 0) err(5, "Could not gettimeofday(a)"); fail = 0; *((int32_t *) echo_data) = htonl(n); r.result = 0; r.echo_size = echo_size; r.echo_data = echo_data; if (ioctl(s, SIOC_L2CAP_L2CA_PING, &r, sizeof(r)) < 0) { r.result = errno; fail = 1; /* warn("Could not ping, dst bdaddr=%s", bt_ntoa(&r.echo_dst, NULL)); */ } if (gettimeofday(&b, NULL) < 0) err(7, "Could not gettimeofday(b)"); tv_sub(&b, &a); fprintf(stdout, "%d bytes from %s seq_no=%d time=%.3f ms result=%#x %s\n", r.echo_size, rname, ntohl(*((int32_t *)(r.echo_data))), tv2msec(&b), r.result, ((fail == 0)? "" : strerror(errno))); if (!flood) { /* Wait */ a.tv_sec = wait; a.tv_usec = 0; select(0, NULL, NULL, NULL, &a); } if (count != -1) count --; } free(rname); free(echo_data); close(s); return (0); } /* main */ /* * a -= b, for timevals */ static void tv_sub(struct timeval *a, struct timeval const *b) { if (a->tv_usec < b->tv_usec) { a->tv_usec += 1000000; a->tv_sec -= 1; } a->tv_usec -= b->tv_usec; a->tv_sec -= b->tv_sec; } /* tv_sub */ /* * convert tv to msec */ static double tv2msec(struct timeval const *tvp) { return(((double)tvp->tv_usec)/1000.0 + ((double)tvp->tv_sec)*1000.0); } /* tv2msec */ /* * Usage */ static void usage(void) { fprintf(stderr, "Usage: l2ping [-fhn] -a remote " \ "[-c count] [-i wait] [-S source] [-s size]\n"); fprintf(stderr, "Where:\n"); fprintf(stderr, " -a remote Specify remote device to ping\n"); fprintf(stderr, " -c count Number of packets to send\n"); fprintf(stderr, " -f No delay between packets\n"); fprintf(stderr, " -h Display this message\n"); fprintf(stderr, " -i wait Delay between packets (sec)\n"); fprintf(stderr, " -n Numeric output only\n"); fprintf(stderr, " -S source Specify source device\n"); fprintf(stderr, " -s size Packet size (bytes), " \ "between %zd and %zd\n", sizeof(int32_t), NG_L2CAP_MAX_ECHO_SIZE); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c =================================================================== --- head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c (revision 281209) +++ head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c (revision 281210) @@ -1,471 +1,471 @@ /* * rfcomm_pppd.c */ /*- * Copyright (c) 2001-2008 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #define RFCOMM_PPPD "rfcomm_pppd" int rfcomm_channel_lookup (bdaddr_t const *local, bdaddr_t const *remote, int service, int *channel, int *error); static void exec_ppp (int s, char *unit, char *label); static void sighandler (int s); static void usage (void); static int done; /* Main */ int main(int argc, char *argv[]) { struct sockaddr_rfcomm sock_addr; char *label = NULL, *unit = NULL, *ep = NULL; bdaddr_t addr; int s, channel, detach, server, service, regdun, regsp; pid_t pid; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); channel = 0; detach = 1; server = 0; service = 0; regdun = 0; regsp = 0; /* Parse command line arguments */ while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) { switch (s) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* client */ server = 0; break; case 'C': /* RFCOMM channel */ channel = strtoul(optarg, &ep, 10); if (*ep != '\0') { channel = 0; switch (tolower(optarg[0])) { case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'l': /* LAN Access Using PPP */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; } } break; case 'd': /* do not detach */ detach = 0; break; case 'D': /* Register DUN service as well as LAN service */ regdun = 1; break; case 'l': /* PPP label */ label = optarg; break; case 's': /* server */ server = 1; break; case 'S': /* Register SP service as well as LAN service */ regsp = 1; break; case 'u': /* PPP -unit option */ strtoul(optarg, &ep, 10); if (*ep != '\0') usage(); /* NOT REACHED */ unit = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we got everything we wanted */ if (label == NULL) errx(1, "Must specify PPP label"); if (!server) { if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) errx(1, "Must specify server BD_ADDR"); /* Check channel, if was not set then obtain it via SDP */ if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, service, &channel, &s) != 0) errc(1, s, "Could not obtain RFCOMM channel"); } if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER); if (detach && daemon(0, 0) < 0) { syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)", strerror(errno), errno); exit(1); } s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) { syslog(LOG_ERR, "Could not create socket. %s (%d)", strerror(errno), errno); exit(1); } if (server) { struct sigaction sa; void *ss = NULL; sdp_lan_profile_t lan; /* Install signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)", strerror(errno), errno); exit(1); } if (sigaction(SIGHUP, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)", strerror(errno), errno); exit(1); } if (sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)", strerror(errno), errno); exit(1); } /* bind socket and listen for incoming connections */ sock_addr.rfcomm_len = sizeof(sock_addr); sock_addr.rfcomm_family = AF_BLUETOOTH; memcpy(&sock_addr.rfcomm_bdaddr, &addr, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = channel; if (bind(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not bind socket. %s (%d)", strerror(errno), errno); exit(1); } if (listen(s, 10) < 0) { syslog(LOG_ERR, "Could not listen on socket. %s (%d)", strerror(errno), errno); exit(1); } ss = sdp_open_local(NULL); if (ss == NULL) { syslog(LOG_ERR, "Unable to create local SDP session"); exit(1); } if (sdp_error(ss) != 0) { syslog(LOG_ERR, "Unable to open local SDP session. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } memset(&lan, 0, sizeof(lan)); lan.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, &addr, (void *) &lan, sizeof(lan), NULL) != 0) { syslog(LOG_ERR, "Unable to register LAN service with " \ "local SDP daemon. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } /* * Register DUN (Dial-Up Networking) service on the same * RFCOMM channel if requested. There is really no good reason * to not to support this. AT-command exchange can be faked * with chat script in ppp.conf */ if (regdun) { sdp_dun_profile_t dun; memset(&dun, 0, sizeof(dun)); dun.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_DIALUP_NETWORKING, &addr, (void *) &dun, sizeof(dun), NULL) != 0) { syslog(LOG_ERR, "Unable to register DUN " \ "service with local SDP daemon. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } } /* * Register SP (Serial Port) service on the same RFCOMM channel * if requested. It appears that some cell phones are using so * called "callback mechanism". In this scenario user is trying * to connect his cell phone to the Internet, and, user's host * computer is acting as the gateway server. It seems that it * is not possible to tell the phone to just connect and start * using the LAN service. Instead the user's host computer must * "jump start" the phone by connecting to the phone's SP * service. What happens next is the phone kills the existing * connection and opens another connection back to the user's * host computer. The phone really wants to use LAN service, * but for whatever reason it looks for SP service on the * user's host computer. This brain damaged behavior was * reported for Nokia 6600 and Sony/Ericsson P900. Both phones * are Symbian-based phones. Perhaps this is a Symbian problem? */ if (regsp) { sdp_sp_profile_t sp; memset(&sp, 0, sizeof(sp)); sp.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_SERIAL_PORT, &addr, (void *) &sp, sizeof(sp), NULL) != 0) { syslog(LOG_ERR, "Unable to register SP " \ "service with local SDP daemon. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } } for (done = 0; !done; ) { socklen_t len = sizeof(sock_addr); int s1 = accept(s, (struct sockaddr *) &sock_addr, &len); if (s1 < 0) { syslog(LOG_ERR, "Could not accept connection " \ "on socket. %s (%d)", strerror(errno), errno); exit(1); } pid = fork(); if (pid == (pid_t) -1) { syslog(LOG_ERR, "Could not fork(). %s (%d)", strerror(errno), errno); exit(1); } if (pid == 0) { sdp_close(ss); close(s); /* Reset signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; sigaction(SIGTERM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); /* Become daemon */ daemon(0, 0); /* * XXX Make sure user does not shoot himself * in the foot. Do not pass unit option to the * PPP when operating in the server mode. */ exec_ppp(s1, NULL, label); } else close(s1); } } else { sock_addr.rfcomm_len = sizeof(sock_addr); sock_addr.rfcomm_family = AF_BLUETOOTH; memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = 0; if (bind(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not bind socket. %s (%d)", strerror(errno), errno); exit(1); } memcpy(&sock_addr.rfcomm_bdaddr, &addr, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not connect socket. %s (%d)", strerror(errno), errno); exit(1); } exec_ppp(s, unit, label); } exit(0); } /* main */ /* * Redirects stdin/stdout to s, stderr to /dev/null and exec * 'ppp -direct -quiet [-unit N] label'. Never returns. */ static void exec_ppp(int s, char *unit, char *label) { char ppp[] = "/usr/sbin/ppp"; char *ppp_args[] = { ppp, "-direct", "-quiet", NULL, NULL, NULL, NULL }; close(0); if (dup(s) < 0) { syslog(LOG_ERR, "Could not dup(0). %s (%d)", strerror(errno), errno); exit(1); } close(1); if (dup(s) < 0) { syslog(LOG_ERR, "Could not dup(1). %s (%d)", strerror(errno), errno); exit(1); } close(2); open("/dev/null", O_RDWR); if (unit != NULL) { ppp_args[3] = "-unit"; ppp_args[4] = unit; ppp_args[5] = label; } else ppp_args[3] = label; if (execv(ppp, ppp_args) < 0) { syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \ "%s (%d)", ppp, (unit != NULL)? " -unit " : "", (unit != NULL)? unit : "", label, strerror(errno), errno); exit(1); } } /* run_ppp */ /* Signal handler */ static void sighandler(int s) { done = 1; } /* sighandler */ /* Display usage and exit */ static void usage(void) { fprintf(stdout, "Usage: %s options\n" \ "Where options are:\n" \ "\t-a address Address to listen on or connect to (required for client)\n" \ "\t-c Act as a clinet (default)\n" \ "\t-C channel RFCOMM channel to listen on or connect to (required)\n" \ "\t-d Run in foreground\n" \ "\t-D Register Dial-Up Networking service (server mode only)\n" \ "\t-l label Use PPP label (required)\n" \ "\t-s Act as a server\n" \ "\t-S Register Serial Port service (server mode only)\n" \ "\t-u N Tell PPP to operate on /dev/tunN (client mode only)\n" \ "\t-h Display this message\n", RFCOMM_PPPD); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c =================================================================== --- head/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c (revision 281210) @@ -1,219 +1,220 @@ /* * sdpcontrol.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sdpcontrol.c,v 1.1 2003/09/08 02:27:27 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include "sdpcontrol.h" /* Prototypes */ static int do_sdp_command (bdaddr_p, char const *, int, int, char **); static struct sdp_command * find_sdp_command (char const *, struct sdp_command *); static void print_sdp_command (struct sdp_command *); static void usage (void); /* Main */ int main(int argc, char *argv[]) { char const *control = SDP_LOCAL_PATH; int n, local; bdaddr_t bdaddr; memset(&bdaddr, 0, sizeof(bdaddr)); local = 0; /* Process command line arguments */ while ((n = getopt(argc, argv, "a:c:lh")) != -1) { switch (n) { case 'a': /* bdaddr */ if (!bt_aton(optarg, &bdaddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&bdaddr, he->h_addr, sizeof(bdaddr)); } break; case 'c': /* control socket */ control = optarg; break; case 'l': /* local sdpd */ local = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; if (*argv == NULL) usage(); return (do_sdp_command(&bdaddr, control, local, argc, argv)); } /* Execute commands */ static int do_sdp_command(bdaddr_p bdaddr, char const *control, int local, int argc, char **argv) { char *cmd = argv[0]; struct sdp_command *c = NULL; void *xs = NULL; int e, help; help = 0; if (strcasecmp(cmd, "help") == 0) { argc --; argv ++; if (argc <= 0) { fprintf(stdout, "Supported commands:\n"); print_sdp_command(sdp_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); return (OK); } help = 1; cmd = argv[0]; } c = find_sdp_command(cmd, sdp_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); return (ERROR); } if (!help) { if (!local) { if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0) usage(); xs = sdp_open(NG_HCI_BDADDR_ANY, bdaddr); } else xs = sdp_open_local(control); if (xs == NULL) errx(1, "Could not create SDP session object"); if (sdp_error(xs) == 0) e = (c->handler)(xs, -- argc, ++ argv); else e = ERROR; } else e = USAGE; switch (e) { case OK: case FAILED: break; case ERROR: fprintf(stdout, "Could not execute command \"%s\". %s\n", cmd, strerror(sdp_error(xs))); break; case USAGE: fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); break; default: assert(0); break; } sdp_close(xs); return (e); } /* do_sdp_command */ /* Try to find command in specified category */ static struct sdp_command * find_sdp_command(char const *command, struct sdp_command *category) { struct sdp_command *c = NULL; for (c = category; c->command != NULL; c++) { char *c_end = strchr(c->command, ' '); if (c_end != NULL) { int len = c_end - c->command; if (strncasecmp(command, c->command, len) == 0) return (c); } else if (strcasecmp(command, c->command) == 0) return (c); } return (NULL); } /* find_sdp_command */ /* Print commands in specified category */ static void print_sdp_command(struct sdp_command *category) { struct sdp_command *c = NULL; for (c = category; c->command != NULL; c++) fprintf(stdout, "\t%s\n", c->command); } /* print_sdp_command */ /* Usage */ static void usage(void) { fprintf(stderr, "Usage: sdpcontrol options command\n" \ "Where options are:\n" " -a address address to connect to\n" \ " -c path path to the control socket (default is %s)\n" \ " -h display usage and quit\n" \ " -l connect to the local SDP server via control socket\n" \ " command one of the supported commands\n", SDP_LOCAL_PATH); exit(255); } /* usage */ Index: head/usr.sbin/bluetooth/sdpcontrol/search.c =================================================================== --- head/usr.sbin/bluetooth/sdpcontrol/search.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpcontrol/search.c (revision 281210) @@ -1,713 +1,714 @@ /* * search.c * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: search.c,v 1.2 2003/09/08 17:35:15 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include "sdpcontrol.h" /* List of the attributes we are looking for */ static uint32_t attrs[] = { SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE, SDP_ATTR_SERVICE_RECORD_HANDLE), SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST, SDP_ATTR_SERVICE_CLASS_ID_LIST), SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST) }; #define attrs_len (sizeof(attrs)/sizeof(attrs[0])) /* Buffer for the attributes */ #define NRECS 25 /* request this much records from the SDP server */ #define BSIZE 256 /* one attribute buffer size */ static uint8_t buffer[NRECS * attrs_len][BSIZE]; /* SDP attributes */ static sdp_attr_t values[NRECS * attrs_len]; #define values_len (sizeof(values)/sizeof(values[0])) /* * Print Service Class ID List * * The ServiceClassIDList attribute consists of a data element sequence in * which each data element is a UUID representing the service classes that * a given service record conforms to. The UUIDs are listed in order from * the most specific class to the most general class. The ServiceClassIDList * must contain at least one service class UUID. */ static void print_service_class_id_list(uint8_t const *start, uint8_t const *end) { uint32_t type, len, value; if (end - start < 2) { fprintf(stderr, "Invalid Service Class ID List. " \ "Too short, len=%zd\n", end - start); return; } SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: fprintf(stderr, "Invalid Service Class ID List. " \ "Not a sequence, type=%#x\n", type); return; /* NOT REACHED */ } while (start < end) { SDP_GET8(type, start); switch (type) { case SDP_DATA_UUID16: SDP_GET16(value, start); fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value), value); break; case SDP_DATA_UUID32: SDP_GET32(value, start); fprintf(stdout, "\t%#8.8x\n", value); break; case SDP_DATA_UUID128: { int128_t uuid; SDP_GET_UUID128(&uuid, start); fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", ntohl(*(uint32_t *)&uuid.b[0]), ntohs(*(uint16_t *)&uuid.b[4]), ntohs(*(uint16_t *)&uuid.b[6]), ntohs(*(uint16_t *)&uuid.b[8]), ntohs(*(uint16_t *)&uuid.b[10]), ntohl(*(uint32_t *)&uuid.b[12])); } break; default: fprintf(stderr, "Invalid Service Class ID List. " \ "Not a UUID, type=%#x\n", type); return; /* NOT REACHED */ } } } /* print_service_class_id_list */ /* * Print Protocol Descriptor List * * If the ProtocolDescriptorList describes a single stack, it takes the form * of a data element sequence in which each element of the sequence is a * protocol descriptor. Each protocol descriptor is, in turn, a data element * sequence whose first element is a UUID identifying the protocol and whose * successive elements are protocol-specific parameters. The protocol * descriptors are listed in order from the lowest layer protocol to the * highest layer protocol used to gain access to the service. If it is possible * for more than one kind of protocol stack to be used to gain access to the * service, the ProtocolDescriptorList takes the form of a data element * alternative where each member is a data element sequence as described above. */ static void print_protocol_descriptor(uint8_t const *start, uint8_t const *end) { union { uint8_t uint8; uint16_t uint16; uint32_t uint32; uint64_t uint64; int128_t int128; } value; uint32_t type, len, param; /* Get Protocol UUID */ SDP_GET8(type, start); switch (type) { case SDP_DATA_UUID16: SDP_GET16(value.uint16, start); fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16), value.uint16); break; case SDP_DATA_UUID32: SDP_GET32(value.uint32, start); fprintf(stdout, "\t%#8.8x\n", value.uint32); break; case SDP_DATA_UUID128: SDP_GET_UUID128(&value.int128, start); fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", ntohl(*(uint32_t *)&value.int128.b[0]), ntohs(*(uint16_t *)&value.int128.b[4]), ntohs(*(uint16_t *)&value.int128.b[6]), ntohs(*(uint16_t *)&value.int128.b[8]), ntohs(*(uint16_t *)&value.int128.b[10]), ntohl(*(uint32_t *)&value.int128.b[12])); break; default: fprintf(stderr, "Invalid Protocol Descriptor. " \ "Not a UUID, type=%#x\n", type); return; /* NOT REACHED */ } /* Protocol specific parameters */ for (param = 1; start < end; param ++) { fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param); SDP_GET8(type, start); switch (type) { case SDP_DATA_NIL: fprintf(stdout, "nil\n"); break; case SDP_DATA_UINT8: case SDP_DATA_INT8: case SDP_DATA_BOOL: SDP_GET8(value.uint8, start); fprintf(stdout, "u/int8/bool %u\n", value.uint8); break; case SDP_DATA_UINT16: case SDP_DATA_INT16: case SDP_DATA_UUID16: SDP_GET16(value.uint16, start); fprintf(stdout, "u/int/uuid16 %u\n", value.uint16); break; case SDP_DATA_UINT32: case SDP_DATA_INT32: case SDP_DATA_UUID32: SDP_GET32(value.uint32, start); fprintf(stdout, "u/int/uuid32 %u\n", value.uint32); break; case SDP_DATA_UINT64: case SDP_DATA_INT64: SDP_GET64(value.uint64, start); fprintf(stdout, "u/int64 %ju\n", value.uint64); break; case SDP_DATA_UINT128: case SDP_DATA_INT128: SDP_GET128(&value.int128, start); fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n", *(uint32_t *)&value.int128.b[0], *(uint32_t *)&value.int128.b[4], *(uint32_t *)&value.int128.b[8], *(uint32_t *)&value.int128.b[12]); break; case SDP_DATA_UUID128: SDP_GET_UUID128(&value.int128, start); fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", ntohl(*(uint32_t *)&value.int128.b[0]), ntohs(*(uint16_t *)&value.int128.b[4]), ntohs(*(uint16_t *)&value.int128.b[6]), ntohs(*(uint16_t *)&value.int128.b[8]), ntohs(*(uint16_t *)&value.int128.b[10]), ntohl(*(uint32_t *)&value.int128.b[12])); break; case SDP_DATA_STR8: case SDP_DATA_URL8: SDP_GET8(len, start); fprintf(stdout, "%*.*s\n", len, len, (char *) start); start += len; break; case SDP_DATA_STR16: case SDP_DATA_URL16: SDP_GET16(len, start); fprintf(stdout, "%*.*s\n", len, len, (char *) start); start += len; break; case SDP_DATA_STR32: case SDP_DATA_URL32: SDP_GET32(len, start); fprintf(stdout, "%*.*s\n", len, len, (char *) start); start += len; break; case SDP_DATA_SEQ8: case SDP_DATA_ALT8: SDP_GET8(len, start); for (; len > 0; start ++, len --) fprintf(stdout, "%#2.2x ", *start); fprintf(stdout, "\n"); break; case SDP_DATA_SEQ16: case SDP_DATA_ALT16: SDP_GET16(len, start); for (; len > 0; start ++, len --) fprintf(stdout, "%#2.2x ", *start); fprintf(stdout, "\n"); break; case SDP_DATA_SEQ32: case SDP_DATA_ALT32: SDP_GET32(len, start); for (; len > 0; start ++, len --) fprintf(stdout, "%#2.2x ", *start); fprintf(stdout, "\n"); break; default: fprintf(stderr, "Invalid Protocol Descriptor. " \ "Unknown data type: %#02x\n", type); return; /* NOT REACHED */ } } } /* print_protocol_descriptor */ static void print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end) { uint32_t type, len; if (end - start < 2) { fprintf(stderr, "Invalid Protocol Descriptor List. " \ "Too short, len=%zd\n", end - start); return; } SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: fprintf(stderr, "Invalid Protocol Descriptor List. " \ "Not a sequence, type=%#x\n", type); return; /* NOT REACHED */ } while (start < end) { SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: fprintf(stderr, "Invalid Protocol Descriptor List. " \ "Not a sequence, type=%#x\n", type); return; /* NOT REACHED */ } print_protocol_descriptor(start, start + len); start += len; } } /* print_protocol_descriptor_list */ /* * Print Bluetooth Profile Descriptor List * * The BluetoothProfileDescriptorList attribute consists of a data element * sequence in which each element is a profile descriptor that contains * information about a Bluetooth profile to which the service represented by * this service record conforms. Each profile descriptor is a data element * sequence whose first element is the UUID assigned to the profile and whose * second element is a 16-bit profile version number. Each version of a profile * is assigned a 16-bit unsigned integer profile version number, which consists * of two 8-bit fields. The higher-order 8 bits contain the major version * number field and the lower-order 8 bits contain the minor version number * field. */ static void print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end) { uint32_t type, len, value; if (end - start < 2) { fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \ "Too short, len=%zd\n", end - start); return; } SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \ "Not a sequence, type=%#x\n", type); return; /* NOT REACHED */ } while (start < end) { SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, start); break; case SDP_DATA_SEQ16: SDP_GET16(len, start); break; case SDP_DATA_SEQ32: SDP_GET32(len, start); break; default: fprintf(stderr, "Invalid Bluetooth Profile " \ "Descriptor List. " \ "Not a sequence, type=%#x\n", type); return; /* NOT REACHED */ } /* Get UUID */ SDP_GET8(type, start); switch (type) { case SDP_DATA_UUID16: SDP_GET16(value, start); fprintf(stdout, "\t%s (%#4.4x) ", sdp_uuid2desc(value), value); break; case SDP_DATA_UUID32: SDP_GET32(value, start); fprintf(stdout, "\t%#8.8x ", value); break; case SDP_DATA_UUID128: { int128_t uuid; SDP_GET_UUID128(&uuid, start); fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ", ntohl(*(uint32_t *)&uuid.b[0]), ntohs(*(uint16_t *)&uuid.b[4]), ntohs(*(uint16_t *)&uuid.b[6]), ntohs(*(uint16_t *)&uuid.b[8]), ntohs(*(uint16_t *)&uuid.b[10]), ntohl(*(uint32_t *)&uuid.b[12])); } break; default: fprintf(stderr, "Invalid Bluetooth Profile " \ "Descriptor List. " \ "Not a UUID, type=%#x\n", type); return; /* NOT REACHED */ } /* Get version */ SDP_GET8(type, start); if (type != SDP_DATA_UINT16) { fprintf(stderr, "Invalid Bluetooth Profile " \ "Descriptor List. " \ "Invalid version type=%#x\n", type); return; } SDP_GET16(value, start); fprintf(stdout, "ver. %d.%d\n", (value >> 8) & 0xff, value & 0xff); } } /* print_bluetooth_profile_descriptor_list */ /* Perform SDP search command */ static int do_sdp_search(void *xs, int argc, char **argv) { char *ep = NULL; int32_t n, type, value; uint16_t service; /* Parse command line arguments */ switch (argc) { case 1: n = strtoul(argv[0], &ep, 16); if (*ep != 0) { switch (tolower(argv[0][0])) { case 'c': /* CIP/CTP */ switch (tolower(argv[0][1])) { case 'i': service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS; break; case 't': service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY; break; default: return (USAGE); /* NOT REACHED */ } break; case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'f': /* Fax/OBEX File Transfer */ switch (tolower(argv[0][1])) { case 'a': service = SDP_SERVICE_CLASS_FAX; break; case 't': service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER; break; default: return (USAGE); /* NOT REACHED */ } break; case 'g': /* GN */ service = SDP_SERVICE_CLASS_GN; break; case 'h': /* Headset/HID */ switch (tolower(argv[0][1])) { case 'i': service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; break; case 's': service = SDP_SERVICE_CLASS_HEADSET; break; default: return (USAGE); /* NOT REACHED */ } break; case 'l': /* LAN Access Using PPP */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; case 'n': /* NAP */ service = SDP_SERVICE_CLASS_NAP; break; case 'o': /* OBEX Object Push */ service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH; break; case 's': /* Serial Port */ service = SDP_SERVICE_CLASS_SERIAL_PORT; break; default: return (USAGE); /* NOT REACHED */ } } else service = (uint16_t) n; break; default: return (USAGE); } /* Initialize attribute values array */ for (n = 0; n < values_len; n ++) { values[n].flags = SDP_ATTR_INVALID; values[n].attr = 0; values[n].vlen = BSIZE; values[n].value = buffer[n]; } /* Do SDP Service Search Attribute Request */ n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values); if (n != 0) return (ERROR); /* Print attributes values */ for (n = 0; n < values_len; n ++) { if (values[n].flags != SDP_ATTR_OK) break; switch (values[n].attr) { case SDP_ATTR_SERVICE_RECORD_HANDLE: fprintf(stdout, "\n"); if (values[n].vlen == 5) { SDP_GET8(type, values[n].value); if (type == SDP_DATA_UINT32) { SDP_GET32(value, values[n].value); fprintf(stdout, "Record Handle: " \ "%#8.8x\n", value); } else fprintf(stderr, "Invalid type=%#x " \ "Record Handle " \ "attribute!\n", type); } else fprintf(stderr, "Invalid size=%d for Record " \ "Handle attribute\n", values[n].vlen); break; case SDP_ATTR_SERVICE_CLASS_ID_LIST: fprintf(stdout, "Service Class ID List:\n"); print_service_class_id_list(values[n].value, values[n].value + values[n].vlen); break; case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: fprintf(stdout, "Protocol Descriptor List:\n"); print_protocol_descriptor_list(values[n].value, values[n].value + values[n].vlen); break; case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST: fprintf(stdout, "Bluetooth Profile Descriptor List:\n"); print_bluetooth_profile_descriptor_list(values[n].value, values[n].value + values[n].vlen); break; default: fprintf(stderr, "Unexpected attribute ID=%#4.4x\n", values[n].attr); break; } } return (OK); } /* do_sdp_search */ /* Perform SDP browse command */ static int do_sdp_browse(void *xs, int argc, char **argv) { #undef _STR #undef STR #define _STR(x) #x #define STR(x) _STR(x) static char const * const av[] = { STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP), NULL }; switch (argc) { case 0: argc = 1; argv = (char **) av; /* FALL THROUGH */ case 1: return (do_sdp_search(xs, argc, argv)); } return (USAGE); } /* do_sdp_browse */ /* List of SDP commands */ struct sdp_command sdp_commands[] = { { "Browse []", "Browse for services. The parameter is a 16-bit UUID of the group\n" \ "to browse. If omitted is set to Public Browse Group.\n\n" \ "\t - xxxx; 16-bit UUID of the group to browse\n", do_sdp_browse }, { "Search ", "Search for the . The parameter is a 16-bit UUID of the\n" \ "service to search for. For some services it is possible to use service name\n"\ "instead of service UUID\n\n" \ "\t - xxxx; 16-bit UUID of the service to search for\n\n" \ "\tKnown service names\n" \ "\t===================\n" \ "\tCIP - Common ISDN Access\n" \ "\tCTP - Cordless Telephony\n" \ "\tDUN - DialUp Networking\n" \ "\tFAX - Fax\n" \ "\tFTRN - OBEX File Transfer\n" \ "\tGN - GN\n" \ "\tHID - Human Interface Device\n" \ "\tHSET - Headset\n" \ "\tLAN - LAN Access Using PPP\n" \ "\tNAP - Network Access Point\n" \ "\tOPUSH - OBEX Object Push\n" \ "\tSP - Serial Port\n", do_sdp_search }, { NULL, NULL, NULL } }; Index: head/usr.sbin/bluetooth/sdpd/bgd.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/bgd.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/bgd.c (revision 281210) @@ -1,102 +1,102 @@ /* * bgd.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: bgd.c,v 1.4 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" static int32_t bgd_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t bgd_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Public Browse Group Root"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t bgd_profile_create_group_id( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 3 > eob) return (-1); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP, buf); return (3); } static attr_t bgd_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, bgd_profile_create_service_class_id_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, bgd_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET, bgd_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_PROVIDER_NAME_OFFSET, common_profile_create_service_provider_name }, { SDP_ATTR_GROUP_ID, bgd_profile_create_group_id }, { 0, NULL } /* end entry */ }; profile_t bgd_profile_descriptor = { SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR, 0, (profile_data_valid_p) NULL, (attr_t const * const) &bgd_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/dun.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/dun.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/dun.c (revision 281210) @@ -1,136 +1,137 @@ /* * dun.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: dun.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t dun_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_DIALUP_NETWORKING }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t dun_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_DIALUP_NETWORKING, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t dun_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "DialUp networking"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t dun_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_dun_profile_p dun = (sdp_dun_profile_p) provider->data; return (rfcomm_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &dun->server_channel, 1)); } static int32_t dun_profile_create_audio_feedback_support( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_dun_profile_p dun = (sdp_dun_profile_p) provider->data; if (buf + 2 > eob) return (-1); SDP_PUT8(SDP_DATA_BOOL, buf); SDP_PUT8(dun->audio_feedback_support, buf); return (2); } static attr_t dun_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, dun_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, dun_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, dun_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, dun_profile_create_protocol_descriptor_list }, { SDP_ATTR_AUDIO_FEEDBACK_SUPPORT, dun_profile_create_audio_feedback_support }, { 0, NULL } /* end entry */ }; profile_t dun_profile_descriptor = { SDP_SERVICE_CLASS_DIALUP_NETWORKING, sizeof(sdp_dun_profile_t), common_profile_server_channel_valid, (attr_t const * const) &dun_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/ftrn.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/ftrn.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/ftrn.c (revision 281210) @@ -1,117 +1,118 @@ /* * ftrn.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ftrn.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t ftrn_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t ftrn_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t ftrn_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "OBEX File Transfer"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t ftrn_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_ftrn_profile_p ftrn = (sdp_ftrn_profile_p) provider->data; return (obex_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &ftrn->server_channel, 1)); } static attr_t ftrn_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, ftrn_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, ftrn_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, ftrn_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, ftrn_profile_create_protocol_descriptor_list }, { 0, NULL } /* end entry */ }; profile_t ftrn_profile_descriptor = { SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER, sizeof(sdp_ftrn_profile_t), common_profile_server_channel_valid, (attr_t const * const) &ftrn_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/gn.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/gn.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/gn.c (revision 281210) @@ -1,172 +1,173 @@ /* * gn.c */ /*- * Copyright (c) 2008 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: gn.c,v 1.1 2008/03/11 00:02:42 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t gn_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_GN }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t gn_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_GN, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t gn_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Group Ad-hoc Network"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t gn_profile_create_service_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_descr[] = "Personal Group Ad-hoc Network Service"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_descr, strlen(service_descr))); } static int32_t gn_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data; return (bnep_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &gn->psm, sizeof(gn->psm))); } static int32_t gn_profile_create_security_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data; return (bnep_profile_create_security_description(buf, eob, (uint8_t const *) &gn->security_description, sizeof(gn->security_description))); } static int32_t gn_profile_create_service_availability( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data; return (common_profile_create_service_availability(buf, eob, &gn->load_factor, 1)); } static int32_t gn_profile_data_valid(uint8_t const *data, uint32_t datalen) { sdp_gn_profile_p gn = (sdp_gn_profile_p) data; return ((gn->psm == 0)? 0 : 1); } static attr_t gn_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, gn_profile_create_service_class_id_list }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, gn_profile_create_protocol_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_SERVICE_AVAILABILITY, gn_profile_create_service_availability }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, gn_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, gn_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET, gn_profile_create_service_description }, { SDP_ATTR_SECURITY_DESCRIPTION, gn_profile_create_security_description }, { 0, NULL } /* end entry */ }; profile_t gn_profile_descriptor = { SDP_SERVICE_CLASS_GN, sizeof(sdp_gn_profile_t), gn_profile_data_valid, (attr_t const * const) &gn_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/irmc.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/irmc.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/irmc.c (revision 281210) @@ -1,133 +1,134 @@ /* * irmc.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: irmc.c,v 1.6 2004/01/13 19:31:54 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t irmc_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_IR_MC_SYNC }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t irmc_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_IR_MC_SYNC, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t irmc_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "IrMC Synchronization"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t irmc_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_irmc_profile_p irmc = (sdp_irmc_profile_p) provider->data; return (obex_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &irmc->server_channel, 1)); } static int32_t irmc_profile_create_supported_formats_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_irmc_profile_p irmc = (sdp_irmc_profile_p) provider->data; return (obex_profile_create_supported_formats_list( buf, eob, (uint8_t const *) irmc->supported_formats, irmc->supported_formats_size)); } static attr_t irmc_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, irmc_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, irmc_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, irmc_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, irmc_profile_create_protocol_descriptor_list }, { SDP_ATTR_SUPPORTED_FORMATS_LIST, irmc_profile_create_supported_formats_list }, { 0, NULL } /* end entry */ }; profile_t irmc_profile_descriptor = { SDP_SERVICE_CLASS_IR_MC_SYNC, sizeof(sdp_irmc_profile_t), obex_profile_data_valid, (attr_t const * const) &irmc_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/irmc_command.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/irmc_command.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/irmc_command.c (revision 281210) @@ -1,117 +1,118 @@ /* * irmc_command_command_command.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: irmc_command.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t irmc_command_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t irmc_command_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t irmc_command_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Sync Command Service"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t irmc_command_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_irmc_command_profile_p irmc_command = (sdp_irmc_command_profile_p) provider->data; return (obex_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &irmc_command->server_channel, 1)); } static attr_t irmc_command_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, irmc_command_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, irmc_command_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, irmc_command_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, irmc_command_profile_create_protocol_descriptor_list }, { 0, NULL } /* end entry */ }; profile_t irmc_command_profile_descriptor = { SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND, sizeof(sdp_irmc_command_profile_t), common_profile_server_channel_valid, (attr_t const * const) &irmc_command_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/lan.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/lan.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/lan.c (revision 281210) @@ -1,172 +1,173 @@ /* * lan.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: lan.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "profile.h" #include "provider.h" static int32_t lan_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t lan_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t lan_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "LAN Access using PPP"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t lan_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data; return (rfcomm_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &lan->server_channel, 1)); } static int32_t lan_profile_create_service_availability( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data; return (common_profile_create_service_availability(buf, eob, &lan->load_factor, 1)); } static int32_t lan_profile_create_ip_subnet( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data; char net[32]; int32_t len; len = snprintf(net, sizeof(net), "%s/%d", inet_ntoa(* (struct in_addr *) &lan->ip_subnet), lan->ip_subnet_radius); if (len < 0 || buf + 2 + len > eob) return (-1); SDP_PUT8(SDP_DATA_STR8, buf); SDP_PUT8(len, buf); memcpy(buf, net, len); return (2 + len); } static int32_t lan_profile_data_valid(uint8_t const *data, uint32_t datalen) { sdp_lan_profile_p lan = (sdp_lan_profile_p) data; if (lan->server_channel < 1 || lan->server_channel > 30 || lan->ip_subnet_radius > 32) return (0); return (1); } static attr_t lan_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, lan_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, lan_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, lan_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, lan_profile_create_protocol_descriptor_list }, { SDP_ATTR_SERVICE_AVAILABILITY, lan_profile_create_service_availability }, { SDP_ATTR_IP_SUBNET, lan_profile_create_ip_subnet }, { 0, NULL } /* end entry */ }; profile_t lan_profile_descriptor = { SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, sizeof(sdp_lan_profile_t), lan_profile_data_valid, (attr_t const * const) &lan_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/main.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/main.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/main.c (revision 281210) @@ -1,235 +1,236 @@ /* * main.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: main.c,v 1.8 2004/01/13 19:31:54 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "server.h" #include #include #include #include "profile.h" #include "provider.h" #define SDPD "sdpd" static int32_t drop_root (char const *user, char const *group); static void sighandler (int32_t s); static void usage (void); static int32_t done; /* * Bluetooth Service Discovery Procotol (SDP) daemon */ int main(int argc, char *argv[]) { server_t server; char const *control = SDP_LOCAL_PATH; char const *user = "nobody", *group = "nobody"; int32_t detach = 1, opt; struct sigaction sa; while ((opt = getopt(argc, argv, "c:dg:hu:")) != -1) { switch (opt) { case 'c': /* control */ control = optarg; break; case 'd': /* do not detach */ detach = 0; break; case 'g': /* group */ group = optarg; break; case 'u': /* user */ user = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } log_open(SDPD, !detach); /* Become daemon if required */ if (detach && daemon(0, 0) < 0) { log_crit("Could not become daemon. %s (%d)", strerror(errno), errno); exit(1); } /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGINT, &sa, NULL) < 0) { log_crit("Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) { log_crit("Could not install signal handlers. %s (%d)", strerror(errno), errno); exit(1); } /* Initialize server */ if (server_init(&server, control) < 0) exit(1); if ((user != NULL || group != NULL) && drop_root(user, group) < 0) exit(1); for (done = 0; !done; ) { if (server_do(&server) != 0) done ++; } server_shutdown(&server); log_close(); return (0); } /* * Drop root */ static int32_t drop_root(char const *user, char const *group) { int uid, gid; char *ep; if ((uid = getuid()) != 0) { log_notice("Cannot set uid/gid. Not a superuser"); return (0); /* dont do anything unless root */ } gid = getgid(); if (user != NULL) { uid = strtol(user, &ep, 10); if (*ep != '\0') { struct passwd *pwd = getpwnam(user); if (pwd == NULL) { log_err("Could not find passwd entry for " \ "user %s", user); return (-1); } uid = pwd->pw_uid; } } if (group != NULL) { gid = strtol(group, &ep, 10); if (*ep != '\0') { struct group *grp = getgrnam(group); if (grp == NULL) { log_err("Could not find group entry for " \ "group %s", group); return (-1); } gid = grp->gr_gid; } } if (setgid(gid) < 0) { log_err("Could not setgid(%s). %s (%d)", group, strerror(errno), errno); return (-1); } if (setuid(uid) < 0) { log_err("Could not setuid(%s). %s (%d)", user, strerror(errno), errno); return (-1); } return (0); } /* * Signal handler */ static void sighandler(int32_t s) { log_notice("Got signal %d. Total number of signals received %d", s, ++ done); } /* * Display usage information and quit */ static void usage(void) { fprintf(stderr, "Usage: %s [options]\n" \ "Where options are:\n" \ " -c specify control socket name (default %s)\n" \ " -d do not detach (run in foreground)\n" \ " -g grp specify group\n" \ " -h display usage and exit\n" \ " -u usr specify user\n", SDPD, SDP_LOCAL_PATH); exit(255); } Index: head/usr.sbin/bluetooth/sdpd/nap.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/nap.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/nap.c (revision 281210) @@ -1,209 +1,210 @@ /* * nap.c */ /*- * Copyright (c) 2008 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: nap.c,v 1.1 2008/03/11 00:02:42 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t nap_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_NAP }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t nap_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_NAP, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t nap_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Network Access Point"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t nap_profile_create_service_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_descr[] = "Personal Ad-hoc Network Service"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_descr, strlen(service_descr))); } static int32_t nap_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data; return (bnep_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &nap->psm, sizeof(nap->psm))); } static int32_t nap_profile_create_security_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data; return (bnep_profile_create_security_description(buf, eob, (uint8_t const *) &nap->security_description, sizeof(nap->security_description))); } static int32_t nap_profile_create_net_access_type( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data; if (buf + 3 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(nap->net_access_type, buf); return (3); } static int32_t nap_profile_create_max_net_access_rate( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data; if (buf + 3 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(nap->max_net_access_rate, buf); return (3); } static int32_t nap_profile_create_service_availability( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data; return (common_profile_create_service_availability(buf, eob, &nap->load_factor, 1)); } static int32_t nap_profile_data_valid(uint8_t const *data, uint32_t datalen) { sdp_nap_profile_p nap = (sdp_nap_profile_p) data; return ((nap->psm == 0)? 0 : 1); } static attr_t nap_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, nap_profile_create_service_class_id_list }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, nap_profile_create_protocol_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_SERVICE_AVAILABILITY, nap_profile_create_service_availability }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, nap_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, nap_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET, nap_profile_create_service_description }, { SDP_ATTR_SECURITY_DESCRIPTION, nap_profile_create_security_description }, { SDP_ATTR_NET_ACCESS_TYPE, nap_profile_create_net_access_type }, { SDP_ATTR_MAX_NET_ACCESS_RATE, nap_profile_create_max_net_access_rate }, { 0, NULL } /* end entry */ }; profile_t nap_profile_descriptor = { SDP_SERVICE_CLASS_NAP, sizeof(sdp_nap_profile_t), nap_profile_data_valid, (attr_t const * const) &nap_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/opush.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/opush.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/opush.c (revision 281210) @@ -1,133 +1,134 @@ /* * opush.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: opush.c,v 1.6 2004/01/13 19:31:54 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t opush_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t opush_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t opush_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "OBEX Object Push"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t opush_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_opush_profile_p opush = (sdp_opush_profile_p) provider->data; return (obex_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &opush->server_channel, 1)); } static int32_t opush_profile_create_supported_formats_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_opush_profile_p opush = (sdp_opush_profile_p) provider->data; return (obex_profile_create_supported_formats_list( buf, eob, (uint8_t const *) opush->supported_formats, opush->supported_formats_size)); } static attr_t opush_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, opush_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, opush_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, opush_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, opush_profile_create_protocol_descriptor_list }, { SDP_ATTR_SUPPORTED_FORMATS_LIST, opush_profile_create_supported_formats_list }, { 0, NULL } /* end entry */ }; profile_t opush_profile_descriptor = { SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH, sizeof(sdp_opush_profile_t), obex_profile_data_valid, (attr_t const * const) &opush_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/panu.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/panu.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/panu.c (revision 281210) @@ -1,172 +1,173 @@ /* * panu.c */ /*- * Copyright (c) 2008 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: panu.c,v 1.1 2008/03/11 00:02:42 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t panu_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_PANU }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t panu_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_PANU, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t panu_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Personal Ad-hoc User Service"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t panu_profile_create_service_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_descr[] = "Personal Ad-hoc User Service"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_descr, strlen(service_descr))); } static int32_t panu_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data; return (bnep_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &panu->psm, sizeof(panu->psm))); } static int32_t panu_profile_data_valid(uint8_t const *data, uint32_t datalen) { sdp_panu_profile_p panu = (sdp_panu_profile_p) data; return ((panu->psm == 0)? 0 : 1); } static int32_t panu_profile_create_service_availability( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data; return (common_profile_create_service_availability( buf, eob, &panu->load_factor, 1)); } static int32_t panu_profile_create_security_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data; return (bnep_profile_create_security_description(buf, eob, (uint8_t const *) &panu->security_description, sizeof(panu->security_description))); } static attr_t panu_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, panu_profile_create_service_class_id_list }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, panu_profile_create_protocol_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_SERVICE_AVAILABILITY, panu_profile_create_service_availability }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, panu_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, panu_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET, panu_profile_create_service_description }, { SDP_ATTR_SECURITY_DESCRIPTION, panu_profile_create_security_description }, { 0, NULL } /* end entry */ }; profile_t panu_profile_descriptor = { SDP_SERVICE_CLASS_PANU, sizeof(sdp_panu_profile_t), panu_profile_data_valid, (attr_t const * const) &panu_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/profile.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/profile.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/profile.c (revision 281210) @@ -1,497 +1,498 @@ /* * profile.c */ /*- * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: profile.c,v 1.6 2004/01/13 19:31:54 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" /* * Lookup profile descriptor */ profile_p profile_get_descriptor(uint16_t uuid) { extern profile_t dun_profile_descriptor; extern profile_t ftrn_profile_descriptor; extern profile_t irmc_profile_descriptor; extern profile_t irmc_command_profile_descriptor; extern profile_t lan_profile_descriptor; extern profile_t opush_profile_descriptor; extern profile_t sp_profile_descriptor; extern profile_t nap_profile_descriptor; extern profile_t gn_profile_descriptor; extern profile_t panu_profile_descriptor; static const profile_p profiles[] = { &dun_profile_descriptor, &ftrn_profile_descriptor, &irmc_profile_descriptor, &irmc_command_profile_descriptor, &lan_profile_descriptor, &opush_profile_descriptor, &sp_profile_descriptor, &nap_profile_descriptor, &gn_profile_descriptor, &panu_profile_descriptor }; int32_t i; for (i = 0; i < sizeof(profiles)/sizeof(profiles[0]); i++) if (profiles[i]->uuid == uuid) return (profiles[i]); return (NULL); } /* * Look attribute in the profile descripror */ profile_attr_create_p profile_get_attr(const profile_p profile, uint16_t attr) { attr_p ad = (attr_p) profile->attrs; for (; ad->create != NULL; ad ++) if (ad->attr == attr) return (ad->create); return (NULL); } /* * uint32 value32 - 5 bytes */ int32_t common_profile_create_service_record_handle( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 5 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT32, buf); SDP_PUT32(((provider_p) data)->handle, buf); return (5); } /* * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes * [ uuid16 value ] */ int32_t common_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { int32_t len = 3 * (datalen >>= 1); if (len <= 0 || len > 0xff || buf + 2 + len > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(len, buf); for (; datalen > 0; datalen --) { SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(*((uint16_t const *)data), buf); data += sizeof(uint16_t); } return (2 + len); } /* * seq8 len8 - 2 bytes * seq 8 len8 - 2 bytes * uuid16 value16 - 3 bytes * uint16 value16 - 3 bytes * [ seq 8 len8 * uuid16 value16 * uint16 value16 ] */ int32_t common_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { int32_t len = 8 * (datalen >>= 2); if (len <= 0 || len > 0xff || buf + 2 + len > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(len, buf); for (; datalen > 0; datalen --) { SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(6, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(*((uint16_t const *)data), buf); data += sizeof(uint16_t); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(*((uint16_t const *)data), buf); data += sizeof(uint16_t); } return (2 + len); } /* * seq8 len8 - 2 bytes * uint16 value16 - 3 bytes * uint16 value16 - 3 bytes * uint16 value16 - 3 bytes */ int32_t common_profile_create_language_base_attribute_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 11 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(9, buf); /* * Language code per ISO 639:1988. Use "en". */ SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(((0x65 << 8) | 0x6e), buf); /* * Encoding. Recommended is UTF-8. ISO639 UTF-8 MIBenum is 106 * (http://www.iana.org/assignments/character-sets) */ SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(106, buf); /* * Offset (Primary Language Base is 0x100) */ SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID, buf); return (11); } /* * Common provider name is "FreeBSD" */ int32_t common_profile_create_service_provider_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { char provider_name[] = "FreeBSD"; return (common_profile_create_string8(buf, eob, (uint8_t const *) provider_name, strlen(provider_name))); } /* * str8 len8 string */ int32_t common_profile_create_string8( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (datalen == 0 || datalen > 0xff || buf + 2 + datalen > eob) return (-1); SDP_PUT8(SDP_DATA_STR8, buf); SDP_PUT8(datalen, buf); memcpy(buf, data, datalen); return (2 + datalen); } /* * Service Availability */ int32_t common_profile_create_service_availability( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (datalen != 1 || buf + 2 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT8, buf); SDP_PUT8(data[0], buf); return (2); } /* * seq8 len8 - 2 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes * uint8 value8 - 2 bytes */ int32_t rfcomm_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (datalen != 1 || buf + 14 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(12, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(5, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_RFCOMM, buf); SDP_PUT8(SDP_DATA_UINT8, buf); SDP_PUT8(*data, buf); return (14); } /* * seq8 len8 - 2 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes * uint8 value8 - 2 bytes * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes */ int32_t obex_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (datalen != 1 || buf + 19 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(17, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(5, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_RFCOMM, buf); SDP_PUT8(SDP_DATA_UINT8, buf); SDP_PUT8(*data, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_OBEX, buf); return (19); } /* * seq8 len8 * uint8 value8 - bytes * [ uint8 value 8 ] */ int32_t obex_profile_create_supported_formats_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { int32_t len = 2 * datalen; if (len <= 0 || len > 0xff || buf + 2 + len > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(len, buf); for (; datalen > 0; datalen --) { SDP_PUT8(SDP_DATA_UINT8, buf); SDP_PUT8(*data++, buf); } return (2 + len); } /* * do not check anything */ int32_t common_profile_always_valid(uint8_t const *data, uint32_t datalen) { return (1); } /* * verify server channel number (the first byte in the data) */ int32_t common_profile_server_channel_valid(uint8_t const *data, uint32_t datalen) { if (data[0] < 1 || data[0] > 30) return (0); return (1); } /* * verify server channel number and supported_formats_size * sdp_opush_profile and sdp_irmc_profile */ int32_t obex_profile_data_valid(uint8_t const *data, uint32_t datalen) { sdp_opush_profile_p opush = (sdp_opush_profile_p) data; if (opush->server_channel < 1 || opush->server_channel > 30 || opush->supported_formats_size == 0 || opush->supported_formats_size > sizeof(opush->supported_formats)) return (0); return (1); } /* * BNEP protocol descriptor */ int32_t bnep_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { /* supported protocol types */ uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ #ifdef INET6 0x86dd, /* IPv6 */ #endif }; uint16_t i, psm, version = 0x0100, nptypes = sizeof(ptype)/sizeof(ptype[0]), nptypes_size = nptypes * 3; if (datalen != 2 || 18 + nptypes_size > 255 || buf + 20 + nptypes_size > eob) return (-1); memcpy(&psm, data, sizeof(psm)); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(18 + nptypes_size, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(6, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(psm, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(8 + nptypes_size, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_BNEP, buf); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(version, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(nptypes_size, buf); for (i = 0; i < nptypes; i ++) { SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(ptype[i], buf); } return (20 + nptypes_size); } /* * BNEP security description */ int32_t bnep_profile_create_security_description( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { uint16_t security_descr; if (datalen != 2 || buf + 3 > eob) return (-1); memcpy(&security_descr, data, sizeof(security_descr)); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(security_descr, buf); return (3); } Index: head/usr.sbin/bluetooth/sdpd/provider.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/provider.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/provider.c (revision 281210) @@ -1,196 +1,197 @@ /* * provider.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: provider.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static TAILQ_HEAD(, provider) providers = TAILQ_HEAD_INITIALIZER(providers); static uint32_t change_state = 0; static uint32_t handle = 0; /* * Register Service Discovery provider. * Should not be called more the once. */ int32_t provider_register_sd(int32_t fd) { extern profile_t sd_profile_descriptor; extern profile_t bgd_profile_descriptor; provider_p sd = calloc(1, sizeof(*sd)); provider_p bgd = calloc(1, sizeof(*bgd)); if (sd == NULL || bgd == NULL) { if (sd != NULL) free(sd); if (bgd != NULL) free(bgd); return (-1); } sd->profile = &sd_profile_descriptor; bgd->handle = 0; sd->fd = fd; TAILQ_INSERT_HEAD(&providers, sd, provider_next); bgd->profile = &bgd_profile_descriptor; bgd->handle = 1; sd->fd = fd; TAILQ_INSERT_AFTER(&providers, sd, bgd, provider_next); change_state ++; return (0); } /* * Register new provider for a given profile, bdaddr and session. */ provider_p provider_register(profile_p const profile, bdaddr_p const bdaddr, int32_t fd, uint8_t const *data, uint32_t datalen) { provider_p provider = calloc(1, sizeof(*provider)); if (provider != NULL) { provider->data = malloc(datalen); if (provider->data != NULL) { provider->profile = profile; memcpy(provider->data, data, datalen); /* * Record handles 0x0 and 0x1 are reserved * for SDP itself */ if (++ handle <= 1) handle = 2; provider->handle = handle; memcpy(&provider->bdaddr, bdaddr, sizeof(provider->bdaddr)); provider->fd = fd; TAILQ_INSERT_TAIL(&providers, provider, provider_next); change_state ++; } else { free(provider); provider = NULL; } } return (provider); } /* * Unregister provider */ void provider_unregister(provider_p provider) { TAILQ_REMOVE(&providers, provider, provider_next); if (provider->data != NULL) free(provider->data); free(provider); change_state ++; } /* * Update provider data */ int32_t provider_update(provider_p provider, uint8_t const *data, uint32_t datalen) { uint8_t *new_data = (uint8_t *) realloc(provider->data, datalen); if (new_data == NULL) return (-1); memcpy(new_data, data, datalen); provider->data = new_data; return (0); } /* * Get a provider for given record handle */ provider_p provider_by_handle(uint32_t handle) { provider_p provider = NULL; TAILQ_FOREACH(provider, &providers, provider_next) if (provider->handle == handle) break; return (provider); } /* * Cursor access */ provider_p provider_get_first(void) { return (TAILQ_FIRST(&providers)); } provider_p provider_get_next(provider_p provider) { return (TAILQ_NEXT(provider, provider_next)); } /* * Return change state */ uint32_t provider_get_change_state(void) { return (change_state); } Index: head/usr.sbin/bluetooth/sdpd/sar.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/sar.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/sar.c (revision 281210) @@ -1,317 +1,318 @@ /* * sar.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sar.c,v 1.2 2004/01/08 23:46:51 max Exp $ * $FreeBSD$ */ #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include /* for NULL */ #include "profile.h" #include "provider.h" #include "server.h" /* * Prepare SDP attr/value pair. Check if profile implements the attribute * and if so call the attribute value function. * * uint16 value16 - 3 bytes (attribute) * value - N bytes (value) */ static int32_t server_prepare_attr_value_pair( provider_p const provider, uint16_t attr, uint8_t *buf, uint8_t const * const eob) { profile_attr_create_p cf = profile_get_attr(provider->profile, attr); int32_t len; if (cf == NULL) return (0); /* no attribute */ if (buf + 3 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(attr, buf); len = cf(buf, eob, (uint8_t const *) provider, sizeof(*provider)); if (len < 0) return (-1); return (3 + len); } /* * seq16 value16 - 3 bytes * attr value - 3+ bytes * [ attr value ] */ int32_t server_prepare_attr_list(provider_p const provider, uint8_t const *req, uint8_t const * const req_end, uint8_t *rsp, uint8_t const * const rsp_end) { uint8_t *ptr = rsp + 3; int32_t type, hi, lo, len; if (ptr > rsp_end) return (-1); while (req < req_end) { SDP_GET8(type, req); switch (type) { case SDP_DATA_UINT16: if (req + 2 > req_end) return (-1); SDP_GET16(lo, req); hi = lo; break; case SDP_DATA_UINT32: if (req + 4 > req_end) return (-1); SDP_GET16(lo, req); SDP_GET16(hi, req); break; default: return (-1); /* NOT REACHED */ } for (; lo <= hi; lo ++) { len = server_prepare_attr_value_pair(provider, lo, ptr, rsp_end); if (len < 0) return (-1); ptr += len; } } len = ptr - rsp; /* we put this much bytes in rsp */ /* Fix SEQ16 header for the rsp */ SDP_PUT8(SDP_DATA_SEQ16, rsp); SDP_PUT16(len - 3, rsp); return (len); } /* * Prepare SDP Service Attribute Response */ int32_t server_prepare_service_attribute_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM; uint8_t *ptr = NULL; provider_t *provider = NULL; uint32_t handle; int32_t type, rsp_limit, aidlen, cslen, cs; /* * Minimal Service Attribute Request request * * value32 - 4 bytes ServiceRecordHandle * value16 - 2 bytes MaximumAttributeByteCount * seq8 len8 - 2 bytes * uint16 value16 - 3 bytes AttributeIDList * value8 - 1 byte ContinuationState */ if (req_end - req < 12) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get ServiceRecordHandle and MaximumAttributeByteCount */ SDP_GET32(handle, req); SDP_GET16(rsp_limit, req); if (rsp_limit <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get size of AttributeIDList */ aidlen = 0; SDP_GET8(type, req); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(aidlen, req); break; case SDP_DATA_SEQ16: SDP_GET16(aidlen, req); break; case SDP_DATA_SEQ32: SDP_GET32(aidlen, req); break; } if (aidlen <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); ptr = (uint8_t *) req + aidlen; /* Get ContinuationState */ if (ptr + 1 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET8(cslen, ptr); if (cslen != 0) { if (cslen != 2 || req_end - ptr != 2) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(cs, ptr); } else cs = 0; /* Process the request. First, check continuation state */ if (srv->fdidx[fd].rsp_cs != cs) return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE); if (srv->fdidx[fd].rsp_size > 0) return (0); /* Lookup record handle */ if ((provider = provider_by_handle(handle)) == NULL) return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE); /* * Service Attribute Response format * * value16 - 2 bytes AttributeListByteCount (not incl.) * seq8 len16 - 3 bytes * attr value - 3+ bytes AttributeList * [ attr value ] */ cs = server_prepare_attr_list(provider, req, req+aidlen, rsp, rsp_end); if (cs < 0) return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES); /* Set reply size (not counting PDU header and continuation state) */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2; if (srv->fdidx[fd].rsp_limit > rsp_limit) srv->fdidx[fd].rsp_limit = rsp_limit; srv->fdidx[fd].rsp_size = cs; srv->fdidx[fd].rsp_cs = 0; return (0); } /* * Send SDP Service [Search] Attribute Response */ int32_t server_send_service_attribute_response(server_p srv, int32_t fd) { uint8_t *rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs; uint8_t *rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size; struct iovec iov[4]; sdp_pdu_t pdu; uint16_t bcount; uint8_t cs[3]; int32_t size; /* First update continuation state (assume we will send all data) */ size = rsp_end - rsp; srv->fdidx[fd].rsp_cs += size; if (size + 1 > srv->fdidx[fd].rsp_limit) { /* * We need to split out response. Add 3 more bytes for the * continuation state and move rsp_end and rsp_cs backwards. */ while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) { rsp_end --; srv->fdidx[fd].rsp_cs --; } cs[0] = 2; cs[1] = srv->fdidx[fd].rsp_cs >> 8; cs[2] = srv->fdidx[fd].rsp_cs & 0xff; } else cs[0] = 0; assert(rsp_end >= rsp); bcount = rsp_end - rsp; if (((sdp_pdu_p)(srv->req))->pid == SDP_PDU_SERVICE_ATTRIBUTE_REQUEST) pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE; else pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; pdu.tid = ((sdp_pdu_p)(srv->req))->tid; pdu.len = htons(sizeof(bcount) + bcount + 1 + cs[0]); bcount = htons(bcount); iov[0].iov_base = &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = &bcount; iov[1].iov_len = sizeof(bcount); iov[2].iov_base = rsp; iov[2].iov_len = rsp_end - rsp; iov[3].iov_base = cs; iov[3].iov_len = 1 + cs[0]; do { size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0])); } while (size < 0 && errno == EINTR); /* Check if we have sent (or failed to sent) last response chunk */ if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) { srv->fdidx[fd].rsp_cs = 0; srv->fdidx[fd].rsp_size = 0; srv->fdidx[fd].rsp_limit = 0; } return ((size < 0)? errno : 0); } Index: head/usr.sbin/bluetooth/sdpd/scr.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/scr.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/scr.c (revision 281210) @@ -1,92 +1,93 @@ /* * scr.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: scr.c,v 1.1 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "profile.h" #include "provider.h" #include "server.h" /* * Prepare Service Change response */ int32_t server_prepare_service_change_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; provider_t *provider = NULL; uint32_t handle; /* * Minimal Service Change Request * * value32 - handle 4 bytes */ if (!srv->fdidx[fd].control || !srv->fdidx[fd].priv || req_end - req < 4) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get handle */ SDP_GET32(handle, req); /* Lookup provider */ provider = provider_by_handle(handle); if (provider == NULL || provider->fd != fd) return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE); /* Validate user data */ if (req_end - req < provider->profile->dsize || provider->profile->valid == NULL || (provider->profile->valid)(req, req_end - req) == 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Update provider */ if (provider_update(provider, req, req_end - req) < 0) return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES); SDP_PUT16(0, rsp); /* Set reply size */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t); srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp; srv->fdidx[fd].rsp_cs = 0; return (0); } Index: head/usr.sbin/bluetooth/sdpd/sd.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/sd.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/sd.c (revision 281210) @@ -1,228 +1,229 @@ /* * sd.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sd.c,v 1.4 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t sd_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t sd_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t sd_profile_create_service_id( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 3 > eob) return (-1); /* * The ServiceID is a UUID that universally and uniquely identifies * the service instance described by the service record. This service * attribute is particularly useful if the same service is described * by service records in more than one SDP server */ SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_SDP, buf); /* XXX ??? */ return (3); } static int32_t sd_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Bluetooth service discovery"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t sd_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 12 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(10, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_UUID_PROTOCOL_SDP, buf); return (12); } static int32_t sd_profile_create_browse_group_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 5 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); /* * The top-level browse group ID, called PublicBrowseRoot and * representing the root of the browsing hierarchy, has the value * 00001002-0000-1000-8000-00805F9B34FB (UUID16: 0x1002) from the * Bluetooth Assigned Numbers document */ SDP_PUT8(SDP_DATA_UUID16, buf); SDP_PUT16(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP, buf); return (5); } static int32_t sd_profile_create_version_number_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { if (buf + 5 > eob) return (-1); SDP_PUT8(SDP_DATA_SEQ8, buf); SDP_PUT8(3, buf); /* * The VersionNumberList is a data element sequence in which each * element of the sequence is a version number supported by the SDP * server. A version number is a 16-bit unsigned integer consisting * of two fields. The higher-order 8 bits contain the major version * number field and the low-order 8 bits contain the minor version * number field. The initial version of SDP has a major version of * 1 and a minor version of 0 */ SDP_PUT8(SDP_DATA_UINT16, buf); SDP_PUT16(0x0100, buf); return (5); } static int32_t sd_profile_create_service_database_state( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { uint32_t change_state = provider_get_change_state(); if (buf + 5 > eob) return (-1); SDP_PUT8(SDP_DATA_UINT32, buf); SDP_PUT32(change_state, buf); return (5); } static attr_t sd_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, sd_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, sd_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_SERVICE_ID, sd_profile_create_service_id }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, sd_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET, sd_profile_create_service_name }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_PROVIDER_NAME_OFFSET, common_profile_create_service_provider_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, sd_profile_create_protocol_descriptor_list }, { SDP_ATTR_BROWSE_GROUP_LIST, sd_profile_create_browse_group_list }, { SDP_ATTR_VERSION_NUMBER_LIST, sd_profile_create_version_number_list }, { SDP_ATTR_SERVICE_DATABASE_STATE, sd_profile_create_service_database_state }, { 0, NULL } /* end entry */ }; profile_t sd_profile_descriptor = { SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER, 0, (profile_data_valid_p) NULL, (attr_t const * const) &sd_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/server.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/server.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/server.c (revision 281210) @@ -1,589 +1,590 @@ /* * server.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: server.c,v 1.6 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include "log.h" #include "profile.h" #include "provider.h" #include "server.h" static void server_accept_client (server_p srv, int32_t fd); static int32_t server_process_request (server_p srv, int32_t fd); static int32_t server_send_error_response (server_p srv, int32_t fd, uint16_t error); static void server_close_fd (server_p srv, int32_t fd); /* * Initialize server */ int32_t server_init(server_p srv, char const *control) { struct sockaddr_un un; struct sockaddr_l2cap l2; int32_t unsock, l2sock; socklen_t size; uint16_t imtu; assert(srv != NULL); assert(control != NULL); memset(srv, 0, sizeof(*srv)); /* Open control socket */ if (unlink(control) < 0 && errno != ENOENT) { log_crit("Could not unlink(%s). %s (%d)", control, strerror(errno), errno); return (-1); } unsock = socket(PF_LOCAL, SOCK_STREAM, 0); if (unsock < 0) { log_crit("Could not create control socket. %s (%d)", strerror(errno), errno); return (-1); } memset(&un, 0, sizeof(un)); un.sun_len = sizeof(un); un.sun_family = AF_LOCAL; strlcpy(un.sun_path, control, sizeof(un.sun_path)); if (bind(unsock, (struct sockaddr *) &un, sizeof(un)) < 0) { log_crit("Could not bind control socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) { log_crit("Could not change permissions on control socket. " \ "%s (%d)", strerror(errno), errno); close(unsock); return (-1); } if (listen(unsock, 10) < 0) { log_crit("Could not listen on control socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } /* Open L2CAP socket */ l2sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (l2sock < 0) { log_crit("Could not create L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } size = sizeof(imtu); if (getsockopt(l2sock, SOL_L2CAP, SO_L2CAP_IMTU, &imtu, &size) < 0) { log_crit("Could not get L2CAP IMTU. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } memset(&l2, 0, sizeof(l2)); l2.l2cap_len = sizeof(l2); l2.l2cap_family = AF_BLUETOOTH; memcpy(&l2.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2.l2cap_bdaddr)); l2.l2cap_psm = htole16(NG_L2CAP_PSM_SDP); if (bind(l2sock, (struct sockaddr *) &l2, sizeof(l2)) < 0) { log_crit("Could not bind L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } if (listen(l2sock, 10) < 0) { log_crit("Could not listen on L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } /* Allocate incoming buffer */ srv->imtu = (imtu > SDP_LOCAL_MTU)? imtu : SDP_LOCAL_MTU; srv->req = (uint8_t *) calloc(srv->imtu, sizeof(srv->req[0])); if (srv->req == NULL) { log_crit("Could not allocate request buffer"); close(unsock); close(l2sock); return (-1); } /* Allocate memory for descriptor index */ srv->fdidx = (fd_idx_p) calloc(FD_SETSIZE, sizeof(srv->fdidx[0])); if (srv->fdidx == NULL) { log_crit("Could not allocate fd index"); free(srv->req); close(unsock); close(l2sock); return (-1); } /* Register Service Discovery profile (attach it to control socket) */ if (provider_register_sd(unsock) < 0) { log_crit("Could not register Service Discovery profile"); free(srv->fdidx); free(srv->req); close(unsock); close(l2sock); return (-1); } /* * If we got here then everything is fine. Add both control sockets * to the index. */ FD_ZERO(&srv->fdset); srv->maxfd = (unsock > l2sock)? unsock : l2sock; FD_SET(unsock, &srv->fdset); srv->fdidx[unsock].valid = 1; srv->fdidx[unsock].server = 1; srv->fdidx[unsock].control = 1; srv->fdidx[unsock].priv = 0; srv->fdidx[unsock].rsp_cs = 0; srv->fdidx[unsock].rsp_size = 0; srv->fdidx[unsock].rsp_limit = 0; srv->fdidx[unsock].omtu = SDP_LOCAL_MTU; srv->fdidx[unsock].rsp = NULL; FD_SET(l2sock, &srv->fdset); srv->fdidx[l2sock].valid = 1; srv->fdidx[l2sock].server = 1; srv->fdidx[l2sock].control = 0; srv->fdidx[l2sock].priv = 0; srv->fdidx[l2sock].rsp_cs = 0; srv->fdidx[l2sock].rsp_size = 0; srv->fdidx[l2sock].rsp_limit = 0; srv->fdidx[l2sock].omtu = 0; /* unknown */ srv->fdidx[l2sock].rsp = NULL; return (0); } /* * Shutdown server */ void server_shutdown(server_p srv) { int fd; assert(srv != NULL); for (fd = 0; fd < srv->maxfd + 1; fd ++) if (srv->fdidx[fd].valid) server_close_fd(srv, fd); free(srv->req); free(srv->fdidx); memset(srv, 0, sizeof(*srv)); } /* * Do one server iteration */ int32_t server_do(server_p srv) { fd_set fdset; int32_t n, fd; assert(srv != NULL); /* Copy cached version of the fd set and call select */ memcpy(&fdset, &srv->fdset, sizeof(fdset)); n = select(srv->maxfd + 1, &fdset, NULL, NULL, NULL); if (n < 0) { if (errno == EINTR) return (0); log_err("Could not select(%d, %p). %s (%d)", srv->maxfd + 1, &fdset, strerror(errno), errno); return (-1); } /* Process descriptors */ for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) { if (!FD_ISSET(fd, &fdset)) continue; assert(srv->fdidx[fd].valid); n --; if (srv->fdidx[fd].server) server_accept_client(srv, fd); else if (server_process_request(srv, fd) != 0) server_close_fd(srv, fd); } return (0); } /* * Accept new client connection and register it with index */ static void server_accept_client(server_p srv, int32_t fd) { uint8_t *rsp = NULL; int32_t cfd, priv; uint16_t omtu; socklen_t size; do { cfd = accept(fd, NULL, NULL); } while (cfd < 0 && errno == EINTR); if (cfd < 0) { log_err("Could not accept connection on %s socket. %s (%d)", srv->fdidx[fd].control? "control" : "L2CAP", strerror(errno), errno); return; } assert(!FD_ISSET(cfd, &srv->fdset)); assert(!srv->fdidx[cfd].valid); priv = 0; if (!srv->fdidx[fd].control) { /* Get local BD_ADDR */ size = sizeof(srv->req_sa); if (getsockname(cfd,(struct sockaddr*)&srv->req_sa,&size) < 0) { log_err("Could not get local BD_ADDR. %s (%d)", strerror(errno), errno); close(cfd); return; } /* Get outgoing MTU */ size = sizeof(omtu); if (getsockopt(cfd,SOL_L2CAP,SO_L2CAP_OMTU,&omtu,&size) < 0) { log_err("Could not get L2CAP OMTU. %s (%d)", strerror(errno), errno); close(cfd); return; } /* * The maximum size of the L2CAP packet is 65536 bytes. * The minimum L2CAP MTU is 43 bytes. That means we need * 65536 / 43 = ~1524 chunks to transfer maximum packet * size with minimum MTU. The "rsp_cs" field in fd_idx_t * is 11 bits wide, which gives us up to 2048 chunks. */ if (omtu < NG_L2CAP_MTU_MINIMUM) { log_err("L2CAP OMTU is too small (%d bytes)", omtu); close(cfd); return; } } else { struct xucred cr; struct passwd *pw; /* Get peer's credentials */ memset(&cr, 0, sizeof(cr)); size = sizeof(cr); if (getsockopt(cfd, 0, LOCAL_PEERCRED, &cr, &size) < 0) { log_err("Could not get peer's credentials. %s (%d)", strerror(errno), errno); close(cfd); return; } /* Check credentials */ pw = getpwuid(cr.cr_uid); if (pw != NULL) priv = (strcmp(pw->pw_name, "root") == 0); else log_warning("Could not verify credentials for uid %d", cr.cr_uid); memcpy(&srv->req_sa.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(srv->req_sa.l2cap_bdaddr)); omtu = srv->fdidx[fd].omtu; } /* * Allocate buffer. This is an overkill, but we can not know how * big our reply is going to be. */ rsp = (uint8_t *) calloc(NG_L2CAP_MTU_MAXIMUM, sizeof(rsp[0])); if (rsp == NULL) { log_crit("Could not allocate response buffer"); close(cfd); return; } /* Add client descriptor to the index */ FD_SET(cfd, &srv->fdset); if (srv->maxfd < cfd) srv->maxfd = cfd; srv->fdidx[cfd].valid = 1; srv->fdidx[cfd].server = 0; srv->fdidx[cfd].control = srv->fdidx[fd].control; srv->fdidx[cfd].priv = priv; srv->fdidx[cfd].rsp_cs = 0; srv->fdidx[cfd].rsp_size = 0; srv->fdidx[cfd].rsp_limit = 0; srv->fdidx[cfd].omtu = omtu; srv->fdidx[cfd].rsp = rsp; } /* * Process request from the client */ static int32_t server_process_request(server_p srv, int32_t fd) { sdp_pdu_p pdu = (sdp_pdu_p) srv->req; int32_t len, error; assert(srv->imtu > 0); assert(srv->req != NULL); assert(FD_ISSET(fd, &srv->fdset)); assert(srv->fdidx[fd].valid); assert(!srv->fdidx[fd].server); assert(srv->fdidx[fd].rsp != NULL); assert(srv->fdidx[fd].omtu >= NG_L2CAP_MTU_MINIMUM); do { len = read(fd, srv->req, srv->imtu); } while (len < 0 && errno == EINTR); if (len < 0) { log_err("Could not receive SDP request from %s socket. %s (%d)", srv->fdidx[fd].control? "control" : "L2CAP", strerror(errno), errno); return (-1); } if (len == 0) { log_info("Client on %s socket has disconnected", srv->fdidx[fd].control? "control" : "L2CAP"); return (-1); } if (len >= sizeof(*pdu) && sizeof(*pdu) + (pdu->len = ntohs(pdu->len)) == len) { switch (pdu->pid) { case SDP_PDU_SERVICE_SEARCH_REQUEST: error = server_prepare_service_search_response(srv, fd); break; case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: error = server_prepare_service_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: error = server_prepare_service_search_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_REGISTER_REQUEST: error = server_prepare_service_register_response(srv, fd); break; case SDP_PDU_SERVICE_UNREGISTER_REQUEST: error = server_prepare_service_unregister_response(srv, fd); break; case SDP_PDU_SERVICE_CHANGE_REQUEST: error = server_prepare_service_change_response(srv, fd); break; default: error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; break; } } else error = SDP_ERROR_CODE_INVALID_PDU_SIZE; if (error == 0) { switch (pdu->pid) { case SDP_PDU_SERVICE_SEARCH_REQUEST: error = server_send_service_search_response(srv, fd); break; case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: error = server_send_service_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: error = server_send_service_search_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_REGISTER_REQUEST: error = server_send_service_register_response(srv, fd); break; case SDP_PDU_SERVICE_UNREGISTER_REQUEST: error = server_send_service_unregister_response(srv, fd); break; case SDP_PDU_SERVICE_CHANGE_REQUEST: error = server_send_service_change_response(srv, fd); break; default: error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; break; } if (error != 0) log_err("Could not send SDP response to %s socket, " \ "pdu->pid=%d, pdu->tid=%d, error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), error); } else { log_err("Could not process SDP request from %s socket, " \ "pdu->pid=%d, pdu->tid=%d, pdu->len=%d, len=%d, " \ "error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), pdu->len, len, error); error = server_send_error_response(srv, fd, error); if (error != 0) log_err("Could not send SDP error response to %s " \ "socket, pdu->pid=%d, pdu->tid=%d, error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), error); } /* On error forget response (if any) */ if (error != 0) { srv->fdidx[fd].rsp_cs = 0; srv->fdidx[fd].rsp_size = 0; srv->fdidx[fd].rsp_limit = 0; } return (error); } /* * Send SDP_Error_Response PDU */ static int32_t server_send_error_response(server_p srv, int32_t fd, uint16_t error) { int32_t size; struct { sdp_pdu_t pdu; uint16_t error; } __attribute__ ((packed)) rsp; /* Prepare and send SDP error response */ rsp.pdu.pid = SDP_PDU_ERROR_RESPONSE; rsp.pdu.tid = ((sdp_pdu_p)(srv->req))->tid; rsp.pdu.len = htons(sizeof(rsp.error)); rsp.error = htons(error); do { size = write(fd, &rsp, sizeof(rsp)); } while (size < 0 && errno == EINTR); return ((size < 0)? errno : 0); } /* * Close descriptor and remove it from index */ static void server_close_fd(server_p srv, int32_t fd) { provider_p provider = NULL, provider_next = NULL; assert(FD_ISSET(fd, &srv->fdset)); assert(srv->fdidx[fd].valid); close(fd); FD_CLR(fd, &srv->fdset); if (fd == srv->maxfd) srv->maxfd --; if (srv->fdidx[fd].rsp != NULL) free(srv->fdidx[fd].rsp); memset(&srv->fdidx[fd], 0, sizeof(srv->fdidx[fd])); for (provider = provider_get_first(); provider != NULL; provider = provider_next) { provider_next = provider_get_next(provider); if (provider->fd == fd) provider_unregister(provider); } } Index: head/usr.sbin/bluetooth/sdpd/sp.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/sp.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/sp.c (revision 281210) @@ -1,117 +1,118 @@ /* * sp.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sp.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" static int32_t sp_profile_create_service_class_id_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t service_classes[] = { SDP_SERVICE_CLASS_SERIAL_PORT }; return (common_profile_create_service_class_id_list( buf, eob, (uint8_t const *) service_classes, sizeof(service_classes))); } static int32_t sp_profile_create_bluetooth_profile_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static uint16_t profile_descriptor_list[] = { SDP_SERVICE_CLASS_SERIAL_PORT, 0x0100 }; return (common_profile_create_bluetooth_profile_descriptor_list( buf, eob, (uint8_t const *) profile_descriptor_list, sizeof(profile_descriptor_list))); } static int32_t sp_profile_create_service_name( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { static char service_name[] = "Serial Port"; return (common_profile_create_string8( buf, eob, (uint8_t const *) service_name, strlen(service_name))); } static int32_t sp_profile_create_protocol_descriptor_list( uint8_t *buf, uint8_t const * const eob, uint8_t const *data, uint32_t datalen) { provider_p provider = (provider_p) data; sdp_sp_profile_p sp = (sdp_sp_profile_p) provider->data; return (rfcomm_profile_create_protocol_descriptor_list( buf, eob, (uint8_t const *) &sp->server_channel, 1)); } static attr_t sp_profile_attrs[] = { { SDP_ATTR_SERVICE_RECORD_HANDLE, common_profile_create_service_record_handle }, { SDP_ATTR_SERVICE_CLASS_ID_LIST, sp_profile_create_service_class_id_list }, { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, sp_profile_create_bluetooth_profile_descriptor_list }, { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, common_profile_create_language_base_attribute_id_list }, { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, sp_profile_create_service_name }, { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, sp_profile_create_protocol_descriptor_list }, { 0, NULL } /* end entry */ }; profile_t sp_profile_descriptor = { SDP_SERVICE_CLASS_SERIAL_PORT, sizeof(sdp_sp_profile_t), common_profile_server_channel_valid, (attr_t const * const) &sp_profile_attrs }; Index: head/usr.sbin/bluetooth/sdpd/srr.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/srr.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/srr.c (revision 281210) @@ -1,139 +1,140 @@ /* * srr.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: srr.c,v 1.1 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "profile.h" #include "provider.h" #include "server.h" /* * Prepare Service Register response */ int32_t server_prepare_service_register_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; profile_t *profile = NULL; provider_t *provider = NULL; bdaddr_t *bdaddr = NULL; int32_t uuid; /* * Minimal Service Register Request * * value16 - uuid 2 bytes * bdaddr - BD_ADDR 6 bytes */ if (!srv->fdidx[fd].control || !srv->fdidx[fd].priv || req_end - req < 8) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get ServiceClass UUID */ SDP_GET16(uuid, req); /* Get BD_ADDR */ bdaddr = (bdaddr_p) req; req += sizeof(*bdaddr); /* Lookup profile descriptror */ profile = profile_get_descriptor(uuid); if (profile == NULL) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Validate user data */ if (req_end - req < profile->dsize || profile->valid == NULL || (profile->valid)(req, req_end - req) == 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Register provider */ provider = provider_register(profile, bdaddr, fd, req, req_end - req); if (provider == NULL) return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES); SDP_PUT16(0, rsp); SDP_PUT32(provider->handle, rsp); /* Set reply size */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t); srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp; srv->fdidx[fd].rsp_cs = 0; return (0); } /* * Send Service Register Response */ int32_t server_send_service_register_response(server_p srv, int32_t fd) { struct iovec iov[2]; sdp_pdu_t pdu; int32_t size; assert(srv->fdidx[fd].rsp_size < srv->fdidx[fd].rsp_limit); pdu.pid = SDP_PDU_ERROR_RESPONSE; pdu.tid = ((sdp_pdu_p)(srv->req))->tid; pdu.len = htons(srv->fdidx[fd].rsp_size); iov[0].iov_base = &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = srv->fdidx[fd].rsp; iov[1].iov_len = srv->fdidx[fd].rsp_size; do { size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0])); } while (size < 0 && errno == EINTR); srv->fdidx[fd].rsp_cs = 0; srv->fdidx[fd].rsp_size = 0; srv->fdidx[fd].rsp_limit = 0; return ((size < 0)? errno : 0); } Index: head/usr.sbin/bluetooth/sdpd/ssar.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/ssar.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/ssar.c (revision 281210) @@ -1,252 +1,253 @@ /* * ssar.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ssar.c,v 1.4 2004/01/12 22:54:31 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include "profile.h" #include "provider.h" #include "server.h" #include "uuid-private.h" /* from sar.c */ int32_t server_prepare_attr_list(provider_p const provider, uint8_t const *req, uint8_t const * const req_end, uint8_t *rsp, uint8_t const * const rsp_end); /* * Prepare SDP Service Search Attribute Response */ int32_t server_prepare_service_search_attribute_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM; uint8_t const *sspptr = NULL, *aidptr = NULL; uint8_t *ptr = NULL; provider_t *provider = NULL; int32_t type, rsp_limit, ssplen, aidlen, cslen, cs; uint128_t uuid, puuid; /* * Minimal Service Search Attribute Request request * * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes ServiceSearchPattern * value16 - 2 bytes MaximumAttributeByteCount * seq8 len8 - 2 bytes * uint16 value16 - 3 bytes AttributeIDList * value8 - 1 byte ContinuationState */ if (req_end - req < 13) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get size of ServiceSearchPattern */ ssplen = 0; SDP_GET8(type, req); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(ssplen, req); break; case SDP_DATA_SEQ16: SDP_GET16(ssplen, req); break; case SDP_DATA_SEQ32: SDP_GET32(ssplen, req); break; } if (ssplen <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); sspptr = req; req += ssplen; /* Get MaximumAttributeByteCount */ if (req + 2 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(rsp_limit, req); if (rsp_limit <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get size of AttributeIDList */ if (req + 1 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); aidlen = 0; SDP_GET8(type, req); switch (type) { case SDP_DATA_SEQ8: if (req + 1 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET8(aidlen, req); break; case SDP_DATA_SEQ16: if (req + 2 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(aidlen, req); break; case SDP_DATA_SEQ32: if (req + 4 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET32(aidlen, req); break; } if (aidlen <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); aidptr = req; req += aidlen; /* Get ContinuationState */ if (req + 1 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET8(cslen, req); if (cslen != 0) { if (cslen != 2 || req_end - req != 2) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(cs, req); } else cs = 0; /* Process the request. First, check continuation state */ if (srv->fdidx[fd].rsp_cs != cs) return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE); if (srv->fdidx[fd].rsp_size > 0) return (0); /* * Service Search Attribute Response format * * value16 - 2 bytes AttributeListByteCount (not incl.) * seq8 len16 - 3 bytes * attr list - 3+ bytes AttributeLists * [ attr list ] */ ptr = rsp + 3; while (ssplen > 0) { SDP_GET8(type, sspptr); ssplen --; switch (type) { case SDP_DATA_UUID16: if (ssplen < 2) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(&uuid, &uuid_base, sizeof(uuid)); uuid.b[2] = *sspptr ++; uuid.b[3] = *sspptr ++; ssplen -= 2; break; case SDP_DATA_UUID32: if (ssplen < 4) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(&uuid, &uuid_base, sizeof(uuid)); uuid.b[0] = *sspptr ++; uuid.b[1] = *sspptr ++; uuid.b[2] = *sspptr ++; uuid.b[3] = *sspptr ++; ssplen -= 4; break; case SDP_DATA_UUID128: if (ssplen < 16) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(uuid.b, sspptr, 16); sspptr += 16; ssplen -= 16; break; default: return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* NOT REACHED */ } for (provider = provider_get_first(); provider != NULL; provider = provider_get_next(provider)) { if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr)) continue; memcpy(&puuid, &uuid_base, sizeof(puuid)); puuid.b[2] = provider->profile->uuid >> 8; puuid.b[3] = provider->profile->uuid; if (memcmp(&uuid, &puuid, sizeof(uuid)) != 0 && memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) != 0) continue; cs = server_prepare_attr_list(provider, aidptr, aidptr + aidlen, ptr, rsp_end); if (cs < 0) return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES); ptr += cs; } } /* Set reply size (not counting PDU header and continuation state) */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2; if (srv->fdidx[fd].rsp_limit > rsp_limit) srv->fdidx[fd].rsp_limit = rsp_limit; srv->fdidx[fd].rsp_size = ptr - rsp; srv->fdidx[fd].rsp_cs = 0; /* Fix AttributeLists sequence header */ ptr = rsp; SDP_PUT8(SDP_DATA_SEQ16, ptr); SDP_PUT16(srv->fdidx[fd].rsp_size - 3, ptr); return (0); } Index: head/usr.sbin/bluetooth/sdpd/ssr.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/ssr.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/ssr.c (revision 281210) @@ -1,282 +1,283 @@ /* * ssr.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ssr.c,v 1.5 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include #include #include #include #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "profile.h" #include "provider.h" #include "server.h" #include "uuid-private.h" /* * Prepare SDP Service Search Response */ int32_t server_prepare_service_search_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM; uint8_t *ptr = NULL; provider_t *provider = NULL; int32_t type, ssplen, rsp_limit, rcount, cslen, cs; uint128_t uuid, puuid; /* * Minimal SDP Service Search Request * * seq8 len8 - 2 bytes * uuid16 value16 - 3 bytes ServiceSearchPattern * value16 - 2 bytes MaximumServiceRecordCount * value8 - 1 byte ContinuationState */ if (req_end - req < 8) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get size of ServiceSearchPattern */ ssplen = 0; SDP_GET8(type, req); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(ssplen, req); break; case SDP_DATA_SEQ16: SDP_GET16(ssplen, req); break; case SDP_DATA_SEQ32: SDP_GET32(ssplen, req); break; } if (ssplen <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); ptr = (uint8_t *) req + ssplen; /* Get MaximumServiceRecordCount */ if (ptr + 2 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(rsp_limit, ptr); if (rsp_limit <= 0) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get ContinuationState */ if (ptr + 1 > req_end) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET8(cslen, ptr); if (cslen != 0) { if (cslen != 2 || req_end - ptr != 2) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); SDP_GET16(cs, ptr); } else cs = 0; /* Process the request. First, check continuation state */ if (srv->fdidx[fd].rsp_cs != cs) return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE); if (srv->fdidx[fd].rsp_size > 0) return (0); /* * Service Search Response format * * value16 - 2 bytes TotalServiceRecordCount (not incl.) * value16 - 2 bytes CurrentServiceRecordCount (not incl.) * value32 - 4 bytes handle * [ value32 ] * * Calculate how many record handles we can fit * in our reply buffer and adjust rlimit. */ ptr = rsp; rcount = (rsp_end - ptr) / 4; if (rcount < rsp_limit) rsp_limit = rcount; /* Look for the record handles */ for (rcount = 0; ssplen > 0 && rcount < rsp_limit; ) { SDP_GET8(type, req); ssplen --; switch (type) { case SDP_DATA_UUID16: if (ssplen < 2) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(&uuid, &uuid_base, sizeof(uuid)); uuid.b[2] = *req ++; uuid.b[3] = *req ++; ssplen -= 2; break; case SDP_DATA_UUID32: if (ssplen < 4) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(&uuid, &uuid_base, sizeof(uuid)); uuid.b[0] = *req ++; uuid.b[1] = *req ++; uuid.b[2] = *req ++; uuid.b[3] = *req ++; ssplen -= 4; break; case SDP_DATA_UUID128: if (ssplen < 16) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); memcpy(uuid.b, req, 16); req += 16; ssplen -= 16; break; default: return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* NOT REACHED */ } for (provider = provider_get_first(); provider != NULL && rcount < rsp_limit; provider = provider_get_next(provider)) { if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr)) continue; memcpy(&puuid, &uuid_base, sizeof(puuid)); puuid.b[2] = provider->profile->uuid >> 8; puuid.b[3] = provider->profile->uuid; if (memcmp(&uuid, &puuid, sizeof(uuid)) == 0 || memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) == 0) { SDP_PUT32(provider->handle, ptr); rcount ++; } } } /* Set reply size (not counting PDU header and continuation state) */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 4; srv->fdidx[fd].rsp_size = ptr - rsp; srv->fdidx[fd].rsp_cs = 0; return (0); } /* * Send SDP Service Search Response */ int32_t server_send_service_search_response(server_p srv, int32_t fd) { uint8_t *rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs; uint8_t *rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size; struct iovec iov[4]; sdp_pdu_t pdu; uint16_t rcounts[2]; uint8_t cs[3]; int32_t size; /* First update continuation state (assume we will send all data) */ size = rsp_end - rsp; srv->fdidx[fd].rsp_cs += size; if (size + 1 > srv->fdidx[fd].rsp_limit) { /* * We need to split out response. Add 3 more bytes for the * continuation state and move rsp_end and rsp_cs backwards. */ while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) { rsp_end -= 4; srv->fdidx[fd].rsp_cs -= 4; } cs[0] = 2; cs[1] = srv->fdidx[fd].rsp_cs >> 8; cs[2] = srv->fdidx[fd].rsp_cs & 0xff; } else cs[0] = 0; assert(rsp_end >= rsp); rcounts[0] = srv->fdidx[fd].rsp_size / 4; /* TotalServiceRecordCount */ rcounts[1] = (rsp_end - rsp) / 4; /* CurrentServiceRecordCount */ pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE; pdu.tid = ((sdp_pdu_p)(srv->req))->tid; pdu.len = htons(sizeof(rcounts) + rcounts[1] * 4 + 1 + cs[0]); rcounts[0] = htons(rcounts[0]); rcounts[1] = htons(rcounts[1]); iov[0].iov_base = &pdu; iov[0].iov_len = sizeof(pdu); iov[1].iov_base = rcounts; iov[1].iov_len = sizeof(rcounts); iov[2].iov_base = rsp; iov[2].iov_len = rsp_end - rsp; iov[3].iov_base = cs; iov[3].iov_len = 1 + cs[0]; do { size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0])); } while (size < 0 && errno == EINTR); /* Check if we have sent (or failed to sent) last response chunk */ if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) { srv->fdidx[fd].rsp_cs = 0; srv->fdidx[fd].rsp_size = 0; srv->fdidx[fd].rsp_limit = 0; } return ((size < 0)? errno : 0); } Index: head/usr.sbin/bluetooth/sdpd/sur.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/sur.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/sur.c (revision 281210) @@ -1,83 +1,84 @@ /* * sur.c * * Copyright (c) 2004 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: sur.c,v 1.1 2004/01/13 01:54:39 max Exp $ * $FreeBSD$ */ #include +#define L2CAP_SOCKET_CHECKED #include #include #include #include #include "profile.h" #include "provider.h" #include "server.h" /* * Prepare Service Unregister response */ int32_t server_prepare_service_unregister_response(server_p srv, int32_t fd) { uint8_t const *req = srv->req + sizeof(sdp_pdu_t); uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; uint8_t *rsp = srv->fdidx[fd].rsp; provider_t *provider = NULL; uint32_t handle; /* * Minimal Service Unregister Request * * value32 - uuid 4 bytes */ if (!srv->fdidx[fd].control || !srv->fdidx[fd].priv || req_end - req < 4) return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); /* Get handle */ SDP_GET32(handle, req); /* Lookup provider */ provider = provider_by_handle(handle); if (provider == NULL || provider->fd != fd) return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE); provider_unregister(provider); SDP_PUT16(0, rsp); /* Set reply size */ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t); srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp; srv->fdidx[fd].rsp_cs = 0; return (0); } Index: head/usr.sbin/bluetooth/sdpd/uuid.c =================================================================== --- head/usr.sbin/bluetooth/sdpd/uuid.c (revision 281209) +++ head/usr.sbin/bluetooth/sdpd/uuid.c (revision 281210) @@ -1,56 +1,56 @@ /* * uuid.c * * Copyright (c) 2005 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: uuid.c,v 1.1 2004/12/09 18:20:26 max Exp $ * $FreeBSD$ */ - +#define L2CAP_SOCKET_CHECKED #include #include #include #include "uuid-private.h" uint128_t uuid_base = { .b = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } }; uint128_t uuid_public_browse_group = { .b = { 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } };