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