xref: /freebsd/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c (revision b1fae13507670cdf7affefa7389db153e56b9ad0)
11a63eb31SJulian Elischer /*
21a63eb31SJulian Elischer  * rfcomm_pppd.c
31a63eb31SJulian Elischer  *
41a63eb31SJulian Elischer  * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
51a63eb31SJulian Elischer  * All rights reserved.
61a63eb31SJulian Elischer  *
71a63eb31SJulian Elischer  * Redistribution and use in source and binary forms, with or without
81a63eb31SJulian Elischer  * modification, are permitted provided that the following conditions
91a63eb31SJulian Elischer  * are met:
101a63eb31SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
111a63eb31SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
121a63eb31SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
131a63eb31SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
141a63eb31SJulian Elischer  *    documentation and/or other materials provided with the distribution.
151a63eb31SJulian Elischer  *
161a63eb31SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171a63eb31SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181a63eb31SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191a63eb31SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201a63eb31SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211a63eb31SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221a63eb31SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231a63eb31SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241a63eb31SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251a63eb31SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261a63eb31SJulian Elischer  * SUCH DAMAGE.
271a63eb31SJulian Elischer  *
280986ab12SMaksim Yevmenkin  * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
291a63eb31SJulian Elischer  * $FreeBSD$
301a63eb31SJulian Elischer  */
311a63eb31SJulian Elischer 
320986ab12SMaksim Yevmenkin #include <bluetooth.h>
330986ab12SMaksim Yevmenkin #include <ctype.h>
340986ab12SMaksim Yevmenkin #include <err.h>
351a63eb31SJulian Elischer #include <errno.h>
361a63eb31SJulian Elischer #include <fcntl.h>
370986ab12SMaksim Yevmenkin #include <sdp.h>
381a63eb31SJulian Elischer #include <signal.h>
391a63eb31SJulian Elischer #include <stdarg.h>
401a63eb31SJulian Elischer #include <stdio.h>
411a63eb31SJulian Elischer #include <stdlib.h>
421a63eb31SJulian Elischer #include <string.h>
431a63eb31SJulian Elischer #include <syslog.h>
441a63eb31SJulian Elischer #include <unistd.h>
451a63eb31SJulian Elischer 
461a63eb31SJulian Elischer #define RFCOMM_PPPD	"rfcomm_pppd"
471a63eb31SJulian Elischer 
480986ab12SMaksim Yevmenkin int		rfcomm_channel_lookup	(bdaddr_t const *local,
490986ab12SMaksim Yevmenkin 					 bdaddr_t const *remote,
500986ab12SMaksim Yevmenkin 					 int service, int *channel, int *error);
510986ab12SMaksim Yevmenkin 
5213706e45SMaksim Yevmenkin static void	exec_ppp	(int s, char *unit, char *label);
531a63eb31SJulian Elischer static void	sighandler	(int s);
541a63eb31SJulian Elischer static void	usage		(void);
551a63eb31SJulian Elischer 
561a63eb31SJulian Elischer static int	done;
571a63eb31SJulian Elischer 
581a63eb31SJulian Elischer /* Main */
591a63eb31SJulian Elischer int
601a63eb31SJulian Elischer main(int argc, char *argv[])
611a63eb31SJulian Elischer {
621a63eb31SJulian Elischer 	struct sockaddr_rfcomm   sock_addr;
6313706e45SMaksim Yevmenkin 	char			*label = NULL, *unit = NULL, *ep = NULL;
641a63eb31SJulian Elischer 	bdaddr_t		 addr;
65b1fae135SMaksim Yevmenkin 	int			 s, channel, detach, server, service,
66b1fae135SMaksim Yevmenkin 				 regdun, regsp;
671a63eb31SJulian Elischer 	pid_t			 pid;
681a63eb31SJulian Elischer 
691a63eb31SJulian Elischer 	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
701a63eb31SJulian Elischer 	channel = 0;
711a63eb31SJulian Elischer 	detach = 1;
721a63eb31SJulian Elischer 	server = 0;
730986ab12SMaksim Yevmenkin 	service = 0;
74b1fae135SMaksim Yevmenkin 	regdun = 0;
751985a3a3SMaksim Yevmenkin 	regsp = 0;
761a63eb31SJulian Elischer 
771a63eb31SJulian Elischer 	/* Parse command line arguments */
78b1fae135SMaksim Yevmenkin 	while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) {
791a63eb31SJulian Elischer 		switch (s) {
800986ab12SMaksim Yevmenkin 		case 'a': /* BDADDR */
810986ab12SMaksim Yevmenkin 			if (!bt_aton(optarg, &addr)) {
820986ab12SMaksim Yevmenkin 				struct hostent	*he = NULL;
831a63eb31SJulian Elischer 
840986ab12SMaksim Yevmenkin 				if ((he = bt_gethostbyname(optarg)) == NULL)
850986ab12SMaksim Yevmenkin 					errx(1, "%s: %s", optarg, hstrerror(h_errno));
861a63eb31SJulian Elischer 
870986ab12SMaksim Yevmenkin 				memcpy(&addr, he->h_addr, sizeof(addr));
880986ab12SMaksim Yevmenkin 			}
890986ab12SMaksim Yevmenkin 			break;
901a63eb31SJulian Elischer 
911a63eb31SJulian Elischer 		case 'c': /* client */
921a63eb31SJulian Elischer 			server = 0;
931a63eb31SJulian Elischer 			break;
941a63eb31SJulian Elischer 
951a63eb31SJulian Elischer 		case 'C': /* RFCOMM channel */
960986ab12SMaksim Yevmenkin 			channel = strtoul(optarg, &ep, 10);
9713706e45SMaksim Yevmenkin 			if (*ep != '\0') {
980986ab12SMaksim Yevmenkin 				channel = 0;
990986ab12SMaksim Yevmenkin 				switch (tolower(optarg[0])) {
1000986ab12SMaksim Yevmenkin 				case 'd': /* DialUp Networking */
1010986ab12SMaksim Yevmenkin 					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
1020986ab12SMaksim Yevmenkin 					break;
1030986ab12SMaksim Yevmenkin 
1040986ab12SMaksim Yevmenkin 				case 'l': /* LAN Access Using PPP */
1050986ab12SMaksim Yevmenkin 					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
1060986ab12SMaksim Yevmenkin 					break;
1070986ab12SMaksim Yevmenkin 				}
1080986ab12SMaksim Yevmenkin 			}
1091a63eb31SJulian Elischer 			break;
1101a63eb31SJulian Elischer 
1111a63eb31SJulian Elischer 		case 'd': /* do not detach */
1121a63eb31SJulian Elischer 			detach = 0;
1131a63eb31SJulian Elischer 			break;
1141a63eb31SJulian Elischer 
115b1fae135SMaksim Yevmenkin 		case 'D': /* Register DUN service as well as LAN service */
116b1fae135SMaksim Yevmenkin 			regdun = 1;
117b1fae135SMaksim Yevmenkin 			break;
118b1fae135SMaksim Yevmenkin 
1191a63eb31SJulian Elischer 		case 'l': /* PPP label */
1201a63eb31SJulian Elischer 			label = optarg;
1211a63eb31SJulian Elischer 			break;
1221a63eb31SJulian Elischer 
12313706e45SMaksim Yevmenkin 		case 's': /* server */
1241a63eb31SJulian Elischer 			server = 1;
1251a63eb31SJulian Elischer 			break;
1261a63eb31SJulian Elischer 
1271985a3a3SMaksim Yevmenkin 		case 'S': /* Register SP service as well as LAN service */
1281985a3a3SMaksim Yevmenkin 			regsp = 1;
1291985a3a3SMaksim Yevmenkin 			break;
1301985a3a3SMaksim Yevmenkin 
13113706e45SMaksim Yevmenkin 		case 'u': /* PPP -unit option */
13213706e45SMaksim Yevmenkin 			strtoul(optarg, &ep, 10);
13313706e45SMaksim Yevmenkin 			if (*ep != '\0')
13413706e45SMaksim Yevmenkin 				usage();
13513706e45SMaksim Yevmenkin 				/* NOT REACHED */
13613706e45SMaksim Yevmenkin 
13713706e45SMaksim Yevmenkin 			unit = optarg;
13813706e45SMaksim Yevmenkin 			break;
13913706e45SMaksim Yevmenkin 
1401a63eb31SJulian Elischer 		case 'h':
1411a63eb31SJulian Elischer 		default:
1421a63eb31SJulian Elischer 			usage();
1431a63eb31SJulian Elischer 			/* NOT REACHED */
1441a63eb31SJulian Elischer 		}
1451a63eb31SJulian Elischer 	}
1461a63eb31SJulian Elischer 
1471a63eb31SJulian Elischer 	/* Check if we got everything we wanted */
1480986ab12SMaksim Yevmenkin 	if (label == NULL)
1490986ab12SMaksim Yevmenkin                 errx(1, "Must specify PPP label");
1500986ab12SMaksim Yevmenkin 
1510986ab12SMaksim Yevmenkin 	if (!server) {
1520986ab12SMaksim Yevmenkin 		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
1530986ab12SMaksim Yevmenkin                 	errx(1, "Must specify server BD_ADDR");
1540986ab12SMaksim Yevmenkin 
1550986ab12SMaksim Yevmenkin 		/* Check channel, if was not set then obtain it via SDP */
1560986ab12SMaksim Yevmenkin 		if (channel == 0 && service != 0)
1570986ab12SMaksim Yevmenkin 			if (rfcomm_channel_lookup(NULL, &addr, service,
1580986ab12SMaksim Yevmenkin 							&channel, &s) != 0)
1590986ab12SMaksim Yevmenkin 				errc(1, s, "Could not obtain RFCOMM channel");
1600986ab12SMaksim Yevmenkin 	}
1610986ab12SMaksim Yevmenkin 
1620986ab12SMaksim Yevmenkin         if (channel <= 0 || channel > 30)
1630986ab12SMaksim Yevmenkin                 errx(1, "Invalid RFCOMM channel number %d", channel);
1641a63eb31SJulian Elischer 
1651a63eb31SJulian Elischer 	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
1661a63eb31SJulian Elischer 
1671a63eb31SJulian Elischer 	if (detach) {
1681a63eb31SJulian Elischer 		pid = fork();
1691a63eb31SJulian Elischer 		if (pid == (pid_t) -1) {
1701a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not fork(). %s (%d)",
1711a63eb31SJulian Elischer 				strerror(errno), errno);
1721a63eb31SJulian Elischer 			exit(1);
1731a63eb31SJulian Elischer 		}
1741a63eb31SJulian Elischer 
1751a63eb31SJulian Elischer 		if (pid != 0)
1761a63eb31SJulian Elischer 			exit(0);
1771a63eb31SJulian Elischer 
1781a63eb31SJulian Elischer 		if (daemon(0, 0) < 0) {
1791a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
1801a63eb31SJulian Elischer 				strerror(errno), errno);
1811a63eb31SJulian Elischer 			exit(1);
1821a63eb31SJulian Elischer 		}
1831a63eb31SJulian Elischer 	}
1841a63eb31SJulian Elischer 
1851a63eb31SJulian Elischer 	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
1861a63eb31SJulian Elischer 	if (s < 0) {
1871a63eb31SJulian Elischer 		syslog(LOG_ERR, "Could not create socket. %s (%d)",
1881a63eb31SJulian Elischer 			strerror(errno), errno);
1891a63eb31SJulian Elischer 		exit(1);
1901a63eb31SJulian Elischer 	}
1911a63eb31SJulian Elischer 
1921a63eb31SJulian Elischer 	if (server) {
1931a63eb31SJulian Elischer 		struct sigaction	 sa;
19413706e45SMaksim Yevmenkin 		void			*ss = NULL;
19513706e45SMaksim Yevmenkin 		sdp_lan_profile_t	 lan;
1961a63eb31SJulian Elischer 
1971a63eb31SJulian Elischer 		/* Install signal handler */
1981a63eb31SJulian Elischer 		memset(&sa, 0, sizeof(sa));
1991a63eb31SJulian Elischer 		sa.sa_handler = sighandler;
2001a63eb31SJulian Elischer 
2011a63eb31SJulian Elischer 		if (sigaction(SIGTERM, &sa, NULL) < 0) {
2021a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
2031a63eb31SJulian Elischer 				strerror(errno), errno);
2041a63eb31SJulian Elischer 			exit(1);
2051a63eb31SJulian Elischer 		}
2061a63eb31SJulian Elischer 
2071a63eb31SJulian Elischer 		if (sigaction(SIGHUP, &sa, NULL) < 0) {
2081a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
2091a63eb31SJulian Elischer 				strerror(errno), errno);
2101a63eb31SJulian Elischer 			exit(1);
2111a63eb31SJulian Elischer 		}
2121a63eb31SJulian Elischer 
2131a63eb31SJulian Elischer 		if (sigaction(SIGINT, &sa, NULL) < 0) {
2141a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
2151a63eb31SJulian Elischer 				strerror(errno), errno);
2161a63eb31SJulian Elischer 			exit(1);
2171a63eb31SJulian Elischer 		}
2181a63eb31SJulian Elischer 
2191a63eb31SJulian Elischer 		sa.sa_handler = SIG_IGN;
2201a63eb31SJulian Elischer 		sa.sa_flags = SA_NOCLDWAIT;
2211a63eb31SJulian Elischer 
2221a63eb31SJulian Elischer 		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
2231a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
2241a63eb31SJulian Elischer 				strerror(errno), errno);
2251a63eb31SJulian Elischer 			exit(1);
2261a63eb31SJulian Elischer 		}
2271a63eb31SJulian Elischer 
2281a63eb31SJulian Elischer 		/* bind socket and listen for incoming connections */
2291a63eb31SJulian Elischer 		sock_addr.rfcomm_len = sizeof(sock_addr);
2301a63eb31SJulian Elischer 		sock_addr.rfcomm_family = AF_BLUETOOTH;
2311a63eb31SJulian Elischer 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
2321a63eb31SJulian Elischer 			sizeof(sock_addr.rfcomm_bdaddr));
2331a63eb31SJulian Elischer 		sock_addr.rfcomm_channel = channel;
2341a63eb31SJulian Elischer 
2351a63eb31SJulian Elischer 		if (bind(s, (struct sockaddr *) &sock_addr,
2361a63eb31SJulian Elischer 				sizeof(sock_addr)) < 0) {
2371a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
2381a63eb31SJulian Elischer 				strerror(errno), errno);
2391a63eb31SJulian Elischer 			exit(1);
2401a63eb31SJulian Elischer 		}
2411a63eb31SJulian Elischer 
2421a63eb31SJulian Elischer 		if (listen(s, 10) < 0) {
2431a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
2441a63eb31SJulian Elischer 				strerror(errno), errno);
2451a63eb31SJulian Elischer 			exit(1);
2461a63eb31SJulian Elischer 		}
2471a63eb31SJulian Elischer 
24813706e45SMaksim Yevmenkin 		ss = sdp_open_local(NULL);
24913706e45SMaksim Yevmenkin 		if (ss == NULL) {
25013706e45SMaksim Yevmenkin 			syslog(LOG_ERR, "Unable to create local SDP session");
25113706e45SMaksim Yevmenkin 			exit(1);
25213706e45SMaksim Yevmenkin 		}
25313706e45SMaksim Yevmenkin 
25413706e45SMaksim Yevmenkin 		if (sdp_error(ss) != 0) {
25513706e45SMaksim Yevmenkin 			syslog(LOG_ERR, "Unable to open local SDP session. " \
25613706e45SMaksim Yevmenkin 				"%s (%d)", strerror(sdp_error(ss)),
25713706e45SMaksim Yevmenkin 				sdp_error(ss));
25813706e45SMaksim Yevmenkin 			exit(1);
25913706e45SMaksim Yevmenkin 		}
26013706e45SMaksim Yevmenkin 
26113706e45SMaksim Yevmenkin 		memset(&lan, 0, sizeof(lan));
26213706e45SMaksim Yevmenkin 		lan.server_channel = channel;
26313706e45SMaksim Yevmenkin 
26413706e45SMaksim Yevmenkin 		if (sdp_register_service(ss,
26513706e45SMaksim Yevmenkin 				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
26613706e45SMaksim Yevmenkin 				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
26713706e45SMaksim Yevmenkin 			syslog(LOG_ERR, "Unable to register LAN service with " \
26813706e45SMaksim Yevmenkin 				"local SDP daemon. %s (%d)",
26913706e45SMaksim Yevmenkin 				strerror(sdp_error(ss)), sdp_error(ss));
27013706e45SMaksim Yevmenkin 			exit(1);
27113706e45SMaksim Yevmenkin 		}
27213706e45SMaksim Yevmenkin 
2731985a3a3SMaksim Yevmenkin 		/*
274b1fae135SMaksim Yevmenkin 		 * Register DUN (Dial-Up Networking) service on the same
275b1fae135SMaksim Yevmenkin 		 * RFCOMM channel if requested. There is really no good reason
276b1fae135SMaksim Yevmenkin 		 * to not to support this. AT-command exchange can be faked
277b1fae135SMaksim Yevmenkin 		 * with chat script in ppp.conf
278b1fae135SMaksim Yevmenkin 		 */
279b1fae135SMaksim Yevmenkin 
280b1fae135SMaksim Yevmenkin 		if (regdun) {
281b1fae135SMaksim Yevmenkin 			sdp_dun_profile_t	dun;
282b1fae135SMaksim Yevmenkin 
283b1fae135SMaksim Yevmenkin 			memset(&dun, 0, sizeof(dun));
284b1fae135SMaksim Yevmenkin 			dun.server_channel = channel;
285b1fae135SMaksim Yevmenkin 
286b1fae135SMaksim Yevmenkin 			if (sdp_register_service(ss,
287b1fae135SMaksim Yevmenkin 					SDP_SERVICE_CLASS_DIALUP_NETWORKING,
288b1fae135SMaksim Yevmenkin 					&addr, (void *) &dun, sizeof(dun),
289b1fae135SMaksim Yevmenkin 					NULL) != 0) {
290b1fae135SMaksim Yevmenkin 				syslog(LOG_ERR, "Unable to register DUN " \
291b1fae135SMaksim Yevmenkin 					"service with local SDP daemon. " \
292b1fae135SMaksim Yevmenkin 					"%s (%d)", strerror(sdp_error(ss)),
293b1fae135SMaksim Yevmenkin 					sdp_error(ss));
294b1fae135SMaksim Yevmenkin 				exit(1);
295b1fae135SMaksim Yevmenkin 			}
296b1fae135SMaksim Yevmenkin 		}
297b1fae135SMaksim Yevmenkin 
298b1fae135SMaksim Yevmenkin 		/*
2991985a3a3SMaksim Yevmenkin 		 * Register SP (Serial Port) service on the same RFCOMM channel
3001985a3a3SMaksim Yevmenkin 		 * if requested. It appears that some cell phones are using so
3011985a3a3SMaksim Yevmenkin 		 * called "callback mechanism". In this scenario user is trying
3021985a3a3SMaksim Yevmenkin 		 * to connect his cell phone to the Internet, and, user's host
3031985a3a3SMaksim Yevmenkin 		 * computer is acting as the gateway server. It seems that it
3041985a3a3SMaksim Yevmenkin 		 * is not possible to tell the phone to just connect and start
3051985a3a3SMaksim Yevmenkin 		 * using the LAN service. Instead the user's host computer must
3061985a3a3SMaksim Yevmenkin 		 * "jump start" the phone by connecting to the phone's SP
3071985a3a3SMaksim Yevmenkin 		 * service. What happens next is the phone kills the existing
3081985a3a3SMaksim Yevmenkin 		 * connection and opens another connection back to the user's
3091985a3a3SMaksim Yevmenkin 		 * host computer. The phone really wants to use LAN service,
3101985a3a3SMaksim Yevmenkin 		 * but for whatever reason it looks for SP service on the
3111985a3a3SMaksim Yevmenkin 		 * user's host computer. This brain damaged behavior was
3121985a3a3SMaksim Yevmenkin 		 * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
3131985a3a3SMaksim Yevmenkin 		 * are Symbian-based phones. Perhaps this is a Symbian problem?
3141985a3a3SMaksim Yevmenkin 		 */
3151985a3a3SMaksim Yevmenkin 
3161985a3a3SMaksim Yevmenkin 		if (regsp) {
3171985a3a3SMaksim Yevmenkin 			sdp_sp_profile_t	sp;
3181985a3a3SMaksim Yevmenkin 
3191985a3a3SMaksim Yevmenkin 			memset(&sp, 0, sizeof(sp));
3201985a3a3SMaksim Yevmenkin 			sp.server_channel = channel;
3211985a3a3SMaksim Yevmenkin 
3221985a3a3SMaksim Yevmenkin 			if (sdp_register_service(ss,
3231985a3a3SMaksim Yevmenkin 					SDP_SERVICE_CLASS_SERIAL_PORT,
3241985a3a3SMaksim Yevmenkin 					&addr, (void *) &sp, sizeof(sp),
3251985a3a3SMaksim Yevmenkin 					NULL) != 0) {
3261985a3a3SMaksim Yevmenkin 				syslog(LOG_ERR, "Unable to register SP " \
3271985a3a3SMaksim Yevmenkin 					"service with local SDP daemon. " \
3281985a3a3SMaksim Yevmenkin 					"%s (%d)", strerror(sdp_error(ss)),
3291985a3a3SMaksim Yevmenkin 					sdp_error(ss));
3301985a3a3SMaksim Yevmenkin 				exit(1);
3311985a3a3SMaksim Yevmenkin 			}
3321985a3a3SMaksim Yevmenkin 		}
3331985a3a3SMaksim Yevmenkin 
3341a63eb31SJulian Elischer 		for (done = 0; !done; ) {
335831a4264SMaksim Yevmenkin 			socklen_t	len = sizeof(sock_addr);
3361a63eb31SJulian Elischer 			int		s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
3371a63eb31SJulian Elischer 
3381a63eb31SJulian Elischer 			if (s1 < 0) {
3391a63eb31SJulian Elischer 				syslog(LOG_ERR, "Could not accept connection " \
3401a63eb31SJulian Elischer 					"on socket. %s (%d)", strerror(errno),
3411a63eb31SJulian Elischer 					errno);
3421a63eb31SJulian Elischer 				exit(1);
3431a63eb31SJulian Elischer 			}
3441a63eb31SJulian Elischer 
3451a63eb31SJulian Elischer 			pid = fork();
3461a63eb31SJulian Elischer 			if (pid == (pid_t) -1) {
3471a63eb31SJulian Elischer 				syslog(LOG_ERR, "Could not fork(). %s (%d)",
3481a63eb31SJulian Elischer 					strerror(errno), errno);
3491a63eb31SJulian Elischer 				exit(1);
3501a63eb31SJulian Elischer 			}
3511a63eb31SJulian Elischer 
3521a63eb31SJulian Elischer 			if (pid == 0) {
35313706e45SMaksim Yevmenkin 				sdp_close(ss);
3541a63eb31SJulian Elischer 				close(s);
3551a63eb31SJulian Elischer 
3561a63eb31SJulian Elischer 				/* Reset signal handler */
3571a63eb31SJulian Elischer 				memset(&sa, 0, sizeof(sa));
3581a63eb31SJulian Elischer 				sa.sa_handler = SIG_DFL;
3591a63eb31SJulian Elischer 
3601a63eb31SJulian Elischer 				sigaction(SIGTERM, &sa, NULL);
3611a63eb31SJulian Elischer 				sigaction(SIGHUP, &sa, NULL);
3621a63eb31SJulian Elischer 				sigaction(SIGINT, &sa, NULL);
3631a63eb31SJulian Elischer 				sigaction(SIGCHLD, &sa, NULL);
3641a63eb31SJulian Elischer 
3651a63eb31SJulian Elischer 				/* Become daemon */
3661a63eb31SJulian Elischer 				daemon(0, 0);
3671a63eb31SJulian Elischer 
36813706e45SMaksim Yevmenkin 				/*
36913706e45SMaksim Yevmenkin 				 * XXX Make sure user does not shoot himself
37013706e45SMaksim Yevmenkin 				 * in the foot. Do not pass unit option to the
37113706e45SMaksim Yevmenkin 				 * PPP when operating in the server mode.
37213706e45SMaksim Yevmenkin 				 */
37313706e45SMaksim Yevmenkin 
37413706e45SMaksim Yevmenkin 				exec_ppp(s1, NULL, label);
3751a63eb31SJulian Elischer 			} else
3761a63eb31SJulian Elischer 				close(s1);
3771a63eb31SJulian Elischer 		}
3781a63eb31SJulian Elischer 	} else {
3791a63eb31SJulian Elischer 		sock_addr.rfcomm_len = sizeof(sock_addr);
3801a63eb31SJulian Elischer 		sock_addr.rfcomm_family = AF_BLUETOOTH;
3811a63eb31SJulian Elischer 		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
3821a63eb31SJulian Elischer 			sizeof(sock_addr.rfcomm_bdaddr));
3831a63eb31SJulian Elischer 		sock_addr.rfcomm_channel = 0;
3841a63eb31SJulian Elischer 
3851a63eb31SJulian Elischer 		if (bind(s, (struct sockaddr *) &sock_addr,
3861a63eb31SJulian Elischer 				sizeof(sock_addr)) < 0) {
3871a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
3881a63eb31SJulian Elischer 				strerror(errno), errno);
3891a63eb31SJulian Elischer 			exit(1);
3901a63eb31SJulian Elischer 		}
3911a63eb31SJulian Elischer 
3921a63eb31SJulian Elischer 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
3931a63eb31SJulian Elischer 			sizeof(sock_addr.rfcomm_bdaddr));
3941a63eb31SJulian Elischer 		sock_addr.rfcomm_channel = channel;
3951a63eb31SJulian Elischer 
3961a63eb31SJulian Elischer 		if (connect(s, (struct sockaddr *) &sock_addr,
3971a63eb31SJulian Elischer 				sizeof(sock_addr)) < 0) {
3981a63eb31SJulian Elischer 			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
3991a63eb31SJulian Elischer 				strerror(errno), errno);
4001a63eb31SJulian Elischer 			exit(1);
4011a63eb31SJulian Elischer 		}
4021a63eb31SJulian Elischer 
40313706e45SMaksim Yevmenkin 		exec_ppp(s, unit, label);
4041a63eb31SJulian Elischer 	}
4051a63eb31SJulian Elischer 
4061a63eb31SJulian Elischer 	exit(0);
4071a63eb31SJulian Elischer } /* main */
4081a63eb31SJulian Elischer 
4091a63eb31SJulian Elischer /*
41013706e45SMaksim Yevmenkin  * Redirects stdin/stdout to s, stderr to /dev/null and exec
41113706e45SMaksim Yevmenkin  * 'ppp -direct -quiet [-unit N] label'. Never returns.
4121a63eb31SJulian Elischer  */
4131a63eb31SJulian Elischer 
4141a63eb31SJulian Elischer static void
41513706e45SMaksim Yevmenkin exec_ppp(int s, char *unit, char *label)
4161a63eb31SJulian Elischer {
4171a63eb31SJulian Elischer 	char	 ppp[] = "/usr/sbin/ppp";
41813706e45SMaksim Yevmenkin 	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
41913706e45SMaksim Yevmenkin 				NULL, NULL,      NULL,     NULL };
4201a63eb31SJulian Elischer 
4211a63eb31SJulian Elischer 	close(0);
4221a63eb31SJulian Elischer 	if (dup(s) < 0) {
4231a63eb31SJulian Elischer 		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
4241a63eb31SJulian Elischer 			strerror(errno), errno);
4251a63eb31SJulian Elischer 		exit(1);
4261a63eb31SJulian Elischer 	}
4271a63eb31SJulian Elischer 
4281a63eb31SJulian Elischer 	close(1);
4291a63eb31SJulian Elischer 	if (dup(s) < 0) {
4301a63eb31SJulian Elischer 		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
4311a63eb31SJulian Elischer 			strerror(errno), errno);
4321a63eb31SJulian Elischer 		exit(1);
4331a63eb31SJulian Elischer 	}
4341a63eb31SJulian Elischer 
4351a63eb31SJulian Elischer 	close(2);
4361a63eb31SJulian Elischer 	open("/dev/null", O_RDWR);
4371a63eb31SJulian Elischer 
43813706e45SMaksim Yevmenkin 	if (unit != NULL) {
43913706e45SMaksim Yevmenkin 		ppp_args[3] = "-unit";
44013706e45SMaksim Yevmenkin 		ppp_args[4] = unit;
44113706e45SMaksim Yevmenkin 		ppp_args[5] = label;
44213706e45SMaksim Yevmenkin 	} else
44313706e45SMaksim Yevmenkin 		ppp_args[3] = label;
44413706e45SMaksim Yevmenkin 
4451a63eb31SJulian Elischer 	if (execv(ppp, ppp_args) < 0) {
44613706e45SMaksim Yevmenkin 		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
44713706e45SMaksim Yevmenkin 			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
44813706e45SMaksim Yevmenkin 			(unit != NULL)? unit : "", label,
44913706e45SMaksim Yevmenkin 			strerror(errno), errno);
4501a63eb31SJulian Elischer 		exit(1);
4511a63eb31SJulian Elischer 	}
4521a63eb31SJulian Elischer } /* run_ppp */
4531a63eb31SJulian Elischer 
4541a63eb31SJulian Elischer /* Signal handler */
4551a63eb31SJulian Elischer static void
4561a63eb31SJulian Elischer sighandler(int s)
4571a63eb31SJulian Elischer {
4581a63eb31SJulian Elischer 	done = 1;
4591a63eb31SJulian Elischer } /* sighandler */
4601a63eb31SJulian Elischer 
4611a63eb31SJulian Elischer /* Display usage and exit */
4621a63eb31SJulian Elischer static void
4631a63eb31SJulian Elischer usage(void)
4641a63eb31SJulian Elischer {
4651a63eb31SJulian Elischer 	fprintf(stdout,
4661a63eb31SJulian Elischer "Usage: %s options\n" \
4671a63eb31SJulian Elischer "Where options are:\n" \
4682aa65cf7SMaksim Yevmenkin "\t-a address   Address to listen on or connect to (required for client)\n" \
4691a63eb31SJulian Elischer "\t-c           Act as a clinet (default)\n" \
4701a63eb31SJulian Elischer "\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
4711a63eb31SJulian Elischer "\t-d           Run in foreground\n" \
4721a63eb31SJulian Elischer "\t-l label     Use PPP label (required)\n" \
4731a63eb31SJulian Elischer "\t-s           Act as a server\n" \
4741985a3a3SMaksim Yevmenkin "\t-S           Register Serial Port service (server mode only)\n" \
47513706e45SMaksim Yevmenkin "\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
4761a63eb31SJulian Elischer "\t-h           Display this message\n", RFCOMM_PPPD);
4771a63eb31SJulian Elischer 
4781a63eb31SJulian Elischer 	exit(255);
4791a63eb31SJulian Elischer } /* usage */
4801a63eb31SJulian Elischer 
481