rfcomm_pppd.c revision 121054
1114879Sjulian/*
2114879Sjulian * rfcomm_pppd.c
3114879Sjulian *
4114879Sjulian * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5114879Sjulian * All rights reserved.
6114879Sjulian *
7114879Sjulian * Redistribution and use in source and binary forms, with or without
8114879Sjulian * modification, are permitted provided that the following conditions
9114879Sjulian * are met:
10114879Sjulian * 1. Redistributions of source code must retain the above copyright
11114879Sjulian *    notice, this list of conditions and the following disclaimer.
12114879Sjulian * 2. Redistributions in binary form must reproduce the above copyright
13114879Sjulian *    notice, this list of conditions and the following disclaimer in the
14114879Sjulian *    documentation and/or other materials provided with the distribution.
15114879Sjulian *
16114879Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17114879Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18114879Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19114879Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20114879Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21114879Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22114879Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23114879Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24114879Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25114879Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26114879Sjulian * SUCH DAMAGE.
27114879Sjulian *
28121054Semax * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
29114879Sjulian * $FreeBSD: head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c 121054 2003-10-12 22:04:24Z emax $
30114879Sjulian */
31114879Sjulian
32121054Semax#include <bluetooth.h>
33121054Semax#include <ctype.h>
34121054Semax#include <err.h>
35114879Sjulian#include <errno.h>
36114879Sjulian#include <fcntl.h>
37121054Semax#include <sdp.h>
38114879Sjulian#include <signal.h>
39114879Sjulian#include <stdarg.h>
40114879Sjulian#include <stdio.h>
41114879Sjulian#include <stdlib.h>
42114879Sjulian#include <string.h>
43114879Sjulian#include <syslog.h>
44114879Sjulian#include <unistd.h>
45114879Sjulian
46114879Sjulian#define RFCOMM_PPPD	"rfcomm_pppd"
47114879Sjulian
48121054Semaxint		rfcomm_channel_lookup	(bdaddr_t const *local,
49121054Semax					 bdaddr_t const *remote,
50121054Semax					 int service, int *channel, int *error);
51121054Semax
52114879Sjulianstatic void	exec_ppp	(int s, char *label);
53114879Sjulianstatic void	sighandler	(int s);
54114879Sjulianstatic void	usage		(void);
55114879Sjulian
56114879Sjulianstatic int	done;
57114879Sjulian
58114879Sjulian/* Main */
59114879Sjulianint
60114879Sjulianmain(int argc, char *argv[])
61114879Sjulian{
62114879Sjulian	struct sockaddr_rfcomm   sock_addr;
63121054Semax	char			*label = NULL, *ep = NULL;
64114879Sjulian	bdaddr_t		 addr;
65121054Semax	int			 s, channel, detach, server, service;
66114879Sjulian	pid_t			 pid;
67114879Sjulian
68114879Sjulian	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
69114879Sjulian	channel = 0;
70114879Sjulian	detach = 1;
71114879Sjulian	server = 0;
72121054Semax	service = 0;
73114879Sjulian
74114879Sjulian	/* Parse command line arguments */
75114879Sjulian	while ((s = getopt(argc, argv, "a:cC:dhl:s")) != -1) {
76114879Sjulian		switch (s) {
77121054Semax		case 'a': /* BDADDR */
78121054Semax			if (!bt_aton(optarg, &addr)) {
79121054Semax				struct hostent	*he = NULL;
80114879Sjulian
81121054Semax				if ((he = bt_gethostbyname(optarg)) == NULL)
82121054Semax					errx(1, "%s: %s", optarg, hstrerror(h_errno));
83114879Sjulian
84121054Semax				memcpy(&addr, he->h_addr, sizeof(addr));
85121054Semax			}
86121054Semax			break;
87114879Sjulian
88114879Sjulian		case 'c': /* client */
89114879Sjulian			server = 0;
90114879Sjulian			break;
91114879Sjulian
92114879Sjulian		case 'C': /* RFCOMM channel */
93121054Semax			channel = strtoul(optarg, &ep, 10);
94121054Semax			if (*ep != 0) {
95121054Semax				channel = 0;
96121054Semax				switch (tolower(optarg[0])) {
97121054Semax				case 'd': /* DialUp Networking */
98121054Semax					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
99121054Semax					break;
100121054Semax
101121054Semax				case 'l': /* LAN Access Using PPP */
102121054Semax					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
103121054Semax					break;
104121054Semax				}
105121054Semax			}
106114879Sjulian			break;
107114879Sjulian
108114879Sjulian		case 'd': /* do not detach */
109114879Sjulian			detach = 0;
110114879Sjulian			break;
111114879Sjulian
112114879Sjulian		case 'l': /* PPP label */
113114879Sjulian			label = optarg;
114114879Sjulian			break;
115114879Sjulian
116114879Sjulian		case 's':
117114879Sjulian			server = 1;
118114879Sjulian			break;
119114879Sjulian
120114879Sjulian		case 'h':
121114879Sjulian		default:
122114879Sjulian			usage();
123114879Sjulian			/* NOT REACHED */
124114879Sjulian		}
125114879Sjulian	}
126114879Sjulian
127114879Sjulian	/* Check if we got everything we wanted */
128121054Semax	if (label == NULL)
129121054Semax                errx(1, "Must specify PPP label");
130114879Sjulian
131121054Semax	if (!server) {
132121054Semax		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
133121054Semax                	errx(1, "Must specify server BD_ADDR");
134121054Semax
135121054Semax		/* Check channel, if was not set then obtain it via SDP */
136121054Semax		if (channel == 0 && service != 0)
137121054Semax			if (rfcomm_channel_lookup(NULL, &addr, service,
138121054Semax							&channel, &s) != 0)
139121054Semax				errc(1, s, "Could not obtain RFCOMM channel");
140121054Semax	}
141121054Semax
142121054Semax        if (channel <= 0 || channel > 30)
143121054Semax                errx(1, "Invalid RFCOMM channel number %d", channel);
144121054Semax
145114879Sjulian	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
146114879Sjulian
147114879Sjulian	if (detach) {
148114879Sjulian		pid = fork();
149114879Sjulian		if (pid == (pid_t) -1) {
150114879Sjulian			syslog(LOG_ERR, "Could not fork(). %s (%d)",
151114879Sjulian				strerror(errno), errno);
152114879Sjulian			exit(1);
153114879Sjulian		}
154114879Sjulian
155114879Sjulian		if (pid != 0)
156114879Sjulian			exit(0);
157114879Sjulian
158114879Sjulian		if (daemon(0, 0) < 0) {
159114879Sjulian			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
160114879Sjulian				strerror(errno), errno);
161114879Sjulian			exit(1);
162114879Sjulian		}
163114879Sjulian	}
164114879Sjulian
165114879Sjulian	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
166114879Sjulian	if (s < 0) {
167114879Sjulian		syslog(LOG_ERR, "Could not create socket. %s (%d)",
168114879Sjulian			strerror(errno), errno);
169114879Sjulian		exit(1);
170114879Sjulian	}
171114879Sjulian
172114879Sjulian	if (server) {
173114879Sjulian		struct sigaction	sa;
174114879Sjulian
175114879Sjulian		/* Install signal handler */
176114879Sjulian		memset(&sa, 0, sizeof(sa));
177114879Sjulian		sa.sa_handler = sighandler;
178114879Sjulian
179114879Sjulian		if (sigaction(SIGTERM, &sa, NULL) < 0) {
180114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
181114879Sjulian				strerror(errno), errno);
182114879Sjulian			exit(1);
183114879Sjulian		}
184114879Sjulian
185114879Sjulian		if (sigaction(SIGHUP, &sa, NULL) < 0) {
186114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
187114879Sjulian				strerror(errno), errno);
188114879Sjulian			exit(1);
189114879Sjulian		}
190114879Sjulian
191114879Sjulian		if (sigaction(SIGINT, &sa, NULL) < 0) {
192114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
193114879Sjulian				strerror(errno), errno);
194114879Sjulian			exit(1);
195114879Sjulian		}
196114879Sjulian
197114879Sjulian		sa.sa_handler = SIG_IGN;
198114879Sjulian		sa.sa_flags = SA_NOCLDWAIT;
199114879Sjulian
200114879Sjulian		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
201114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
202114879Sjulian				strerror(errno), errno);
203114879Sjulian			exit(1);
204114879Sjulian		}
205114879Sjulian
206114879Sjulian		/* bind socket and listen for incoming connections */
207114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
208114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
209114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
210114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
211114879Sjulian		sock_addr.rfcomm_channel = channel;
212114879Sjulian
213114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
214114879Sjulian				sizeof(sock_addr)) < 0) {
215114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
216114879Sjulian				strerror(errno), errno);
217114879Sjulian			exit(1);
218114879Sjulian		}
219114879Sjulian
220114879Sjulian		if (listen(s, 10) < 0) {
221114879Sjulian			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
222114879Sjulian				strerror(errno), errno);
223114879Sjulian			exit(1);
224114879Sjulian		}
225114879Sjulian
226114879Sjulian		for (done = 0; !done; ) {
227114879Sjulian			int	len = sizeof(sock_addr);
228114879Sjulian			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
229114879Sjulian
230114879Sjulian			if (s1 < 0) {
231114879Sjulian				syslog(LOG_ERR, "Could not accept connection " \
232114879Sjulian					"on socket. %s (%d)", strerror(errno),
233114879Sjulian					errno);
234114879Sjulian				exit(1);
235114879Sjulian			}
236114879Sjulian
237114879Sjulian			pid = fork();
238114879Sjulian			if (pid == (pid_t) -1) {
239114879Sjulian				syslog(LOG_ERR, "Could not fork(). %s (%d)",
240114879Sjulian					strerror(errno), errno);
241114879Sjulian				exit(1);
242114879Sjulian			}
243114879Sjulian
244114879Sjulian			if (pid == 0) {
245114879Sjulian				close(s);
246114879Sjulian
247114879Sjulian				/* Reset signal handler */
248114879Sjulian				memset(&sa, 0, sizeof(sa));
249114879Sjulian				sa.sa_handler = SIG_DFL;
250114879Sjulian
251114879Sjulian				sigaction(SIGTERM, &sa, NULL);
252114879Sjulian				sigaction(SIGHUP, &sa, NULL);
253114879Sjulian				sigaction(SIGINT, &sa, NULL);
254114879Sjulian				sigaction(SIGCHLD, &sa, NULL);
255114879Sjulian
256114879Sjulian				/* Become daemon */
257114879Sjulian				daemon(0, 0);
258114879Sjulian
259114879Sjulian				exec_ppp(s1, label);
260114879Sjulian			} else
261114879Sjulian				close(s1);
262114879Sjulian		}
263114879Sjulian	} else {
264114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
265114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
266114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
267114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
268114879Sjulian		sock_addr.rfcomm_channel = 0;
269114879Sjulian
270114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
271114879Sjulian				sizeof(sock_addr)) < 0) {
272114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
273114879Sjulian				strerror(errno), errno);
274114879Sjulian			exit(1);
275114879Sjulian		}
276114879Sjulian
277114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
278114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
279114879Sjulian		sock_addr.rfcomm_channel = channel;
280114879Sjulian
281114879Sjulian		if (connect(s, (struct sockaddr *) &sock_addr,
282114879Sjulian				sizeof(sock_addr)) < 0) {
283114879Sjulian			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
284114879Sjulian				strerror(errno), errno);
285114879Sjulian			exit(1);
286114879Sjulian		}
287114879Sjulian
288114879Sjulian		exec_ppp(s, label);
289114879Sjulian	}
290114879Sjulian
291114879Sjulian	exit(0);
292114879Sjulian} /* main */
293114879Sjulian
294114879Sjulian/*
295114879Sjulian * Redirects stdin/stdout to s, stderr to /dev/null and exec ppp -direct label.
296114879Sjulian * Never retruns.
297114879Sjulian */
298114879Sjulian
299114879Sjulianstatic void
300114879Sjulianexec_ppp(int s, char *label)
301114879Sjulian{
302114879Sjulian	char	 ppp[] = "/usr/sbin/ppp";
303114879Sjulian	char	*ppp_args[] = { ppp, "-direct", NULL, NULL };
304114879Sjulian
305114879Sjulian	close(0);
306114879Sjulian	if (dup(s) < 0) {
307114879Sjulian		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
308114879Sjulian			strerror(errno), errno);
309114879Sjulian		exit(1);
310114879Sjulian	}
311114879Sjulian
312114879Sjulian	close(1);
313114879Sjulian	if (dup(s) < 0) {
314114879Sjulian		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
315114879Sjulian			strerror(errno), errno);
316114879Sjulian		exit(1);
317114879Sjulian	}
318114879Sjulian
319114879Sjulian	close(2);
320114879Sjulian	open("/dev/null", O_RDWR);
321114879Sjulian
322114879Sjulian	ppp_args[2] = label;
323114879Sjulian	if (execv(ppp, ppp_args) < 0) {
324114879Sjulian		syslog(LOG_ERR, "Could not exec(%s -direct %s). %s (%d)",
325114879Sjulian			ppp, label, strerror(errno), errno);
326114879Sjulian		exit(1);
327114879Sjulian	}
328114879Sjulian} /* run_ppp */
329114879Sjulian
330114879Sjulian/* Signal handler */
331114879Sjulianstatic void
332114879Sjuliansighandler(int s)
333114879Sjulian{
334114879Sjulian	done = 1;
335114879Sjulian} /* sighandler */
336114879Sjulian
337114879Sjulian/* Display usage and exit */
338114879Sjulianstatic void
339114879Sjulianusage(void)
340114879Sjulian{
341114879Sjulian	fprintf(stdout,
342114879Sjulian"Usage: %s options\n" \
343114879Sjulian"Where options are:\n" \
344114879Sjulian"\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
345114879Sjulian"\t-c           Act as a clinet (default)\n" \
346114879Sjulian"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
347114879Sjulian"\t-d           Run in foreground\n" \
348114879Sjulian"\t-l label     Use PPP label (required)\n" \
349114879Sjulian"\t-s           Act as a server\n" \
350114879Sjulian"\t-h           Display this message\n", RFCOMM_PPPD);
351114879Sjulian
352114879Sjulian	exit(255);
353114879Sjulian} /* usage */
354114879Sjulian
355