Tomato 1.28
[tomato.git] / release / src / router / pptp-client / pptp.c
blob192f1630dfb929796c6b7ae777e99ddb02fdb77e
1 /* pptp.c ... client shell to launch call managers, data handlers, and
2 * the pppd from the command line.
3 * C. Scott Ananian <cananian@alumni.princeton.edu>
5 * $Id: pptp.c,v 1.2 2002/08/20 07:11:40 honor Exp $
6 */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #if defined(__FreeBSD__)
11 #include <libutil.h>
12 #elif defined(__NetBSD__)
13 #include <util.h>
14 #else
15 #include <pty.h>
16 #endif
17 //#ifdef USER_PPP
18 #include <fcntl.h>
19 //#endif
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <sys/un.h>
23 #include <netdb.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <setjmp.h>
30 #include <errno.h>
31 #include <sys/wait.h>
32 #include <getopt.h>
33 #include <limits.h>
34 #include "config.h"
35 #include "pptp_callmgr.h"
36 #include "pptp_gre.h"
37 #include "version.h"
38 #include "inststr.h"
39 #include "util.h"
40 #include "pptp_quirks.h"
42 #ifndef PPPD_BINARY
43 #define PPPD_BINARY "pppd"
44 #endif
46 struct in_addr get_ip_address(char *name);
47 int open_callmgr(struct in_addr inetaddr, char *phonenr, int argc,char **argv,char **envp);
48 void launch_callmgr(struct in_addr inetaddr, char *phonenr, int argc,char **argv,char **envp);
49 int get_call_id(int sock, pid_t gre, pid_t pppd,
50 u_int16_t *call_id, u_int16_t *peer_call_id);
51 void launch_pppd(char *ttydev, int argc, char **argv);
54 /* TODO: redesign to avoid longjmp/setjmp. Several variables here
55 have a volatile qualifier to silence warnings from gcc < 3.0.
56 Remove the volatile qualifiers when longjmp/setjmp are removed. */
57 /* Create pseudo tty master slave pair and set terminal attributes
58 according to TERMP and WINP. Return handles for both ends in
59 AMASTER and ASLAVE, and return the name of the slave end in NAME. */
61 /* from uClibc 0.9.9 openpty.c */
62 int
63 my_openpty (int *amaster, int *aslave, char *name, struct termios *termp,
64 struct winsize *winp)
66 #if 0
67 #ifdef PATH_MAX
68 char _buf[PATH_MAX];
69 #else
70 char _buf[512];
71 #endif
72 char *buf = _buf;
73 #else
74 #ifdef PATH_MAX
75 char buf[PATH_MAX];
76 #else
77 char buf[512];
78 #endif
79 #endif
80 int master, slave;
82 master = getpt ();
83 if (master == -1)
84 return -1;
86 if (grantpt (master))
87 goto fail;
89 if (unlockpt (master))
90 goto fail;
92 #if 0
93 if (pts_name (master, &buf, sizeof (_buf)))
94 #else
95 if (ptsname_r (master, buf, sizeof buf))
96 #endif
97 goto fail;
99 slave = open (buf, O_RDWR | O_NOCTTY);
100 if (slave == -1)
102 #if 0
103 if (buf != _buf)
104 free (buf);
105 #endif
106 goto fail;
109 /* XXX Should we ignore errors here? */
110 if(termp)
111 tcsetattr (slave, TCSAFLUSH, termp);
112 if (winp)
113 ioctl (slave, TIOCSWINSZ, winp);
115 *amaster = master;
116 *aslave = slave;
117 if (name != NULL)
118 strcpy (name, buf);
120 #if 0
121 if (buf != _buf)
122 free (buf);
123 #endif
124 return 0;
126 fail:
127 close (master);
128 return -1;
131 void usage(char *progname) {
132 fprintf(stderr,
133 "%s\n"
134 "Usage:\n"
135 " %s hostname [[--phone <phone number>] [--quirks ISP_NAME] -- ][ pppd options]\n"
136 "\nOr using pppd option pty: \n"
137 " pty \" %s hostname --nolaunchpppd [--phone <phone number>] [--quirks ISP_NAME]\"\n"
138 "Currently recognized ISP_NAMEs for quirks are BEZEQ_ISRAEL\n",
139 version, progname, progname);
140 log("%s called with wrong arguments, program not started.", progname);
142 exit(1);
145 static int signaled = 0;
147 void do_nothing(int sig) {
148 /* do nothing signal handler. Better than SIG_IGN. */
149 signaled = 1;
152 sigjmp_buf env;
153 void sighandler(int sig) {
154 siglongjmp(env, 1);
157 /* TODO: redesign to avoid longjmp/setjmp. Several variables here
158 have a volatile qualifier to silence warnings from gcc < 3.0.
159 Remove the volatile qualifiers when longjmp/setjmp are removed. */
161 int main(int argc, char **argv, char **envp) {
162 struct in_addr inetaddr;
163 volatile int callmgr_sock = -1;
164 char ttydev[PATH_MAX];
165 int pty_fd, tty_fd, rc;
166 volatile pid_t parent_pid, child_pid;
167 u_int16_t call_id, peer_call_id;
168 int pppdargc;
169 char **pppdargv;
170 char phonenrbuf[65]; /* maximum length of field plus one for the trailing
171 * '\0' */
172 char * volatile phonenr = NULL;
173 volatile int launchpppd = 1;
174 if (argc < 2)
175 usage(argv[0]);
177 /* Step 1a: Get IP address for the hostname in argv[1] */
178 inetaddr = get_ip_address(argv[1]);
180 /* step 1b: Find the ppp options, extract phone number */
181 argc--;
182 argv++;
183 while(1){
184 /* structure with all recognised options for pptp */
185 static struct option long_options[] = {
186 {"phone", 1, 0, 0},
187 {"nolaunchpppd", 0, 0, 0},
188 {"quirks", 1, 0, 0},
189 {0, 0, 0, 0}
191 int option_index = 0;
192 int c;
193 opterr=0; /* suppress "unrecognised option" message, here
194 * we assume that it is a pppd option */
195 c = getopt_long (argc, argv, "", long_options, &option_index);
196 if( c==-1) break; /* no more options */
197 switch (c) {
198 case 0:
199 if(option_index == 0) { /* --phone specified */
200 /* copy it to a buffer, as the argv's will be overwritten by
201 * inststr() */
202 strncpy(phonenrbuf,optarg,sizeof(phonenrbuf));
203 phonenrbuf[sizeof(phonenrbuf)-1]='\0';
204 phonenr=phonenrbuf;
205 }else if(option_index == 1) {/* --nolaunchpppd specified */
206 launchpppd=0;
207 }else if(option_index == 2) {/* --quirks specified */
208 if (set_quirk_index(find_quirk(optarg)))
209 usage(argv[0]);
211 /* other pptp options come here */
212 break;
213 case '?': /* unrecognised option, treat it as the first pppd option */
214 /* fall through */
215 default:
216 c = -1;
217 break;
219 if( c==-1) break; /* no more options for pptp */
221 pppdargc = argc - optind;
222 pppdargv = argv + optind;
224 /* Step 3: Find an open pty/tty pair. */
225 if(launchpppd){
226 rc = my_openpty (&pty_fd, &tty_fd, ttydev, NULL, NULL);
227 if (rc < 0) {
228 close(callmgr_sock);
229 fatal("Could not find free pty.");
232 /* Step 4: fork and wait. */
233 signal(SIGUSR1, do_nothing); /* don't die */
234 parent_pid = getpid();
235 switch (child_pid = fork()) {
236 case -1:
237 fatal("Could not fork pppd process");
239 case 0: /* I'm the child! */
240 close (tty_fd);
241 signal(SIGUSR1, SIG_DFL);
242 child_pid = getpid();
243 break;
244 default: /* parent */
245 close (pty_fd);
247 * There is still a very small race condition here. If a signal
248 * occurs after signaled is checked but before pause is called,
249 * things will hang.
251 if (!signaled) {
252 pause(); /* wait for the signal */
254 launch_pppd(ttydev, pppdargc, pppdargv); /* launch pppd */
255 perror("Error");
256 fatal("Could not launch pppd");
258 } else { /* ! launchpppd */
259 pty_fd=tty_fd=0;
260 child_pid=getpid();
261 parent_pid=0; /* don't kill pppd */
264 do {
266 * Step 2: Open connection to call manager
267 * (Launch call manager if necessary.)
269 callmgr_sock = open_callmgr(inetaddr, phonenr, argc, argv, envp);
271 /* Step 5: Exchange PIDs, get call ID */
272 } while (get_call_id(callmgr_sock, parent_pid, child_pid,
273 &call_id, &peer_call_id) < 0);
275 /* Step 5b: Send signal to wake up pppd task */
276 if(launchpppd){
277 kill(parent_pid, SIGUSR1);
278 sleep(2);
282 char buf[128];
283 snprintf(buf, sizeof(buf), "pptp: GRE-to-PPP gateway on %s", ttyname(tty_fd));
284 inststr(argc,argv,envp, buf);
287 if (sigsetjmp(env, 1)!=0) goto shutdown;
288 signal(SIGINT, sighandler);
289 signal(SIGTERM, sighandler);
290 signal(SIGKILL, sighandler);
292 /* Step 6: Do GRE copy until close. */
293 pptp_gre_copy(call_id, peer_call_id, pty_fd, inetaddr);
295 shutdown:
296 /* on close, kill all. */
297 if(launchpppd)
298 kill(parent_pid, SIGTERM);
299 close(pty_fd);
300 close(callmgr_sock);
301 sleep(3); /* give ctrl manager a chance to exit */
302 exit(0);
305 struct in_addr get_ip_address(char *name) {
306 struct in_addr retval;
307 struct hostent *host = gethostbyname(name);
308 if (host==NULL) {
309 if (h_errno == HOST_NOT_FOUND)
310 fatal("gethostbyname: HOST NOT FOUND");
311 else if (h_errno == NO_ADDRESS)
312 fatal("gethostbyname: NO IP ADDRESS");
313 else
314 fatal("gethostbyname: name server error");
317 if (host->h_addrtype != AF_INET)
318 fatal("Host has non-internet address");
320 memcpy(&retval.s_addr, host->h_addr, sizeof(retval.s_addr));
321 return retval;
324 int open_callmgr(struct in_addr inetaddr, char *phonenr, int argc, char **argv, char **envp)
326 /* Try to open unix domain socket to call manager. */
327 struct sockaddr_un where;
328 const int NUM_TRIES = 3;
329 int i, fd;
330 pid_t pid;
331 int status;
333 /* Open socket */
334 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
335 fatal("Could not create unix domain socket: %s", strerror(errno));
338 /* Make address */
339 where.sun_family = AF_UNIX;
340 snprintf(where.sun_path, sizeof(where.sun_path),
341 PPTP_SOCKET_PREFIX "%s", inet_ntoa(inetaddr));
343 for (i=0; i<NUM_TRIES; i++) {
344 if (connect(fd, (struct sockaddr *) &where, sizeof(where)) < 0) {
345 /* couldn't connect. We'll have to launch this guy. */
347 unlink (where.sun_path);
349 /* fork and launch call manager process */
350 switch (pid=fork()) {
351 case -1: /* failure */
352 fatal("fork() to launch call manager failed.");
353 case 0: /* child */
355 close (fd);
356 launch_callmgr(inetaddr, phonenr, argc,argv,envp);
358 default: /* parent */
359 waitpid(pid, &status, 0);
360 if (status!=0)
361 fatal("Call manager exited with error %d", status);
362 break;
364 sleep(1);
366 else return fd;
368 close(fd);
369 fatal("Could not launch call manager after %d tries.", i);
370 return -1; /* make gcc happy */
373 void launch_callmgr(struct in_addr inetaddr, char *phonenr, int argc,
374 char**argv,char**envp)
376 int callmgr_main(int argc, char**argv, char**envp);
377 char *my_argv[3] = { argv[0], inet_ntoa(inetaddr), phonenr };
378 char buf[128];
379 snprintf(buf, sizeof(buf), "pptp: call manager for %s", my_argv[1]);
380 inststr(argc,argv,envp,buf);
381 exit(callmgr_main(3, my_argv, envp));
383 const char *callmgr = PPTP_CALLMGR_BINARY;
384 execlp(callmgr, callmgr, inet_ntoa(inetaddr), NULL);
385 fatal("execlp() of call manager [%s] failed: %s",
386 callmgr, strerror(errno));
390 /* XXX need better error checking XXX */
391 int get_call_id(int sock, pid_t gre, pid_t pppd,
392 u_int16_t *call_id, u_int16_t *peer_call_id)
394 u_int16_t m_call_id, m_peer_call_id;
395 /* write pid's to socket */
396 /* don't bother with network byte order, because pid's are meaningless
397 * outside the local host.
399 int rc;
400 rc = write(sock, &gre, sizeof(gre));
401 if (rc != sizeof(gre))
402 return -1;
403 rc = write(sock, &pppd, sizeof(pppd));
404 if (rc != sizeof(pppd))
405 return -1;
406 rc = read(sock, &m_call_id, sizeof(m_call_id));
407 if (rc != sizeof(m_call_id))
408 return -1;
409 rc = read(sock, &m_peer_call_id, sizeof(m_peer_call_id));
410 if (rc != sizeof(m_peer_call_id))
411 return -1;
413 * XXX FIX ME ... DO ERROR CHECKING & TIME-OUTS XXX
414 * (Rhialto: I am assuming for now that timeouts are not relevant
415 * here, because the read and write calls would return -1 (fail) when
416 * the peer goes away during the process. We know it is (or was)
417 * running because the connect() call succeeded.)
419 *call_id = m_call_id;
420 *peer_call_id = m_peer_call_id;
422 return 0;
425 void launch_pppd(char *ttydev, int argc, char **argv) {
426 char *new_argv[argc+4]; /* XXX if not using GCC, hard code a limit here. */
427 int i = 0, j;
429 new_argv[i++] = PPPD_BINARY;
430 #ifdef USER_PPP
431 new_argv[i++] = "-direct";
432 /* ppp expects to have stdin connected to ttydev */
433 if ((j = open(ttydev, O_RDWR)) == -1)
434 fatal("Cannot open %s: %s", ttydev, strerror(errno));
435 if (dup2(j, 0) == -1)
436 fatal("dup2 failed: %s", strerror(errno));
437 close(j);
438 #else
439 new_argv[i++] = ttydev;
440 new_argv[i++] = "38400";
441 #endif
442 for (j=0; j<argc; j++)
443 new_argv[i++] = argv[j];
444 new_argv[i] = NULL;
445 execvp(new_argv[0], new_argv);
448 /*************** COMPILE call manager into same binary *********/
449 #define main callmgr_main
450 #define sighandler callmgr_sighandler
451 #define do_nothing callmgr_do_nothing
452 #define env callmgr_env
453 #include "pptp_callmgr.c"