rfcomm_pppd.c revision 126169
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 126169 2004-02-23 17:30:59Z 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 *unit, 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, *unit = 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:su:")) != -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': /* server */
117			server = 1;
118			break;
119
120		case 'u': /* PPP -unit option */
121			strtoul(optarg, &ep, 10);
122			if (*ep != '\0')
123				usage();
124				/* NOT REACHED */
125
126			unit = optarg;
127			break;
128
129		case 'h':
130		default:
131			usage();
132			/* NOT REACHED */
133		}
134	}
135
136	/* Check if we got everything we wanted */
137	if (label == NULL)
138                errx(1, "Must specify PPP label");
139
140	if (!server) {
141		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
142                	errx(1, "Must specify server BD_ADDR");
143
144		/* Check channel, if was not set then obtain it via SDP */
145		if (channel == 0 && service != 0)
146			if (rfcomm_channel_lookup(NULL, &addr, service,
147							&channel, &s) != 0)
148				errc(1, s, "Could not obtain RFCOMM channel");
149	}
150
151        if (channel <= 0 || channel > 30)
152                errx(1, "Invalid RFCOMM channel number %d", channel);
153
154	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
155
156	if (detach) {
157		pid = fork();
158		if (pid == (pid_t) -1) {
159			syslog(LOG_ERR, "Could not fork(). %s (%d)",
160				strerror(errno), errno);
161			exit(1);
162		}
163
164		if (pid != 0)
165			exit(0);
166
167		if (daemon(0, 0) < 0) {
168			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
169				strerror(errno), errno);
170			exit(1);
171		}
172	}
173
174	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
175	if (s < 0) {
176		syslog(LOG_ERR, "Could not create socket. %s (%d)",
177			strerror(errno), errno);
178		exit(1);
179	}
180
181	if (server) {
182		struct sigaction	 sa;
183		void			*ss = NULL;
184		sdp_lan_profile_t	 lan;
185
186		/* Install signal handler */
187		memset(&sa, 0, sizeof(sa));
188		sa.sa_handler = sighandler;
189
190		if (sigaction(SIGTERM, &sa, NULL) < 0) {
191			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
192				strerror(errno), errno);
193			exit(1);
194		}
195
196		if (sigaction(SIGHUP, &sa, NULL) < 0) {
197			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
198				strerror(errno), errno);
199			exit(1);
200		}
201
202		if (sigaction(SIGINT, &sa, NULL) < 0) {
203			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
204				strerror(errno), errno);
205			exit(1);
206		}
207
208		sa.sa_handler = SIG_IGN;
209		sa.sa_flags = SA_NOCLDWAIT;
210
211		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
212			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
213				strerror(errno), errno);
214			exit(1);
215		}
216
217		/* bind socket and listen for incoming connections */
218		sock_addr.rfcomm_len = sizeof(sock_addr);
219		sock_addr.rfcomm_family = AF_BLUETOOTH;
220		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
221			sizeof(sock_addr.rfcomm_bdaddr));
222		sock_addr.rfcomm_channel = channel;
223
224		if (bind(s, (struct sockaddr *) &sock_addr,
225				sizeof(sock_addr)) < 0) {
226			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
227				strerror(errno), errno);
228			exit(1);
229		}
230
231		if (listen(s, 10) < 0) {
232			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
233				strerror(errno), errno);
234			exit(1);
235		}
236
237		ss = sdp_open_local(NULL);
238		if (ss == NULL) {
239			syslog(LOG_ERR, "Unable to create local SDP session");
240			exit(1);
241		}
242
243		if (sdp_error(ss) != 0) {
244			syslog(LOG_ERR, "Unable to open local SDP session. " \
245				"%s (%d)", strerror(sdp_error(ss)),
246				sdp_error(ss));
247			exit(1);
248		}
249
250		memset(&lan, 0, sizeof(lan));
251		lan.server_channel = channel;
252
253		if (sdp_register_service(ss,
254				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
255				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
256			syslog(LOG_ERR, "Unable to register LAN service with " \
257				"local SDP daemon. %s (%d)",
258				strerror(sdp_error(ss)), sdp_error(ss));
259			exit(1);
260		}
261
262		for (done = 0; !done; ) {
263			int	len = sizeof(sock_addr);
264			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
265
266			if (s1 < 0) {
267				syslog(LOG_ERR, "Could not accept connection " \
268					"on socket. %s (%d)", strerror(errno),
269					errno);
270				exit(1);
271			}
272
273			pid = fork();
274			if (pid == (pid_t) -1) {
275				syslog(LOG_ERR, "Could not fork(). %s (%d)",
276					strerror(errno), errno);
277				exit(1);
278			}
279
280			if (pid == 0) {
281				sdp_close(ss);
282				close(s);
283
284				/* Reset signal handler */
285				memset(&sa, 0, sizeof(sa));
286				sa.sa_handler = SIG_DFL;
287
288				sigaction(SIGTERM, &sa, NULL);
289				sigaction(SIGHUP, &sa, NULL);
290				sigaction(SIGINT, &sa, NULL);
291				sigaction(SIGCHLD, &sa, NULL);
292
293				/* Become daemon */
294				daemon(0, 0);
295
296				/*
297				 * XXX Make sure user does not shoot himself
298				 * in the foot. Do not pass unit option to the
299				 * PPP when operating in the server mode.
300				 */
301
302				exec_ppp(s1, NULL, label);
303			} else
304				close(s1);
305		}
306	} else {
307		sock_addr.rfcomm_len = sizeof(sock_addr);
308		sock_addr.rfcomm_family = AF_BLUETOOTH;
309		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
310			sizeof(sock_addr.rfcomm_bdaddr));
311		sock_addr.rfcomm_channel = 0;
312
313		if (bind(s, (struct sockaddr *) &sock_addr,
314				sizeof(sock_addr)) < 0) {
315			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
316				strerror(errno), errno);
317			exit(1);
318		}
319
320		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
321			sizeof(sock_addr.rfcomm_bdaddr));
322		sock_addr.rfcomm_channel = channel;
323
324		if (connect(s, (struct sockaddr *) &sock_addr,
325				sizeof(sock_addr)) < 0) {
326			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
327				strerror(errno), errno);
328			exit(1);
329		}
330
331		exec_ppp(s, unit, label);
332	}
333
334	exit(0);
335} /* main */
336
337/*
338 * Redirects stdin/stdout to s, stderr to /dev/null and exec
339 * 'ppp -direct -quiet [-unit N] label'. Never returns.
340 */
341
342static void
343exec_ppp(int s, char *unit, char *label)
344{
345	char	 ppp[] = "/usr/sbin/ppp";
346	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
347				NULL, NULL,      NULL,     NULL };
348
349	close(0);
350	if (dup(s) < 0) {
351		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
352			strerror(errno), errno);
353		exit(1);
354	}
355
356	close(1);
357	if (dup(s) < 0) {
358		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
359			strerror(errno), errno);
360		exit(1);
361	}
362
363	close(2);
364	open("/dev/null", O_RDWR);
365
366	if (unit != NULL) {
367		ppp_args[3] = "-unit";
368		ppp_args[4] = unit;
369		ppp_args[5] = label;
370	} else
371		ppp_args[3] = label;
372
373	if (execv(ppp, ppp_args) < 0) {
374		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
375			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
376			(unit != NULL)? unit : "", label,
377			strerror(errno), errno);
378		exit(1);
379	}
380} /* run_ppp */
381
382/* Signal handler */
383static void
384sighandler(int s)
385{
386	done = 1;
387} /* sighandler */
388
389/* Display usage and exit */
390static void
391usage(void)
392{
393	fprintf(stdout,
394"Usage: %s options\n" \
395"Where options are:\n" \
396"\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
397"\t-c           Act as a clinet (default)\n" \
398"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
399"\t-d           Run in foreground\n" \
400"\t-l label     Use PPP label (required)\n" \
401"\t-s           Act as a server\n" \
402"\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
403"\t-h           Display this message\n", RFCOMM_PPPD);
404
405	exit(255);
406} /* usage */
407
408