Tomato 1.28
[tomato.git] / release / src / router / pptp-client / pptp_callmgr.c
blobd9b77e2020f2eb4a05bf1e7968dbc68c249474ab
1 /* pptp_callmgr.c ... Call manager for PPTP connections.
2 * Handles TCP port 1723 protocol.
3 * C. Scott Ananian <cananian@alumni.princeton.edu>
5 * $Id: pptp_callmgr.c,v 1.2 2002/08/23 01:23:28 honor Exp $
6 */
7 #include <signal.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <sys/un.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <assert.h>
19 #include <setjmp.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include "pptp_callmgr.h"
23 #include "pptp_ctrl.h"
24 #include "pptp_msg.h"
25 #include "dirutil.h"
26 #include "vector.h"
27 #include "util.h"
28 #include <fcntl.h>
30 int open_inetsock(struct in_addr inetaddr);
31 int open_unixsock(struct in_addr inetaddr);
32 void close_inetsock(int fd, struct in_addr inetaddr);
33 void close_unixsock(int fd, struct in_addr inetaddr);
35 sigjmp_buf env;
37 void sighandler(int sig) {
38 siglongjmp (env, 1);
41 void do_nothing(int sig) {
42 /* do nothing signal handler */
45 struct local_callinfo {
46 int unix_sock;
47 pid_t pid[2];
50 struct local_conninfo {
51 VECTOR * call_list;
52 fd_set * call_set;
55 /* Connection callback */
56 void conn_callback(PPTP_CONN *conn, enum conn_state state) {
58 switch(state) {
59 case CONN_OPEN_FAIL:
60 case CONN_CLOSE_DONE:
61 /* get outta here */
62 siglongjmp(env, 1);
63 break;
64 default:
65 log("Unhandled connection callback state [%d].", (int) state);
66 break;
69 /* Call callback */
70 void call_callback(PPTP_CONN *conn, PPTP_CALL *call, enum call_state state) {
71 struct local_callinfo *lci;
72 struct local_conninfo *conninfo;
73 u_int16_t call_id[2];
75 switch(state) {
76 case CALL_OPEN_DONE:
77 /* okey dokey. This means that the call_id and peer_call_id are now
78 * valid, so lets send them on to our friends who requested this call.
80 lci = pptp_call_closure_get(conn, call); assert(lci != NULL);
81 pptp_call_get_ids(conn, call, &call_id[0], &call_id[1]);
82 write(lci->unix_sock, &call_id, sizeof(call_id));
83 /* Our duty to the fatherland is now complete. */
84 break;
85 case CALL_OPEN_FAIL:
86 case CALL_CLOSE_RQST:
87 case CALL_CLOSE_DONE:
88 /* don't need to do anything here, except make sure tables are sync'ed */
89 log("Closing connection");
90 conninfo = pptp_conn_closure_get(conn);
91 lci = pptp_call_closure_get(conn, call);
92 assert(lci != NULL && conninfo != NULL);
93 if (vector_contains(conninfo->call_list, lci->unix_sock)) {
94 vector_remove(conninfo->call_list, lci->unix_sock);
95 close(lci->unix_sock);
96 FD_CLR(lci->unix_sock, conninfo->call_set);
97 if(lci->pid[0]) kill(lci->pid[0], SIGTERM);
98 if(lci->pid[1]) kill(lci->pid[1], SIGTERM);
100 break;
101 default:
102 log("Unhandled call callback state [%d].", (int) state);
103 break;
107 /* Call Manager */
109 /* NOTE ABOUT 'VOLATILE': */
110 /* several variables here get a volatile qualifier to silence warnings */
111 /* from older (before 3.0) gccs. once the longjmp stuff is removed, */
112 /* the volatile qualifiers should be removed as well. */
113 int main(int argc, char **argv, char **envp) {
114 struct in_addr inetaddr;
115 int inet_sock, unix_sock;
116 fd_set call_set;
117 PPTP_CONN * conn;
118 VECTOR * call_list;
119 int max_fd=0;
120 volatile int first=1;
121 int retval;
122 int i;
123 char * volatile phonenr;
125 /* Step 0: Check arguments */
126 if (argc < 2)
127 fatal("Usage: %s ip.add.ress.here [--phone <phone number>]", argv[0]);
128 phonenr = argc==3 ? argv[2] : NULL;
129 if (inet_aton(argv[1], &inetaddr)==0)
130 fatal("Invalid IP address: %s", argv[1]);
132 /* Step 1: Open sockets. */
133 if ((inet_sock = open_inetsock(inetaddr)) < 0)
134 fatal("Could not open control connection to %s", argv[1]);
135 if ((unix_sock = open_unixsock(inetaddr)) < 0)
136 fatal("Could not open unix socket for %s", argv[1]);
138 /* Step 1b: FORK and return status to calling process. */
139 switch (fork()) {
140 case 0: /* child. stick around. */
141 break;
142 case -1: /* failure. Fatal. */
143 fatal("Could not fork.");
144 default: /* Parent. Return status to caller. */
145 exit(0);
148 /* Step 1c: Clean up unix socket on TERM */
149 if (sigsetjmp(env, 1)!=0)
150 goto cleanup;
152 signal(SIGINT, sighandler);
153 signal(SIGTERM, sighandler);
155 signal(SIGPIPE, do_nothing);
156 signal(SIGUSR1, do_nothing); /* signal state change; wake up accept */
158 /* Step 2: Open control connection and register callback */
159 if ((conn = pptp_conn_open(inet_sock, 1, NULL/* callback */)) == NULL) {
160 close(unix_sock); close(inet_sock); fatal("Could not open connection.");
163 FD_ZERO(&call_set);
164 max_fd = unix_sock;
165 call_list = vector_create();
167 struct local_conninfo *conninfo = malloc(sizeof(*conninfo));
168 if (conninfo==NULL) {
169 close(unix_sock); close(inet_sock); fatal("No memory.");
171 conninfo->call_list = call_list;
172 conninfo->call_set = &call_set;
173 pptp_conn_closure_put(conn, conninfo);
176 if (sigsetjmp(env, 1)!=0) goto shutdown;
178 /* Step 3: Get FD_SETs */
179 do {
180 int rc;
181 fd_set read_set = call_set, write_set;
182 FD_ZERO (&write_set);
183 FD_SET (unix_sock, &read_set);
184 pptp_fd_set(conn, &read_set, &write_set, &max_fd);
186 for (; max_fd > 0 ; max_fd--) {
187 if (FD_ISSET (max_fd, &read_set) ||
188 FD_ISSET (max_fd, &write_set))
189 break;
192 /* Step 4: Wait on INET or UNIX event */
194 if ((rc = select(max_fd+1, &read_set, &write_set, NULL, NULL)) <0)
195 /* a signal or somesuch. */
196 continue;
198 /* Step 5a: Handle INET events */
199 pptp_dispatch(conn, &read_set, &write_set);
201 /* Step 5b: Handle new connection to UNIX socket */
202 if (FD_ISSET(unix_sock, &read_set)) {
203 /* New call! */
204 struct sockaddr_un from;
205 int len = sizeof(from);
206 PPTP_CALL * call;
207 struct local_callinfo *lci;
208 int s;
210 /* Accept the socket */
211 FD_CLR (unix_sock, &read_set);
212 if ((s = accept(unix_sock, (struct sockaddr *) &from, &len))<0) {
213 warn("Socket not accepted: %s", strerror(errno));
214 goto skip_accept;
216 /* Allocate memory for local call information structure. */
217 if ((lci = malloc(sizeof(*lci))) == NULL) {
218 warn("Out of memory."); close(s); goto skip_accept;
220 lci->unix_sock = s;
222 /* Give the initiator time to write the PIDs while we open the call */
223 call = pptp_call_open(conn, call_callback, phonenr);
224 /* Read and store the associated pids */
225 read(s, &lci->pid[0], sizeof(lci->pid[0]));
226 read(s, &lci->pid[1], sizeof(lci->pid[1]));
227 /* associate the local information with the call */
228 pptp_call_closure_put(conn, call, (void *) lci);
229 /* The rest is done on callback. */
231 /* Keep alive; wait for close */
232 retval = vector_insert(call_list, s, call); assert(retval);
233 if (s > max_fd) max_fd = s;
234 FD_SET(s, &call_set);
235 first = 0;
237 skip_accept:
238 /* Step 5c: Handle socket close */
239 for (i=0; i<max_fd+1; i++)
240 if (FD_ISSET(i, &read_set)) {
241 /* close it */
242 PPTP_CALL * call;
243 retval = vector_search(call_list, i, &call);
244 if (retval) {
245 struct local_callinfo *lci = pptp_call_closure_get(conn, call);
246 log("Closing connection");
247 if(lci->pid[0]) kill(lci->pid[0], SIGTERM);
248 if(lci->pid[1]) kill(lci->pid[1], SIGTERM);
249 free(lci);
250 /* soft shutdown. Callback will do hard shutdown later */
251 pptp_call_close(conn, call);
252 vector_remove(call_list, i);
254 FD_CLR(i, &call_set);
255 close(i);
257 } while (vector_size(call_list)>0 || first);
259 shutdown:
261 fd_set read_set, write_set;
263 /* warn("Shutdown"); */
264 /* kill all open calls */
265 for (i=0; i<vector_size(call_list); i++) {
266 PPTP_CALL *call = vector_get_Nth(call_list, i);
267 struct local_callinfo *lci = pptp_call_closure_get(conn, call);
268 log("Closing connection");
269 pptp_call_close(conn, call);
270 if(lci->pid[0]) kill(lci->pid[0], SIGTERM);
271 if(lci->pid[1]) kill(lci->pid[1], SIGTERM);
273 /* attempt to dispatch these messages */
274 FD_ZERO(&read_set);
275 FD_ZERO(&write_set);
276 pptp_fd_set(conn, &read_set, &write_set, &max_fd);
277 FD_ZERO(&read_set);
278 pptp_dispatch(conn, &read_set, &write_set);
279 if (i>0) sleep(2);
280 /* no more open calls. Close the connection. */
281 pptp_conn_close(conn, PPTP_STOP_LOCAL_SHUTDOWN);
282 FD_ZERO(&read_set);
283 FD_ZERO(&write_set);
284 pptp_fd_set(conn, &read_set, &write_set, &max_fd);
285 FD_ZERO(&read_set);
286 pptp_dispatch(conn, &read_set, &write_set);
287 sleep(2);
288 /* with extreme prejudice */
289 pptp_conn_destroy(conn);
290 vector_destroy(call_list);
292 cleanup:
293 close_inetsock(inet_sock, inetaddr);
294 close_unixsock(unix_sock, inetaddr);
295 return 0;
299 * Connect socket, but with a timeout.
300 * A signal will interrupt this function.
302 * param: msec is the timeout in milliseconds
303 * return: -1 on error, errno is set
304 * -2 on timeout
305 * 0 on successful
307 int connect_timeout(int socket, struct sockaddr *addr, socklen_t len, int msec)
309 int oldflags; /* flags to be restored later */
310 int newflags; /* nonblocking sockopt for socket */
311 int ret; /* Result of syscalls */
312 int value; /* Value to be returned */
314 /* Make sure the socket is nonblocking */
315 oldflags = fcntl(socket, F_GETFL, 0);
316 if(oldflags == -1)
317 return -1;
318 newflags = oldflags | O_NONBLOCK;
319 ret = fcntl(socket, F_SETFL, newflags);
320 if(ret == -1)
321 return -1;
323 /* Issue the connect request */
324 ret = connect(socket, addr, len);
325 value = ret;
327 if(ret == 0)
329 /* Connect finished directly */
331 else if(errno == EINPROGRESS || errno == EWOULDBLOCK)
333 fd_set wset;
334 struct timeval tv;
336 FD_ZERO(&wset);
337 FD_SET(socket, &wset);
339 tv.tv_sec = msec / 1000;
340 tv.tv_usec = 1000*(msec % 1000);
342 ret = select(socket+1, NULL, &wset, NULL, &tv);
344 if(ret == 1 && FD_ISSET(socket, &wset))
346 int optval;
347 socklen_t optlen = sizeof(optval);
349 ret = getsockopt(socket, SOL_SOCKET, SO_ERROR, &optval, &optlen);
350 if(ret == -1)
351 return -1;
353 if(optval==0)
354 value = 0;
355 else
356 value = -1;
358 else if(ret == 0) {
359 warn("connect timeout for %d seconds",msec / 1000);
360 value = -2; /* select timeout */
362 else {
363 warn("select error");
364 value = -1; /* select error */
367 else
368 value = -1;
370 /* Restore the socket flags */
371 ret = fcntl(socket, F_SETFL, oldflags);
372 if(ret == -1)
373 perror("restoring socket flags: fcntl");
375 return value;
378 int open_inetsock(struct in_addr inetaddr) {
379 struct sockaddr_in dest;
380 int s;
382 dest.sin_family = AF_INET;
383 dest.sin_port = htons(PPTP_PORT);
384 dest.sin_addr = inetaddr;
386 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
387 warn("socket: %s", strerror(errno));
388 return s;
390 if (connect_timeout(s, (struct sockaddr *) &dest, sizeof(dest), 15000) < 0) {
391 warn("connect: %s", strerror(errno));
392 close(s); return -1;
394 return s;
396 int open_unixsock(struct in_addr inetaddr) {
397 struct sockaddr_un where;
398 struct stat st;
399 char *dir;
400 int s;
402 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
403 warn("socket: %s", strerror(errno));
404 return s;
407 where.sun_family = AF_UNIX;
408 snprintf(where.sun_path, sizeof(where.sun_path),
409 PPTP_SOCKET_PREFIX "%s", inet_ntoa(inetaddr));
411 if (stat(where.sun_path, &st) >= 0) {
412 warn("Call manager for %s is already running.", inet_ntoa(inetaddr));
413 close(s); return -1;
416 /* Make sure path is valid. */
417 dir = dirname(where.sun_path);
418 if (!make_valid_path(dir, 0770))
419 fatal("Could not make path to %s: %s", where.sun_path, strerror(errno));
420 free(dir);
422 if (bind(s, (struct sockaddr *) &where, sizeof(where)) < 0) {
423 warn("bind: %s", strerror(errno));
424 close(s); return -1;
427 chmod(where.sun_path, 0777);
429 listen(s, 127);
431 return s;
433 void close_inetsock(int fd, struct in_addr inetaddr) {
434 close(fd);
436 void close_unixsock(int fd, struct in_addr inetaddr) {
437 struct sockaddr_un where;
438 close(fd);
439 snprintf(where.sun_path, sizeof(where.sun_path),
440 PPTP_SOCKET_PREFIX "%s", inet_ntoa(inetaddr));
441 unlink(where.sun_path);