1188013Semax/*	$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $	*/
2187938Semax
3187938Semax/*-
4187938Semax * Copyright (c) 2008 Iain Hibbert
5187938Semax * All rights reserved.
6187938Semax *
7187938Semax * Redistribution and use in source and binary forms, with or without
8187938Semax * modification, are permitted provided that the following conditions
9187938Semax * are met:
10187938Semax * 1. Redistributions of source code must retain the above copyright
11187938Semax *    notice, this list of conditions and the following disclaimer.
12187938Semax * 2. Redistributions in binary form must reproduce the above copyright
13187938Semax *    notice, this list of conditions and the following disclaimer in the
14187938Semax *    documentation and/or other materials provided with the distribution.
15187938Semax *
16187938Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17187938Semax * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18187938Semax * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19187938Semax * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20187938Semax * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21187938Semax * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22187938Semax * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23187938Semax * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24187938Semax * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25187938Semax * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26187938Semax */
27187938Semax
28187938Semax/* $FreeBSD$ */
29187938Semax
30187938Semax#include <sys/cdefs.h>
31188013Semax__RCSID("$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $");
32187938Semax
33187938Semax#include <sys/ioctl.h>
34187938Semax
35187938Semax#include <bluetooth.h>
36188013Semax#include <inttypes.h>
37187938Semax#include <errno.h>
38187938Semax#include <sdp.h>
39187938Semax#include <unistd.h>
40187938Semax
41187938Semax#include "btpand.h"
42187938Semax#include "bnep.h"
43187938Semax
44187938Semaxstatic struct event	server_ev;
45187938Semaxstatic int		server_fd;
46188013Semaxstatic int		server_avail;
47187938Semax
48187938Semaxstatic void *		server_ss;
49187938Semaxstatic uint32_t		server_handle;
50187938Semax
51187938Semaxstatic void server_open(void);
52187938Semaxstatic void server_close(void);
53187938Semaxstatic void server_read(int, short, void *);
54187938Semaxstatic void server_register(void);
55187938Semax
56187938Semaxvoid
57187938Semaxserver_init(void)
58187938Semax{
59187938Semax
60187938Semax	server_fd = -1;
61187938Semax}
62187938Semax
63187938Semax/*
64187938Semax * The server_update() function is called whenever the channel count is
65187938Semax * changed. We maintain the SDP record and open or close the server socket
66187938Semax * as required.
67187938Semax */
68187938Semaxvoid
69187938Semaxserver_update(int count)
70187938Semax{
71187938Semax
72187938Semax	if (server_limit == 0)
73187938Semax		return;
74187938Semax
75187938Semax	log_debug("count %d", count);
76187938Semax
77188013Semax	server_avail = UINT8_MAX - (count - 1) * UINT8_MAX / server_limit;
78188013Semax	log_info("Service Availability: %d/%d", server_avail, UINT8_MAX);
79187938Semax
80188013Semax	if (server_avail == 0 && server_fd != -1)
81187938Semax		server_close();
82187938Semax
83188013Semax	if (server_avail > 0 && server_fd == -1)
84187938Semax		server_open();
85187938Semax
86187938Semax	if (service_name)
87187938Semax		server_register();
88187938Semax}
89187938Semax
90187938Semaxstatic void
91187938Semaxserver_open(void)
92187938Semax{
93187938Semax	struct sockaddr_l2cap sa;
94187938Semax	uint16_t mru;
95187938Semax
96187938Semax	server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
97187938Semax	if (server_fd == -1) {
98187938Semax		log_err("Could not open L2CAP socket: %m");
99187938Semax		exit(EXIT_FAILURE);
100187938Semax	}
101187938Semax
102187938Semax	memset(&sa, 0, sizeof(sa));
103187938Semax	sa.l2cap_family = AF_BLUETOOTH;
104187938Semax	sa.l2cap_len = sizeof(sa);
105187938Semax	sa.l2cap_psm = htole16(l2cap_psm);
106187938Semax	bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr);
107187938Semax	if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
108187938Semax		log_err("Could not bind server socket: %m");
109187938Semax		exit(EXIT_FAILURE);
110187938Semax	}
111187938Semax
112187938Semax	mru = BNEP_MTU_MIN;
113187938Semax	if (setsockopt(server_fd, SOL_L2CAP,
114187938Semax	    SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
115187938Semax		log_err("Could not set L2CAP IMTU (%d): %m", mru);
116187938Semax		exit(EXIT_FAILURE);
117187938Semax	}
118187938Semax
119187938Semax	if (listen(server_fd, 0) == -1) {
120187938Semax		log_err("Could not listen on server socket: %m");
121187938Semax		exit(EXIT_FAILURE);
122187938Semax	}
123187938Semax
124187938Semax	event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL);
125187938Semax	if (event_add(&server_ev, NULL) == -1) {
126187938Semax		log_err("Could not add server event: %m");
127187938Semax		exit(EXIT_FAILURE);
128187938Semax	}
129187938Semax
130187938Semax	log_info("server socket open");
131187938Semax}
132187938Semax
133187938Semaxstatic void
134187938Semaxserver_close(void)
135187938Semax{
136187938Semax
137187938Semax	event_del(&server_ev);
138187938Semax	close(server_fd);
139187938Semax	server_fd = -1;
140187938Semax
141187938Semax	log_info("server socket closed");
142187938Semax}
143187938Semax
144187938Semax/*
145187938Semax * handle connection request
146187938Semax */
147187938Semaxstatic void
148187938Semaxserver_read(int s, short ev, void *arg)
149187938Semax{
150187938Semax	struct sockaddr_l2cap ra, la;
151187938Semax	channel_t *chan;
152187938Semax	socklen_t len;
153187938Semax	int fd, n;
154187938Semax	uint16_t mru, mtu;
155187938Semax
156187938Semax	len = sizeof(ra);
157187938Semax	fd = accept(s, (struct sockaddr *)&ra, &len);
158187938Semax	if (fd == -1)
159187938Semax		return;
160187938Semax
161187938Semax	n = 1;
162187938Semax	if (ioctl(fd, FIONBIO, &n) == -1) {
163187938Semax		log_err("Could not set NonBlocking IO: %m");
164187938Semax		close(fd);
165187938Semax		return;
166187938Semax	}
167187938Semax
168187938Semax	len = sizeof(mru);
169187938Semax	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
170187938Semax		log_err("Could not get L2CAP IMTU: %m");
171187938Semax		close(fd);
172187938Semax		return;
173187938Semax	}
174187938Semax	if(mru < BNEP_MTU_MIN) {
175187938Semax		log_err("L2CAP IMTU too small (%d)", mru);
176187938Semax		close(fd);
177187938Semax		return;
178187938Semax	}
179187938Semax
180187938Semax	len = sizeof(mtu);
181187938Semax	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
182187938Semax		log_err("Could not get L2CAP OMTU: %m");
183187938Semax		close(fd);
184187938Semax		return;
185187938Semax	}
186187938Semax	if (mtu < BNEP_MTU_MIN) {
187187938Semax		log_err("L2CAP OMTU too small (%d)", mtu);
188187938Semax		close(fd);
189187938Semax		return;
190187938Semax	}
191187938Semax
192187938Semax	len = sizeof(n);
193187938Semax	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
194187938Semax		log_err("Could not get socket send buffer size: %m");
195187938Semax		close(fd);
196187938Semax		return;
197187938Semax	}
198187938Semax
199187938Semax	if (n < (mtu * 2)) {
200187938Semax		n = mtu * 2;
201187938Semax		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
202187938Semax			log_err("Could not set socket send buffer size (%d): %m", n);
203187938Semax			close(fd);
204187938Semax			return;
205187938Semax		}
206187938Semax	}
207187938Semax
208187938Semax	n = mtu;
209187938Semax	if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
210187938Semax		log_err("Could not set socket low water mark (%d): %m", n);
211187938Semax		close(fd);
212187938Semax		return;
213187938Semax	}
214187938Semax
215187938Semax	len = sizeof(la);
216187938Semax	if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) {
217187938Semax		log_err("Could not get socket address: %m");
218187938Semax		close(fd);
219187938Semax		return;
220187938Semax	}
221187938Semax
222187938Semax	log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL));
223187938Semax
224187938Semax	chan = channel_alloc();
225187938Semax	if (chan == NULL) {
226187938Semax		close(fd);
227187938Semax		return;
228187938Semax	}
229187938Semax
230187938Semax	chan->send = bnep_send;
231187938Semax	chan->recv = bnep_recv;
232187938Semax	chan->mru = mru;
233187938Semax	chan->mtu = mtu;
234187938Semax	b2eaddr(chan->raddr, &ra.l2cap_bdaddr);
235187938Semax	b2eaddr(chan->laddr, &la.l2cap_bdaddr);
236187938Semax	chan->state = CHANNEL_WAIT_CONNECT_REQ;
237187938Semax	channel_timeout(chan, 10);
238187938Semax	if (!channel_open(chan, fd)) {
239187938Semax		chan->state = CHANNEL_CLOSED;
240187938Semax		channel_free(chan);
241187938Semax		close(fd);
242187938Semax		return;
243187938Semax	}
244187938Semax}
245187938Semax
246187938Semaxstatic void
247187938Semaxserver_register(void)
248187938Semax{
249187938Semax	sdp_nap_profile_t p;
250187938Semax	int rv;
251187938Semax
252187938Semax	if (server_ss == NULL) {
253187938Semax		server_ss = sdp_open_local(control_path);
254187938Semax		if (server_ss == NULL || sdp_error(server_ss) != 0) {
255187938Semax			log_err("failed to contact SDP server");
256187938Semax			return;
257187938Semax		}
258187938Semax	}
259187938Semax
260187938Semax	memset(&p, 0, sizeof(p));
261187938Semax	p.psm = l2cap_psm;
262188013Semax	p.load_factor = server_avail;
263188013Semax	p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001);
264187938Semax
265187938Semax	if (server_handle)
266187938Semax		rv = sdp_change_service(server_ss, server_handle,
267187938Semax		    (uint8_t *)&p, sizeof(p));
268187938Semax	else
269187938Semax		rv = sdp_register_service(server_ss, service_class,
270187938Semax		    &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle);
271187938Semax
272187938Semax	if (rv != 0) {
273187938Semax		errno = sdp_error(server_ss);
274187938Semax		log_err("%s: %m", service_name);
275187938Semax		exit(EXIT_FAILURE);
276187938Semax	}
277187938Semax}
278