/*************************************************************************** * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #include #include "if_pppox.h" #include #include 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" }, { 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) { socklen_t len; struct sockaddr_pppox src_addr,dst_addr; struct hostent *hostinfo; hostinfo=gethostbyname(pptp_server); if (!hostinfo) { error("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))) { close(sock); error("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) { error("PPTP: failed to create PPTP socket (%s)\n",strerror(errno)); return -1; } if (bind(pptp_fd,(struct sockaddr*)&src_addr,sizeof(src_addr))) { close(pptp_fd); error("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); if (callmgr_sock<0) { close(pptp_fd); return -1; } /* 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))) { close(callmgr_sock); close(pptp_fd); error("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) { if (pptp_server) close(callmgr_sock); 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) { close(fd); error("Call manager exited with error %d", status); return -1; } break; } sleep(1); } else return fd; } close(fd); error("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; }