xref: /freebsd/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c (revision d37ea99837e6ad50837fd9fe1771ddf1c3ba6002)
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$
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 
48 int		rfcomm_channel_lookup	(bdaddr_t const *local,
49 					 bdaddr_t const *remote,
50 					 int service, int *channel, int *error);
51 
52 static void	exec_ppp	(int s, char *unit, char *label);
53 static void	sighandler	(int s);
54 static void	usage		(void);
55 
56 static int	done;
57 
58 /* Main */
59 int
60 main(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;
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 
74 	/* Parse command line arguments */
75 	while ((s = getopt(argc, argv, "a:cC:dhl:su:")) != -1) {
76 		switch (s) {
77 		case 'a': /* BDADDR */
78 			if (!bt_aton(optarg, &addr)) {
79 				struct hostent	*he = NULL;
80 
81 				if ((he = bt_gethostbyname(optarg)) == NULL)
82 					errx(1, "%s: %s", optarg, hstrerror(h_errno));
83 
84 				memcpy(&addr, he->h_addr, sizeof(addr));
85 			}
86 			break;
87 
88 		case 'c': /* client */
89 			server = 0;
90 			break;
91 
92 		case 'C': /* RFCOMM channel */
93 			channel = strtoul(optarg, &ep, 10);
94 			if (*ep != '\0') {
95 				channel = 0;
96 				switch (tolower(optarg[0])) {
97 				case 'd': /* DialUp Networking */
98 					service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
99 					break;
100 
101 				case 'l': /* LAN Access Using PPP */
102 					service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
103 					break;
104 				}
105 			}
106 			break;
107 
108 		case 'd': /* do not detach */
109 			detach = 0;
110 			break;
111 
112 		case 'l': /* PPP label */
113 			label = optarg;
114 			break;
115 
116 		case 's': /* server */
117 			server = 1;
118 			break;
119 
120 		case 'u': /* PPP -unit option */
121 			strtoul(optarg, &ep, 10);
122 			if (*ep != '\0')
123 				usage();
124 				/* NOT REACHED */
125 
126 			unit = optarg;
127 			break;
128 
129 		case 'h':
130 		default:
131 			usage();
132 			/* NOT REACHED */
133 		}
134 	}
135 
136 	/* Check if we got everything we wanted */
137 	if (label == NULL)
138                 errx(1, "Must specify PPP label");
139 
140 	if (!server) {
141 		if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
142                 	errx(1, "Must specify server BD_ADDR");
143 
144 		/* Check channel, if was not set then obtain it via SDP */
145 		if (channel == 0 && service != 0)
146 			if (rfcomm_channel_lookup(NULL, &addr, service,
147 							&channel, &s) != 0)
148 				errc(1, s, "Could not obtain RFCOMM channel");
149 	}
150 
151         if (channel <= 0 || channel > 30)
152                 errx(1, "Invalid RFCOMM channel number %d", channel);
153 
154 	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
155 
156 	if (detach) {
157 		pid = fork();
158 		if (pid == (pid_t) -1) {
159 			syslog(LOG_ERR, "Could not fork(). %s (%d)",
160 				strerror(errno), errno);
161 			exit(1);
162 		}
163 
164 		if (pid != 0)
165 			exit(0);
166 
167 		if (daemon(0, 0) < 0) {
168 			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
169 				strerror(errno), errno);
170 			exit(1);
171 		}
172 	}
173 
174 	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
175 	if (s < 0) {
176 		syslog(LOG_ERR, "Could not create socket. %s (%d)",
177 			strerror(errno), errno);
178 		exit(1);
179 	}
180 
181 	if (server) {
182 		struct sigaction	 sa;
183 		void			*ss = NULL;
184 		sdp_lan_profile_t	 lan;
185 
186 		/* Install signal handler */
187 		memset(&sa, 0, sizeof(sa));
188 		sa.sa_handler = sighandler;
189 
190 		if (sigaction(SIGTERM, &sa, NULL) < 0) {
191 			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
192 				strerror(errno), errno);
193 			exit(1);
194 		}
195 
196 		if (sigaction(SIGHUP, &sa, NULL) < 0) {
197 			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
198 				strerror(errno), errno);
199 			exit(1);
200 		}
201 
202 		if (sigaction(SIGINT, &sa, NULL) < 0) {
203 			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
204 				strerror(errno), errno);
205 			exit(1);
206 		}
207 
208 		sa.sa_handler = SIG_IGN;
209 		sa.sa_flags = SA_NOCLDWAIT;
210 
211 		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
212 			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
213 				strerror(errno), errno);
214 			exit(1);
215 		}
216 
217 		/* bind socket and listen for incoming connections */
218 		sock_addr.rfcomm_len = sizeof(sock_addr);
219 		sock_addr.rfcomm_family = AF_BLUETOOTH;
220 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
221 			sizeof(sock_addr.rfcomm_bdaddr));
222 		sock_addr.rfcomm_channel = channel;
223 
224 		if (bind(s, (struct sockaddr *) &sock_addr,
225 				sizeof(sock_addr)) < 0) {
226 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
227 				strerror(errno), errno);
228 			exit(1);
229 		}
230 
231 		if (listen(s, 10) < 0) {
232 			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
233 				strerror(errno), errno);
234 			exit(1);
235 		}
236 
237 		ss = sdp_open_local(NULL);
238 		if (ss == NULL) {
239 			syslog(LOG_ERR, "Unable to create local SDP session");
240 			exit(1);
241 		}
242 
243 		if (sdp_error(ss) != 0) {
244 			syslog(LOG_ERR, "Unable to open local SDP session. " \
245 				"%s (%d)", strerror(sdp_error(ss)),
246 				sdp_error(ss));
247 			exit(1);
248 		}
249 
250 		memset(&lan, 0, sizeof(lan));
251 		lan.server_channel = channel;
252 
253 		if (sdp_register_service(ss,
254 				SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
255 				&addr, (void *) &lan, sizeof(lan), NULL) != 0) {
256 			syslog(LOG_ERR, "Unable to register LAN service with " \
257 				"local SDP daemon. %s (%d)",
258 				strerror(sdp_error(ss)), sdp_error(ss));
259 			exit(1);
260 		}
261 
262 		for (done = 0; !done; ) {
263 			int	len = sizeof(sock_addr);
264 			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
265 
266 			if (s1 < 0) {
267 				syslog(LOG_ERR, "Could not accept connection " \
268 					"on socket. %s (%d)", strerror(errno),
269 					errno);
270 				exit(1);
271 			}
272 
273 			pid = fork();
274 			if (pid == (pid_t) -1) {
275 				syslog(LOG_ERR, "Could not fork(). %s (%d)",
276 					strerror(errno), errno);
277 				exit(1);
278 			}
279 
280 			if (pid == 0) {
281 				sdp_close(ss);
282 				close(s);
283 
284 				/* Reset signal handler */
285 				memset(&sa, 0, sizeof(sa));
286 				sa.sa_handler = SIG_DFL;
287 
288 				sigaction(SIGTERM, &sa, NULL);
289 				sigaction(SIGHUP, &sa, NULL);
290 				sigaction(SIGINT, &sa, NULL);
291 				sigaction(SIGCHLD, &sa, NULL);
292 
293 				/* Become daemon */
294 				daemon(0, 0);
295 
296 				/*
297 				 * XXX Make sure user does not shoot himself
298 				 * in the foot. Do not pass unit option to the
299 				 * PPP when operating in the server mode.
300 				 */
301 
302 				exec_ppp(s1, NULL, label);
303 			} else
304 				close(s1);
305 		}
306 	} else {
307 		sock_addr.rfcomm_len = sizeof(sock_addr);
308 		sock_addr.rfcomm_family = AF_BLUETOOTH;
309 		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
310 			sizeof(sock_addr.rfcomm_bdaddr));
311 		sock_addr.rfcomm_channel = 0;
312 
313 		if (bind(s, (struct sockaddr *) &sock_addr,
314 				sizeof(sock_addr)) < 0) {
315 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
316 				strerror(errno), errno);
317 			exit(1);
318 		}
319 
320 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
321 			sizeof(sock_addr.rfcomm_bdaddr));
322 		sock_addr.rfcomm_channel = channel;
323 
324 		if (connect(s, (struct sockaddr *) &sock_addr,
325 				sizeof(sock_addr)) < 0) {
326 			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
327 				strerror(errno), errno);
328 			exit(1);
329 		}
330 
331 		exec_ppp(s, unit, label);
332 	}
333 
334 	exit(0);
335 } /* main */
336 
337 /*
338  * Redirects stdin/stdout to s, stderr to /dev/null and exec
339  * 'ppp -direct -quiet [-unit N] label'. Never returns.
340  */
341 
342 static void
343 exec_ppp(int s, char *unit, char *label)
344 {
345 	char	 ppp[] = "/usr/sbin/ppp";
346 	char	*ppp_args[] = { ppp,  "-direct", "-quiet",
347 				NULL, NULL,      NULL,     NULL };
348 
349 	close(0);
350 	if (dup(s) < 0) {
351 		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
352 			strerror(errno), errno);
353 		exit(1);
354 	}
355 
356 	close(1);
357 	if (dup(s) < 0) {
358 		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
359 			strerror(errno), errno);
360 		exit(1);
361 	}
362 
363 	close(2);
364 	open("/dev/null", O_RDWR);
365 
366 	if (unit != NULL) {
367 		ppp_args[3] = "-unit";
368 		ppp_args[4] = unit;
369 		ppp_args[5] = label;
370 	} else
371 		ppp_args[3] = label;
372 
373 	if (execv(ppp, ppp_args) < 0) {
374 		syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
375 			"%s (%d)", ppp, (unit != NULL)? " -unit " : "",
376 			(unit != NULL)? unit : "", label,
377 			strerror(errno), errno);
378 		exit(1);
379 	}
380 } /* run_ppp */
381 
382 /* Signal handler */
383 static void
384 sighandler(int s)
385 {
386 	done = 1;
387 } /* sighandler */
388 
389 /* Display usage and exit */
390 static void
391 usage(void)
392 {
393 	fprintf(stdout,
394 "Usage: %s options\n" \
395 "Where options are:\n" \
396 "\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
397 "\t-c           Act as a clinet (default)\n" \
398 "\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
399 "\t-d           Run in foreground\n" \
400 "\t-l label     Use PPP label (required)\n" \
401 "\t-s           Act as a server\n" \
402 "\t-u N         Tell PPP to operate on /dev/tunN (client mode only)\n" \
403 "\t-h           Display this message\n", RFCOMM_PPPD);
404 
405 	exit(255);
406 } /* usage */
407 
408