diff options
Diffstat (limited to 'pppd_plugin/src/pptp.c')
-rw-r--r-- | pppd_plugin/src/pptp.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/pppd_plugin/src/pptp.c b/pppd_plugin/src/pptp.c new file mode 100644 index 0000000..79bb1d1 --- /dev/null +++ b/pppd_plugin/src/pptp.c @@ -0,0 +1,334 @@ +/*************************************************************************** + * Copyright (C) 2006 by Kozlov D. * + * xeb@mail.ru * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/ioctl.h> + +#include "pppd/pppd.h" +#include "pppd/fsm.h" +#include "pppd/lcp.h" +#include "pppd/ipcp.h" +#include "pppd/ccp.h" +#include "pppd/pathnames.h" + +#include "pptp_callmgr.h" +#include <net/if.h> +#include <net/ethernet.h> +#include "if_pppox.h" + +#include <stdio.h> +#include <stdlib.h> + + + +extern char** environ; + +char pppd_version[] = PPPD_VERSION; +extern int new_style_driver; + + +char *pptp_server = NULL; +char *pptp_client = NULL; +char *pptp_phone = NULL; +int pptp_sock=-1; +int pptp_timeout=100000; +struct in_addr localbind = { INADDR_NONE }; + +static int callmgr_sock; +static int pptp_fd; +int call_ID; + +//static struct in_addr get_ip_address(char *name); +static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window); +static void launch_callmgr(int call_is,struct in_addr inetaddr, char *phonenr,int window); +static int get_call_id(int sock, pid_t gre, pid_t pppd, u_int16_t *peer_call_id); + +//static int pptp_devname_hook(char *cmd, char **argv, int doit); +static option_t Options[] = +{ + { "pptp_server", o_string, &pptp_server, + "PPTP Server" }, + { "pptp_client", o_string, &pptp_client, + "PPTP Client" }, + { "pptp_sock",o_int, &pptp_sock, + "PPTP socket" }, + { "pptp_phone", o_string, &pptp_phone, + "PPTP Phone number" }, + { "pptp_timeout", o_int, &pptp_timeout, + "timeout for waiting reordered packets and acks"}, + { NULL } +}; + +static int pptp_connect(void); +//static void pptp_send_config(int mtu,u_int32_t asyncmap,int pcomp,int accomp); +//static void pptp_recv_config(int mru,u_int32_t asyncmap,int pcomp,int accomp); +static void pptp_disconnect(void); + +struct channel pptp_channel = { + options: Options, + //process_extra_options: &PPPOEDeviceOptions, + check_options: NULL, + connect: &pptp_connect, + disconnect: &pptp_disconnect, + establish_ppp: &generic_establish_ppp, + disestablish_ppp: &generic_disestablish_ppp, + //send_config: &pptp_send_config, + //recv_config: &pptp_recv_config, + close: NULL, + cleanup: NULL +}; + +static int pptp_start_server(void) +{ + pptp_fd=pptp_sock; + sprintf(ppp_devnam,"pptp (%s)",pptp_client); + + return pptp_fd; +} +static int pptp_start_client(void) +{ + int len; + struct sockaddr_pppox src_addr,dst_addr; + struct hostent *hostinfo; + + hostinfo=gethostbyname(pptp_server); + if (!hostinfo) + { + fatal("PPTP: Unknown host %s\n", pptp_server); + return -1; + } + dst_addr.sa_addr.pptp.sin_addr=*(struct in_addr*)hostinfo->h_addr; + { + int sock; + struct sockaddr_in addr; + len=sizeof(addr); + addr.sin_addr=dst_addr.sa_addr.pptp.sin_addr; + addr.sin_family=AF_INET; + addr.sin_port=htons(1700); + sock=socket(AF_INET,SOCK_DGRAM,0); + if (connect(sock,(struct sockaddr*)&addr,sizeof(addr))) + { + fatal("PPTP: connect failed (%s)\n",strerror(errno)); + return -1; + } + getsockname(sock,(struct sockaddr*)&addr,&len); + src_addr.sa_addr.pptp.sin_addr=addr.sin_addr; + close(sock); + } + //info("PPTP: connect server=%s\n",inet_ntoa(conn.sin_addr)); + //conn.loc_addr.s_addr=INADDR_NONE; + //conn.timeout=1; + //conn.window=pptp_window; + + src_addr.sa_family=AF_PPPOX; + src_addr.sa_protocol=PX_PROTO_PPTP; + src_addr.sa_addr.pptp.call_id=0; + + dst_addr.sa_family=AF_PPPOX; + dst_addr.sa_protocol=PX_PROTO_PPTP; + dst_addr.sa_addr.pptp.call_id=0; + + pptp_fd=socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_PPTP); + if (pptp_fd<0) + { + fatal("PPTP: failed to create PPTP socket (%s)\n",strerror(errno)); + return -1; + } + if (setsockopt(pptp_fd,0,PPTP_SO_TIMEOUT,&pptp_timeout,sizeof(pptp_timeout))) + warn("PPTP: failed to setsockopt PPTP_SO_TIMEOUT (%s)\n",strerror(errno)); + if (bind(pptp_fd,(struct sockaddr*)&src_addr,sizeof(src_addr))) + { + fatal("PPTP: failed to bind PPTP socket (%s)\n",strerror(errno)); + return -1; + } + len=sizeof(src_addr); + getsockname(pptp_fd,(struct sockaddr*)&src_addr,&len); + call_ID=src_addr.sa_addr.pptp.call_id; + + do { + /* + * Open connection to call manager (Launch call manager if necessary.) + */ + callmgr_sock = open_callmgr(src_addr.sa_addr.pptp.call_id,dst_addr.sa_addr.pptp.sin_addr, pptp_phone,50); + /* Exchange PIDs, get call ID */ + } while (get_call_id(callmgr_sock, getpid(), getpid(), &dst_addr.sa_addr.pptp.call_id) < 0); + + if (connect(pptp_fd,(struct sockaddr*)&dst_addr,sizeof(dst_addr))) + { + fatal("PPTP: failed to connect PPTP socket (%s)\n",strerror(errno)); + return -1; + } + + sprintf(ppp_devnam,"pptp (%s)",pptp_server); + + return pptp_fd; +} +static int pptp_connect(void) +{ + if ((!pptp_server && !pptp_client) || (pptp_server && pptp_client)) + { + fatal("PPTP: unknown mode (you must specify pptp_server or pptp_client option)"); + return -1; + } + + if (pptp_server) return pptp_start_client(); + return pptp_start_server(); +} + +static void pptp_disconnect(void) +{ + close(pptp_fd); +} + +static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window) +{ + /* Try to open unix domain socket to call manager. */ + struct sockaddr_un where; + const int NUM_TRIES = 3; + int i, fd; + pid_t pid; + int status; + /* Open socket */ + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + fatal("Could not create unix domain socket: %s", strerror(errno)); + } + /* Make address */ + callmgr_name_unixsock(&where, inetaddr, localbind); + for (i = 0; i < NUM_TRIES; i++) + { + if (connect(fd, (struct sockaddr *) &where, sizeof(where)) < 0) + { + /* couldn't connect. We'll have to launch this guy. */ + + unlink (where.sun_path); + + /* fork and launch call manager process */ + switch (pid = fork()) + { + case -1: /* failure */ + fatal("fork() to launch call manager failed."); + case 0: /* child */ + { + close (fd); + //close(pptp_fd); + /* close the pty and gre in the call manager */ + // close(pty_fd); + //close(gre_fd); + launch_callmgr(call_id,inetaddr, phonenr,window); + } + default: /* parent */ + waitpid(pid, &status, 0); + if (status!= 0) + fatal("Call manager exited with error %d", status); + break; + } + sleep(1); + } + else return fd; + } + close(fd); + fatal("Could not launch call manager after %d tries.", i); + return -1; /* make gcc happy */ +} + +/*** call the call manager main ***********************************************/ +static void launch_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window) +{ + char win[10]; + char call[10]; + char *my_argv[9] = { "pptp", inet_ntoa(inetaddr), "--call_id",call,"--phone",phonenr,"--window",win,NULL }; + char buf[128]; + sprintf(win,"%u",window); + sprintf(call,"%u",call_id); + snprintf(buf, sizeof(buf), "pptp: call manager for %s", my_argv[1]); + //inststr(argc, argv, envp, buf); + exit(callmgr_main(8, my_argv, environ)); +} + +/*** exchange data with the call manager *************************************/ +/* XXX need better error checking XXX */ +static int get_call_id(int sock, pid_t gre, pid_t pppd, + u_int16_t *peer_call_id) +{ + u_int16_t m_call_id, m_peer_call_id; + /* write pid's to socket */ + /* don't bother with network byte order, because pid's are meaningless + * outside the local host. + */ + int rc; + rc = write(sock, &gre, sizeof(gre)); + if (rc != sizeof(gre)) + return -1; + rc = write(sock, &pppd, sizeof(pppd)); + if (rc != sizeof(pppd)) + return -1; + rc = read(sock, &m_call_id, sizeof(m_call_id)); + if (rc != sizeof(m_call_id)) + return -1; + rc = read(sock, &m_peer_call_id, sizeof(m_peer_call_id)); + if (rc != sizeof(m_peer_call_id)) + return -1; + /* + * XXX FIXME ... DO ERROR CHECKING & TIME-OUTS XXX + * (Rhialto: I am assuming for now that timeouts are not relevant + * here, because the read and write calls would return -1 (fail) when + * the peer goes away during the process. We know it is (or was) + * running because the connect() call succeeded.) + * (James: on the other hand, if the route to the peer goes away, we + * wouldn't get told by read() or write() for quite some time.) + */ + *peer_call_id = m_peer_call_id; + return 0; +} + +void plugin_init(void) +{ + /*if (!ppp_available() && !new_style_driver) + { + fatal("Linux kernel does not support PPP -- are you running 2.4.x?"); + }*/ + + add_options(Options); + + info("PPTP plugin version %s compiled for pppd-%s, linux-%s", + VERSION, PPPD_VERSION,KERNELVERSION); + + the_channel = &pptp_channel; + modem = 0; +} + |