1/*
2 * rfcomm_pppd.c
3 */
4
5/*-
6 * Copyright (c) 2001-2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
31 * $FreeBSD$
32 */
33
34#include <bluetooth.h>
35#include <ctype.h>
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <sdp.h>
40#include <signal.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <unistd.h>
47
48#define RFCOMM_PPPD	"rfcomm_pppd"
49
50int		rfcomm_channel_lookup	(bdaddr_t const *local,
51					 bdaddr_t const *remote,
52					 int service, int *channel, int *error);
53
54static void	exec_ppp	(int s, char *unit, char *label);
55static void	sighandler	(int s);
56static void	usage		(void);
57
58static int	done;
59
60/* Main */
61int
62main(int argc, char *argv[])
63{
64	struct sockaddr_rfcomm   sock_addr;
65	char			*label = NULL, *unit = NULL, *ep = NULL;
66	bdaddr_t		 addr;
67	int			 s, channel, detach, server, service,
68				 regdun, regsp;
69	pid_t			 pid;
70
71	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
72	channel = 0;
73	detach = 1;
74	server = 0;
75	service = 0;
76	regdun = 0;
77	regsp = 0;
78
79	/* Parse command line arguments */
80	while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) {
81		switch (s) {
82		case 'a': /* BDADDR */
83			if (!bt_aton(optarg, &addr)) {
84				struct hostent	*he = NULL;
85
86				if ((he = bt_gethostbyname(optarg)) == NULL)
87					errx(1, "%s: %s", optarg, hstrerror(h_errno));
88
89				memcpy(&addr, he->h_addr, sizeof(addr));
90			}
91			break;
92
93		case 'c': /* client */
94			server = 0;
95			break;
96
97		case 'C': /* RFCOMM channel */
98			channel = strtoul(optarg, &ep, 10);
99			if (*ep != '\0') {
100				channel = 0;
101				switch (tolower(optarg[0])) {
102				case 'd': /* DialUp Networking */
103					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
104					break;
105
106				case 'l': /* LAN Access Using PPP */
107					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
108					break;
109				}
110			}
111			break;
112
113		case 'd': /* do not detach */
114			detach = 0;
115			break;
116
117		case 'D': /* Register DUN service as well as LAN service */
118			regdun = 1;
119			break;
120
121		case 'l': /* PPP label */
122			label = optarg;
123			break;
124
125		case 's': /* server */
126			server = 1;
127			break;
128
129		case 'S': /* Register SP service as well as LAN service */
130			regsp = 1;
131			break;
132
133		case 'u': /* PPP -unit option */
134			strtoul(optarg, &ep, 10);
135			if (*ep != '\0')
136				usage();
137				/* NOT REACHED */
138
139			unit = optarg;
140			break;
141
142		case 'h':
143		default:
144			usage();
145			/* NOT REACHED */
146		}
147	}
148
149	/* Check if we got everything we wanted */
150	if (label == NULL)
151                errx(1, "Must specify PPP label");
152
153	if (!server) {
154		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
155                	errx(1, "Must specify server BD_ADDR");
156
157		/* Check channel, if was not set then obtain it via SDP */
158		if (channel == 0 && service != 0)
159			if (rfcomm_channel_lookup(NULL, &addr, service,
160							&channel, &s) != 0)
161				errc(1, s, "Could not obtain RFCOMM channel");
162	}
163
164        if (channel <= 0 || channel > 30)
165                errx(1, "Invalid RFCOMM channel number %d", channel);
166
167	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
168
169	if (detach && daemon(0, 0) < 0) {
170		syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
171			strerror(errno), errno);
172		exit(1);
173	}
174
175	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
176	if (s < 0) {
177		syslog(LOG_ERR, "Could not create socket. %s (%d)",
178			strerror(errno), errno);
179		exit(1);
180	}
181
182	if (server) {
183		struct sigaction	 sa;
184		void			*ss = NULL;
185		sdp_lan_profile_t	 lan;
186
187		/* Install signal handler */
188		memset(&sa, 0, sizeof(sa));
189		sa.sa_handler = sighandler;
190
191		if (sigaction(SIGTERM, &sa, NULL) < 0) {
192			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
193				strerror(errno), errno);
194			exit(1);
195		}
196
197		if (sigaction(SIGHUP, &sa, NULL) < 0) {
198			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
199				strerror(errno), errno);
200			exit(1);
201		}
202
203		if (sigaction(SIGINT, &sa, NULL) < 0) {
204			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
205				strerror(errno), errno);
206			exit(1);
207		}
208
209		sa.sa_handler = SIG_IGN;
210		sa.sa_flags = SA_NOCLDWAIT;
211
212		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
213			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
214				strerror(errno), errno);
215			exit(1);
216		}
217
218		/* bind socket and listen for incoming connections */
219		sock_addr.rfcomm_len = sizeof(sock_addr);
220		sock_addr.rfcomm_family = AF_BLUETOOTH;
221		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
222			sizeof(sock_addr.rfcomm_bdaddr));
223		sock_addr.rfcomm_channel = channel;
224
225		if (bind(s, (struct sockaddr *) &sock_addr,
226				sizeof(sock_addr)) < 0) {
227			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
228				strerror(errno), errno);
229			exit(1);
230		}
231
232		if (listen(s, 10) < 0) {
233			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
234				strerror(errno), errno);
235			exit(1);
236		}
237
238		ss = sdp_open_local(NULL);
239		if (ss == NULL) {
240			syslog(LOG_ERR, "Unable to create local SDP session");
241			exit(1);
242		}
243
244		if (sdp_error(ss) != 0) {
245			syslog(LOG_ERR, "Unable to open local SDP session. " \
246				"%s (%d)", strerror(sdp_error(ss)),
247				sdp_error(ss));
248			exit(1);
249		}
250
251		memset(&lan, 0, sizeof(lan));
252		lan.server_channel = channel;
253
254		if (sdp_register_service(ss,
255				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
256				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
257			syslog(LOG_ERR, "Unable to register LAN service with " \
258				"local SDP daemon. %s (%d)",
259				strerror(sdp_error(ss)), sdp_error(ss));
260			exit(1);
261		}
262
263		/*
264		 * Register DUN (Dial-Up Networking) service on the same
265		 * RFCOMM channel if requested. There is really no good reason
266		 * to not to support this. AT-command exchange can be faked
267		 * with chat script in ppp.conf
268		 */
269
270		if (regdun) {
271			sdp_dun_profile_t	dun;
272
273			memset(&dun, 0, sizeof(dun));
274			dun.server_channel = channel;
275
276			if (sdp_register_service(ss,
277					SDP_SERVICE_CLASS_DIALUP_NETWORKING,
278					&addr, (void *) &dun, sizeof(dun),
279					NULL) != 0) {
280				syslog(LOG_ERR, "Unable to register DUN " \
281					"service with local SDP daemon. " \
282					"%s (%d)", strerror(sdp_error(ss)),
283					sdp_error(ss));
284				exit(1);
285			}
286		}
287
288		/*
289		 * Register SP (Serial Port) service on the same RFCOMM channel
290		 * if requested. It appears that some cell phones are using so
291		 * called "callback mechanism". In this scenario user is trying
292		 * to connect his cell phone to the Internet, and, user's host
293		 * computer is acting as the gateway server. It seems that it
294		 * is not possible to tell the phone to just connect and start
295		 * using the LAN service. Instead the user's host computer must
296		 * "jump start" the phone by connecting to the phone's SP
297		 * service. What happens next is the phone kills the existing
298		 * connection and opens another connection back to the user's
299		 * host computer. The phone really wants to use LAN service,
300		 * but for whatever reason it looks for SP service on the
301		 * user's host computer. This brain damaged behavior was
302		 * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
303		 * are Symbian-based phones. Perhaps this is a Symbian problem?
304		 */
305
306		if (regsp) {
307			sdp_sp_profile_t	sp;
308
309			memset(&sp, 0, sizeof(sp));
310			sp.server_channel = channel;
311
312			if (sdp_register_service(ss,
313					SDP_SERVICE_CLASS_SERIAL_PORT,
314					&addr, (void *) &sp, sizeof(sp),
315					NULL) != 0) {
316				syslog(LOG_ERR, "Unable to register SP " \
317					"service with local SDP daemon. " \
318					"%s (%d)", strerror(sdp_error(ss)),
319					sdp_error(ss));
320				exit(1);
321			}
322		}
323
324		for (done = 0; !done; ) {
325			socklen_t	len = sizeof(sock_addr);
326			int		s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
327
328			if (s1 < 0) {
329				syslog(LOG_ERR, "Could not accept connection " \
330					"on socket. %s (%d)", strerror(errno),
331					errno);
332				exit(1);
333			}
334
335			pid = fork();
336			if (pid == (pid_t) -1) {
337				syslog(LOG_ERR, "Could not fork(). %s (%d)",
338					strerror(errno), errno);
339				exit(1);
340			}
341
342			if (pid == 0) {
343				sdp_close(ss);
344				close(s);
345
346				/* Reset signal handler */
347				memset(&sa, 0, sizeof(sa));
348				sa.sa_handler = SIG_DFL;
349
350				sigaction(SIGTERM, &sa, NULL);
351				sigaction(SIGHUP, &sa, NULL);
352				sigaction(SIGINT, &sa, NULL);
353				sigaction(SIGCHLD, &sa, NULL);
354
355				/* Become daemon */
356				daemon(0, 0);
357
358				/*
359				 * XXX Make sure user does not shoot himself
360				 * in the foot. Do not pass unit option to the
361				 * PPP when operating in the server mode.
362				 */
363
364				exec_ppp(s1, NULL, label);
365			} else
366				close(s1);
367		}
368	} else {
369		sock_addr.rfcomm_len = sizeof(sock_addr);
370		sock_addr.rfcomm_family = AF_BLUETOOTH;
371		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
372			sizeof(sock_addr.rfcomm_bdaddr));
373		sock_addr.rfcomm_channel = 0;
374
375		if (bind(s, (struct sockaddr *) &sock_addr,
376				sizeof(sock_addr)) < 0) {
377			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
378				strerror(errno), errno);
379			exit(1);
380		}
381
382		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
383			sizeof(sock_addr.rfcomm_bdaddr));
384		sock_addr.rfcomm_channel = channel;
385
386		if (connect(s, (struct sockaddr *) &sock_addr,
387				sizeof(sock_addr)) < 0) {
388			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
389				strerror(errno), errno);
390			exit(1);
391		}
392
393		exec_ppp(s, unit, label);
394	}
395
396	exit(0);
397} /* main */
398
399/*
400 * Redirects stdin/stdout to s, stderr to /dev/null and exec
401 * 'ppp -direct -quiet [-unit N] label'. Never returns.
402 */
403
404static void
405exec_ppp(int s, char *unit, char *label)
406{
407	char	 ppp[] = "/usr/sbin/ppp";
408	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
409				NULL, NULL,      NULL,     NULL };
410
411	close(0);
412	if (dup(s) < 0) {
413		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
414			strerror(errno), errno);
415		exit(1);
416	}
417
418	close(1);
419	if (dup(s) < 0) {
420		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
421			strerror(errno), errno);
422		exit(1);
423	}
424
425	close(2);
426	open("/dev/null", O_RDWR);
427
428	if (unit != NULL) {
429		ppp_args[3] = "-unit";
430		ppp_args[4] = unit;
431		ppp_args[5] = label;
432	} else
433		ppp_args[3] = label;
434
435	if (execv(ppp, ppp_args) < 0) {
436		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
437			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
438			(unit != NULL)? unit : "", label,
439			strerror(errno), errno);
440		exit(1);
441	}
442} /* run_ppp */
443
444/* Signal handler */
445static void
446sighandler(int s)
447{
448	done = 1;
449} /* sighandler */
450
451/* Display usage and exit */
452static void
453usage(void)
454{
455	fprintf(stdout,
456"Usage: %s options\n" \
457"Where options are:\n" \
458"\t-a address   Address to listen on or connect to (required for client)\n" \
459"\t-c           Act as a clinet (default)\n" \
460"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
461"\t-d           Run in foreground\n" \
462"\t-D           Register Dial-Up Networking service (server mode only)\n" \
463"\t-l label     Use PPP label (required)\n" \
464"\t-s           Act as a server\n" \
465"\t-S           Register Serial Port service (server mode only)\n" \
466"\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
467"\t-h           Display this message\n", RFCOMM_PPPD);
468
469	exit(255);
470} /* usage */
471
472