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); } }
static int bt_query(struct l2cap_info *info, uint16_t service_class) { sdp_attr_t values[BT_NUM_VALUES]; uint8_t buffer[BT_NUM_VALUES][BT_BUF_SIZE]; void *ss; int psm = -1; int n; memset(buffer, 0, sizeof(buffer)); memset(values, 0, sizeof(values)); ss = sdp_open(&info->laddr, &info->raddr); if (ss == NULL || sdp_error(ss) != 0) { DPRINTF("Could not open SDP\n"); return (psm); } /* Initialize attribute values array */ for (n = 0; n != BT_NUM_VALUES; n++) { values[n].flags = SDP_ATTR_INVALID; values[n].vlen = BT_BUF_SIZE; values[n].value = buffer[n]; } /* Do SDP Service Search Attribute Request */ n = sdp_search(ss, 1, &service_class, 1, bt_attrs, BT_NUM_VALUES, values); if (n != 0) { DPRINTF("SDP search failed\n"); goto done; } /* Print attributes values */ for (n = 0; n != BT_NUM_VALUES; n++) { if (values[n].flags != SDP_ATTR_OK) break; if (values[n].attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) continue; psm = bt_find_psm(values[n].value, values[n].value + values[n].vlen); if (psm > -1) break; } done: sdp_close(ss); return (psm); }
int cmd_browse(struct command *cmd) { int status = -1; uuid_t uuid; if (!__argv[optind]) { printf("server address is not given\n"); return -1; } status = __sdp_connect(__argv[optind]); if (status) { printf("Connection to SDP server failed: %s\n", sdp_error(status)); return status; } sdp_val2uuid16(&uuid, SDP_UUID_PUBLIC_BROWSE_GROUP); status = svcBrowse(&uuid); __sdp_close(); return status; }
/* 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 */
int cmd_pan_connect(struct command *cmd) { int err, role = AFFIX_PAN_NAP; BD_ADDR bda; struct sockaddr_affix saddr; __argv = &__argv[optind]; if (!*__argv) { printf("Address missing\n"); print_usage(cmd); return 1; } err = btdev_get_bda(&bda, *__argv); if (err) { printf("Incorrect address given\n"); return 1; } if (*(++__argv)) { /* role */ role = str2role(*__argv); if (!role) { fprintf(stderr, "invalid role: %s\n", *__argv); return 1; } } saddr.family = PF_AFFIX; saddr.bda = bda; saddr.devnum = HCIDEV_ANY; #if defined(CONFIG_AFFIX_SDP) if (sdpmode) { uint16_t ServiceID; uint16_t count; slist_t *searchList = NULL; slist_t *attrList = NULL; slist_t *svcList = NULL; sdpsvc_t *svcRec; if (role == AFFIX_PAN_NAP) ServiceID = SDP_UUID_NAP; else if (role == AFFIX_PAN_GN) ServiceID = SDP_UUID_GN; else ServiceID = SDP_UUID_PANU; printf("Connecting to host %s ...\n", bda2str(&bda)); /* search for service ServiceID */ s_list_append_uuid16(&searchList, ServiceID); /* set attributes to find */ s_list_append_uint(&attrList, SDP_ATTR_SERVICE_RECORD_HANDLE); s_list_append_uint(&attrList, SDP_ATTR_PROTO_DESC_LIST); err = __sdp_search_attr_req(&saddr, searchList, IndividualAttributes, attrList, 0xffff, &svcList, &count); s_list_destroy(&searchList); s_list_free(&attrList); if (err) { fprintf(stderr, "%s\n", sdp_error(err)); return -1; } if (count == 0) { printf("services [%s] not found\n", val2str(sdp_service_map, ServiceID)); return -1; } printf("Service found\n"); svcRec = s_list_dequeue(&svcList); sdp_free_svc(svcRec); sdp_free_svclist(&svcList); } #endif saddr.devnum = hci_devnum(btdev); err = affix_pan_connect(&saddr, role); if (err) { if (errno == EINVAL) fprintf(stderr, "Connection not allowed in this role.\n"); else fprintf(stderr, "failed.\n"); } else fprintf(stderr, "connected.\n"); return 0; }
static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id) { int i, channel = -1; char name[64]; void *ss = NULL; uint32_t attrs[] = { SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), SDP_ATTR_RANGE( SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET), }; /* Buffer for the attributes */ static uint8_t buffer[NRECS * attrs_len][BSIZE]; /* SDP attributes */ static sdp_attr_t values[NRECS * attrs_len]; /* Initialize attribute values array */ for (i = 0; i < values_len; i ++) { values[i].flags = SDP_ATTR_INVALID; values[i].attr = 0; values[i].vlen = BSIZE; values[i].value = buffer[i]; } if ((ss = sdp_open(adapter, device)) == NULL) return -1; if (sdp_error(ss) != 0) goto end; if (sdp_search(ss, 1, &svclass_id, attrs_len, attrs, values_len, values) != 0) goto end; for (i = 0; i < values_len; i++) { union { uint8_t uint8; uint16_t uint16; uint32_t uint32; uint64_t uint64; int128_t int128; } value; uint8_t *start, *end; uint32_t type, len; if (values[i].flags != SDP_ATTR_OK) break; start = values[i].value; end = values[i].value + values[i].vlen; switch (values[i].attr) { case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 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: goto end; break; } 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: goto end; } while (start < end) { SDP_GET8(type, start); switch (type) { case SDP_DATA_UUID16: SDP_GET16(value.uint16, start); break; case SDP_DATA_UUID32: SDP_GET32(value.uint32, start); break; case SDP_DATA_UUID128: SDP_GET_UUID128(&value.int128, start); break; default: goto end; } if (value.uint16 == 3) { SDP_GET8(type, start); switch (type) { case SDP_DATA_UINT8: case SDP_DATA_INT8: SDP_GET8(value.uint8, start); channel = value.uint8; break; case SDP_DATA_UINT16: case SDP_DATA_INT16: SDP_GET16(value.uint16, start); channel = value.uint16; break; case SDP_DATA_UINT32: case SDP_DATA_INT32: SDP_GET32(value.uint32, start); channel = value.uint32; break; default: goto end; } } else { SDP_GET8(type, start); switch (type) { case SDP_DATA_SEQ8: case SDP_DATA_UINT8: case SDP_DATA_INT8: case SDP_DATA_BOOL: SDP_GET8(value.uint8, start); break; case SDP_DATA_SEQ16: case SDP_DATA_UINT16: case SDP_DATA_INT16: case SDP_DATA_UUID16: SDP_GET16(value.uint16, start); break; case SDP_DATA_SEQ32: case SDP_DATA_UINT32: case SDP_DATA_INT32: case SDP_DATA_UUID32: SDP_GET32(value.uint32, start); break; case SDP_DATA_UINT64: case SDP_DATA_INT64: SDP_GET64(value.uint64, start); break; case SDP_DATA_UINT128: case SDP_DATA_INT128: SDP_GET128(&value.int128, start); break; default: goto end; } } } start += len; break; case SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET: if (channel == -1) break; SDP_GET8(type, start); switch (type) { case SDP_DATA_STR8: case SDP_DATA_URL8: SDP_GET8(len, start); snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start); start += len; break; case SDP_DATA_STR16: case SDP_DATA_URL16: SDP_GET16(len, start); snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start); start += len; break; case SDP_DATA_STR32: case SDP_DATA_URL32: SDP_GET32(len, start); snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start); start += len; break; default: goto end; } if (name == NULL) break; if (only_gnapplet != 0) { if (strcmp(name, "gnapplet") == 0) goto end; else { channel = -1; break; } } if (strstr(name, "Nokia PC Suite") != NULL) { channel = -1; break; } if (strstr(name, "Bluetooth Serial Port") != NULL) { channel = -1; break; } if (strstr(name, "m-Router Connectivity") != NULL) { channel = -1; break; } goto end; } } end: sdp_close(ss); return channel; }
/* 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 */
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); }
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)); }
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); }
/* 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); }