17718ced0SMaksim Yevmenkin /* $NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */
27718ced0SMaksim Yevmenkin
37718ced0SMaksim Yevmenkin /*-
4*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
51de7b4b8SPedro F. Giffuni *
67718ced0SMaksim Yevmenkin * Copyright (c) 2008 Iain Hibbert
77718ced0SMaksim Yevmenkin * All rights reserved.
87718ced0SMaksim Yevmenkin *
97718ced0SMaksim Yevmenkin * Redistribution and use in source and binary forms, with or without
107718ced0SMaksim Yevmenkin * modification, are permitted provided that the following conditions
117718ced0SMaksim Yevmenkin * are met:
127718ced0SMaksim Yevmenkin * 1. Redistributions of source code must retain the above copyright
137718ced0SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer.
147718ced0SMaksim Yevmenkin * 2. Redistributions in binary form must reproduce the above copyright
157718ced0SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer in the
167718ced0SMaksim Yevmenkin * documentation and/or other materials provided with the distribution.
177718ced0SMaksim Yevmenkin *
187718ced0SMaksim Yevmenkin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
197718ced0SMaksim Yevmenkin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
207718ced0SMaksim Yevmenkin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
217718ced0SMaksim Yevmenkin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
227718ced0SMaksim Yevmenkin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
237718ced0SMaksim Yevmenkin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247718ced0SMaksim Yevmenkin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
257718ced0SMaksim Yevmenkin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
267718ced0SMaksim Yevmenkin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
277718ced0SMaksim Yevmenkin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
287718ced0SMaksim Yevmenkin */
297718ced0SMaksim Yevmenkin
307718ced0SMaksim Yevmenkin
317718ced0SMaksim Yevmenkin #include <sys/cdefs.h>
327718ced0SMaksim Yevmenkin __RCSID("$NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $");
337718ced0SMaksim Yevmenkin
348d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
357718ced0SMaksim Yevmenkin #include <bluetooth.h>
367718ced0SMaksim Yevmenkin #include <errno.h>
377718ced0SMaksim Yevmenkin #include <sdp.h>
387718ced0SMaksim Yevmenkin #include <unistd.h>
397718ced0SMaksim Yevmenkin
407718ced0SMaksim Yevmenkin #include "btpand.h"
417718ced0SMaksim Yevmenkin #include "bnep.h"
427718ced0SMaksim Yevmenkin #include "sdp.h"
437718ced0SMaksim Yevmenkin
447718ced0SMaksim Yevmenkin static void client_query(void);
457718ced0SMaksim Yevmenkin
467718ced0SMaksim Yevmenkin void
client_init(void)477718ced0SMaksim Yevmenkin client_init(void)
487718ced0SMaksim Yevmenkin {
497718ced0SMaksim Yevmenkin struct sockaddr_l2cap sa;
507718ced0SMaksim Yevmenkin channel_t *chan;
517718ced0SMaksim Yevmenkin socklen_t len;
522dcf7e97SMaksim Yevmenkin int fd, n;
537718ced0SMaksim Yevmenkin uint16_t mru, mtu;
547718ced0SMaksim Yevmenkin
557718ced0SMaksim Yevmenkin if (bdaddr_any(&remote_bdaddr))
567718ced0SMaksim Yevmenkin return;
577718ced0SMaksim Yevmenkin
587718ced0SMaksim Yevmenkin if (service_name)
597718ced0SMaksim Yevmenkin client_query();
607718ced0SMaksim Yevmenkin
617718ced0SMaksim Yevmenkin fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
627718ced0SMaksim Yevmenkin if (fd == -1) {
637718ced0SMaksim Yevmenkin log_err("Could not open L2CAP socket: %m");
647718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
657718ced0SMaksim Yevmenkin }
667718ced0SMaksim Yevmenkin
677718ced0SMaksim Yevmenkin memset(&sa, 0, sizeof(sa));
687718ced0SMaksim Yevmenkin sa.l2cap_family = AF_BLUETOOTH;
697718ced0SMaksim Yevmenkin sa.l2cap_len = sizeof(sa);
708d6f425dSTakanori Watanabe sa.l2cap_bdaddr_type = BDADDR_BREDR;
718d6f425dSTakanori Watanabe sa.l2cap_cid = 0;
728d6f425dSTakanori Watanabe
737718ced0SMaksim Yevmenkin bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr);
747718ced0SMaksim Yevmenkin if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
757718ced0SMaksim Yevmenkin log_err("Could not bind client socket: %m");
767718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
777718ced0SMaksim Yevmenkin }
787718ced0SMaksim Yevmenkin
797718ced0SMaksim Yevmenkin mru = BNEP_MTU_MIN;
807718ced0SMaksim Yevmenkin if (setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
817718ced0SMaksim Yevmenkin log_err("Could not set L2CAP IMTU (%d): %m", mru);
827718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
837718ced0SMaksim Yevmenkin }
847718ced0SMaksim Yevmenkin
857718ced0SMaksim Yevmenkin log_info("Opening connection to service 0x%4.4x at %s",
867718ced0SMaksim Yevmenkin service_class, bt_ntoa(&remote_bdaddr, NULL));
877718ced0SMaksim Yevmenkin
887718ced0SMaksim Yevmenkin sa.l2cap_psm = htole16(l2cap_psm);
897718ced0SMaksim Yevmenkin bdaddr_copy(&sa.l2cap_bdaddr, &remote_bdaddr);
907718ced0SMaksim Yevmenkin if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
917718ced0SMaksim Yevmenkin log_err("Could not connect: %m");
927718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
937718ced0SMaksim Yevmenkin }
947718ced0SMaksim Yevmenkin
957718ced0SMaksim Yevmenkin len = sizeof(mru);
967718ced0SMaksim Yevmenkin if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
977718ced0SMaksim Yevmenkin log_err("Could not get IMTU: %m");
987718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
997718ced0SMaksim Yevmenkin }
1007718ced0SMaksim Yevmenkin if (mru < BNEP_MTU_MIN) {
1017718ced0SMaksim Yevmenkin log_err("L2CAP IMTU too small (%d)", mru);
1027718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1037718ced0SMaksim Yevmenkin }
1047718ced0SMaksim Yevmenkin
1052dcf7e97SMaksim Yevmenkin len = sizeof(n);
1062dcf7e97SMaksim Yevmenkin if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) {
1072dcf7e97SMaksim Yevmenkin log_err("Could not read SO_RCVBUF");
1082dcf7e97SMaksim Yevmenkin exit(EXIT_FAILURE);
1092dcf7e97SMaksim Yevmenkin }
1102dcf7e97SMaksim Yevmenkin if (n < (mru * 10)) {
1112dcf7e97SMaksim Yevmenkin n = mru * 10;
1122dcf7e97SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
1132dcf7e97SMaksim Yevmenkin log_info("Could not increase SO_RCVBUF (from %d)", n);
1142dcf7e97SMaksim Yevmenkin }
1152dcf7e97SMaksim Yevmenkin
1167718ced0SMaksim Yevmenkin len = sizeof(mtu);
1177718ced0SMaksim Yevmenkin if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
1187718ced0SMaksim Yevmenkin log_err("Could not get L2CAP OMTU: %m");
1197718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1207718ced0SMaksim Yevmenkin }
1217718ced0SMaksim Yevmenkin if (mtu < BNEP_MTU_MIN) {
1227718ced0SMaksim Yevmenkin log_err("L2CAP OMTU too small (%d)", mtu);
1237718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1247718ced0SMaksim Yevmenkin }
1257718ced0SMaksim Yevmenkin
1262dcf7e97SMaksim Yevmenkin len = sizeof(n);
1272dcf7e97SMaksim Yevmenkin if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
1282dcf7e97SMaksim Yevmenkin log_err("Could not get socket send buffer size: %m");
1292dcf7e97SMaksim Yevmenkin close(fd);
1302dcf7e97SMaksim Yevmenkin return;
1312dcf7e97SMaksim Yevmenkin }
1322dcf7e97SMaksim Yevmenkin if (n < (mtu * 2)) {
1332dcf7e97SMaksim Yevmenkin n = mtu * 2;
1342dcf7e97SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
1352dcf7e97SMaksim Yevmenkin log_err("Could not set socket send buffer size (%d): %m", n);
1362dcf7e97SMaksim Yevmenkin close(fd);
1372dcf7e97SMaksim Yevmenkin return;
1382dcf7e97SMaksim Yevmenkin }
1392dcf7e97SMaksim Yevmenkin }
1402dcf7e97SMaksim Yevmenkin n = mtu;
1412dcf7e97SMaksim Yevmenkin if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
1422dcf7e97SMaksim Yevmenkin log_err("Could not set socket low water mark (%d): %m", n);
1432dcf7e97SMaksim Yevmenkin close(fd);
1442dcf7e97SMaksim Yevmenkin return;
1452dcf7e97SMaksim Yevmenkin }
1462dcf7e97SMaksim Yevmenkin
1477718ced0SMaksim Yevmenkin chan = channel_alloc();
1487718ced0SMaksim Yevmenkin if (chan == NULL)
1497718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1507718ced0SMaksim Yevmenkin
1517718ced0SMaksim Yevmenkin chan->send = bnep_send;
1527718ced0SMaksim Yevmenkin chan->recv = bnep_recv;
1537718ced0SMaksim Yevmenkin chan->mru = mru;
1547718ced0SMaksim Yevmenkin chan->mtu = mtu;
1557718ced0SMaksim Yevmenkin b2eaddr(chan->raddr, &remote_bdaddr);
1567718ced0SMaksim Yevmenkin b2eaddr(chan->laddr, &local_bdaddr);
1577718ced0SMaksim Yevmenkin chan->state = CHANNEL_WAIT_CONNECT_RSP;
1587718ced0SMaksim Yevmenkin channel_timeout(chan, 10);
1597718ced0SMaksim Yevmenkin if (!channel_open(chan, fd))
1607718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1617718ced0SMaksim Yevmenkin
1627718ced0SMaksim Yevmenkin bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST,
1637718ced0SMaksim Yevmenkin 2, service_class, SDP_SERVICE_CLASS_PANU);
1647718ced0SMaksim Yevmenkin }
1657718ced0SMaksim Yevmenkin
1667718ced0SMaksim Yevmenkin static void
client_query(void)1677718ced0SMaksim Yevmenkin client_query(void)
1687718ced0SMaksim Yevmenkin {
1697718ced0SMaksim Yevmenkin uint8_t buffer[512];
1707718ced0SMaksim Yevmenkin sdp_attr_t attr;
1717718ced0SMaksim Yevmenkin uint32_t range;
1727718ced0SMaksim Yevmenkin void *ss;
1737718ced0SMaksim Yevmenkin int rv;
1747718ced0SMaksim Yevmenkin uint8_t *seq0, *seq1;
1757718ced0SMaksim Yevmenkin
1767718ced0SMaksim Yevmenkin attr.flags = SDP_ATTR_INVALID;
1777718ced0SMaksim Yevmenkin attr.attr = 0;
1787718ced0SMaksim Yevmenkin attr.vlen = sizeof(buffer);
1797718ced0SMaksim Yevmenkin attr.value = buffer;
1807718ced0SMaksim Yevmenkin
1817718ced0SMaksim Yevmenkin range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
1827718ced0SMaksim Yevmenkin SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
1837718ced0SMaksim Yevmenkin
1847718ced0SMaksim Yevmenkin ss = sdp_open(&local_bdaddr, &remote_bdaddr);
1857718ced0SMaksim Yevmenkin if (ss == NULL || (errno = sdp_error(ss)) != 0) {
1867718ced0SMaksim Yevmenkin log_err("%s: %m", service_name);
1877718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1887718ced0SMaksim Yevmenkin }
1897718ced0SMaksim Yevmenkin
1907718ced0SMaksim Yevmenkin log_info("Searching for %s service at %s",
1917718ced0SMaksim Yevmenkin service_name, bt_ntoa(&remote_bdaddr, NULL));
1927718ced0SMaksim Yevmenkin
1937718ced0SMaksim Yevmenkin rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr);
1947718ced0SMaksim Yevmenkin if (rv != 0) {
1957718ced0SMaksim Yevmenkin log_err("%s: %s", service_name, strerror(sdp_error(ss)));
1967718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
1977718ced0SMaksim Yevmenkin }
1987718ced0SMaksim Yevmenkin
1997718ced0SMaksim Yevmenkin sdp_close(ss);
2007718ced0SMaksim Yevmenkin
2017718ced0SMaksim Yevmenkin if (attr.flags != SDP_ATTR_OK
2027718ced0SMaksim Yevmenkin || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) {
2037718ced0SMaksim Yevmenkin log_err("%s service not found", service_name);
2047718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
2057718ced0SMaksim Yevmenkin }
2067718ced0SMaksim Yevmenkin
2077718ced0SMaksim Yevmenkin /*
2087718ced0SMaksim Yevmenkin * we expect the following protocol descriptor list
2097718ced0SMaksim Yevmenkin *
2107718ced0SMaksim Yevmenkin * seq len
2117718ced0SMaksim Yevmenkin * seq len
2127718ced0SMaksim Yevmenkin * uuid value == L2CAP
2137718ced0SMaksim Yevmenkin * uint16 value16 => PSM
2147718ced0SMaksim Yevmenkin * seq len
2157718ced0SMaksim Yevmenkin * uuid value == BNEP
2167718ced0SMaksim Yevmenkin */
2177718ced0SMaksim Yevmenkin if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0)
2187718ced0SMaksim Yevmenkin && _sdp_get_seq(&seq0, attr.value, &seq1)
2197718ced0SMaksim Yevmenkin && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP)
2207718ced0SMaksim Yevmenkin && _sdp_get_uint16(&seq1, seq0, &l2cap_psm)
2217718ced0SMaksim Yevmenkin && _sdp_get_seq(&seq0, attr.value, &seq1)
2227718ced0SMaksim Yevmenkin && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) {
2237718ced0SMaksim Yevmenkin log_info("Found PSM %d for service %s", l2cap_psm, service_name);
2247718ced0SMaksim Yevmenkin return;
2257718ced0SMaksim Yevmenkin }
2267718ced0SMaksim Yevmenkin
2277718ced0SMaksim Yevmenkin log_err("%s query failed", service_name);
2287718ced0SMaksim Yevmenkin exit(EXIT_FAILURE);
2297718ced0SMaksim Yevmenkin }
230