1/*
2 * rfcomm_sppd.c
3 */
4
5/*-
6 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
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 AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: rfcomm_sppd.c,v 1.4 2003/09/07 18:15:55 max Exp $
31 * $FreeBSD$
32 */
33
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <bluetooth.h>
37#include <ctype.h>
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <grp.h>
42#include <limits.h>
43#include <paths.h>
44#include <sdp.h>
45#include <signal.h>
46#include <stdarg.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <syslog.h>
51#include <termios.h>
52#include <unistd.h>
53#include <libutil.h>
54
55#define SPPD_IDENT		"rfcomm_sppd"
56#define SPPD_BUFFER_SIZE	1024
57#define max(a, b)		(((a) > (b))? (a) : (b))
58
59int		rfcomm_channel_lookup	(bdaddr_t const *local,
60					 bdaddr_t const *remote,
61					 int service, int *channel, int *error);
62
63static int	sppd_ttys_open	(char **tty, int *amaster, int *aslave);
64static int	sppd_read	(int fd, char *buffer, int size);
65static int	sppd_write	(int fd, char *buffer, int size);
66static void	sppd_sighandler	(int s);
67static void	usage		(void);
68
69static int	done;	/* are we done? */
70
71/* Main */
72int
73main(int argc, char *argv[])
74{
75	struct sigaction	 sa;
76	struct sockaddr_rfcomm	 ra;
77	bdaddr_t		 addr;
78	int			 n, background, channel, service,
79				 s, amaster, aslave, fd, doserver,
80				 dopty;
81	fd_set			 rfd;
82	char			*tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE];
83
84	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
85	background = channel = 0;
86	service = SDP_SERVICE_CLASS_SERIAL_PORT;
87	doserver = 0;
88	dopty = 0;
89
90	/* Parse command line options */
91	while ((n = getopt(argc, argv, "a:bc:thS")) != -1) {
92		switch (n) {
93		case 'a': /* BDADDR */
94			if (!bt_aton(optarg, &addr)) {
95				struct hostent	*he = NULL;
96
97				if ((he = bt_gethostbyname(optarg)) == NULL)
98					errx(1, "%s: %s", optarg, hstrerror(h_errno));
99
100				memcpy(&addr, he->h_addr, sizeof(addr));
101			}
102			break;
103
104		case 'c': /* RFCOMM channel */
105			channel = strtoul(optarg, &ep, 10);
106			if (*ep != '\0') {
107				channel = 0;
108				switch (tolower(optarg[0])) {
109				case 'd': /* DialUp Networking */
110					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
111					break;
112
113				case 'f': /* Fax */
114					service = SDP_SERVICE_CLASS_FAX;
115					break;
116
117				case 'l': /* LAN */
118					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
119					break;
120
121				case 's': /* Serial Port */
122					service = SDP_SERVICE_CLASS_SERIAL_PORT;
123					break;
124
125				default:
126					errx(1, "Unknown service name: %s",
127						optarg);
128					/* NOT REACHED */
129				}
130			}
131			break;
132
133		case 'b': /* Run in background */
134			background = 1;
135			break;
136
137		case 't': /* Open pseudo TTY */
138			dopty = 1;
139			break;
140
141		case 'S':
142			doserver = 1;
143			break;
144
145		case 'h':
146		default:
147			usage();
148			/* NOT REACHED */
149		}
150	}
151
152	/* Check if we have everything we need */
153	if (!doserver && memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
154		usage();
155		/* NOT REACHED */
156
157	/* Set signal handlers */
158	memset(&sa, 0, sizeof(sa));
159	sa.sa_handler = sppd_sighandler;
160
161	if (sigaction(SIGTERM, &sa, NULL) < 0)
162		err(1, "Could not sigaction(SIGTERM)");
163
164	if (sigaction(SIGHUP, &sa, NULL) < 0)
165		err(1, "Could not sigaction(SIGHUP)");
166
167	if (sigaction(SIGINT, &sa, NULL) < 0)
168		err(1, "Could not sigaction(SIGINT)");
169
170	sa.sa_handler = SIG_IGN;
171	sa.sa_flags = SA_NOCLDWAIT;
172
173	if (sigaction(SIGCHLD, &sa, NULL) < 0)
174		err(1, "Could not sigaction(SIGCHLD)");
175
176	/* Open TTYs */
177	if (dopty) {
178		if (sppd_ttys_open(&tty, &amaster, &aslave) < 0)
179			exit(1);
180
181		fd = amaster;
182	} else {
183		if (background)
184			usage();
185
186		amaster = STDIN_FILENO;
187		fd = STDOUT_FILENO;
188	}
189
190	/* Open RFCOMM connection */
191
192	if (doserver) {
193		struct sockaddr_rfcomm	 ma;
194		bdaddr_t		 bt_addr_any;
195		sdp_sp_profile_t	 sp;
196		void			*ss;
197		uint32_t		 sdp_handle;
198		int			 acceptsock, aaddrlen;
199
200		acceptsock = socket(PF_BLUETOOTH, SOCK_STREAM,
201					BLUETOOTH_PROTO_RFCOMM);
202		if (acceptsock < 0)
203			err(1, "Could not create socket");
204
205		memcpy(&bt_addr_any, NG_HCI_BDADDR_ANY, sizeof(bt_addr_any));
206
207		memset(&ma, 0, sizeof(ma));
208		ma.rfcomm_len = sizeof(ma);
209		ma.rfcomm_family = AF_BLUETOOTH;
210		memcpy(&ma.rfcomm_bdaddr, &bt_addr_any, sizeof(bt_addr_any));
211		ma.rfcomm_channel = channel;
212
213		if (bind(acceptsock, (struct sockaddr *)&ma, sizeof(ma)) < 0)
214			err(1, "Could not bind socket on channel %d", channel);
215		if (listen(acceptsock, 10) != 0)
216			err(1, "Could not listen on socket");
217
218		aaddrlen = sizeof(ma);
219		if (getsockname(acceptsock, (struct sockaddr *)&ma, &aaddrlen) < 0)
220			err(1, "Could not get socket name");
221		channel = ma.rfcomm_channel;
222
223		ss = sdp_open_local(NULL);
224		if (ss == NULL)
225			errx(1, "Unable to create local SDP session");
226		if (sdp_error(ss) != 0)
227			errx(1, "Unable to open local SDP session. %s (%d)",
228			    strerror(sdp_error(ss)), sdp_error(ss));
229		memset(&sp, 0, sizeof(sp));
230		sp.server_channel = channel;
231
232		if (sdp_register_service(ss, SDP_SERVICE_CLASS_SERIAL_PORT,
233				&bt_addr_any, (void *)&sp, sizeof(sp),
234				&sdp_handle) != 0) {
235			errx(1, "Unable to register LAN service with "
236			    "local SDP daemon. %s (%d)",
237			    strerror(sdp_error(ss)), sdp_error(ss));
238		}
239
240		s = -1;
241		while (s < 0) {
242			aaddrlen = sizeof(ra);
243			s = accept(acceptsock, (struct sockaddr *)&ra,
244			    &aaddrlen);
245			if (s < 0)
246				err(1, "Unable to accept()");
247			if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) &&
248			    memcmp(&addr, &ra.rfcomm_bdaddr, sizeof(addr))) {
249				warnx("Connect from wrong client");
250				close(s);
251				s = -1;
252			}
253		}
254		sdp_unregister_service(ss, sdp_handle);
255		sdp_close(ss);
256		close(acceptsock);
257	} else {
258		/* Check channel, if was not set then obtain it via SDP */
259		if (channel == 0 && service != 0)
260			if (rfcomm_channel_lookup(NULL, &addr,
261				    service, &channel, &n) != 0)
262				errc(1, n, "Could not obtain RFCOMM channel");
263		if (channel <= 0 || channel > 30)
264			errx(1, "Invalid RFCOMM channel number %d", channel);
265
266		s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
267		if (s < 0)
268			err(1, "Could not create socket");
269
270		memset(&ra, 0, sizeof(ra));
271		ra.rfcomm_len = sizeof(ra);
272		ra.rfcomm_family = AF_BLUETOOTH;
273
274		if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0)
275			err(1, "Could not bind socket");
276
277		memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr));
278		ra.rfcomm_channel = channel;
279
280		if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0)
281			err(1, "Could not connect socket");
282	}
283
284	/* Became daemon if required */
285	if (background && daemon(0, 0) < 0)
286		err(1, "Could not daemon()");
287
288	openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON);
289	syslog(LOG_INFO, "Starting on %s...", (tty != NULL)? tty : "stdin/stdout");
290
291	/* Print used tty on stdout for wrappers to pick up */
292	if (!background)
293		fprintf(stdout, "%s\n", tty);
294
295	for (done = 0; !done; ) {
296		FD_ZERO(&rfd);
297		FD_SET(amaster, &rfd);
298		FD_SET(s, &rfd);
299
300		n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL);
301		if (n < 0) {
302			if (errno == EINTR)
303				continue;
304
305			syslog(LOG_ERR, "Could not select(). %s",
306					strerror(errno));
307			exit(1);
308		}
309
310		if (n == 0)
311			continue;
312
313		if (FD_ISSET(amaster, &rfd)) {
314			n = sppd_read(amaster, buf, sizeof(buf));
315			if (n < 0) {
316				syslog(LOG_ERR, "Could not read master pty, " \
317					"fd=%d. %s", amaster, strerror(errno));
318				exit(1);
319			}
320
321			if (n == 0)
322				break; /* XXX */
323
324			if (sppd_write(s, buf, n) < 0) {
325				syslog(LOG_ERR, "Could not write to socket, " \
326					"fd=%d, size=%d. %s",
327					s, n, strerror(errno));
328				exit(1);
329			}
330		}
331
332		if (FD_ISSET(s, &rfd)) {
333			n = sppd_read(s, buf, sizeof(buf));
334			if (n < 0) {
335				syslog(LOG_ERR, "Could not read socket, " \
336					"fd=%d. %s", s, strerror(errno));
337				exit(1);
338			}
339
340			if (n == 0)
341				break;
342
343			if (sppd_write(fd, buf, n) < 0) {
344				syslog(LOG_ERR, "Could not write to master " \
345					"pty, fd=%d, size=%d. %s",
346					fd, n, strerror(errno));
347				exit(1);
348			}
349		}
350	}
351
352	syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout");
353	closelog();
354
355	close(s);
356
357	if (tty != NULL) {
358		close(aslave);
359		close(amaster);
360	}
361
362	return (0);
363}
364
365/* Open TTYs */
366static int
367sppd_ttys_open(char **tty, int *amaster, int *aslave)
368{
369	char		 pty[PATH_MAX];
370	struct termios	 tio;
371
372	cfmakeraw(&tio);
373
374	if (openpty(amaster, aslave, pty, &tio, NULL) == -1) {
375		syslog(LOG_ERR, "Could not openpty(). %s", strerror(errno));
376		return (-1);
377	}
378
379	if ((*tty = strdup(pty)) == NULL) {
380		syslog(LOG_ERR, "Could not strdup(). %s", strerror(errno));
381		close(*aslave);
382		close(*amaster);
383		return (-1);
384	}
385
386	return (0);
387} /* sppd_ttys_open */
388
389/* Read data */
390static int
391sppd_read(int fd, char *buffer, int size)
392{
393	int	n;
394
395again:
396	n = read(fd, buffer, size);
397	if (n < 0) {
398		if (errno == EINTR)
399			goto again;
400
401		return (-1);
402	}
403
404	return (n);
405} /* sppd_read */
406
407/* Write data */
408static int
409sppd_write(int fd, char *buffer, int size)
410{
411	int	n, wrote;
412
413	for (wrote = 0; size > 0; ) {
414		n = write(fd, buffer, size);
415		switch (n) {
416		case -1:
417			if (errno != EINTR)
418				return (-1);
419			break;
420
421		case 0:
422			/* XXX can happen? */
423			break;
424
425		default:
426			wrote += n;
427			buffer += n;
428			size -= n;
429			break;
430		}
431	}
432
433	return (wrote);
434} /* sppd_write */
435
436/* Signal handler */
437static void
438sppd_sighandler(int s)
439{
440	syslog(LOG_INFO, "Signal %d received. Total %d signals received\n",
441			s, ++ done);
442} /* sppd_sighandler */
443
444/* Display usage and exit */
445static void
446usage(void)
447{
448	fprintf(stdout,
449"Usage: %s options\n" \
450"Where options are:\n" \
451"\t-a address Peer address (required in client mode)\n" \
452"\t-b         Run in background\n" \
453"\t-c channel RFCOMM channel to connect to or listen on\n" \
454"\t-t         use slave pseudo tty (required in background mode)\n" \
455"\t-S         Server mode\n" \
456"\t-h         Display this message\n", SPPD_IDENT);
457	exit(255);
458} /* usage */
459
460