xref: /freebsd/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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.3 2003/04/26 23:59:49 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <bitstring.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <ng_hci.h>
38 #include <ng_l2cap.h>
39 #include <ng_btsocket.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 
50 static void	exec_ppp	(int s, char *label);
51 static void	sighandler	(int s);
52 static void	usage		(void);
53 
54 static int	done;
55 
56 /* Main */
57 int
58 main(int argc, char *argv[])
59 {
60 	struct sockaddr_rfcomm   sock_addr;
61 	char			*label = NULL;
62 	bdaddr_t		 addr;
63 	int			 s, channel, detach, server;
64 	pid_t			 pid;
65 
66 	memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
67 	channel = 0;
68 	detach = 1;
69 	server = 0;
70 
71 	/* Parse command line arguments */
72 	while ((s = getopt(argc, argv, "a:cC:dhl:s")) != -1) {
73 		switch (s) {
74 		case 'a': { /* BDADDR */
75 			int	a0, a1, a2, a3, a4, a5;
76 
77 			if (sscanf(optarg, "%x:%x:%x:%x:%x:%x",
78 					&a5, &a4, &a3, &a2, &a1, &a0) != 6)
79 				usage();
80 				/* NOT REACHED */
81 
82 			addr.b[0] = a0 & 0xff;
83 			addr.b[1] = a1 & 0xff;
84 			addr.b[2] = a2 & 0xff;
85 			addr.b[3] = a3 & 0xff;
86 			addr.b[4] = a4 & 0xff;
87 			addr.b[5] = a5 & 0xff;
88 			} break;
89 
90 		case 'c': /* client */
91 			server = 0;
92 			break;
93 
94 		case 'C': /* RFCOMM channel */
95 			channel = atoi(optarg);
96 			break;
97 
98 		case 'd': /* do not detach */
99 			detach = 0;
100 			break;
101 
102 		case 'l': /* PPP label */
103 			label = optarg;
104 			break;
105 
106 		case 's':
107 			server = 1;
108 			break;
109 
110 		case 'h':
111 		default:
112 			usage();
113 			/* NOT REACHED */
114 		}
115 	}
116 
117 	/* Check if we got everything we wanted */
118 	if ((channel <= 0 || channel > 30) || label == NULL ||
119 	    (!server && memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0))
120 		usage();
121 		/* NOT REACHED */
122 
123 	openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
124 
125 	if (detach) {
126 		pid = fork();
127 		if (pid == (pid_t) -1) {
128 			syslog(LOG_ERR, "Could not fork(). %s (%d)",
129 				strerror(errno), errno);
130 			exit(1);
131 		}
132 
133 		if (pid != 0)
134 			exit(0);
135 
136 		if (daemon(0, 0) < 0) {
137 			syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
138 				strerror(errno), errno);
139 			exit(1);
140 		}
141 	}
142 
143 	s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
144 	if (s < 0) {
145 		syslog(LOG_ERR, "Could not create socket. %s (%d)",
146 			strerror(errno), errno);
147 		exit(1);
148 	}
149 
150 	if (server) {
151 		struct sigaction	sa;
152 
153 		/* Install signal handler */
154 		memset(&sa, 0, sizeof(sa));
155 		sa.sa_handler = sighandler;
156 
157 		if (sigaction(SIGTERM, &sa, NULL) < 0) {
158 			syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
159 				strerror(errno), errno);
160 			exit(1);
161 		}
162 
163 		if (sigaction(SIGHUP, &sa, NULL) < 0) {
164 			syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
165 				strerror(errno), errno);
166 			exit(1);
167 		}
168 
169 		if (sigaction(SIGINT, &sa, NULL) < 0) {
170 			syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
171 				strerror(errno), errno);
172 			exit(1);
173 		}
174 
175 		sa.sa_handler = SIG_IGN;
176 		sa.sa_flags = SA_NOCLDWAIT;
177 
178 		if (sigaction(SIGCHLD, &sa, NULL) < 0) {
179 			syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
180 				strerror(errno), errno);
181 			exit(1);
182 		}
183 
184 		/* bind socket and listen for incoming connections */
185 		sock_addr.rfcomm_len = sizeof(sock_addr);
186 		sock_addr.rfcomm_family = AF_BLUETOOTH;
187 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
188 			sizeof(sock_addr.rfcomm_bdaddr));
189 		sock_addr.rfcomm_channel = channel;
190 
191 		if (bind(s, (struct sockaddr *) &sock_addr,
192 				sizeof(sock_addr)) < 0) {
193 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
194 				strerror(errno), errno);
195 			exit(1);
196 		}
197 
198 		if (listen(s, 10) < 0) {
199 			syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
200 				strerror(errno), errno);
201 			exit(1);
202 		}
203 
204 		for (done = 0; !done; ) {
205 			int	len = sizeof(sock_addr);
206 			int	s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
207 
208 			if (s1 < 0) {
209 				syslog(LOG_ERR, "Could not accept connection " \
210 					"on socket. %s (%d)", strerror(errno),
211 					errno);
212 				exit(1);
213 			}
214 
215 			pid = fork();
216 			if (pid == (pid_t) -1) {
217 				syslog(LOG_ERR, "Could not fork(). %s (%d)",
218 					strerror(errno), errno);
219 				exit(1);
220 			}
221 
222 			if (pid == 0) {
223 				close(s);
224 
225 				/* Reset signal handler */
226 				memset(&sa, 0, sizeof(sa));
227 				sa.sa_handler = SIG_DFL;
228 
229 				sigaction(SIGTERM, &sa, NULL);
230 				sigaction(SIGHUP, &sa, NULL);
231 				sigaction(SIGINT, &sa, NULL);
232 				sigaction(SIGCHLD, &sa, NULL);
233 
234 				/* Become daemon */
235 				daemon(0, 0);
236 
237 				exec_ppp(s1, label);
238 			} else
239 				close(s1);
240 		}
241 	} else {
242 		sock_addr.rfcomm_len = sizeof(sock_addr);
243 		sock_addr.rfcomm_family = AF_BLUETOOTH;
244 		memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
245 			sizeof(sock_addr.rfcomm_bdaddr));
246 		sock_addr.rfcomm_channel = 0;
247 
248 		if (bind(s, (struct sockaddr *) &sock_addr,
249 				sizeof(sock_addr)) < 0) {
250 			syslog(LOG_ERR, "Could not bind socket. %s (%d)",
251 				strerror(errno), errno);
252 			exit(1);
253 		}
254 
255 		memcpy(&sock_addr.rfcomm_bdaddr, &addr,
256 			sizeof(sock_addr.rfcomm_bdaddr));
257 		sock_addr.rfcomm_channel = channel;
258 
259 		if (connect(s, (struct sockaddr *) &sock_addr,
260 				sizeof(sock_addr)) < 0) {
261 			syslog(LOG_ERR, "Could not connect socket. %s (%d)",
262 				strerror(errno), errno);
263 			exit(1);
264 		}
265 
266 		exec_ppp(s, label);
267 	}
268 
269 	exit(0);
270 } /* main */
271 
272 /*
273  * Redirects stdin/stdout to s, stderr to /dev/null and exec ppp -direct label.
274  * Never retruns.
275  */
276 
277 static void
278 exec_ppp(int s, char *label)
279 {
280 	char	 ppp[] = "/usr/sbin/ppp";
281 	char	*ppp_args[] = { ppp, "-direct", NULL, NULL };
282 
283 	close(0);
284 	if (dup(s) < 0) {
285 		syslog(LOG_ERR, "Could not dup(0). %s (%d)",
286 			strerror(errno), errno);
287 		exit(1);
288 	}
289 
290 	close(1);
291 	if (dup(s) < 0) {
292 		syslog(LOG_ERR, "Could not dup(1). %s (%d)",
293 			strerror(errno), errno);
294 		exit(1);
295 	}
296 
297 	close(2);
298 	open("/dev/null", O_RDWR);
299 
300 	ppp_args[2] = label;
301 	if (execv(ppp, ppp_args) < 0) {
302 		syslog(LOG_ERR, "Could not exec(%s -direct %s). %s (%d)",
303 			ppp, label, strerror(errno), errno);
304 		exit(1);
305 	}
306 } /* run_ppp */
307 
308 /* Signal handler */
309 static void
310 sighandler(int s)
311 {
312 	done = 1;
313 } /* sighandler */
314 
315 /* Display usage and exit */
316 static void
317 usage(void)
318 {
319 	fprintf(stdout,
320 "Usage: %s options\n" \
321 "Where options are:\n" \
322 "\t-a bdaddr    BDADDR to listen on or connect to (required for client)\n" \
323 "\t-c           Act as a clinet (default)\n" \
324 "\t-C channel   RFCOMM channel to listen on or connect to (required)\n" \
325 "\t-d           Run in foreground\n" \
326 "\t-l label     Use PPP label (required)\n" \
327 "\t-s           Act as a server\n" \
328 "\t-h           Display this message\n", RFCOMM_PPPD);
329 
330 	exit(255);
331 } /* usage */
332 
333