rfcomm_pppd.c revision 121054
1/*
2 * rfcomm_pppd.c
3 *
4 * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
29 * $FreeBSD: head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c 121054 2003-10-12 22:04:24Z emax $
30 */
31
32#include <bluetooth.h>
33#include <ctype.h>
34#include <err.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <sdp.h>
38#include <signal.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <syslog.h>
44#include <unistd.h>
45
46#define RFCOMM_PPPD	"rfcomm_pppd"
47
48int		rfcomm_channel_lookup	(bdaddr_t const *local,
49					 bdaddr_t const *remote,
50					 int service, int *channel, int *error);
51
52static void	exec_ppp	(int s, char *label);
53static void	sighandler	(int s);
54static void	usage		(void);
55
56static int	done;
57
58/* Main */
59int
60main(int argc, char *argv[])
61{
62	struct sockaddr_rfcomm   sock_addr;
63	char			*label = NULL, *ep = NULL;
64	bdaddr_t		 addr;
65	int			 s, channel, detach, server, service;
66	pid_t			 pid;
67
68	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
69	channel = 0;
70	detach = 1;
71	server = 0;
72	service = 0;
73
74	/* Parse command line arguments */
75	while ((s = getopt(argc, argv, "a:cC:dhl:s")) != -1) {
76		switch (s) {
77		case 'a': /* BDADDR */
78			if (!bt_aton(optarg, &addr)) {
79				struct hostent	*he = NULL;
80
81				if ((he = bt_gethostbyname(optarg)) == NULL)
82					errx(1, "%s: %s", optarg, hstrerror(h_errno));
83
84				memcpy(&addr, he->h_addr, sizeof(addr));
85			}
86			break;
87
88		case 'c': /* client */
89			server = 0;
90			break;
91
92		case 'C': /* RFCOMM channel */
93			channel = strtoul(optarg, &ep, 10);
94			if (*ep != 0) {
95				channel = 0;
96				switch (tolower(optarg[0])) {
97				case 'd': /* DialUp Networking */
98					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
99					break;
100
101				case 'l': /* LAN Access Using PPP */
102					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
103					break;
104				}
105			}
106			break;
107
108		case 'd': /* do not detach */
109			detach = 0;
110			break;
111
112		case 'l': /* PPP label */
113			label = optarg;
114			break;
115
116		case 's':
117			server = 1;
118			break;
119
120		case 'h':
121		default:
122			usage();
123			/* NOT REACHED */
124		}
125	}
126
127	/* Check if we got everything we wanted */
128	if (label == NULL)
129                errx(1, "Must specify PPP label");
130
131	if (!server) {
132		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
133                	errx(1, "Must specify server BD_ADDR");
134
135		/* Check channel, if was not set then obtain it via SDP */
136		if (channel == 0 && service != 0)
137			if (rfcomm_channel_lookup(NULL, &addr, service,
138							&channel, &s) != 0)
139				errc(1, s, "Could not obtain RFCOMM channel");
140	}
141
142        if (channel <= 0 || channel > 30)
143                errx(1, "Invalid RFCOMM channel number %d", channel);
144
145	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
146
147	if (detach) {
148		pid = fork();
149		if (pid == (pid_t) -1) {
150			syslog(LOG_ERR, "Could not fork(). %s (%d)",
151				strerror(errno), errno);
152			exit(1);
153		}
154
155		if (pid != 0)
156			exit(0);
157
158		if (daemon(0, 0) < 0) {
159			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
160				strerror(errno), errno);
161			exit(1);
162		}
163	}
164
165	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
166	if (s < 0) {
167		syslog(LOG_ERR, "Could not create socket. %s (%d)",
168			strerror(errno), errno);
169		exit(1);
170	}
171
172	if (server) {
173		struct sigaction	sa;
174
175		/* Install signal handler */
176		memset(&sa, 0, sizeof(sa));
177		sa.sa_handler = sighandler;
178
179		if (sigaction(SIGTERM, &sa, NULL) < 0) {
180			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
181				strerror(errno), errno);
182			exit(1);
183		}
184
185		if (sigaction(SIGHUP, &sa, NULL) < 0) {
186			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
187				strerror(errno), errno);
188			exit(1);
189		}
190
191		if (sigaction(SIGINT, &sa, NULL) < 0) {
192			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
193				strerror(errno), errno);
194			exit(1);
195		}
196
197		sa.sa_handler = SIG_IGN;
198		sa.sa_flags = SA_NOCLDWAIT;
199
200		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
201			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
202				strerror(errno), errno);
203			exit(1);
204		}
205
206		/* bind socket and listen for incoming connections */
207		sock_addr.rfcomm_len = sizeof(sock_addr);
208		sock_addr.rfcomm_family = AF_BLUETOOTH;
209		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
210			sizeof(sock_addr.rfcomm_bdaddr));
211		sock_addr.rfcomm_channel = channel;
212
213		if (bind(s, (struct sockaddr *) &sock_addr,
214				sizeof(sock_addr)) < 0) {
215			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
216				strerror(errno), errno);
217			exit(1);
218		}
219
220		if (listen(s, 10) < 0) {
221			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
222				strerror(errno), errno);
223			exit(1);
224		}
225
226		for (done = 0; !done; ) {
227			int	len = sizeof(sock_addr);
228			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
229
230			if (s1 < 0) {
231				syslog(LOG_ERR, "Could not accept connection " \
232					"on socket. %s (%d)", strerror(errno),
233					errno);
234				exit(1);
235			}
236
237			pid = fork();
238			if (pid == (pid_t) -1) {
239				syslog(LOG_ERR, "Could not fork(). %s (%d)",
240					strerror(errno), errno);
241				exit(1);
242			}
243
244			if (pid == 0) {
245				close(s);
246
247				/* Reset signal handler */
248				memset(&sa, 0, sizeof(sa));
249				sa.sa_handler = SIG_DFL;
250
251				sigaction(SIGTERM, &sa, NULL);
252				sigaction(SIGHUP, &sa, NULL);
253				sigaction(SIGINT, &sa, NULL);
254				sigaction(SIGCHLD, &sa, NULL);
255
256				/* Become daemon */
257				daemon(0, 0);
258
259				exec_ppp(s1, label);
260			} else
261				close(s1);
262		}
263	} else {
264		sock_addr.rfcomm_len = sizeof(sock_addr);
265		sock_addr.rfcomm_family = AF_BLUETOOTH;
266		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
267			sizeof(sock_addr.rfcomm_bdaddr));
268		sock_addr.rfcomm_channel = 0;
269
270		if (bind(s, (struct sockaddr *) &sock_addr,
271				sizeof(sock_addr)) < 0) {
272			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
273				strerror(errno), errno);
274			exit(1);
275		}
276
277		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
278			sizeof(sock_addr.rfcomm_bdaddr));
279		sock_addr.rfcomm_channel = channel;
280
281		if (connect(s, (struct sockaddr *) &sock_addr,
282				sizeof(sock_addr)) < 0) {
283			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
284				strerror(errno), errno);
285			exit(1);
286		}
287
288		exec_ppp(s, label);
289	}
290
291	exit(0);
292} /* main */
293
294/*
295 * Redirects stdin/stdout to s, stderr to /dev/null and exec ppp -direct label.
296 * Never retruns.
297 */
298
299static void
300exec_ppp(int s, char *label)
301{
302	char	 ppp[] = "/usr/sbin/ppp";
303	char	*ppp_args[] = { ppp, "-direct", NULL, NULL };
304
305	close(0);
306	if (dup(s) < 0) {
307		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
308			strerror(errno), errno);
309		exit(1);
310	}
311
312	close(1);
313	if (dup(s) < 0) {
314		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
315			strerror(errno), errno);
316		exit(1);
317	}
318
319	close(2);
320	open("/dev/null", O_RDWR);
321
322	ppp_args[2] = label;
323	if (execv(ppp, ppp_args) < 0) {
324		syslog(LOG_ERR, "Could not exec(%s -direct %s). %s (%d)",
325			ppp, label, strerror(errno), errno);
326		exit(1);
327	}
328} /* run_ppp */
329
330/* Signal handler */
331static void
332sighandler(int s)
333{
334	done = 1;
335} /* sighandler */
336
337/* Display usage and exit */
338static void
339usage(void)
340{
341	fprintf(stdout,
342"Usage: %s options\n" \
343"Where options are:\n" \
344"\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
345"\t-c           Act as a clinet (default)\n" \
346"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
347"\t-d           Run in foreground\n" \
348"\t-l label     Use PPP label (required)\n" \
349"\t-s           Act as a server\n" \
350"\t-h           Display this message\n", RFCOMM_PPPD);
351
352	exit(255);
353} /* usage */
354
355