1114879Sjulian/*
2114879Sjulian * rfcomm_pppd.c
3177174Semax */
4177174Semax
5177174Semax/*-
6330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7330449Seadler *
8177174Semax * Copyright (c) 2001-2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9114879Sjulian * All rights reserved.
10114879Sjulian *
11114879Sjulian * Redistribution and use in source and binary forms, with or without
12114879Sjulian * modification, are permitted provided that the following conditions
13114879Sjulian * are met:
14114879Sjulian * 1. Redistributions of source code must retain the above copyright
15114879Sjulian *    notice, this list of conditions and the following disclaimer.
16114879Sjulian * 2. Redistributions in binary form must reproduce the above copyright
17114879Sjulian *    notice, this list of conditions and the following disclaimer in the
18114879Sjulian *    documentation and/or other materials provided with the distribution.
19114879Sjulian *
20114879Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21114879Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22114879Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23114879Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24114879Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25114879Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26114879Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27114879Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28114879Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29114879Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30114879Sjulian * SUCH DAMAGE.
31114879Sjulian *
32121054Semax * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
33114879Sjulian * $FreeBSD: stable/11/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c 330449 2018-03-05 07:26:05Z eadler $
34114879Sjulian */
35281210Stakawata#define L2CAP_SOCKET_CHECKED
36121054Semax#include <bluetooth.h>
37121054Semax#include <ctype.h>
38121054Semax#include <err.h>
39114879Sjulian#include <errno.h>
40114879Sjulian#include <fcntl.h>
41121054Semax#include <sdp.h>
42114879Sjulian#include <signal.h>
43114879Sjulian#include <stdarg.h>
44114879Sjulian#include <stdio.h>
45114879Sjulian#include <stdlib.h>
46114879Sjulian#include <string.h>
47114879Sjulian#include <syslog.h>
48114879Sjulian#include <unistd.h>
49114879Sjulian
50114879Sjulian#define RFCOMM_PPPD	"rfcomm_pppd"
51114879Sjulian
52121054Semaxint		rfcomm_channel_lookup	(bdaddr_t const *local,
53121054Semax					 bdaddr_t const *remote,
54121054Semax					 int service, int *channel, int *error);
55121054Semax
56126169Semaxstatic void	exec_ppp	(int s, char *unit, char *label);
57114879Sjulianstatic void	sighandler	(int s);
58114879Sjulianstatic void	usage		(void);
59114879Sjulian
60114879Sjulianstatic int	done;
61114879Sjulian
62114879Sjulian/* Main */
63114879Sjulianint
64114879Sjulianmain(int argc, char *argv[])
65114879Sjulian{
66114879Sjulian	struct sockaddr_rfcomm   sock_addr;
67126169Semax	char			*label = NULL, *unit = NULL, *ep = NULL;
68114879Sjulian	bdaddr_t		 addr;
69176857Semax	int			 s, channel, detach, server, service,
70176857Semax				 regdun, regsp;
71114879Sjulian	pid_t			 pid;
72114879Sjulian
73114879Sjulian	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
74114879Sjulian	channel = 0;
75114879Sjulian	detach = 1;
76114879Sjulian	server = 0;
77121054Semax	service = 0;
78176857Semax	regdun = 0;
79132711Semax	regsp = 0;
80114879Sjulian
81114879Sjulian	/* Parse command line arguments */
82176857Semax	while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) {
83114879Sjulian		switch (s) {
84121054Semax		case 'a': /* BDADDR */
85121054Semax			if (!bt_aton(optarg, &addr)) {
86121054Semax				struct hostent	*he = NULL;
87114879Sjulian
88121054Semax				if ((he = bt_gethostbyname(optarg)) == NULL)
89121054Semax					errx(1, "%s: %s", optarg, hstrerror(h_errno));
90114879Sjulian
91121054Semax				memcpy(&addr, he->h_addr, sizeof(addr));
92121054Semax			}
93121054Semax			break;
94114879Sjulian
95114879Sjulian		case 'c': /* client */
96114879Sjulian			server = 0;
97114879Sjulian			break;
98114879Sjulian
99114879Sjulian		case 'C': /* RFCOMM channel */
100121054Semax			channel = strtoul(optarg, &ep, 10);
101126169Semax			if (*ep != '\0') {
102121054Semax				channel = 0;
103121054Semax				switch (tolower(optarg[0])) {
104121054Semax				case 'd': /* DialUp Networking */
105121054Semax					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
106121054Semax					break;
107121054Semax
108121054Semax				case 'l': /* LAN Access Using PPP */
109121054Semax					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
110121054Semax					break;
111121054Semax				}
112121054Semax			}
113114879Sjulian			break;
114114879Sjulian
115114879Sjulian		case 'd': /* do not detach */
116114879Sjulian			detach = 0;
117114879Sjulian			break;
118114879Sjulian
119176857Semax		case 'D': /* Register DUN service as well as LAN service */
120176857Semax			regdun = 1;
121176857Semax			break;
122176857Semax
123114879Sjulian		case 'l': /* PPP label */
124114879Sjulian			label = optarg;
125114879Sjulian			break;
126114879Sjulian
127126169Semax		case 's': /* server */
128114879Sjulian			server = 1;
129114879Sjulian			break;
130114879Sjulian
131132711Semax		case 'S': /* Register SP service as well as LAN service */
132132711Semax			regsp = 1;
133132711Semax			break;
134132711Semax
135126169Semax		case 'u': /* PPP -unit option */
136126169Semax			strtoul(optarg, &ep, 10);
137126169Semax			if (*ep != '\0')
138126169Semax				usage();
139126169Semax				/* NOT REACHED */
140126169Semax
141126169Semax			unit = optarg;
142126169Semax			break;
143126169Semax
144114879Sjulian		case 'h':
145114879Sjulian		default:
146114879Sjulian			usage();
147114879Sjulian			/* NOT REACHED */
148114879Sjulian		}
149114879Sjulian	}
150114879Sjulian
151114879Sjulian	/* Check if we got everything we wanted */
152121054Semax	if (label == NULL)
153121054Semax                errx(1, "Must specify PPP label");
154114879Sjulian
155121054Semax	if (!server) {
156121054Semax		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
157121054Semax                	errx(1, "Must specify server BD_ADDR");
158121054Semax
159121054Semax		/* Check channel, if was not set then obtain it via SDP */
160121054Semax		if (channel == 0 && service != 0)
161121054Semax			if (rfcomm_channel_lookup(NULL, &addr, service,
162121054Semax							&channel, &s) != 0)
163121054Semax				errc(1, s, "Could not obtain RFCOMM channel");
164121054Semax	}
165121054Semax
166121054Semax        if (channel <= 0 || channel > 30)
167121054Semax                errx(1, "Invalid RFCOMM channel number %d", channel);
168121054Semax
169114879Sjulian	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
170114879Sjulian
171188130Semax	if (detach && daemon(0, 0) < 0) {
172188130Semax		syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
173188130Semax			strerror(errno), errno);
174188130Semax		exit(1);
175114879Sjulian	}
176114879Sjulian
177114879Sjulian	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
178114879Sjulian	if (s < 0) {
179114879Sjulian		syslog(LOG_ERR, "Could not create socket. %s (%d)",
180114879Sjulian			strerror(errno), errno);
181114879Sjulian		exit(1);
182114879Sjulian	}
183114879Sjulian
184114879Sjulian	if (server) {
185126169Semax		struct sigaction	 sa;
186126169Semax		void			*ss = NULL;
187126169Semax		sdp_lan_profile_t	 lan;
188114879Sjulian
189114879Sjulian		/* Install signal handler */
190114879Sjulian		memset(&sa, 0, sizeof(sa));
191114879Sjulian		sa.sa_handler = sighandler;
192114879Sjulian
193114879Sjulian		if (sigaction(SIGTERM, &sa, NULL) < 0) {
194114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
195114879Sjulian				strerror(errno), errno);
196114879Sjulian			exit(1);
197114879Sjulian		}
198114879Sjulian
199114879Sjulian		if (sigaction(SIGHUP, &sa, NULL) < 0) {
200114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
201114879Sjulian				strerror(errno), errno);
202114879Sjulian			exit(1);
203114879Sjulian		}
204114879Sjulian
205114879Sjulian		if (sigaction(SIGINT, &sa, NULL) < 0) {
206114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
207114879Sjulian				strerror(errno), errno);
208114879Sjulian			exit(1);
209114879Sjulian		}
210114879Sjulian
211114879Sjulian		sa.sa_handler = SIG_IGN;
212114879Sjulian		sa.sa_flags = SA_NOCLDWAIT;
213114879Sjulian
214114879Sjulian		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
215114879Sjulian			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
216114879Sjulian				strerror(errno), errno);
217114879Sjulian			exit(1);
218114879Sjulian		}
219114879Sjulian
220114879Sjulian		/* bind socket and listen for incoming connections */
221114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
222114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
223114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
224114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
225114879Sjulian		sock_addr.rfcomm_channel = channel;
226114879Sjulian
227114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
228114879Sjulian				sizeof(sock_addr)) < 0) {
229114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
230114879Sjulian				strerror(errno), errno);
231114879Sjulian			exit(1);
232114879Sjulian		}
233114879Sjulian
234114879Sjulian		if (listen(s, 10) < 0) {
235114879Sjulian			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
236114879Sjulian				strerror(errno), errno);
237114879Sjulian			exit(1);
238114879Sjulian		}
239114879Sjulian
240126169Semax		ss = sdp_open_local(NULL);
241126169Semax		if (ss == NULL) {
242126169Semax			syslog(LOG_ERR, "Unable to create local SDP session");
243126169Semax			exit(1);
244126169Semax		}
245126169Semax
246126169Semax		if (sdp_error(ss) != 0) {
247126169Semax			syslog(LOG_ERR, "Unable to open local SDP session. " \
248126169Semax				"%s (%d)", strerror(sdp_error(ss)),
249126169Semax				sdp_error(ss));
250126169Semax			exit(1);
251126169Semax		}
252126169Semax
253126169Semax		memset(&lan, 0, sizeof(lan));
254126169Semax		lan.server_channel = channel;
255126169Semax
256126169Semax		if (sdp_register_service(ss,
257126169Semax				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
258126169Semax				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
259126169Semax			syslog(LOG_ERR, "Unable to register LAN service with " \
260126169Semax				"local SDP daemon. %s (%d)",
261126169Semax				strerror(sdp_error(ss)), sdp_error(ss));
262126169Semax			exit(1);
263126169Semax		}
264132711Semax
265132711Semax		/*
266176857Semax		 * Register DUN (Dial-Up Networking) service on the same
267176857Semax		 * RFCOMM channel if requested. There is really no good reason
268176857Semax		 * to not to support this. AT-command exchange can be faked
269176857Semax		 * with chat script in ppp.conf
270176857Semax		 */
271176857Semax
272176857Semax		if (regdun) {
273176857Semax			sdp_dun_profile_t	dun;
274176857Semax
275176857Semax			memset(&dun, 0, sizeof(dun));
276176857Semax			dun.server_channel = channel;
277176857Semax
278176857Semax			if (sdp_register_service(ss,
279176857Semax					SDP_SERVICE_CLASS_DIALUP_NETWORKING,
280176857Semax					&addr, (void *) &dun, sizeof(dun),
281176857Semax					NULL) != 0) {
282176857Semax				syslog(LOG_ERR, "Unable to register DUN " \
283176857Semax					"service with local SDP daemon. " \
284176857Semax					"%s (%d)", strerror(sdp_error(ss)),
285176857Semax					sdp_error(ss));
286176857Semax				exit(1);
287176857Semax			}
288176857Semax		}
289176857Semax
290176857Semax		/*
291132711Semax		 * Register SP (Serial Port) service on the same RFCOMM channel
292132711Semax		 * if requested. It appears that some cell phones are using so
293132711Semax		 * called "callback mechanism". In this scenario user is trying
294132711Semax		 * to connect his cell phone to the Internet, and, user's host
295132711Semax		 * computer is acting as the gateway server. It seems that it
296132711Semax		 * is not possible to tell the phone to just connect and start
297132711Semax		 * using the LAN service. Instead the user's host computer must
298132711Semax		 * "jump start" the phone by connecting to the phone's SP
299132711Semax		 * service. What happens next is the phone kills the existing
300132711Semax		 * connection and opens another connection back to the user's
301132711Semax		 * host computer. The phone really wants to use LAN service,
302132711Semax		 * but for whatever reason it looks for SP service on the
303132711Semax		 * user's host computer. This brain damaged behavior was
304132711Semax		 * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
305132711Semax		 * are Symbian-based phones. Perhaps this is a Symbian problem?
306132711Semax		 */
307132711Semax
308132711Semax		if (regsp) {
309132711Semax			sdp_sp_profile_t	sp;
310132711Semax
311132711Semax			memset(&sp, 0, sizeof(sp));
312132711Semax			sp.server_channel = channel;
313132711Semax
314132711Semax			if (sdp_register_service(ss,
315132711Semax					SDP_SERVICE_CLASS_SERIAL_PORT,
316132711Semax					&addr, (void *) &sp, sizeof(sp),
317132711Semax					NULL) != 0) {
318132711Semax				syslog(LOG_ERR, "Unable to register SP " \
319132711Semax					"service with local SDP daemon. " \
320132711Semax					"%s (%d)", strerror(sdp_error(ss)),
321132711Semax					sdp_error(ss));
322132711Semax				exit(1);
323132711Semax			}
324132711Semax		}
325126169Semax
326114879Sjulian		for (done = 0; !done; ) {
327162494Semax			socklen_t	len = sizeof(sock_addr);
328162494Semax			int		s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
329114879Sjulian
330114879Sjulian			if (s1 < 0) {
331114879Sjulian				syslog(LOG_ERR, "Could not accept connection " \
332114879Sjulian					"on socket. %s (%d)", strerror(errno),
333114879Sjulian					errno);
334114879Sjulian				exit(1);
335114879Sjulian			}
336114879Sjulian
337114879Sjulian			pid = fork();
338114879Sjulian			if (pid == (pid_t) -1) {
339114879Sjulian				syslog(LOG_ERR, "Could not fork(). %s (%d)",
340114879Sjulian					strerror(errno), errno);
341114879Sjulian				exit(1);
342114879Sjulian			}
343114879Sjulian
344114879Sjulian			if (pid == 0) {
345126169Semax				sdp_close(ss);
346114879Sjulian				close(s);
347114879Sjulian
348114879Sjulian				/* Reset signal handler */
349114879Sjulian				memset(&sa, 0, sizeof(sa));
350114879Sjulian				sa.sa_handler = SIG_DFL;
351114879Sjulian
352114879Sjulian				sigaction(SIGTERM, &sa, NULL);
353114879Sjulian				sigaction(SIGHUP, &sa, NULL);
354114879Sjulian				sigaction(SIGINT, &sa, NULL);
355114879Sjulian				sigaction(SIGCHLD, &sa, NULL);
356114879Sjulian
357114879Sjulian				/* Become daemon */
358114879Sjulian				daemon(0, 0);
359114879Sjulian
360126169Semax				/*
361126169Semax				 * XXX Make sure user does not shoot himself
362126169Semax				 * in the foot. Do not pass unit option to the
363126169Semax				 * PPP when operating in the server mode.
364126169Semax				 */
365126169Semax
366126169Semax				exec_ppp(s1, NULL, label);
367114879Sjulian			} else
368114879Sjulian				close(s1);
369114879Sjulian		}
370114879Sjulian	} else {
371114879Sjulian		sock_addr.rfcomm_len = sizeof(sock_addr);
372114879Sjulian		sock_addr.rfcomm_family = AF_BLUETOOTH;
373114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
374114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
375114879Sjulian		sock_addr.rfcomm_channel = 0;
376114879Sjulian
377114879Sjulian		if (bind(s, (struct sockaddr *) &sock_addr,
378114879Sjulian				sizeof(sock_addr)) < 0) {
379114879Sjulian			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
380114879Sjulian				strerror(errno), errno);
381114879Sjulian			exit(1);
382114879Sjulian		}
383114879Sjulian
384114879Sjulian		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
385114879Sjulian			sizeof(sock_addr.rfcomm_bdaddr));
386114879Sjulian		sock_addr.rfcomm_channel = channel;
387114879Sjulian
388114879Sjulian		if (connect(s, (struct sockaddr *) &sock_addr,
389114879Sjulian				sizeof(sock_addr)) < 0) {
390114879Sjulian			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
391114879Sjulian				strerror(errno), errno);
392114879Sjulian			exit(1);
393114879Sjulian		}
394114879Sjulian
395126169Semax		exec_ppp(s, unit, label);
396114879Sjulian	}
397114879Sjulian
398114879Sjulian	exit(0);
399114879Sjulian} /* main */
400114879Sjulian
401114879Sjulian/*
402126169Semax * Redirects stdin/stdout to s, stderr to /dev/null and exec
403126169Semax * 'ppp -direct -quiet [-unit N] label'. Never returns.
404114879Sjulian */
405114879Sjulian
406114879Sjulianstatic void
407126169Semaxexec_ppp(int s, char *unit, char *label)
408114879Sjulian{
409114879Sjulian	char	 ppp[] = "/usr/sbin/ppp";
410126169Semax	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
411126169Semax				NULL, NULL,      NULL,     NULL };
412114879Sjulian
413114879Sjulian	close(0);
414114879Sjulian	if (dup(s) < 0) {
415114879Sjulian		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
416114879Sjulian			strerror(errno), errno);
417114879Sjulian		exit(1);
418114879Sjulian	}
419114879Sjulian
420114879Sjulian	close(1);
421114879Sjulian	if (dup(s) < 0) {
422114879Sjulian		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
423114879Sjulian			strerror(errno), errno);
424114879Sjulian		exit(1);
425114879Sjulian	}
426114879Sjulian
427114879Sjulian	close(2);
428114879Sjulian	open("/dev/null", O_RDWR);
429114879Sjulian
430126169Semax	if (unit != NULL) {
431126169Semax		ppp_args[3] = "-unit";
432126169Semax		ppp_args[4] = unit;
433126169Semax		ppp_args[5] = label;
434126169Semax	} else
435126169Semax		ppp_args[3] = label;
436126169Semax
437114879Sjulian	if (execv(ppp, ppp_args) < 0) {
438126169Semax		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
439126169Semax			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
440126169Semax			(unit != NULL)? unit : "", label,
441126169Semax			strerror(errno), errno);
442114879Sjulian		exit(1);
443114879Sjulian	}
444114879Sjulian} /* run_ppp */
445114879Sjulian
446114879Sjulian/* Signal handler */
447114879Sjulianstatic void
448114879Sjuliansighandler(int s)
449114879Sjulian{
450114879Sjulian	done = 1;
451114879Sjulian} /* sighandler */
452114879Sjulian
453114879Sjulian/* Display usage and exit */
454114879Sjulianstatic void
455114879Sjulianusage(void)
456114879Sjulian{
457114879Sjulian	fprintf(stdout,
458114879Sjulian"Usage: %s options\n" \
459114879Sjulian"Where options are:\n" \
460133178Semax"\t-a address   Address to listen on or connect to (required for client)\n" \
461114879Sjulian"\t-c           Act as a clinet (default)\n" \
462114879Sjulian"\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
463114879Sjulian"\t-d           Run in foreground\n" \
464177174Semax"\t-D           Register Dial-Up Networking service (server mode only)\n" \
465114879Sjulian"\t-l label     Use PPP label (required)\n" \
466114879Sjulian"\t-s           Act as a server\n" \
467132711Semax"\t-S           Register Serial Port service (server mode only)\n" \
468126169Semax"\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
469114879Sjulian"\t-h           Display this message\n", RFCOMM_PPPD);
470114879Sjulian
471114879Sjulian	exit(255);
472114879Sjulian} /* usage */
473114879Sjulian
474