Column sorting fixes, thanks to Tony550
[tomato.git] / release / src / router / pptpd / pptpmanager.c
blob1363feccde67bfda7a710c60ad64109a2dede476
1 /*
2 * pptpmanager.c
4 * Manages the PoPToP sessions.
6 * $Id: pptpmanager.c,v 1.14 2005/12/29 09:59:49 quozl Exp $
7 */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
13 #ifdef __linux__
14 #define _GNU_SOURCE 1 /* broken arpa/inet.h */
15 #endif
17 #include "our_syslog.h"
19 #include <errno.h>
20 #include <netdb.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/un.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <fcntl.h>
36 #if HAVE_LIBWRAP
37 /* re-include, just in case HAVE_SYSLOG_H wasn't defined */
38 #include <syslog.h>
39 #include <tcpd.h>
41 int allow_severity = LOG_WARNING;
42 int deny_severity = LOG_WARNING;
43 #endif
45 #ifdef __UCLIBC__
46 #define socklen_t int
47 #endif
49 #include "configfile.h"
50 #include "defaults.h"
51 #include "pptpctrl.h"
52 #include "pptpdefs.h"
53 #include "pptpmanager.h"
54 #include "compat.h"
56 /* command line arg variables */
57 extern char *ppp_binary;
58 extern char *pppdoptstr;
59 extern char *speedstr;
60 extern char *bindaddr;
61 extern int pptp_debug;
62 extern int pptp_noipparam;
63 extern int pptp_logwtmp;
64 extern int pptp_delegate;
66 /* option for timeout on starting ctrl connection */
67 extern int pptp_stimeout;
69 extern int pptp_connections;
71 /* local function prototypes */
72 static void connectCall(int clientSocket, int clientNumber);
73 static int createHostSocket(int *hostSocket);
75 /* this end's call identifier */
76 uint16_t unique_call_id = 0;
78 /* slots - begin */
80 /* data about connection slots */
81 struct slot {
82 pid_t pid;
83 char *local;
84 char *remote;
85 } *slots;
87 /* number of connection slots allocated */
88 int slot_count;
90 static void slot_iterate(struct slot *slots, int count, void (*callback) (struct slot *slot))
92 int i;
93 for(i=0; i<count; i++)
94 (*callback)(&slots[i]);
97 static void slot_slot_init(struct slot *slot)
99 slot->pid = 0;
100 slot->local = NULL;
101 slot->remote = NULL;
104 void slot_init(int count)
106 slot_count = count;
107 slots = (struct slot *) calloc(slot_count, sizeof(struct slot));
108 slot_iterate(slots, slot_count, slot_slot_init);
111 static void slot_slot_free(struct slot *slot)
113 slot->pid = 0;
114 if (slot->local) free(slot->local);
115 slot->local = NULL;
116 if (slot->remote) free(slot->remote);
117 slot->remote = NULL;
120 void slot_free()
122 slot_iterate(slots, slot_count, slot_slot_free);
123 free(slots);
124 slots = NULL;
125 slot_count = 0;
128 void slot_set_local(int i, char *ip)
130 struct slot *slot = &slots[i];
131 if (slot->local) free(slot->local);
132 slot->local = strdup(ip);
135 void slot_set_remote(int i, char *ip)
137 struct slot *slot = &slots[i];
138 if (slot->remote) free(slot->remote);
139 slot->remote = strdup(ip);
142 void slot_set_pid(int i, pid_t pid)
144 struct slot *slot = &slots[i];
145 slot->pid = pid;
148 int slot_find_by_pid(pid_t pid)
150 int i;
151 for(i=0; i<slot_count; i++) {
152 struct slot *slot = &slots[i];
153 if (slot->pid == pid) return i;
155 return -1;
158 int slot_find_empty()
160 return slot_find_by_pid(0);
163 char *slot_get_local(int i)
165 struct slot *slot = &slots[i];
166 return slot->local;
169 char *slot_get_remote(int i)
171 struct slot *slot = &slots[i];
172 return slot->remote;
175 /* slots - end */
177 static void sigchld_responder(int sig)
179 int child, status;
181 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
182 if (pptp_delegate) {
183 if (pptp_debug) syslog(LOG_DEBUG, "MGR: Reaped child %d", child);
184 } else {
185 int i;
186 i = slot_find_by_pid(child);
187 if (i != -1) {
188 slot_set_pid(i, 0);
189 if (pptp_debug) syslog(LOG_DEBUG, "MGR: Reaped child %d", child);
190 } else {
191 syslog(LOG_INFO, "MGR: Reaped unknown child %d", child);
197 int pptp_manager(int argc, char **argv)
199 int firstOpen = -1;
200 int ctrl_pid;
201 socklen_t addrsize;
203 int hostSocket;
204 fd_set connSet;
206 int rc, sig_fd;
208 rc = sigpipe_create();
209 if (rc < 0) {
210 syslog(LOG_ERR, "MGR: unable to setup sigchld pipe!");
211 syslog_perror("sigpipe_create");
212 exit(-1);
215 sigpipe_assign(SIGCHLD);
216 sigpipe_assign(SIGTERM);
217 sig_fd = sigpipe_fd();
219 /* openlog() not required, done in pptpd.c */
221 syslog(LOG_INFO, "MGR: Manager process started");
223 if (!pptp_delegate) {
224 syslog(LOG_INFO, "MGR: Maximum of %d connections available",
225 pptp_connections);
228 /* Connect the host socket and activate it for listening */
229 if (createHostSocket(&hostSocket) < 0) {
230 syslog(LOG_ERR, "MGR: Couldn't create host socket");
231 syslog_perror("createHostSocket");
232 exit(-1);
235 while (1) {
236 int max_fd;
237 FD_ZERO(&connSet);
238 if (pptp_delegate) {
239 FD_SET(hostSocket, &connSet);
240 } else {
241 firstOpen = slot_find_empty();
242 if (firstOpen == -1) {
243 syslog(LOG_ERR, "MGR: No free connection slots or IPs - no more clients can connect!");
244 } else {
245 FD_SET(hostSocket, &connSet);
248 max_fd = hostSocket;
250 FD_SET(sig_fd, &connSet);
251 if (max_fd < sig_fd) max_fd = sig_fd;
253 while (1) {
254 if (select(max_fd + 1, &connSet, NULL, NULL, NULL) != -1) break;
255 if (errno == EINTR) continue;
256 syslog(LOG_ERR, "MGR: Error with manager select()!");
257 syslog_perror("select");
258 exit(-1);
261 if (FD_ISSET(sig_fd, &connSet)) { /* SIGCHLD */
262 int signum = sigpipe_read();
263 if (signum == SIGCHLD)
264 sigchld_responder(signum);
265 else if (signum == SIGTERM)
266 return signum;
269 if (FD_ISSET(hostSocket, &connSet)) { /* A call came! */
270 int clientSocket;
271 struct sockaddr_in client_addr;
273 /* Accept call and launch PPTPCTRL */
274 addrsize = sizeof(client_addr);
275 clientSocket = accept(hostSocket, (struct sockaddr *) &client_addr, &addrsize);
277 #if HAVE_LIBWRAP
278 if (clientSocket != -1) {
279 struct request_info r;
280 request_init(&r, RQ_DAEMON, "pptpd", RQ_FILE, clientSocket, NULL);
281 fromhost(&r);
282 if (!hosts_access(&r)) {
283 /* send a permission denied message? this is a tcp wrapper
284 * type deny so probably best to just drop it immediately like
285 * this, as tcp wrappers usually do.
287 close(clientSocket);
288 /* this would never be file descriptor 0, so use it as a error
289 * value
291 clientSocket = 0;
294 #endif
295 if (clientSocket == -1) {
296 /* accept failed, but life goes on... */
297 syslog(LOG_ERR, "MGR: accept() failed");
298 syslog_perror("accept");
299 } else if (clientSocket != 0) {
300 fd_set rfds;
301 struct timeval tv;
302 struct pptp_header ph;
304 /* TODO: this select below prevents
305 other connections from being
306 processed during the wait for the
307 first data packet from the
308 client. */
311 * DOS protection: get a peek at the first packet
312 * and do some checks on it before we continue.
313 * A 10 second timeout on the first packet seems reasonable
314 * to me, if anything looks sus, throw it away.
317 FD_ZERO(&rfds);
318 FD_SET(clientSocket, &rfds);
319 tv.tv_sec = pptp_stimeout;
320 tv.tv_usec = 0;
321 if (select(clientSocket + 1, &rfds, NULL, NULL, &tv) <= 0) {
322 syslog(LOG_ERR, "MGR: dropped slow initial connection");
323 close(clientSocket);
324 continue;
327 if (recv(clientSocket, &ph, sizeof(ph), MSG_PEEK) !=
328 sizeof(ph)) {
329 syslog(LOG_ERR, "MGR: dropped small initial connection");
330 close(clientSocket);
331 continue;
334 ph.length = ntohs(ph.length);
335 ph.pptp_type = ntohs(ph.pptp_type);
336 ph.magic = ntohl(ph.magic);
337 ph.ctrl_type = ntohs(ph.ctrl_type);
339 if (ph.length <= 0 || ph.length > PPTP_MAX_CTRL_PCKT_SIZE) {
340 syslog(LOG_WARNING, "MGR: initial packet length %d outside "
341 "(0 - %d)", ph.length, PPTP_MAX_CTRL_PCKT_SIZE);
342 goto dos_exit;
345 if (ph.magic != PPTP_MAGIC_COOKIE) {
346 syslog(LOG_WARNING, "MGR: initial packet bad magic");
347 goto dos_exit;
350 if (ph.pptp_type != PPTP_CTRL_MESSAGE) {
351 syslog(LOG_WARNING, "MGR: initial packet has bad type");
352 goto dos_exit;
355 if (ph.ctrl_type != START_CTRL_CONN_RQST) {
356 syslog(LOG_WARNING, "MGR: initial packet has bad ctrl type "
357 "0x%x", ph.ctrl_type);
358 dos_exit:
359 close(clientSocket);
360 continue;
363 #ifndef HAVE_FORK
364 switch (ctrl_pid = vfork()) {
365 #else
366 switch (ctrl_pid = fork()) {
367 #endif
368 case -1: /* error */
369 syslog(LOG_ERR, "MGR: fork() failed launching " PPTP_CTRL_BIN);
370 close(clientSocket);
371 break;
373 case 0: /* child */
374 close(hostSocket);
375 if (pptp_debug)
376 syslog(LOG_DEBUG, "MGR: Launching " PPTP_CTRL_BIN " to handle client");
377 connectCall(clientSocket, !pptp_delegate ? firstOpen : 0);
378 _exit(1);
379 /* NORETURN */
380 default: /* parent */
381 close(clientSocket);
382 unique_call_id += MAX_CALLS_PER_TCP_LINK;
383 if (!pptp_delegate)
384 slot_set_pid(firstOpen, ctrl_pid);
385 break;
388 } /* FD_ISSET(hostSocket, &connSet) */
389 } /* while (1) */
390 } /* pptp_manager() */
393 * Author: Kevin Thayer
395 * This creates a socket to listen on, sets the max # of pending connections and
396 * various other options.
398 * Returns the fd of the host socket.
400 * The function return values are:
401 * 0 for sucessful
402 * -1 for bad socket creation
403 * -2 for bad socket options
404 * -3 for bad bind
405 * -4 for bad listen
407 static int createHostSocket(int *hostSocket)
409 int opt = 1;
410 struct sockaddr_in address;
411 #ifdef HAVE_GETSERVBYNAME
412 struct servent *serv;
413 #endif
415 /* create the master socket and check it worked */
416 if ((*hostSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
417 return -1;
419 /* set master socket to allow daemon to be restarted with connections active */
420 if (setsockopt(*hostSocket, SOL_SOCKET, SO_REUSEADDR,
421 (char *) &opt, sizeof(opt)) < 0)
422 return -2;
424 /* set up socket */
425 memset(&address, 0, sizeof(address));
426 address.sin_family = AF_INET;
427 if(bindaddr)
428 address.sin_addr.s_addr = inet_addr(bindaddr);
429 else
430 address.sin_addr.s_addr = INADDR_ANY;
431 #ifdef HAVE_GETSERVBYNAME
432 if ((serv = getservbyname("pptp", "tcp")) != NULL) {
433 address.sin_port = serv->s_port;
434 } else
435 #endif
436 address.sin_port = htons(PPTP_PORT);
438 /* bind the socket to the pptp port */
439 if (bind(*hostSocket, (struct sockaddr *) &address, sizeof(address)) < 0)
440 return -3;
442 /* minimal backlog to avoid DoS */
443 if (listen(*hostSocket, 3) < 0)
444 return -4;
446 return 0;
450 * Author: Kevin Thayer
452 * this routine sets up the arguments for the call handler and calls it.
455 static void connectCall(int clientSocket, int clientNumber)
458 #define NUM2ARRAY(array, num) snprintf(array, sizeof(array), "%d", num)
460 char *ctrl_argv[16]; /* arguments for launching 'pptpctrl' binary */
462 int pptpctrl_argc = 0; /* count the number of arguments sent to pptpctrl */
464 /* lame strings to hold passed args. */
465 char ctrl_debug[2];
466 char ctrl_noipparam[2];
467 char pppdoptfile_argv[2];
468 char speedgiven_argv[2];
469 extern char **environ;
471 char callid_argv[16];
474 * Launch the CTRL manager binary; we send it some information such as
475 * speed and option file on the command line.
478 ctrl_argv[pptpctrl_argc++] = PPTP_CTRL_BIN " ";
480 /* Pass socket as stdin */
481 if (clientSocket != 0) {
482 dup2(clientSocket, 0);
483 close(clientSocket);
486 /* get argv set up */
487 NUM2ARRAY(ctrl_debug, pptp_debug ? 1 : 0);
488 ctrl_debug[1] = '\0';
489 ctrl_argv[pptpctrl_argc++] = ctrl_debug;
491 NUM2ARRAY(ctrl_noipparam, pptp_noipparam ? 1 : 0);
492 ctrl_noipparam[1] = '\0';
493 ctrl_argv[pptpctrl_argc++] = ctrl_noipparam;
495 /* optionfile = TRUE or FALSE; so the CTRL manager knows whether to load a non-standard options file */
496 NUM2ARRAY(pppdoptfile_argv, pppdoptstr ? 1 : 0);
497 pppdoptfile_argv[1] = '\0';
498 ctrl_argv[pptpctrl_argc++] = pppdoptfile_argv;
499 if (pppdoptstr) {
500 /* send the option filename so the CTRL manager can launch pppd with this alternate file */
501 ctrl_argv[pptpctrl_argc++] = pppdoptstr;
503 /* tell the ctrl manager whether we were given a speed */
504 NUM2ARRAY(speedgiven_argv, speedstr ? 1 : 0);
505 speedgiven_argv[1] = '\0';
506 ctrl_argv[pptpctrl_argc++] = speedgiven_argv;
507 if (speedstr) {
508 /* send the CTRL manager the speed of the connection so it can fire pppd at that speed */
509 ctrl_argv[pptpctrl_argc++] = speedstr;
511 if (pptp_delegate) {
512 /* no local or remote address to specify */
513 ctrl_argv[pptpctrl_argc++] = "0";
514 ctrl_argv[pptpctrl_argc++] = "0";
515 } else {
516 /* specify local & remote addresses for this call */
517 ctrl_argv[pptpctrl_argc++] = "1";
518 ctrl_argv[pptpctrl_argc++] = slot_get_local(clientNumber);
519 ctrl_argv[pptpctrl_argc++] = "1";
520 ctrl_argv[pptpctrl_argc++] = slot_get_remote(clientNumber);
523 /* our call id to be included in GRE packets the client
524 * will send to us */
525 NUM2ARRAY(callid_argv, unique_call_id);
526 ctrl_argv[pptpctrl_argc++] = callid_argv;
528 /* pass path to ppp binary */
529 ctrl_argv[pptpctrl_argc++] = ppp_binary;
531 /* pass logwtmp flag */
532 ctrl_argv[pptpctrl_argc++] = pptp_logwtmp ? "1" : "0";
534 /* note: update pptpctrl.8 if the argument list format is changed */
536 /* terminate argv array with a NULL */
537 ctrl_argv[pptpctrl_argc] = NULL;
538 pptpctrl_argc++;
540 /* ok, args are setup: invoke the call handler */
541 execve(PPTP_CTRL_BIN, ctrl_argv, environ);
542 syslog(LOG_ERR, "MGR: Failed to exec " PPTP_CTRL_BIN "!");
543 _exit(1);