1*c61317e3Splunky /* $NetBSD: server.c,v 1.8 2012/10/14 08:31:35 plunky Exp $ */
21fc74d21Splunky
31fc74d21Splunky /*-
4493f204dSplunky * Copyright (c) 2008-2009 Iain Hibbert
51fc74d21Splunky * All rights reserved.
61fc74d21Splunky *
71fc74d21Splunky * Redistribution and use in source and binary forms, with or without
81fc74d21Splunky * modification, are permitted provided that the following conditions
91fc74d21Splunky * are met:
101fc74d21Splunky * 1. Redistributions of source code must retain the above copyright
111fc74d21Splunky * notice, this list of conditions and the following disclaimer.
121fc74d21Splunky * 2. Redistributions in binary form must reproduce the above copyright
131fc74d21Splunky * notice, this list of conditions and the following disclaimer in the
141fc74d21Splunky * documentation and/or other materials provided with the distribution.
151fc74d21Splunky *
161fc74d21Splunky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171fc74d21Splunky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181fc74d21Splunky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191fc74d21Splunky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201fc74d21Splunky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
211fc74d21Splunky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221fc74d21Splunky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231fc74d21Splunky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241fc74d21Splunky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
251fc74d21Splunky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261fc74d21Splunky */
271fc74d21Splunky
281fc74d21Splunky #include <sys/cdefs.h>
29*c61317e3Splunky __RCSID("$NetBSD: server.c,v 1.8 2012/10/14 08:31:35 plunky Exp $");
301fc74d21Splunky
311fc74d21Splunky #include <sys/ioctl.h>
321fc74d21Splunky
33ab1f45acSplunky #include <net/ethertypes.h>
34ab1f45acSplunky
351fc74d21Splunky #include <bluetooth.h>
361fc74d21Splunky #include <errno.h>
371fc74d21Splunky #include <sdp.h>
381fc74d21Splunky #include <unistd.h>
391fc74d21Splunky
401fc74d21Splunky #include "btpand.h"
411fc74d21Splunky #include "bnep.h"
421fc74d21Splunky
431fc74d21Splunky static struct event server_ev;
44493f204dSplunky static int server_count;
451fc74d21Splunky
46ab1f45acSplunky static sdp_session_t server_ss;
471fc74d21Splunky static uint32_t server_handle;
48ab1f45acSplunky static sdp_data_t server_record;
49ab1f45acSplunky
50ab1f45acSplunky static char * server_ipv4_subnet;
51ab1f45acSplunky static char * server_ipv6_subnet;
52ab1f45acSplunky static uint16_t server_proto[] = { ETHERTYPE_IP, ETHERTYPE_ARP, ETHERTYPE_IPV6 };
53ab1f45acSplunky static size_t server_nproto = __arraycount(server_proto);
541fc74d21Splunky
551fc74d21Splunky static void server_open(void);
561fc74d21Splunky static void server_read(int, short, void *);
57493f204dSplunky static void server_down(channel_t *);
58493f204dSplunky static void server_update(void);
59ab1f45acSplunky static void server_mkrecord(void);
601fc74d21Splunky
611fc74d21Splunky void
server_init(void)621fc74d21Splunky server_init(void)
631fc74d21Splunky {
641fc74d21Splunky
651fc74d21Splunky if (server_limit == 0)
661fc74d21Splunky return;
671fc74d21Splunky
681fc74d21Splunky server_open();
69493f204dSplunky server_update();
701fc74d21Splunky }
711fc74d21Splunky
72493f204dSplunky /*
73493f204dSplunky * Start listening on server socket
74493f204dSplunky */
751fc74d21Splunky static void
server_open(void)761fc74d21Splunky server_open(void)
771fc74d21Splunky {
781fc74d21Splunky struct sockaddr_bt sa;
79b04b174cSplunky socklen_t len;
801fc74d21Splunky uint16_t mru;
81493f204dSplunky int fd;
821fc74d21Splunky
83493f204dSplunky fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
84493f204dSplunky if (fd == -1) {
851fc74d21Splunky log_err("Could not open L2CAP socket: %m");
861fc74d21Splunky exit(EXIT_FAILURE);
871fc74d21Splunky }
881fc74d21Splunky
891fc74d21Splunky memset(&sa, 0, sizeof(sa));
901fc74d21Splunky sa.bt_family = AF_BLUETOOTH;
911fc74d21Splunky sa.bt_len = sizeof(sa);
921fc74d21Splunky sa.bt_psm = l2cap_psm;
931fc74d21Splunky bdaddr_copy(&sa.bt_bdaddr, &local_bdaddr);
94493f204dSplunky if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
951fc74d21Splunky log_err("Could not bind server socket: %m");
961fc74d21Splunky exit(EXIT_FAILURE);
971fc74d21Splunky }
981fc74d21Splunky
99493f204dSplunky if (setsockopt(fd, BTPROTO_L2CAP,
1001fc74d21Splunky SO_L2CAP_LM, &l2cap_mode, sizeof(l2cap_mode)) == -1) {
1011fc74d21Splunky log_err("Could not set link mode (0x%4.4x): %m", l2cap_mode);
1021fc74d21Splunky exit(EXIT_FAILURE);
1031fc74d21Splunky }
104b04b174cSplunky len = sizeof(l2cap_mode);
105b04b174cSplunky getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_LM, &l2cap_mode, &len);
1061fc74d21Splunky
1071fc74d21Splunky mru = BNEP_MTU_MIN;
108493f204dSplunky if (setsockopt(fd, BTPROTO_L2CAP,
1091fc74d21Splunky SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
1101fc74d21Splunky log_err("Could not set L2CAP IMTU (%d): %m", mru);
1111fc74d21Splunky exit(EXIT_FAILURE);
1121fc74d21Splunky }
1131fc74d21Splunky
114493f204dSplunky if (listen(fd, 0) == -1) {
1151fc74d21Splunky log_err("Could not listen on server socket: %m");
1161fc74d21Splunky exit(EXIT_FAILURE);
1171fc74d21Splunky }
1181fc74d21Splunky
119493f204dSplunky event_set(&server_ev, fd, EV_READ | EV_PERSIST, server_read, NULL);
1201fc74d21Splunky if (event_add(&server_ev, NULL) == -1) {
1211fc74d21Splunky log_err("Could not add server event: %m");
1221fc74d21Splunky exit(EXIT_FAILURE);
1231fc74d21Splunky }
1241fc74d21Splunky
1251fc74d21Splunky log_info("server socket open");
1261fc74d21Splunky }
1271fc74d21Splunky
1281fc74d21Splunky /*
1291fc74d21Splunky * handle connection request
1301fc74d21Splunky */
1311fc74d21Splunky static void
server_read(int s,short ev,void * arg)1321fc74d21Splunky server_read(int s, short ev, void *arg)
1331fc74d21Splunky {
1341fc74d21Splunky struct sockaddr_bt ra, la;
1351fc74d21Splunky channel_t *chan;
1361fc74d21Splunky socklen_t len;
137*c61317e3Splunky int fd, n;
1381fc74d21Splunky uint16_t mru, mtu;
1391fc74d21Splunky
140493f204dSplunky assert(server_count < server_limit);
141493f204dSplunky
1421fc74d21Splunky len = sizeof(ra);
1431fc74d21Splunky fd = accept(s, (struct sockaddr *)&ra, &len);
1441fc74d21Splunky if (fd == -1)
1451fc74d21Splunky return;
1461fc74d21Splunky
1471fc74d21Splunky n = 1;
1481fc74d21Splunky if (ioctl(fd, FIONBIO, &n) == -1) {
1491fc74d21Splunky log_err("Could not set NonBlocking IO: %m");
1501fc74d21Splunky close(fd);
1511fc74d21Splunky return;
1521fc74d21Splunky }
1531fc74d21Splunky
1541fc74d21Splunky len = sizeof(mru);
1551fc74d21Splunky if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
1561fc74d21Splunky log_err("Could not get L2CAP IMTU: %m");
1571fc74d21Splunky close(fd);
1581fc74d21Splunky return;
1591fc74d21Splunky }
1601fc74d21Splunky if(mru < BNEP_MTU_MIN) {
1611fc74d21Splunky log_err("L2CAP IMTU too small (%d)", mru);
1621fc74d21Splunky close(fd);
1631fc74d21Splunky return;
1641fc74d21Splunky }
1651fc74d21Splunky
166*c61317e3Splunky len = sizeof(n);
167*c61317e3Splunky if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) {
1689119e9c8Splunky log_err("Could not read SO_RCVBUF");
1699119e9c8Splunky close(fd);
1709119e9c8Splunky return;
1719119e9c8Splunky }
172*c61317e3Splunky if (n < 10 * mru) {
173*c61317e3Splunky n = 10 * mru;
174*c61317e3Splunky if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
175*c61317e3Splunky log_info("Could not increase SO_RCVBUF (to %d)", n);
1769119e9c8Splunky }
1779119e9c8Splunky
1781fc74d21Splunky len = sizeof(mtu);
1791fc74d21Splunky if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
1801fc74d21Splunky log_err("Could not get L2CAP OMTU: %m");
1811fc74d21Splunky close(fd);
1821fc74d21Splunky return;
1831fc74d21Splunky }
1841fc74d21Splunky if (mtu < BNEP_MTU_MIN) {
1851fc74d21Splunky log_err("L2CAP OMTU too small (%d)", mtu);
1861fc74d21Splunky close(fd);
1871fc74d21Splunky return;
1881fc74d21Splunky }
1891fc74d21Splunky
1901fc74d21Splunky len = sizeof(n);
1911fc74d21Splunky if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
1921fc74d21Splunky log_err("Could not get socket send buffer size: %m");
1931fc74d21Splunky close(fd);
1941fc74d21Splunky return;
1951fc74d21Splunky }
1961fc74d21Splunky if (n < (mtu * 2)) {
1971fc74d21Splunky n = mtu * 2;
1981fc74d21Splunky if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
1991fc74d21Splunky log_err("Could not set socket send buffer size (%d): %m", n);
2001fc74d21Splunky close(fd);
2011fc74d21Splunky return;
2021fc74d21Splunky }
2031fc74d21Splunky }
2041fc74d21Splunky n = mtu;
2051fc74d21Splunky if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
2061fc74d21Splunky log_err("Could not set socket low water mark (%d): %m", n);
2071fc74d21Splunky close(fd);
2081fc74d21Splunky return;
2091fc74d21Splunky }
2101fc74d21Splunky
2111fc74d21Splunky len = sizeof(la);
2121fc74d21Splunky if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) {
2131fc74d21Splunky log_err("Could not get socket address: %m");
2141fc74d21Splunky close(fd);
2151fc74d21Splunky return;
2161fc74d21Splunky }
2171fc74d21Splunky
2181fc74d21Splunky log_info("Accepted connection from %s", bt_ntoa(&ra.bt_bdaddr, NULL));
2191fc74d21Splunky
2201fc74d21Splunky chan = channel_alloc();
2211fc74d21Splunky if (chan == NULL) {
2221fc74d21Splunky close(fd);
2231fc74d21Splunky return;
2241fc74d21Splunky }
2251fc74d21Splunky
2261fc74d21Splunky chan->send = bnep_send;
2271fc74d21Splunky chan->recv = bnep_recv;
228493f204dSplunky chan->down = server_down;
2291fc74d21Splunky chan->mru = mru;
2301fc74d21Splunky chan->mtu = mtu;
2311fc74d21Splunky b2eaddr(chan->raddr, &ra.bt_bdaddr);
2321fc74d21Splunky b2eaddr(chan->laddr, &la.bt_bdaddr);
2331fc74d21Splunky chan->state = CHANNEL_WAIT_CONNECT_REQ;
2341fc74d21Splunky channel_timeout(chan, 10);
2351fc74d21Splunky if (!channel_open(chan, fd)) {
2361fc74d21Splunky chan->state = CHANNEL_CLOSED;
2371fc74d21Splunky channel_free(chan);
2381fc74d21Splunky close(fd);
2391fc74d21Splunky return;
2401fc74d21Splunky }
241493f204dSplunky
242493f204dSplunky if (++server_count == server_limit) {
243493f204dSplunky log_info("Server limit reached, closing server socket");
244493f204dSplunky event_del(&server_ev);
245493f204dSplunky close(s);
246493f204dSplunky }
247493f204dSplunky
248493f204dSplunky server_update();
249493f204dSplunky }
250493f204dSplunky
251493f204dSplunky /*
252493f204dSplunky * Shut down a server channel, we need to update the service record and
253493f204dSplunky * may want to restart accepting connections on the server socket
254493f204dSplunky */
255493f204dSplunky static void
server_down(channel_t * chan)256493f204dSplunky server_down(channel_t *chan)
257493f204dSplunky {
258493f204dSplunky
259493f204dSplunky assert(server_count > 0);
260493f204dSplunky
261493f204dSplunky channel_close(chan);
262493f204dSplunky
263493f204dSplunky if (server_count-- == server_limit)
264493f204dSplunky server_open();
265493f204dSplunky
266493f204dSplunky server_update();
2671fc74d21Splunky }
2681fc74d21Splunky
2691fc74d21Splunky static void
server_update(void)270493f204dSplunky server_update(void)
2711fc74d21Splunky {
272ab1f45acSplunky bool rv;
2731fc74d21Splunky
274ab1f45acSplunky if (service_type == NULL)
275493f204dSplunky return;
276493f204dSplunky
2771fc74d21Splunky if (server_ss == NULL) {
2781fc74d21Splunky server_ss = sdp_open_local(control_path);
279ab1f45acSplunky if (server_ss == NULL) {
2801fc74d21Splunky log_err("failed to contact SDP server");
2811fc74d21Splunky return;
2821fc74d21Splunky }
2831fc74d21Splunky }
2841fc74d21Splunky
285ab1f45acSplunky server_mkrecord();
2861fc74d21Splunky
287ab1f45acSplunky if (server_handle == 0)
288ab1f45acSplunky rv = sdp_record_insert(server_ss, &local_bdaddr,
289ab1f45acSplunky &server_handle, &server_record);
2901fc74d21Splunky else
291ab1f45acSplunky rv = sdp_record_update(server_ss, server_handle,
292ab1f45acSplunky &server_record);
2931fc74d21Splunky
294ab1f45acSplunky if (!rv) {
295ab1f45acSplunky log_err("%s: %m", service_type);
2961fc74d21Splunky exit(EXIT_FAILURE);
2971fc74d21Splunky }
2981fc74d21Splunky }
299ab1f45acSplunky
300ab1f45acSplunky static void
server_mkrecord(void)301ab1f45acSplunky server_mkrecord(void)
302ab1f45acSplunky {
303ab1f45acSplunky static uint8_t data[256]; /* tis enough */
304ab1f45acSplunky sdp_data_t buf;
305ab1f45acSplunky size_t i;
306ab1f45acSplunky
307ab1f45acSplunky buf.next = data;
308ab1f45acSplunky buf.end = data + sizeof(data);
309ab1f45acSplunky
310ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
311ab1f45acSplunky sdp_put_uint32(&buf, 0x00000000);
312ab1f45acSplunky
313ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
314ab1f45acSplunky sdp_put_seq(&buf, 3);
315ab1f45acSplunky sdp_put_uuid16(&buf, service_class);
316ab1f45acSplunky
317ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
318ab1f45acSplunky sdp_put_seq(&buf, 8 + 10 + 3 * server_nproto);
319ab1f45acSplunky sdp_put_seq(&buf, 6);
320ab1f45acSplunky sdp_put_uuid16(&buf, SDP_UUID_PROTOCOL_L2CAP);
321ab1f45acSplunky sdp_put_uint16(&buf, l2cap_psm);
322ab1f45acSplunky sdp_put_seq(&buf, 8 + 3 * server_nproto);
323ab1f45acSplunky sdp_put_uuid16(&buf, SDP_UUID_PROTOCOL_BNEP);
324ab1f45acSplunky sdp_put_uint16(&buf, 0x0100); /* v1.0 */
325ab1f45acSplunky sdp_put_seq(&buf, 3 * server_nproto);
326ab1f45acSplunky for (i = 0; i < server_nproto; i++)
327ab1f45acSplunky sdp_put_uint16(&buf, server_proto[i]);
328ab1f45acSplunky
329ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_BROWSE_GROUP_LIST);
330ab1f45acSplunky sdp_put_seq(&buf, 3);
331ab1f45acSplunky sdp_put_uuid16(&buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);
332ab1f45acSplunky
333ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
334ab1f45acSplunky sdp_put_seq(&buf, 9);
335ab1f45acSplunky sdp_put_uint16(&buf, 0x656e); /* "en" */
336ab1f45acSplunky sdp_put_uint16(&buf, 106); /* UTF-8 */
337ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);
338ab1f45acSplunky
339ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_SERVICE_AVAILABILITY);
340ab1f45acSplunky sdp_put_uint8(&buf, (UINT8_MAX - server_count * UINT8_MAX / server_limit));
341ab1f45acSplunky
342ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
343ab1f45acSplunky sdp_put_seq(&buf, 8);
344ab1f45acSplunky sdp_put_seq(&buf, 6);
345ab1f45acSplunky sdp_put_uuid16(&buf, service_class);
346ab1f45acSplunky sdp_put_uint16(&buf, 0x0100); /* v1.0 */
347ab1f45acSplunky
348ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
349ab1f45acSplunky + SDP_ATTR_SERVICE_NAME_OFFSET);
350ab1f45acSplunky sdp_put_str(&buf, service_name, -1);
351ab1f45acSplunky
352ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
353ab1f45acSplunky + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET);
354ab1f45acSplunky sdp_put_str(&buf, service_desc, -1);
355ab1f45acSplunky
356ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_SECURITY_DESCRIPTION);
357b04b174cSplunky sdp_put_uint16(&buf, (l2cap_mode & L2CAP_LM_AUTH) ? 0x0001 : 0x0000);
358ab1f45acSplunky
359ab1f45acSplunky if (service_class == SDP_SERVICE_CLASS_NAP) {
360ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_NET_ACCESS_TYPE);
361ab1f45acSplunky sdp_put_uint16(&buf, 0x0004); /* 10Mb Ethernet */
362ab1f45acSplunky
363ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_MAX_NET_ACCESS_RATE);
364f379f083Splunky sdp_put_uint32(&buf, IF_Mbps(10) / 8); /* octets/second */
365ab1f45acSplunky }
366ab1f45acSplunky
367ab1f45acSplunky if (service_class == SDP_SERVICE_CLASS_NAP
368ab1f45acSplunky || service_class == SDP_SERVICE_CLASS_GN) {
369ab1f45acSplunky if (server_ipv4_subnet) {
370ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_IPV4_SUBNET);
371ab1f45acSplunky sdp_put_str(&buf, server_ipv4_subnet, -1);
372ab1f45acSplunky }
373ab1f45acSplunky
374ab1f45acSplunky if (server_ipv6_subnet) {
375ab1f45acSplunky sdp_put_uint16(&buf, SDP_ATTR_IPV6_SUBNET);
376ab1f45acSplunky sdp_put_str(&buf, server_ipv6_subnet, -1);
377ab1f45acSplunky }
378ab1f45acSplunky }
379ab1f45acSplunky
380ab1f45acSplunky server_record.next = data;
381ab1f45acSplunky server_record.end = buf.next;
382ab1f45acSplunky }
383