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 $
9 #include <sys/socket.h>
10 #if defined(__FreeBSD__)
12 #elif defined(__NetBSD__)
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
35 #include "pptp_callmgr.h"
40 #include "pptp_quirks.h"
43 #define PPPD_BINARY "pppd"
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 */
63 my_openpty (int *amaster
, int *aslave
, char *name
, struct termios
*termp
,
89 if (unlockpt (master
))
93 if (pts_name (master
, &buf
, sizeof (_buf
)))
95 if (ptsname_r (master
, buf
, sizeof buf
))
99 slave
= open (buf
, O_RDWR
| O_NOCTTY
);
109 /* XXX Should we ignore errors here? */
111 tcsetattr (slave
, TCSAFLUSH
, termp
);
113 ioctl (slave
, TIOCSWINSZ
, winp
);
131 void usage(char *progname
) {
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
);
145 static int signaled
= 0;
147 void do_nothing(int sig
) {
148 /* do nothing signal handler. Better than SIG_IGN. */
153 void sighandler(int sig
) {
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
;
170 char phonenrbuf
[65]; /* maximum length of field plus one for the trailing
172 char * volatile phonenr
= NULL
;
173 volatile int launchpppd
= 1;
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 */
184 /* structure with all recognised options for pptp */
185 static struct option long_options
[] = {
187 {"nolaunchpppd", 0, 0, 0},
191 int option_index
= 0;
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 */
199 if(option_index
== 0) { /* --phone specified */
200 /* copy it to a buffer, as the argv's will be overwritten by
202 strncpy(phonenrbuf
,optarg
,sizeof(phonenrbuf
));
203 phonenrbuf
[sizeof(phonenrbuf
)-1]='\0';
205 }else if(option_index
== 1) {/* --nolaunchpppd specified */
207 }else if(option_index
== 2) {/* --quirks specified */
208 if (set_quirk_index(find_quirk(optarg
)))
211 /* other pptp options come here */
213 case '?': /* unrecognised option, treat it as the first pppd option */
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. */
226 rc
= my_openpty (&pty_fd
, &tty_fd
, ttydev
, NULL
, NULL
);
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()) {
237 fatal("Could not fork pppd process");
239 case 0: /* I'm the child! */
241 signal(SIGUSR1
, SIG_DFL
);
242 child_pid
= getpid();
244 default: /* parent */
247 * There is still a very small race condition here. If a signal
248 * occurs after signaled is checked but before pause is called,
252 pause(); /* wait for the signal */
254 launch_pppd(ttydev
, pppdargc
, pppdargv
); /* launch pppd */
256 fatal("Could not launch pppd");
258 } else { /* ! launchpppd */
261 parent_pid
=0; /* don't kill pppd */
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 */
277 kill(parent_pid
, SIGUSR1
);
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
);
296 /* on close, kill all. */
298 kill(parent_pid
, SIGTERM
);
301 sleep(3); /* give ctrl manager a chance to exit */
305 struct in_addr
get_ip_address(char *name
) {
306 struct in_addr retval
;
307 struct hostent
*host
= gethostbyname(name
);
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");
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
));
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;
334 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
335 fatal("Could not create unix domain socket: %s", strerror(errno
));
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.");
356 launch_callmgr(inetaddr
, phonenr
, argc
,argv
,envp
);
358 default: /* parent */
359 waitpid(pid
, &status
, 0);
361 fatal("Call manager exited with error %d", status
);
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
};
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.
400 rc
= write(sock
, &gre
, sizeof(gre
));
401 if (rc
!= sizeof(gre
))
403 rc
= write(sock
, &pppd
, sizeof(pppd
));
404 if (rc
!= sizeof(pppd
))
406 rc
= read(sock
, &m_call_id
, sizeof(m_call_id
));
407 if (rc
!= sizeof(m_call_id
))
409 rc
= read(sock
, &m_peer_call_id
, sizeof(m_peer_call_id
));
410 if (rc
!= sizeof(m_peer_call_id
))
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
;
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. */
429 new_argv
[i
++] = PPPD_BINARY
;
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
));
439 new_argv
[i
++] = ttydev
;
440 new_argv
[i
++] = "38400";
442 for (j
=0; j
<argc
; j
++)
443 new_argv
[i
++] = argv
[j
];
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"