rfcomm_pppd.c revision 132711
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 132711 2004-07-27 22:40:42Z 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, regsp;
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	regsp = 0;
74
75	/* Parse command line arguments */
76	while ((s = getopt(argc, argv, "a:cC:dhl:sSu:")) != -1) {
77		switch (s) {
78		case 'a': /* BDADDR */
79			if (!bt_aton(optarg, &addr)) {
80				struct hostent	*he = NULL;
81
82				if ((he = bt_gethostbyname(optarg)) == NULL)
83					errx(1, "%s: %s", optarg, hstrerror(h_errno));
84
85				memcpy(&addr, he->h_addr, sizeof(addr));
86			}
87			break;
88
89		case 'c': /* client */
90			server = 0;
91			break;
92
93		case 'C': /* RFCOMM channel */
94			channel = strtoul(optarg, &ep, 10);
95			if (*ep != '\0') {
96				channel = 0;
97				switch (tolower(optarg[0])) {
98				case 'd': /* DialUp Networking */
99					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
100					break;
101
102				case 'l': /* LAN Access Using PPP */
103					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
104					break;
105				}
106			}
107			break;
108
109		case 'd': /* do not detach */
110			detach = 0;
111			break;
112
113		case 'l': /* PPP label */
114			label = optarg;
115			break;
116
117		case 's': /* server */
118			server = 1;
119			break;
120
121		case 'S': /* Register SP service as well as LAN service */
122			regsp = 1;
123			break;
124
125		case 'u': /* PPP -unit option */
126			strtoul(optarg, &ep, 10);
127			if (*ep != '\0')
128				usage();
129				/* NOT REACHED */
130
131			unit = optarg;
132			break;
133
134		case 'h':
135		default:
136			usage();
137			/* NOT REACHED */
138		}
139	}
140
141	/* Check if we got everything we wanted */
142	if (label == NULL)
143                errx(1, "Must specify PPP label");
144
145	if (!server) {
146		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
147                	errx(1, "Must specify server BD_ADDR");
148
149		/* Check channel, if was not set then obtain it via SDP */
150		if (channel == 0 && service != 0)
151			if (rfcomm_channel_lookup(NULL, &addr, service,
152							&channel, &s) != 0)
153				errc(1, s, "Could not obtain RFCOMM channel");
154	}
155
156        if (channel <= 0 || channel > 30)
157                errx(1, "Invalid RFCOMM channel number %d", channel);
158
159	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
160
161	if (detach) {
162		pid = fork();
163		if (pid == (pid_t) -1) {
164			syslog(LOG_ERR, "Could not fork(). %s (%d)",
165				strerror(errno), errno);
166			exit(1);
167		}
168
169		if (pid != 0)
170			exit(0);
171
172		if (daemon(0, 0) < 0) {
173			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
174				strerror(errno), errno);
175			exit(1);
176		}
177	}
178
179	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
180	if (s < 0) {
181		syslog(LOG_ERR, "Could not create socket. %s (%d)",
182			strerror(errno), errno);
183		exit(1);
184	}
185
186	if (server) {
187		struct sigaction	 sa;
188		void			*ss = NULL;
189		sdp_lan_profile_t	 lan;
190
191		/* Install signal handler */
192		memset(&sa, 0, sizeof(sa));
193		sa.sa_handler = sighandler;
194
195		if (sigaction(SIGTERM, &sa, NULL) < 0) {
196			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
197				strerror(errno), errno);
198			exit(1);
199		}
200
201		if (sigaction(SIGHUP, &sa, NULL) < 0) {
202			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
203				strerror(errno), errno);
204			exit(1);
205		}
206
207		if (sigaction(SIGINT, &sa, NULL) < 0) {
208			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
209				strerror(errno), errno);
210			exit(1);
211		}
212
213		sa.sa_handler = SIG_IGN;
214		sa.sa_flags = SA_NOCLDWAIT;
215
216		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
217			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
218				strerror(errno), errno);
219			exit(1);
220		}
221
222		/* bind socket and listen for incoming connections */
223		sock_addr.rfcomm_len = sizeof(sock_addr);
224		sock_addr.rfcomm_family = AF_BLUETOOTH;
225		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
226			sizeof(sock_addr.rfcomm_bdaddr));
227		sock_addr.rfcomm_channel = channel;
228
229		if (bind(s, (struct sockaddr *) &sock_addr,
230				sizeof(sock_addr)) < 0) {
231			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
232				strerror(errno), errno);
233			exit(1);
234		}
235
236		if (listen(s, 10) < 0) {
237			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
238				strerror(errno), errno);
239			exit(1);
240		}
241
242		ss = sdp_open_local(NULL);
243		if (ss == NULL) {
244			syslog(LOG_ERR, "Unable to create local SDP session");
245			exit(1);
246		}
247
248		if (sdp_error(ss) != 0) {
249			syslog(LOG_ERR, "Unable to open local SDP session. " \
250				"%s (%d)", strerror(sdp_error(ss)),
251				sdp_error(ss));
252			exit(1);
253		}
254
255		memset(&lan, 0, sizeof(lan));
256		lan.server_channel = channel;
257
258		if (sdp_register_service(ss,
259				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
260				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
261			syslog(LOG_ERR, "Unable to register LAN service with " \
262				"local SDP daemon. %s (%d)",
263				strerror(sdp_error(ss)), sdp_error(ss));
264			exit(1);
265		}
266
267		/*
268		 * Register SP (Serial Port) service on the same RFCOMM channel
269		 * if requested. It appears that some cell phones are using so
270		 * called "callback mechanism". In this scenario user is trying
271		 * to connect his cell phone to the Internet, and, user's host
272		 * computer is acting as the gateway server. It seems that it
273		 * is not possible to tell the phone to just connect and start
274		 * using the LAN service. Instead the user's host computer must
275		 * "jump start" the phone by connecting to the phone's SP
276		 * service. What happens next is the phone kills the existing
277		 * connection and opens another connection back to the user's
278		 * host computer. The phone really wants to use LAN service,
279		 * but for whatever reason it looks for SP service on the
280		 * user's host computer. This brain damaged behavior was
281		 * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
282		 * are Symbian-based phones. Perhaps this is a Symbian problem?
283		 */
284
285		if (regsp) {
286			sdp_sp_profile_t	sp;
287
288			memset(&sp, 0, sizeof(sp));
289			sp.server_channel = channel;
290
291			if (sdp_register_service(ss,
292					SDP_SERVICE_CLASS_SERIAL_PORT,
293					&addr, (void *) &sp, sizeof(sp),
294					NULL) != 0) {
295				syslog(LOG_ERR, "Unable to register SP " \
296					"service with local SDP daemon. " \
297					"%s (%d)", strerror(sdp_error(ss)),
298					sdp_error(ss));
299				exit(1);
300			}
301		}
302
303		for (done = 0; !done; ) {
304			int	len = sizeof(sock_addr);
305			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
306
307			if (s1 < 0) {
308				syslog(LOG_ERR, "Could not accept connection " \
309					"on socket. %s (%d)", strerror(errno),
310					errno);
311				exit(1);
312			}
313
314			pid = fork();
315			if (pid == (pid_t) -1) {
316				syslog(LOG_ERR, "Could not fork(). %s (%d)",
317					strerror(errno), errno);
318				exit(1);
319			}
320
321			if (pid == 0) {
322				sdp_close(ss);
323				close(s);
324
325				/* Reset signal handler */
326				memset(&sa, 0, sizeof(sa));
327				sa.sa_handler = SIG_DFL;
328
329				sigaction(SIGTERM, &sa, NULL);
330				sigaction(SIGHUP, &sa, NULL);
331				sigaction(SIGINT, &sa, NULL);
332				sigaction(SIGCHLD, &sa, NULL);
333
334				/* Become daemon */
335				daemon(0, 0);
336
337				/*
338				 * XXX Make sure user does not shoot himself
339				 * in the foot. Do not pass unit option to the
340				 * PPP when operating in the server mode.
341				 */
342
343				exec_ppp(s1, NULL, label);
344			} else
345				close(s1);
346		}
347	} else {
348		sock_addr.rfcomm_len = sizeof(sock_addr);
349		sock_addr.rfcomm_family = AF_BLUETOOTH;
350		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
351			sizeof(sock_addr.rfcomm_bdaddr));
352		sock_addr.rfcomm_channel = 0;
353
354		if (bind(s, (struct sockaddr *) &sock_addr,
355				sizeof(sock_addr)) < 0) {
356			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
357				strerror(errno), errno);
358			exit(1);
359		}
360
361		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
362			sizeof(sock_addr.rfcomm_bdaddr));
363		sock_addr.rfcomm_channel = channel;
364
365		if (connect(s, (struct sockaddr *) &sock_addr,
366				sizeof(sock_addr)) < 0) {
367			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
368				strerror(errno), errno);
369			exit(1);
370		}
371
372		exec_ppp(s, unit, label);
373	}
374
375	exit(0);
376} /* main */
377
378/*
379 * Redirects stdin/stdout to s, stderr to /dev/null and exec
380 * 'ppp -direct -quiet [-unit N] label'. Never returns.
381 */
382
383static void
384exec_ppp(int s, char *unit, char *label)
385{
386	char	 ppp[] = "/usr/sbin/ppp";
387	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
388				NULL, NULL,      NULL,     NULL };
389
390	close(0);
391	if (dup(s) < 0) {
392		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
393			strerror(errno), errno);
394		exit(1);
395	}
396
397	close(1);
398	if (dup(s) < 0) {
399		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
400			strerror(errno), errno);
401		exit(1);
402	}
403
404	close(2);
405	open("/dev/null", O_RDWR);
406
407	if (unit != NULL) {
408		ppp_args[3] = "-unit";
409		ppp_args[4] = unit;
410		ppp_args[5] = label;
411	} else
412		ppp_args[3] = label;
413
414	if (execv(ppp, ppp_args) < 0) {
415		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
416			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
417			(unit != NULL)? unit : "", label,
418			strerror(errno), errno);
419		exit(1);
420	}
421} /* run_ppp */
422
423/* Signal handler */
424static void
425sighandler(int s)
426{
427	done = 1;
428} /* sighandler */
429
430/* Display usage and exit */
431static void
432usage(void)
433{
434	fprintf(stdout,
435"Usage: %s options\n" \
436"Where options are:\n" \
437"\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
438"\t-c           Act as a clinet (default)\n" \
439"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
440"\t-d           Run in foreground\n" \
441"\t-l label     Use PPP label (required)\n" \
442"\t-s           Act as a server\n" \
443"\t-S           Register Serial Port service (server mode only)\n" \
444"\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
445"\t-h           Display this message\n", RFCOMM_PPPD);
446
447	exit(255);
448} /* usage */
449
450