1/***************************************************************************
2 *   Copyright (C) 2006 by Kozlov D.   *
3 *   xeb@mail.ru   *
4 *                                                                         *
5 *   This program is free software; you can redistribute it and/or modify  *
6 *   it under the terms of the GNU General Public License as published by  *
7 *   the Free Software Foundation; either version 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 *                                                                         *
10 *   This program is distributed in the hope that it will be useful,       *
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13 *   GNU General Public License for more details.                          *
14 *                                                                         *
15 *   You should have received a copy of the GNU General Public License     *
16 *   along with this program; if not, write to the                         *
17 *   Free Software Foundation, Inc.,                                       *
18 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19 ***************************************************************************/
20
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <netinet/in.h>
27#include <arpa/inet.h>
28#include <sys/un.h>
29#include <netdb.h>
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
33#include <syslog.h>
34#include <unistd.h>
35#include <signal.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <sys/wait.h>
39#include <sys/ioctl.h>
40
41#include "pppd/pppd.h"
42#include "pppd/fsm.h"
43#include "pppd/lcp.h"
44#include "pppd/ipcp.h"
45#include "pppd/ccp.h"
46#include "pppd/pathnames.h"
47
48#include "pptp_callmgr.h"
49#include <net/if.h>
50#include <net/ethernet.h>
51#include <linux/if_pppox.h>
52
53#include <stdio.h>
54#include <stdlib.h>
55
56#include <net/route.h>
57#include <features.h>
58#include <resolv.h>
59#define sin_addr(s) (((struct sockaddr_in *)(s))->sin_addr)
60
61extern char** environ;
62
63char pppd_version[] = VERSION;
64extern int new_style_driver;
65
66
67char *pptp_server = NULL;
68char *pptp_client = NULL;
69char *pptp_phone = NULL;
70int pptp_sock=-1;
71int pptp_timeout=100000;
72int log_level = 0;
73struct in_addr localbind = { INADDR_NONE };
74struct rtentry rt;
75
76static int callmgr_sock;
77static int pptp_fd;
78int call_ID;
79
80//static struct in_addr get_ip_address(char *name);
81static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window);
82static void launch_callmgr(int call_is,struct in_addr inetaddr, char *phonenr,int window);
83static int get_call_id(int sock, pid_t gre, pid_t pppd, u_int16_t *peer_call_id);
84
85static int route_add(const struct in_addr inetaddr, struct rtentry *rt);
86static int route_del(struct rtentry *rt);
87
88//static int pptp_devname_hook(char *cmd, char **argv, int doit);
89static option_t Options[] =
90{
91    { "pptp_server", o_string, &pptp_server,
92      "PPTP Server" },
93    { "pptp_client", o_string, &pptp_client,
94      "PPTP Client" },
95    { "pptp_sock",o_int, &pptp_sock,
96      "PPTP socket" },
97    { "pptp_phone", o_string, &pptp_phone,
98      "PPTP Phone number" },
99    { "loglevel", o_int, &log_level,
100      "debugging level (0=low, 1=default, 2=high)"},
101    { NULL }
102};
103
104static int pptp_connect(void);
105//static void pptp_send_config(int mtu,u_int32_t asyncmap,int pcomp,int accomp);
106//static void pptp_recv_config(int mru,u_int32_t asyncmap,int pcomp,int accomp);
107static void pptp_disconnect(void);
108
109struct channel pptp_channel = {
110    options: Options,
111    //process_extra_options: &PPPOEDeviceOptions,
112    check_options: NULL,
113    connect: &pptp_connect,
114    disconnect: &pptp_disconnect,
115    establish_ppp: &generic_establish_ppp,
116    disestablish_ppp: &generic_disestablish_ppp,
117    //send_config: &pptp_send_config,
118    //recv_config: &pptp_recv_config,
119    close: NULL,
120    cleanup: NULL
121};
122
123static int pptp_start_server(void)
124{
125	pptp_fd=pptp_sock;
126	sprintf(ppp_devnam,"pptp (%s)",pptp_client);
127
128	return pptp_fd;
129}
130static int pptp_start_client(void)
131{
132	socklen_t len;
133	struct sockaddr_pppox src_addr,dst_addr;
134	struct hostent *hostinfo;
135
136#if !defined(__UCLIBC__) \
137 || (__UCLIBC_MAJOR__ == 0 \
138 && (__UCLIBC_MINOR__ < 9 || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 31)))
139	/* force ns refresh from resolv.conf with uClibc pre-0.9.31 */
140	res_init();
141#endif
142	hostinfo=gethostbyname(pptp_server);
143	if (!hostinfo)
144	{
145		error("PPTP: Unknown host %s\n", pptp_server);
146		return -1;
147	}
148	dst_addr.sa_addr.pptp.sin_addr=*(struct in_addr*)hostinfo->h_addr;
149
150 	memset(&rt, 0, sizeof(rt));
151 	route_add(dst_addr.sa_addr.pptp.sin_addr, &rt);
152
153	{
154		int sock;
155		struct sockaddr_in addr;
156		len=sizeof(addr);
157		addr.sin_addr=dst_addr.sa_addr.pptp.sin_addr;
158		addr.sin_family=AF_INET;
159		addr.sin_port=htons(1700);
160		sock=socket(AF_INET,SOCK_DGRAM,0);
161		if (connect(sock,(struct sockaddr*)&addr,sizeof(addr)))
162		{
163			close(sock);
164			error("PPTP: connect failed (%s)\n",strerror(errno));
165			return -1;
166		}
167		getsockname(sock,(struct sockaddr*)&addr,&len);
168		src_addr.sa_addr.pptp.sin_addr=addr.sin_addr;
169		close(sock);
170	}
171	//info("PPTP: connect server=%s\n",inet_ntoa(conn.sin_addr));
172	//conn.loc_addr.s_addr=INADDR_NONE;
173	//conn.timeout=1;
174	//conn.window=pptp_window;
175
176	src_addr.sa_family=AF_PPPOX;
177	src_addr.sa_protocol=PX_PROTO_PPTP;
178	src_addr.sa_addr.pptp.call_id=0;
179
180	dst_addr.sa_family=AF_PPPOX;
181	dst_addr.sa_protocol=PX_PROTO_PPTP;
182	dst_addr.sa_addr.pptp.call_id=0;
183
184	pptp_fd=socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_PPTP);
185	if (pptp_fd<0)
186	{
187		error("PPTP: failed to create PPTP socket (%s)\n",strerror(errno));
188		return -1;
189	}
190	if (bind(pptp_fd,(struct sockaddr*)&src_addr,sizeof(src_addr)))
191	{
192		close(pptp_fd);
193		error("PPTP: failed to bind PPTP socket (%s)\n",strerror(errno));
194		return -1;
195	}
196	len=sizeof(src_addr);
197	getsockname(pptp_fd,(struct sockaddr*)&src_addr,&len);
198	call_ID=src_addr.sa_addr.pptp.call_id;
199
200   do {
201        /*
202         * Open connection to call manager (Launch call manager if necessary.)
203         */
204        callmgr_sock = open_callmgr(src_addr.sa_addr.pptp.call_id,dst_addr.sa_addr.pptp.sin_addr, pptp_phone,50);
205	if (callmgr_sock<0)
206	{
207		close(pptp_fd);
208		return -1;
209	}
210        /* Exchange PIDs, get call ID */
211    } while (get_call_id(callmgr_sock, getpid(), getpid(), &dst_addr.sa_addr.pptp.call_id) < 0);
212
213	if (connect(pptp_fd,(struct sockaddr*)&dst_addr,sizeof(dst_addr)))
214	{
215		close(callmgr_sock);
216		close(pptp_fd);
217		error("PPTP: failed to connect PPTP socket (%s)\n",strerror(errno));
218		return -1;
219	}
220
221	sprintf(ppp_devnam,"pptp (%s)", inet_ntoa(dst_addr.sa_addr.pptp.sin_addr));
222
223	return pptp_fd;
224}
225static int pptp_connect(void)
226{
227	if ((!pptp_server && !pptp_client) || (pptp_server && pptp_client))
228	{
229		fatal("PPTP: unknown mode (you must specify pptp_server or pptp_client option)");
230		return -1;
231	}
232
233	if (pptp_server) return pptp_start_client();
234	return pptp_start_server();
235}
236
237static void pptp_disconnect(void)
238{
239	if (pptp_server) close(callmgr_sock);
240	close(pptp_fd);
241	//route_del(&rt); // don't delete, as otherwise it would try to use pppX in demand mode
242}
243
244static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window)
245{
246    /* Try to open unix domain socket to call manager. */
247    struct sockaddr_un where;
248    const int NUM_TRIES = 3;
249    int i, fd;
250    pid_t pid;
251    int status;
252    /* Open socket */
253    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
254    {
255        fatal("Could not create unix domain socket: %s", strerror(errno));
256    }
257    /* Make address */
258    callmgr_name_unixsock(&where, inetaddr, localbind);
259    for (i = 0; i < NUM_TRIES; i++)
260    {
261        if (connect(fd, (struct sockaddr *) &where, sizeof(where)) < 0)
262        {
263            /* couldn't connect.  We'll have to launch this guy. */
264
265            unlink (where.sun_path);
266
267            /* fork and launch call manager process */
268            switch (pid = fork())
269            {
270                case -1: /* failure */
271                    fatal("fork() to launch call manager failed.");
272                case 0: /* child */
273                {
274                    close (fd);
275                    close(pptp_fd);
276                    /* close the pty and gre in the call manager */
277                    //close(pty_fd);
278                    //close(gre_fd);
279                    launch_callmgr(call_id,inetaddr, phonenr,window);
280                }
281                default: /* parent */
282                    waitpid(pid, &status, 0);
283                    if (status!= 0)
284                    {
285                    	close(fd);
286			error("Call manager exited with error %d", status);
287                       	return -1;
288                    }
289                    break;
290            }
291            sleep(1);
292        }
293        else return fd;
294    }
295    close(fd);
296    error("Could not launch call manager after %d tries.", i);
297    return -1;   /* make gcc happy */
298}
299
300/*** call the call manager main ***********************************************/
301static void launch_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window)
302{
303			char win[10];
304			char call[10];
305      char *my_argv[9] = { "pptp", inet_ntoa(inetaddr), "--call_id",call,"--phone",phonenr,"--window",win,NULL };
306      char buf[128];
307      sprintf(win,"%u",window);
308      sprintf(call,"%u",call_id);
309      snprintf(buf, sizeof(buf), "pptp: call manager for %s", my_argv[1]);
310      //inststr(argc, argv, envp, buf);
311      exit(callmgr_main(8, my_argv, environ));
312}
313
314/*** exchange data with the call manager  *************************************/
315/* XXX need better error checking XXX */
316static int get_call_id(int sock, pid_t gre, pid_t pppd,
317		u_int16_t *peer_call_id)
318{
319    u_int16_t m_call_id, m_peer_call_id;
320    /* write pid's to socket */
321    /* don't bother with network byte order, because pid's are meaningless
322     * outside the local host.
323     */
324    int rc;
325    rc = write(sock, &gre, sizeof(gre));
326    if (rc != sizeof(gre))
327        return -1;
328    rc = write(sock, &pppd, sizeof(pppd));
329    if (rc != sizeof(pppd))
330        return -1;
331    rc = read(sock,  &m_call_id, sizeof(m_call_id));
332    if (rc != sizeof(m_call_id))
333        return -1;
334    rc = read(sock,  &m_peer_call_id, sizeof(m_peer_call_id));
335    if (rc != sizeof(m_peer_call_id))
336        return -1;
337    /*
338     * XXX FIXME ... DO ERROR CHECKING & TIME-OUTS XXX
339     * (Rhialto: I am assuming for now that timeouts are not relevant
340     * here, because the read and write calls would return -1 (fail) when
341     * the peer goes away during the process. We know it is (or was)
342     * running because the connect() call succeeded.)
343     * (James: on the other hand, if the route to the peer goes away, we
344     * wouldn't get told by read() or write() for quite some time.)
345     */
346    *peer_call_id = m_peer_call_id;
347    return 0;
348}
349
350void plugin_init(void)
351{
352    if (!ppp_available() && !new_style_driver)
353	fatal("Kernel doesn't support ppp_generic - needed for PPTP");
354
355    add_options(Options);
356
357    info("PPTP plugin version %s compiled for pppd-%s",
358	 PLUGINVERSION, VERSION);
359
360    the_channel = &pptp_channel;
361    modem = 0;
362}
363
364static int
365route_ctrl(int ctrl, struct rtentry *rt)
366{
367	int s;
368
369	/* Open a raw socket to the kernel */
370	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||	ioctl(s, ctrl, rt) < 0)
371	        warn("route_ctrl: %s", strerror(errno));
372	else errno = 0;
373
374	close(s);
375	return errno;
376}
377
378static int
379route_del(struct rtentry *rt)
380{
381	if (rt->rt_dev) {
382		route_ctrl(SIOCDELRT, rt);
383		free(rt->rt_dev), rt->rt_dev = NULL;
384	}
385
386	return 0;
387}
388
389static int
390route_add(const struct in_addr inetaddr, struct rtentry *rt)
391{
392	char buf[256], dev[64];
393	int metric, flags;
394	u_int32_t dest, mask;
395
396	FILE *f = fopen("/proc/net/route", "r");
397	if (f == NULL) {
398	        warn("/proc/net/route: %s", strerror(errno));
399		return -1;
400	}
401
402	while (fgets(buf, sizeof(buf), f))
403	{
404		if (sscanf(buf, "%63s %x %x %X %*s %*s %d %x", dev, &dest,
405		    	&sin_addr(&rt->rt_gateway).s_addr, &flags, &metric, &mask) != 6)
406			continue;
407		if ((flags & RTF_UP) == RTF_UP && (inetaddr.s_addr & mask) == dest &&
408			(dest || strncmp(dev, "ppp", 3)) /* avoid default via pppX to avoid on-demand loops*/)
409		{
410			rt->rt_metric = metric + 1;
411			rt->rt_gateway.sa_family = AF_INET;
412			break;
413		}
414	}
415
416	fclose(f);
417
418	/* check for no route */
419	if (rt->rt_gateway.sa_family != AF_INET)
420	{
421		/* warn("route_add: no route to host"); */
422		return -1;
423	}
424
425	/* check for existing route to this host,
426	add if missing based on the existing routes */
427	if (flags & RTF_HOST) {
428		/* warn("route_add: not adding existing route"); */
429		return -1;
430	}
431
432	sin_addr(&rt->rt_dst) = inetaddr;
433	rt->rt_dst.sa_family = AF_INET;
434
435	sin_addr(&rt->rt_genmask).s_addr = INADDR_BROADCAST;
436	rt->rt_genmask.sa_family = AF_INET;
437
438	rt->rt_flags = RTF_UP | RTF_HOST;
439	if (flags & RTF_GATEWAY)
440		rt->rt_flags |= RTF_GATEWAY;
441
442	rt->rt_metric++;
443	rt->rt_dev = strdup(dev);
444
445	if (!rt->rt_dev)
446	{
447	        warn("route_add: no memory");
448		return -1;
449	}
450
451	if (!route_ctrl(SIOCADDRT, rt))
452		return 0;
453
454	free(rt->rt_dev), rt->rt_dev = NULL;
455
456	return -1;
457}
458