4 * Manages the PoPToP sessions.
6 * $Id: pptpmanager.c,v 1.14 2005/12/29 09:59:49 quozl Exp $
14 #define _GNU_SOURCE 1 /* broken arpa/inet.h */
17 #include "our_syslog.h"
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
37 /* re-include, just in case HAVE_SYSLOG_H wasn't defined */
41 int allow_severity
= LOG_WARNING
;
42 int deny_severity
= LOG_WARNING
;
49 #include "configfile.h"
53 #include "pptpmanager.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;
80 /* data about connection slots */
87 /* number of connection slots allocated */
90 static void slot_iterate(struct slot
*slots
, int count
, void (*callback
) (struct slot
*slot
))
93 for(i
=0; i
<count
; i
++)
94 (*callback
)(&slots
[i
]);
97 static void slot_slot_init(struct slot
*slot
)
104 void slot_init(int 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
)
114 if (slot
->local
) free(slot
->local
);
116 if (slot
->remote
) free(slot
->remote
);
122 slot_iterate(slots
, slot_count
, slot_slot_free
);
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
];
148 int slot_find_by_pid(pid_t pid
)
151 for(i
=0; i
<slot_count
; i
++) {
152 struct slot
*slot
= &slots
[i
];
153 if (slot
->pid
== pid
) return i
;
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
];
169 char *slot_get_remote(int i
)
171 struct slot
*slot
= &slots
[i
];
177 static void sigchld_responder(int sig
)
181 while ((child
= waitpid(-1, &status
, WNOHANG
)) > 0) {
183 if (pptp_debug
) syslog(LOG_DEBUG
, "MGR: Reaped child %d", child
);
186 i
= slot_find_by_pid(child
);
189 if (pptp_debug
) syslog(LOG_DEBUG
, "MGR: Reaped child %d", child
);
191 syslog(LOG_INFO
, "MGR: Reaped unknown child %d", child
);
197 int pptp_manager(int argc
, char **argv
)
208 rc
= sigpipe_create();
210 syslog(LOG_ERR
, "MGR: unable to setup sigchld pipe!");
211 syslog_perror("sigpipe_create");
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",
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");
239 FD_SET(hostSocket
, &connSet
);
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!");
245 FD_SET(hostSocket
, &connSet
);
250 FD_SET(sig_fd
, &connSet
);
251 if (max_fd
< sig_fd
) max_fd
= sig_fd
;
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");
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
)
269 if (FD_ISSET(hostSocket
, &connSet
)) { /* A call came! */
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
);
278 if (clientSocket
!= -1) {
279 struct request_info r
;
280 request_init(&r
, RQ_DAEMON
, "pptpd", RQ_FILE
, clientSocket
, NULL
);
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.
288 /* this would never be file descriptor 0, so use it as a error
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) {
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
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.
318 FD_SET(clientSocket
, &rfds
);
319 tv
.tv_sec
= pptp_stimeout
;
321 if (select(clientSocket
+ 1, &rfds
, NULL
, NULL
, &tv
) <= 0) {
322 syslog(LOG_ERR
, "MGR: dropped slow initial connection");
327 if (recv(clientSocket
, &ph
, sizeof(ph
), MSG_PEEK
) !=
329 syslog(LOG_ERR
, "MGR: dropped small initial connection");
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
);
345 if (ph
.magic
!= PPTP_MAGIC_COOKIE
) {
346 syslog(LOG_WARNING
, "MGR: initial packet bad magic");
350 if (ph
.pptp_type
!= PPTP_CTRL_MESSAGE
) {
351 syslog(LOG_WARNING
, "MGR: initial packet has bad type");
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
);
364 switch (ctrl_pid
= vfork()) {
366 switch (ctrl_pid
= fork()) {
369 syslog(LOG_ERR
, "MGR: fork() failed launching " PPTP_CTRL_BIN
);
376 syslog(LOG_DEBUG
, "MGR: Launching " PPTP_CTRL_BIN
" to handle client");
377 connectCall(clientSocket
, !pptp_delegate
? firstOpen
: 0);
380 default: /* parent */
382 unique_call_id
+= MAX_CALLS_PER_TCP_LINK
;
384 slot_set_pid(firstOpen
, ctrl_pid
);
388 } /* FD_ISSET(hostSocket, &connSet) */
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:
402 * -1 for bad socket creation
403 * -2 for bad socket options
407 static int createHostSocket(int *hostSocket
)
410 struct sockaddr_in address
;
411 #ifdef HAVE_GETSERVBYNAME
412 struct servent
*serv
;
415 /* create the master socket and check it worked */
416 if ((*hostSocket
= socket(AF_INET
, SOCK_STREAM
, 0)) == 0)
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)
425 memset(&address
, 0, sizeof(address
));
426 address
.sin_family
= AF_INET
;
428 address
.sin_addr
.s_addr
= inet_addr(bindaddr
);
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
;
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)
442 /* minimal backlog to avoid DoS */
443 if (listen(*hostSocket
, 3) < 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. */
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);
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
;
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
;
508 /* send the CTRL manager the speed of the connection so it can fire pppd at that speed */
509 ctrl_argv
[pptpctrl_argc
++] = speedstr
;
512 /* no local or remote address to specify */
513 ctrl_argv
[pptpctrl_argc
++] = "0";
514 ctrl_argv
[pptpctrl_argc
++] = "0";
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
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
;
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
"!");