xref: /netbsd-src/usr.sbin/btpand/server.c (revision c61317e3bf5092d043ad515e3631c97eeba79180)
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