#!/usr/bin/perl # # configuration variables # $tty = "cua0"; $debug = 0; $timeout = 60; $delay = 2; $verbose = 0; $phone = "12345678"; $login = "MYLOGIN"; $passwd = "MYPASSWORD"; $network = "128.145"; $idletime = 600; $threshold = 20; $busysleep = 10; $busyretries = 5; $LCKDIR = '/var/spool/locks'; $ENV{PATH} = '/usr/bin:/etc:/usr/etc'; # initialization $network =~ s/\./\\./g; $user = (getpwuid($<))[0]; require "sys/ttold.ph"; require "getopts.pl"; require "syslog.pl"; &openlog("slipup $user", 'pid,cons,ndelay', 'daemon'); # # process arguments # &Getopts('dvr:s:k:'); $debug = 1 if defined $opt_d; $verbose = 1 if defined $opt_v; $busyretries = $opt_r if defined $opt_r; $busysleep = $opt_s if defined $opt_s; $KEEPTILL = time + $opt_k * 3600 if defined $opt_k; eval '&main'; &CLEANUP($@); exit 1; sub CLEANUP { local($arg) = @_; if ($arg) { $0 = "slipup ($arg)"; warn "\n".$arg; &syslog(WARNING, $arg); } else { $0 = "slipup (shutting down normally)"; } if ($sequencenumber) { $sequencenumber = 0; $SIG{ALRM} = CLEANUP; alarm(15); shutdown(ECHO,2); close ECHO; alarm(0); } kill 'TERM', $pid if $pid; if ($ttyopen) { system "stty 0"; close STDIN; close STDOUT; system "/bin/stty 0 >/dev/$tty"; } unlink "$LCKDIR/LCK..$$"; if ($locked) { system("/usr/etc/route -f") && warn "route flush failed\n"; unlink "$LCKDIR/LCK..$tty"; } exit length($arg) != 0; } sub SIGALRM { die "SIGALRM: ECHO timed out\n"; } sub SIGPIPE { die "SIGPIPE: ECHO closed other end\n"; } sub main { # # grab the line # $0 = "slipup (locking $tty)"; if (`/usr/ucb/netstat -i -n` =~ /$network/) { warn "Slip line is already up\n" if $verbose; exit 0; } warn "Opening /dev/$tty...\n" if $verbose; die "$tty is busy\n" if -e "$LCKDIR/LCK..$tty"; open(LCK, ">$LCKDIR/LCK..$$") || die "Can't create lock: $!\n"; printf LCK "%10d\n", $$; close LCK; die "$tty is busy\n" unless link("$LCKDIR/LCK..$$", "$LCKDIR/LCK..$tty"); unlink "$LCKDIR/LCK..$$"; $locked++; $0 = "slipup (opening $tty)"; die "$tty is not a tty\n" if ! -c "/dev/$tty"; die "Cannot open $tty: $!\n" if !open(STDOUT, "+>/dev/$tty"); $ttyopen++; die "Cannot dup STDOUT to STDIN\n" if !open(STDIN, "+<&STDOUT"); &syslog("Can't ioctl(TIOCEXCL) on $tty: $!") unless ioctl(STDOUT, &TIOCEXCL, 0); $SIG{'INT'} = 'INTR_HAND'; $SIG{'HUP'} = 'INTR_HAND'; $SIG{'QUIT'} = 'INTR_HAND'; $SIG{'TERM'} = 'INTR_HAND'; $SIG{'USR1'} = 'UNDEBUG'; # # configure the line # die "stty failed\n" if system "/bin/stty 19200 raw -echo min 1 time 0"; $| = 1; # # dial the modem # $0 = "slipup (dialing $tty)"; warn "Dialing modem...\n" if $verbose; #&send("ATE1V1X2Q0S2=0S58=2S68=2S48=1S50=6S51=5S94=0S180=2S181=1S190=4\r\n"); &send("ATE1V1X2Q0S2=0S58=2S68=2S48=1S50=6S51=5S94=0S180=2\r\n"); &wait_for("OK"); &send("ATDT$phone\r"); $countdown = $busyretries; while (&wait_pat("(BUSY|CONNECT|CARRIER)") ne 'CONNECT') { $tries = $countdown . ($countdown == 1 ? " try" : " tries"); $0 = "slipup (redialing $tty, $tries left)"; die "BUSY\n" if $countdown <= 0; warn "BUSY--trying again in $busysleep seconds, $tries left\n" if $verbose; $countdown--; &send("ATH\r"); sleep $busysleep; &send("ATDT$phone\r"); } # # log into the terminal server # $0 = "slipup (logging in on $tty)"; warn "Logging in...\n" if $verbose; &send("\r"); sleep($delay); &send("\r"); sleep($delay); &send("\r"); sleep($delay); &send("\r"); sleep($delay); &send("\r"); sleep($delay); &wait_for("name:"); &send("$login\r"); &wait_for("ssword:"); &send("$passwd\r"); &wait_for("ts>"); # # start slip # $0 = "slipup (starting slip on $tty)"; warn "Starting slip...\n" if $verbose; &send("slip\n"); $_ = &wait_pat("Your IP address is .* MTU is 1500 bytes"); die "Cannot find slip address\n" if !/Your IP address is (\d+\.\d+\.\d+\.\d+),/; $addr = $1; $0 = "slipup (starting slip on $addr)"; die "stty failed\n" if system "/bin/stty crtscts"; die "route flush failed\n" if system "/usr/etc/route -f >/dev/null"; $ppid = $$; if (fork) { $SIG{HUP} = EXIT; sleep 1 while 1; } $SIG{'TSTP'} = 'IGNORE'; if (!($pid = fork())) { $< = $>; warn "/usr/etc/slip-attach /dev/$tty 19200 $addr $addr 255.255.0.0\n" if $debug; exec "/usr/etc/slip-attach /dev/$tty 19200 $addr $addr 255.255.0.0"; die "Couldn't exec slip-attach: $!\n"; } $0 = "slipup (adding route for $addr)"; sleep 2; if (`/usr/bin/ps auxww` =~ /root *(\d+).*slip-attach/) { $pid = $1; } else { $pid++; } die "route add default failed\n" if system "/usr/etc/route add default $addr 1 >/dev/null"; warn "slip is up: process $pid, local address $addr\n" if $verbose; &syslog(NOTICE, "started $pid on $tty, address $addr"); $SIG{'INT'} = 'IGNORE'; $SIG{'HUP'} = 'IGNORE'; $SIG{'QUIT'} = 'IGNORE'; kill 'HUP', $ppid; sleep 1; kill 'KILL', $ppid; # # now loop forever waiting for an idle line # setpgrp(0,$$); sleep 2; `/usr/ucb/netstat -i -n` =~ /(slip|stream)\d*\s*\d*\s*$network[.\d]*\s*[.\d]*\s*(\d*)/; $lasti = $2; warn "input: $lasti\n" if $debug; &getsock(ECHO, $addr, 'tcp', 'echo'); $SIG{PIPE} = SIGPIPE; while (1) { $KEEPTILL = 0 if time > $KEEPTILL; if ($KEEPTILL) { $tmp = sprintf("%4.2f", ($KEEPTILL - time) / 3600); $0 = "slipup (monitoring $pid--keeping $tmp more hours)"; } else { $0 = "slipup (monitoring $pid for $addr)"; } sleep $idletime; print ECHO ++$sequencenumber, "\n"; $SIG{ALRM} = SIGALRM; alarm(60); $echoed = ; alarm(0); $echoed == $sequencenumber || die "ECHO sequence mismatch\n"; unless (kill 0, $pid) { system "/usr/etc/route -fn >/dev/null 2>&1" || &syslog(WARNING, "route flush failed\n"); &CLEANUP("slip-attach for $network [$pid] died!\n"); } $_ = `/usr/ucb/netstat -i -n`; ($slipstream, $i) = /(slip|stream)\d+\s+\d+\s+$network[.\d]*\s+[.\d]+\s+(\d+)/; unless ($slipstream) { system "/usr/etc/route -fn >/dev/null 2>&1" || &syslog(WARNING, "route flush failed\n"); &CLEANUP("slip interface for $network disappeared!\n"); } $diff = $i - $lasti; $lasti = $i; warn "input: $diff, seqnum: $echoed\n" if $debug; if ($diff < $threshold && time > $KEEPTILL) { $0 = "slipup (idlekilling pid $pid)"; kill 'TERM', $pid; sleep 3; kill 'KILL', $pid; system "/usr/etc/route -fn >/dev/null 2>&1" || die "route flush failed\n"; &syslog(NOTICE, "idle shutdown ($diff/$threshold)"); warn "Idle too long--shutting down slip\n" if $verbose; &CLEANUP(""); } } } sub get_char { local($rmask, $nfound, $timeleft, $thisbuf); $endtime = time + $timeout; $rmask = ""; vec($rmask,fileno(STDIN),1) = 1; ($nfound, $timeleft) = select($rmask, undef, undef, $endtime - time); if ((0 + $endtime - time) <= 0) { die "Timed out\n"; } if ($nfound) { $nread = sysread(STDIN, $thisbuf, 1024); if (defined($nread)) { print STDERR $thisbuf if $debug; $str .= $thisbuf; return "" if $nread == 0; # eof } } else { return undef; # timeout ? } } sub wait_for { local($pattern); warn "WAITING FOR:\n\n$_[0]\n\n" if $debug; ($pattern = $_[0]) =~ s/(\W)/\\$1/g; while (1) { if ($str =~ /$pattern/) { $str = $'; return $&; } &get_char; } } sub wait_pat { warn "WAITING FOR PATTERN:\n\n$_[0]\n\n" if $debug; while (1) { if ($str =~ /$_[0]/) { $str = $'; return $&; } &get_char; } } sub send { local ($send) = @_; warn "SENDING:\n\n$send\n\n" if $debug; print "$send"; } sub INTR_HAND { &CLEANUP("Received SIG$_[0]--aborting...\n"); } sub UNDEBUG { $debug = !$debug; warn "debug = $debug\n" if $verbose; } sub EXIT { exit 0; } sub getsock { local($SOCK, $SERVER, $PROTO, $PORT) = @_; $pat = 'S n C4 x8'; $af_unix = 1; $af_inet = 2; $stream = 1; $datagram = 2; local($name,$aliases,$proto) = getprotobyname($PROTO); local($name,$aliases,$port,$proto) = getservbyname($PORT,$PROTO); if ($SERVER =~ /^\d+\./) { @bytes = split(/\./,$SERVER); } else { ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($SERVER); die "Can't lookup $SERVER\n" unless $name; @bytes = unpack("C4",$addrs[0]); } $this = pack($pat,$af_inet,0, 0,0,0,0); $that = pack($pat,$af_inet,$port,@bytes); socket($SOCK,$af_inet,$stream,$proto) || die "socket: $!\n"; bind($SOCK,$this) || die "bind: $!\n"; connect($SOCK,$that) || die "connect to @bytes $port: $!\n"; select((select($SOCK), $| = 1)[0]); 1; }