#!/usr/bin/perl # # $Id: pptp_fe.pl,v 1.1 2001/11/29 05:19:10 quozl Exp $ # # pptp_fe.pl, privileged portion of xpptp_fe.pl # Copyright (C) 2001 Smoot Carl-Mitchell (smoot@tic.com) # # 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 # use strict; use Getopt::Std; use Time::localtime; use IO::Handle; my $Usage = "usage: pptp_fe [-c config_file] [-d] [-h] [-k] [-n network] [-p] [-r routes] [-t timeout] [host] where: -c - configuration file (default is ~/.pptp_fe.conf) -d - pppd debug flag -h - this help message -k - kill pppd daemon with route to network -n - network number of remote private network in x.x.x.x/n notation -r - routes to add to routing table separated by commas -p - suppress prompting -t - connection timeout retry interval in seconds (default 60 seconds) host - remote PPTP server name "; my %Opt; getopts("c:dhkn:pr:t:", \%Opt); my $Config_File = $Opt{'c'}; $Config_File = "$ENV{'HOME'}/.pptp_fe.conf" unless $Opt{'c'}; my $Config; my $Debug = $Opt{'d'}; $Debug = 0 unless $Debug; my $Debug_Flag = "debug" if $Debug; my $Help = $Opt{'h'}; my $Kill = $Opt{'k'}; my $Net = $Opt{'n'}; my $No_Prompt = $Opt{'p'}; my $Route = $Opt{'r'}; my $Timeout = $Opt{'t'}; $Timeout = 60 unless $Timeout; print($Usage), exit(1) if $Help; my $Server = $ARGV[0]; my $State = "disconnected"; system("modprobe ppp-compress-18"); $Config = cmd_read_config_file($Config_File); for my $cmd (@$Config) { cmd_set($cmd, 1); } print "($State) > " unless $No_Prompt; STDOUT->flush; for (;;) { my $rin = ''; my $rout = ''; vec($rin, fileno(STDIN), 1) = 1; command() if select($rout=$rin, undef, undef, 5); my $interface = ""; if ($State eq "connected" && ! ($interface = net_interface_up($Net))) { print "\n"; print "interface $interface for $Net not up - restarting\n"; cmd_connect(); print "($State) > " unless $No_Prompt;; } } sub command { my $input; sysread(STDIN, $input, 1024); for my $line1 (split("\n", $input)) { my $line = $line1; $line =~ s/\s*$//; $line =~ s/^\s*//; my ($command, $arguments) = split(" ", $line, 2); if ($command eq "c") { cmd_connect(); } elsif ($command eq "d") { cmd_disconnect(); } elsif ($command eq "h") { cmd_help(); } elsif ($command eq "l") { cmd_list(); } elsif ($command eq "q") { cmd_disconnect(); exit 0; } elsif ($command eq "r") { $Config = cmd_read_config_file($arguments); } elsif ($command eq "s") { cmd_set($arguments, 0); } elsif ($command eq "w") { cmd_write_config_file($arguments); } elsif ($command ne "") { print "unknown command\n"; } } print "($State) > " unless $No_Prompt; STDOUT->flush; } sub cmd_connect { cmd_disconnect() if $State eq "connected"; my $start_time = time(); my $date_string = ctime($start_time); print "$date_string Running pptp $Server $Debug_Flag"; system("pptp $Server $Debug_Flag"); my $interface = ""; do { sleep 1; $interface = net_interface_up($Net); print "."; } until ($interface || time() > $start_time + $Timeout); if (time() > $start_time + $Timeout) { print "timed out after $Timeout sec\n"; $State = "disconnected"; return 0; } print "\n"; my $ifcfg = `ifconfig $interface`; $ifcfg =~ /P-t-P:(.*) Mask/; my $ip = $1; print "setting route to network $Net to interface $interface\n"; system("route add -net $Net dev $interface metric 2"); # Routes are separated by commas my @route = split(/,/, $Route); for my $route (@route) { my $net_flag = ""; $net_flag = "-net" if $route =~ /\//; print "setting route to $route to interface $interface\n"; system("route add $net_flag $route dev $interface"); } $State = "connected"; print "connected\n"; return 1; } sub cmd_disconnect { return 1 if $State eq "disconnected"; my $interface = net_interface_up($Net); my $pid_file = "/var/run/$interface.pid"; # delete the named pipes - XXX this is a bit crude system("rm -f /var/run/pptp/*"); $State = "disconnected", return 1 unless $interface && -f $pid_file; my $pid = `cat $pid_file`; chomp $pid; print "killing pppd($pid)\n"; kill("HUP", $pid); print "waiting for pppd to die"; do { sleep 1; print "."; } until (kill(0, $pid)); print "\n"; $State = "disconnected"; print "disconnected\n"; return 1; } sub cmd_list { print "Server = $Server\n"; print "Network = $Net\n"; print "Routes = $Route\n"; print "Debug = $Debug_Flag\n"; print "No_Prompt = $No_Prompt\n"; print "Timeout = $Timeout\n"; print "\n"; } sub cmd_help { print "Commands are:\n"; print "c - initiate PPTP connection\n"; print "d - disconnect PPTP\n"; print "h - this help message\n"; print "l - list current configuration\n"; print "q - quite the program\n"; print "r - read configuration file\n"; print "s - set configuration variable (l for a list)\n"; print "w - write the configuration file\n"; } sub cmd_set { my $input = shift; my $no_replace = shift; my ($variable, $value) = split(/\s*=\s*/, $input); $variable = "\L$variable"; if (! $variable) { print "syntax: s variable = value\n"; return 0; } if ($variable eq "server") { $Server = $value unless $no_replace && $Server; } elsif ($variable eq "network") { $Net = $value unless $no_replace && $Net; } elsif ($variable eq "routes") { $Route = $value unless $no_replace && $Route; } elsif ($variable eq "debug") { $Debug_Flag = $value unless $no_replace && $Debug_Flag; } elsif ($variable eq "no_prompt") { $No_Prompt = $value unless $no_replace && $No_Prompt; } elsif ($variable eq "timeout") { $Timeout = $value unless $no_replace && $Timeout; } elsif ($variable eq "config_file") { $Config_File = $value unless $no_replace && $Config_File; } else { print "unknown variable\n"; } } sub cmd_read_config_file { my $file = shift; my $config = []; $file = $Config_File unless $file; local *IN; if (!open(IN, $file)) { print "cannot open $file\n"; return $config; } my @config_file = ; close IN; push @config_file, "\n"; chomp @config_file; for my $line (@config_file) { next if /\s*#/; if ($line =~ /\S/) { $line =~ s/^\s*//; $line =~ s/\s*$//; push @$config, $line; next; } } return $config; } sub cmd_write_config_file { my $file = shift; $file = $Config_File unless $file; local *OUT; if (!open(OUT, ">$file")) { print "cannot open $file\n"; return 0; } my $oldfh = select OUT; cmd_list(); close OUT; select $oldfh; return 1; } sub net_interface_up { my $cidr = shift; # cidr is net/bits my($net, $nbits) = split(/\//, $cidr); # compute the network number my $netnum = netnum($net, $nbits); local(*INTERFACE); open(INTERFACE, "ifconfig|") || die "cannot run ifconfig - $!\n"; my $interface = ""; my @interface = ; close INTERFACE; for (@interface) { chomp; # new interface if (/^[a-zA-Z]/) { if ($interface =~ /(.*) Link.*P-t-P:(.*) Mask/) { my $interface_name = $1; my $ip = $2; return $interface_name if netnum($ip, $nbits) == $netnum; } $interface = ""; } $interface .= $_; } return ""; } sub netnum { my $net = shift; my $bits = shift; my @octets = split(/\./, $net); my $netnum = 0; for my $octet (@octets) { $netnum <<= 8; $netnum |= $octet; } my $mask = 0; for (1..$bits) { $mask <<= 1; $mask |= 1; } $mask = $mask << (32-$bits); $netnum &= $mask; return $netnum; }