rfcomm_pppd.c revision 162494
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 162494 2006-09-21 02:32:28Z 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
52126169Semaxstatic void	exec_ppp	(int s, char *unit, 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;
63126169Semax	char			*label = NULL, *unit = NULL, *ep = NULL;
64114879Sjulian	bdaddr_t		 addr;
65132711Semax	int			 s, channel, detach, server, service, regsp;
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;
73132711Semax	regsp = 0;
74114879Sjulian
75114879Sjulian	/* Parse command line arguments */
76132711Semax	while ((s = getopt(argc, argv, "a:cC:dhl:sSu:")) != -1) {
77114879Sjulian		switch (s) {
78121054Semax		case 'a': /* BDADDR */
79121054Semax			if (!bt_aton(optarg, &addr)) {
80121054Semax				struct hostent	*he = NULL;
81114879Sjulian
82121054Semax				if ((he = bt_gethostbyname(optarg)) == NULL)
83121054Semax					errx(1, "%s: %s", optarg, hstrerror(h_errno));
84114879Sjulian
85121054Semax				memcpy(&addr, he->h_addr, sizeof(addr));
86121054Semax			}
87121054Semax			break;
88114879Sjulian
89114879Sjulian		case 'c': /* client */
90114879Sjulian			server = 0;
91114879Sjulian			break;
92114879Sjulian
93114879Sjulian		case 'C': /* RFCOMM channel */
94121054Semax			channel = strtoul(optarg, &ep, 10);
95126169Semax			if (*ep != '\0') {
96121054Semax				channel = 0;
97121054Semax				switch (tolower(optarg[0])) {
98121054Semax				case 'd': /* DialUp Networking */
99121054Semax					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
100121054Semax					break;
101121054Semax
102121054Semax				case 'l': /* LAN Access Using PPP */
103121054Semax					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
104121054Semax					break;
105121054Semax				}
106121054Semax			}
107114879Sjulian			break;
108114879Sjulian
109114879Sjulian		case 'd': /* do not detach */
110114879Sjulian			detach = 0;
111114879Sjulian			break;
112114879Sjulian
113114879Sjulian		case 'l': /* PPP label */
114114879Sjulian			label = optarg;
115114879Sjulian			break;
116114879Sjulian
117126169Semax		case 's': /* server */
118114879Sjulian			server = 1;
119114879Sjulian			break;
120114879Sjulian
121132711Semax		case 'S': /* Register SP service as well as LAN service */
122132711Semax			regsp = 1;
123132711Semax			break;
124132711Semax
125126169Semax		case 'u': /* PPP -unit option */
126126169Semax			strtoul(optarg, &ep, 10);
127126169Semax			if (*ep != '\0')
128126169Semax				usage();
129126169Semax				/* NOT REACHED */
130126169Semax
131126169Semax			unit = optarg;
132126169Semax			break;
133126169Semax
134114879Sjulian		case 'h':
135114879Sjulian		default:
136114879Sjulian			usage();
137114879Sjulian			/* NOT REACHED */
138114879Sjulian		}
139114879Sjulian	}
140114879Sjulian
141114879Sjulian	/* Check if we got everything we wanted */
142121054Semax	if (label == NULL)
143121054Semax                errx(1, "Must specify PPP label");
144114879Sjulian
145121054Semax	if (!server) {
146121054Semax		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
147121054Semax                	errx(1, "Must specify server BD_ADDR");
148121054Semax
149121054Semax		/* Check channel, if was not set then obtain it via SDP */
150121054Semax		if (channel == 0 && service != 0)
151121054Semax			if (rfcomm_channel_lookup(NULL, &addr, service,
152121054Semax							&channel, &s) != 0)
153121054Semax				errc(1, s, "Could not obtain RFCOMM channel");
154121054Semax	}
155121054Semax
156121054Semax        if (channel <= 0 || channel > 30)
157121054Semax                errx(1, "Invalid RFCOMM channel number %d", channel);
158121054Semax
159114879Sjulian	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
160114879Sjulian
161114879Sjulian	if (detach) {
162114879Sjulian		pid = fork();
163114879Sjulian		if (pid == (pid_t) -1) {
164114879Sjulian			syslog(LOG_ERR, "Could not fork(). %s (%d)",
165114879Sjulian				strerror(errno), errno);
166114879Sjulian			exit(1);
167114879Sjulian		}
168114879Sjulian
169114879Sjulian		if (pid != 0)
170114879Sjulian			exit(0);
171114879Sjulian
172114879Sjulian		if (daemon(0, 0) < 0) {
173114879Sjulian			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
174114879Sjulian				strerror(errno), errno);
175114879Sjulian			exit(1);
176114879Sjulian		}
177114879Sjulian	}
178114879Sjulian
179114879Sjulian	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
180114879Sjulian	if (s < 0) {
181114879Sjulian		syslog(LOG_ERR, "Could not create socket. %s (%d)",
182114879Sjulian			strerror(errno), errno);
183114879Sjulian		exit(1);
184114879Sjulian	}
185114879Sjulian
186114879Sjulian	if (server) {
187126169Semax		struct sigaction	 sa;
188126169Semax		void			*ss = NULL;
189126169Semax		sdp_lan_profile_t	 lan;
190114879Sjulian
191114879Sjulian		/* Install signal handler */
192114879Sjulian		memset(&sa, 0, sizeof(sa));
193114879Sjulian		sa.sa_handler = sighandler;
194114879Sjulian
195114879Sjulian		if (sigaction(SIGTERM, &sa, NULL) < 0) {
196114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
197114879Sjulian				strerror(errno), errno);
198114879Sjulian			exit(1);
199114879Sjulian		}
200114879Sjulian
201114879Sjulian		if (sigaction(SIGHUP, &sa, NULL) < 0) {
202114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
203114879Sjulian				strerror(errno), errno);
204114879Sjulian			exit(1);
205114879Sjulian		}
206114879Sjulian
207114879Sjulian		if (sigaction(SIGINT, &sa, NULL) < 0) {
208114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
209114879Sjulian				strerror(errno), errno);
210114879Sjulian			exit(1);
211114879Sjulian		}
212114879Sjulian
213114879Sjulian		sa.sa_handler = SIG_IGN;
214114879Sjulian		sa.sa_flags = SA_NOCLDWAIT;
215114879Sjulian
216114879Sjulian		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
217114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
218114879Sjulian				strerror(errno), errno);
219114879Sjulian			exit(1);
220114879Sjulian		}
221114879Sjulian
222114879Sjulian		/* bind socket and listen for incoming connections */
223114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
224114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
225114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
226114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
227114879Sjulian		sock_addr.rfcomm_channel = channel;
228114879Sjulian
229114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
230114879Sjulian				sizeof(sock_addr)) < 0) {
231114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
232114879Sjulian				strerror(errno), errno);
233114879Sjulian			exit(1);
234114879Sjulian		}
235114879Sjulian
236114879Sjulian		if (listen(s, 10) < 0) {
237114879Sjulian			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
238114879Sjulian				strerror(errno), errno);
239114879Sjulian			exit(1);
240114879Sjulian		}
241114879Sjulian
242126169Semax		ss = sdp_open_local(NULL);
243126169Semax		if (ss == NULL) {
244126169Semax			syslog(LOG_ERR, "Unable to create local SDP session");
245126169Semax			exit(1);
246126169Semax		}
247126169Semax
248126169Semax		if (sdp_error(ss) != 0) {
249126169Semax			syslog(LOG_ERR, "Unable to open local SDP session. " \
250126169Semax				"%s (%d)", strerror(sdp_error(ss)),
251126169Semax				sdp_error(ss));
252126169Semax			exit(1);
253126169Semax		}
254126169Semax
255126169Semax		memset(&lan, 0, sizeof(lan));
256126169Semax		lan.server_channel = channel;
257126169Semax
258126169Semax		if (sdp_register_service(ss,
259126169Semax				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
260126169Semax				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
261126169Semax			syslog(LOG_ERR, "Unable to register LAN service with " \
262126169Semax				"local SDP daemon. %s (%d)",
263126169Semax				strerror(sdp_error(ss)), sdp_error(ss));
264126169Semax			exit(1);
265126169Semax		}
266132711Semax
267132711Semax		/*
268132711Semax		 * Register SP (Serial Port) service on the same RFCOMM channel
269132711Semax		 * if requested. It appears that some cell phones are using so
270132711Semax		 * called "callback mechanism". In this scenario user is trying
271132711Semax		 * to connect his cell phone to the Internet, and, user's host
272132711Semax		 * computer is acting as the gateway server. It seems that it
273132711Semax		 * is not possible to tell the phone to just connect and start
274132711Semax		 * using the LAN service. Instead the user's host computer must
275132711Semax		 * "jump start" the phone by connecting to the phone's SP
276132711Semax		 * service. What happens next is the phone kills the existing
277132711Semax		 * connection and opens another connection back to the user's
278132711Semax		 * host computer. The phone really wants to use LAN service,
279132711Semax		 * but for whatever reason it looks for SP service on the
280132711Semax		 * user's host computer. This brain damaged behavior was
281132711Semax		 * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
282132711Semax		 * are Symbian-based phones. Perhaps this is a Symbian problem?
283132711Semax		 */
284132711Semax
285132711Semax		if (regsp) {
286132711Semax			sdp_sp_profile_t	sp;
287132711Semax
288132711Semax			memset(&sp, 0, sizeof(sp));
289132711Semax			sp.server_channel = channel;
290132711Semax
291132711Semax			if (sdp_register_service(ss,
292132711Semax					SDP_SERVICE_CLASS_SERIAL_PORT,
293132711Semax					&addr, (void *) &sp, sizeof(sp),
294132711Semax					NULL) != 0) {
295132711Semax				syslog(LOG_ERR, "Unable to register SP " \
296132711Semax					"service with local SDP daemon. " \
297132711Semax					"%s (%d)", strerror(sdp_error(ss)),
298132711Semax					sdp_error(ss));
299132711Semax				exit(1);
300132711Semax			}
301132711Semax		}
302126169Semax
303114879Sjulian		for (done = 0; !done; ) {
304162494Semax			socklen_t	len = sizeof(sock_addr);
305162494Semax			int		s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
306114879Sjulian
307114879Sjulian			if (s1 < 0) {
308114879Sjulian				syslog(LOG_ERR, "Could not accept connection " \
309114879Sjulian					"on socket. %s (%d)", strerror(errno),
310114879Sjulian					errno);
311114879Sjulian				exit(1);
312114879Sjulian			}
313114879Sjulian
314114879Sjulian			pid = fork();
315114879Sjulian			if (pid == (pid_t) -1) {
316114879Sjulian				syslog(LOG_ERR, "Could not fork(). %s (%d)",
317114879Sjulian					strerror(errno), errno);
318114879Sjulian				exit(1);
319114879Sjulian			}
320114879Sjulian
321114879Sjulian			if (pid == 0) {
322126169Semax				sdp_close(ss);
323114879Sjulian				close(s);
324114879Sjulian
325114879Sjulian				/* Reset signal handler */
326114879Sjulian				memset(&sa, 0, sizeof(sa));
327114879Sjulian				sa.sa_handler = SIG_DFL;
328114879Sjulian
329114879Sjulian				sigaction(SIGTERM, &sa, NULL);
330114879Sjulian				sigaction(SIGHUP, &sa, NULL);
331114879Sjulian				sigaction(SIGINT, &sa, NULL);
332114879Sjulian				sigaction(SIGCHLD, &sa, NULL);
333114879Sjulian
334114879Sjulian				/* Become daemon */
335114879Sjulian				daemon(0, 0);
336114879Sjulian
337126169Semax				/*
338126169Semax				 * XXX Make sure user does not shoot himself
339126169Semax				 * in the foot. Do not pass unit option to the
340126169Semax				 * PPP when operating in the server mode.
341126169Semax				 */
342126169Semax
343126169Semax				exec_ppp(s1, NULL, label);
344114879Sjulian			} else
345114879Sjulian				close(s1);
346114879Sjulian		}
347114879Sjulian	} else {
348114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
349114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
350114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
351114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
352114879Sjulian		sock_addr.rfcomm_channel = 0;
353114879Sjulian
354114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
355114879Sjulian				sizeof(sock_addr)) < 0) {
356114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
357114879Sjulian				strerror(errno), errno);
358114879Sjulian			exit(1);
359114879Sjulian		}
360114879Sjulian
361114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
362114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
363114879Sjulian		sock_addr.rfcomm_channel = channel;
364114879Sjulian
365114879Sjulian		if (connect(s, (struct sockaddr *) &sock_addr,
366114879Sjulian				sizeof(sock_addr)) < 0) {
367114879Sjulian			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
368114879Sjulian				strerror(errno), errno);
369114879Sjulian			exit(1);
370114879Sjulian		}
371114879Sjulian
372126169Semax		exec_ppp(s, unit, label);
373114879Sjulian	}
374114879Sjulian
375114879Sjulian	exit(0);
376114879Sjulian} /* main */
377114879Sjulian
378114879Sjulian/*
379126169Semax * Redirects stdin/stdout to s, stderr to /dev/null and exec
380126169Semax * 'ppp -direct -quiet [-unit N] label'. Never returns.
381114879Sjulian */
382114879Sjulian
383114879Sjulianstatic void
384126169Semaxexec_ppp(int s, char *unit, char *label)
385114879Sjulian{
386114879Sjulian	char	 ppp[] = "/usr/sbin/ppp";
387126169Semax	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
388126169Semax				NULL, NULL,      NULL,     NULL };
389114879Sjulian
390114879Sjulian	close(0);
391114879Sjulian	if (dup(s) < 0) {
392114879Sjulian		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
393114879Sjulian			strerror(errno), errno);
394114879Sjulian		exit(1);
395114879Sjulian	}
396114879Sjulian
397114879Sjulian	close(1);
398114879Sjulian	if (dup(s) < 0) {
399114879Sjulian		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
400114879Sjulian			strerror(errno), errno);
401114879Sjulian		exit(1);
402114879Sjulian	}
403114879Sjulian
404114879Sjulian	close(2);
405114879Sjulian	open("/dev/null", O_RDWR);
406114879Sjulian
407126169Semax	if (unit != NULL) {
408126169Semax		ppp_args[3] = "-unit";
409126169Semax		ppp_args[4] = unit;
410126169Semax		ppp_args[5] = label;
411126169Semax	} else
412126169Semax		ppp_args[3] = label;
413126169Semax
414114879Sjulian	if (execv(ppp, ppp_args) < 0) {
415126169Semax		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
416126169Semax			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
417126169Semax			(unit != NULL)? unit : "", label,
418126169Semax			strerror(errno), errno);
419114879Sjulian		exit(1);
420114879Sjulian	}
421114879Sjulian} /* run_ppp */
422114879Sjulian
423114879Sjulian/* Signal handler */
424114879Sjulianstatic void
425114879Sjuliansighandler(int s)
426114879Sjulian{
427114879Sjulian	done = 1;
428114879Sjulian} /* sighandler */
429114879Sjulian
430114879Sjulian/* Display usage and exit */
431114879Sjulianstatic void
432114879Sjulianusage(void)
433114879Sjulian{
434114879Sjulian	fprintf(stdout,
435114879Sjulian"Usage: %s options\n" \
436114879Sjulian"Where options are:\n" \
437133178Semax"\t-a address   Address to listen on or connect to (required for client)\n" \
438114879Sjulian"\t-c           Act as a clinet (default)\n" \
439114879Sjulian"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
440114879Sjulian"\t-d           Run in foreground\n" \
441114879Sjulian"\t-l label     Use PPP label (required)\n" \
442114879Sjulian"\t-s           Act as a server\n" \
443132711Semax"\t-S           Register Serial Port service (server mode only)\n" \
444126169Semax"\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
445114879Sjulian"\t-h           Display this message\n", RFCOMM_PPPD);
446114879Sjulian
447114879Sjulian	exit(255);
448114879Sjulian} /* usage */
449114879Sjulian
450