1/*	$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5 *
6 * Copyright (c) 2008 Iain Hibbert
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/* $FreeBSD: stable/11/usr.sbin/bluetooth/btpand/server.c 330449 2018-03-05 07:26:05Z eadler $ */
31
32#include <sys/cdefs.h>
33__RCSID("$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $");
34
35#include <sys/ioctl.h>
36
37#define L2CAP_SOCKET_CHECKED
38#include <bluetooth.h>
39#include <inttypes.h>
40#include <errno.h>
41#include <sdp.h>
42#include <unistd.h>
43
44#include "btpand.h"
45#include "bnep.h"
46
47static struct event	server_ev;
48static int		server_fd;
49static int		server_avail;
50
51static void *		server_ss;
52static uint32_t		server_handle;
53
54static void server_open(void);
55static void server_close(void);
56static void server_read(int, short, void *);
57static void server_register(void);
58
59void
60server_init(void)
61{
62
63	server_fd = -1;
64}
65
66/*
67 * The server_update() function is called whenever the channel count is
68 * changed. We maintain the SDP record and open or close the server socket
69 * as required.
70 */
71void
72server_update(int count)
73{
74
75	if (server_limit == 0)
76		return;
77
78	log_debug("count %d", count);
79
80	server_avail = UINT8_MAX - (count - 1) * UINT8_MAX / server_limit;
81	log_info("Service Availability: %d/%d", server_avail, UINT8_MAX);
82
83	if (server_avail == 0 && server_fd != -1)
84		server_close();
85
86	if (server_avail > 0 && server_fd == -1)
87		server_open();
88
89	if (service_name)
90		server_register();
91}
92
93static void
94server_open(void)
95{
96	struct sockaddr_l2cap sa;
97	uint16_t mru;
98
99	server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
100	if (server_fd == -1) {
101		log_err("Could not open L2CAP socket: %m");
102		exit(EXIT_FAILURE);
103	}
104
105	memset(&sa, 0, sizeof(sa));
106	sa.l2cap_family = AF_BLUETOOTH;
107	sa.l2cap_len = sizeof(sa);
108	sa.l2cap_psm = htole16(l2cap_psm);
109	sa.l2cap_bdaddr_type = BDADDR_BREDR;
110	sa.l2cap_cid = 0;
111
112	bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr);
113	if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
114		log_err("Could not bind server socket: %m");
115		exit(EXIT_FAILURE);
116	}
117
118	mru = BNEP_MTU_MIN;
119	if (setsockopt(server_fd, SOL_L2CAP,
120	    SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
121		log_err("Could not set L2CAP IMTU (%d): %m", mru);
122		exit(EXIT_FAILURE);
123	}
124
125	if (listen(server_fd, 0) == -1) {
126		log_err("Could not listen on server socket: %m");
127		exit(EXIT_FAILURE);
128	}
129
130	event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL);
131	if (event_add(&server_ev, NULL) == -1) {
132		log_err("Could not add server event: %m");
133		exit(EXIT_FAILURE);
134	}
135
136	log_info("server socket open");
137}
138
139static void
140server_close(void)
141{
142
143	event_del(&server_ev);
144	close(server_fd);
145	server_fd = -1;
146
147	log_info("server socket closed");
148}
149
150/*
151 * handle connection request
152 */
153static void
154server_read(int s, short ev, void *arg)
155{
156	struct sockaddr_l2cap ra, la;
157	channel_t *chan;
158	socklen_t len;
159	int fd, n;
160	uint16_t mru, mtu;
161
162	len = sizeof(ra);
163	fd = accept(s, (struct sockaddr *)&ra, &len);
164	if (fd == -1)
165		return;
166
167	n = 1;
168	if (ioctl(fd, FIONBIO, &n) == -1) {
169		log_err("Could not set NonBlocking IO: %m");
170		close(fd);
171		return;
172	}
173
174	len = sizeof(mru);
175	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
176		log_err("Could not get L2CAP IMTU: %m");
177		close(fd);
178		return;
179	}
180	if(mru < BNEP_MTU_MIN) {
181		log_err("L2CAP IMTU too small (%d)", mru);
182		close(fd);
183		return;
184	}
185
186	len = sizeof(n);
187	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) {
188		log_err("Could not read SO_RCVBUF");
189		close(fd);
190		return;
191	}
192	if (n < (mru * 10)) {
193		n = mru * 10;
194		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
195			log_info("Could not increase SO_RCVBUF (from %d)", n);
196	}
197
198	len = sizeof(mtu);
199	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
200		log_err("Could not get L2CAP OMTU: %m");
201		close(fd);
202		return;
203	}
204	if (mtu < BNEP_MTU_MIN) {
205		log_err("L2CAP OMTU too small (%d)", mtu);
206		close(fd);
207		return;
208	}
209
210	len = sizeof(n);
211	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
212		log_err("Could not get socket send buffer size: %m");
213		close(fd);
214		return;
215	}
216
217	if (n < (mtu * 2)) {
218		n = mtu * 2;
219		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
220			log_err("Could not set socket send buffer size (%d): %m", n);
221			close(fd);
222			return;
223		}
224	}
225
226	n = mtu;
227	if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
228		log_err("Could not set socket low water mark (%d): %m", n);
229		close(fd);
230		return;
231	}
232
233	len = sizeof(la);
234	if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) {
235		log_err("Could not get socket address: %m");
236		close(fd);
237		return;
238	}
239
240	log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL));
241
242	chan = channel_alloc();
243	if (chan == NULL) {
244		close(fd);
245		return;
246	}
247
248	chan->send = bnep_send;
249	chan->recv = bnep_recv;
250	chan->mru = mru;
251	chan->mtu = mtu;
252	b2eaddr(chan->raddr, &ra.l2cap_bdaddr);
253	b2eaddr(chan->laddr, &la.l2cap_bdaddr);
254	chan->state = CHANNEL_WAIT_CONNECT_REQ;
255	channel_timeout(chan, 10);
256	if (!channel_open(chan, fd)) {
257		chan->state = CHANNEL_CLOSED;
258		channel_free(chan);
259		close(fd);
260		return;
261	}
262}
263
264static void
265server_register(void)
266{
267	sdp_nap_profile_t p;
268	int rv;
269
270	if (server_ss == NULL) {
271		server_ss = sdp_open_local(control_path);
272		if (server_ss == NULL || sdp_error(server_ss) != 0) {
273			log_err("failed to contact SDP server");
274			return;
275		}
276	}
277
278	memset(&p, 0, sizeof(p));
279	p.psm = l2cap_psm;
280	p.load_factor = server_avail;
281	p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001);
282
283	if (server_handle)
284		rv = sdp_change_service(server_ss, server_handle,
285		    (uint8_t *)&p, sizeof(p));
286	else
287		rv = sdp_register_service(server_ss, service_class,
288		    &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle);
289
290	if (rv != 0) {
291		errno = sdp_error(server_ss);
292		log_err("%s: %m", service_name);
293		exit(EXIT_FAILURE);
294	}
295}
296