#!/bin/sh # see "perldoc mailtunnel" for documentation UNCONFIGURED=1 # comment out this line once you've configured this start_ssh_window () { # you definitely need to change this -- at least the hostname! $PREFERRED_XTERM_APP -name 'SSH tunnel' -title 'SSH tunnel' \ -e ssh -M -C -x -a \ -L 8143:127.0.0.1:143 \ -L 8128:127.0.0.1:3128 \ -L 8025:127.0.0.1:25 \ -D 9050 \ yourserver.example.com # ssh command-line switches, in case you were wondering: # -C=compression, -x=no-X11-fwd, -a=no-auth-fwd, -L=forwarding, -D=socks # -M=master } # the human-readable description of this set of tunnels DESCRIPTION=" yourserver.example.com: ports 8143 (IMAP), 8025 (outgoing SMTP), 9050 (SOCKS), 8128 (HTTP proxy). " # if a connection can be made to this port, the conn is up. Use one # of the forwarded ports from above CONNECT_TEST_PORT=8143 # Your preferred form of terminal window app. something lightweight # is good PREFERRED_XTERM_APP=xterm [ -x /usr/local/bin/rxvt ] && PREFERRED_XTERM_APP=/usr/local/bin/rxvt [ -x /usr/bin/rxvt ] && PREFERRED_XTERM_APP=rxvt # I use fetchmail to deliver my mail locally, so I start and stop # fetchmail from this script depending on whether the link is up or # not. Replace with whatever commands you may want to run (or just # leave the functions empty if you like). run_at_startup () { fetchmail --quit # ensure no fetchmail is running at startup } run_connection_up () { fetchmail --all --nodetach # start fetching! } run_connection_down () { fetchmail --quit # once the conn dies, kill fetchmail as well } # should we keep trying silently, or stop and ask if the connection # fails to restart 3 times? set to 1 for the latter. Silent retries # is the least intrusive, in my experience. STOP_AND_ASK_ON_REPEATED_CONN_FAILURES=0 ########################################################################### # that's the end of the configuration bit. echo " mailtunnel - maintains SSH-encrypted tunnels to another host. Tunnels: $DESCRIPTION " if [ "${UNCONFIGURED:-x}" = 1 ] ; then echo "You have not configured this script yet. Run" 1>&2 echo "'perldoc mailtunnel' and read the CONFIGURATION section." 1>&2 exit 2 fi PATH=$PATH:/usr/local/bin tmpdir=$HOME/.mailtunnel pidsfile=$tmpdir/pids.tmp waitforkey () { $PREFERRED_XTERM_APP -title 'SSH tunnel' -name 'SSH tunnel' -e sh -c \ "echo $*. press return when link ready...; read" } check_any_if_up () { if netstat -nr | egrep '^0.0.0.0[ ]' > /dev/null 2>&1 ; then true else false fi } mkdir -p $tmpdir > /dev/null 2>&1 rm -f $pidsfile # create a temporary perl script, since opening sockets from sh is # a bit tricky portability-wise otherwise. echo ' open(P,">>&=4");print P $$." "; close P; use Socket; for $r (1..30) { socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")); $s=sockaddr_in($ARGV[0],inet_aton("127.0.0.1")); connect(S,$s) and exit; print "port $ARGV[0] not yet forwarded, retry $r\n"; sleep 2; }' > $tmpdir/waitforport.pl run_at_startup while true ; do sleep 4 if check_any_if_up ; then true ; else echo -n "no network interface is up: "; date while true ; do sleep 4 if check_any_if_up ; then break ; fi done echo -n "network interface(s) up: "; date fi # zero the pids file : > $pidsfile ( # hack: get the subprocess' PID, not the parent PID perl -e 'print getppid." "' 1>&4 # wait for the port to start accept()ing perl -w $tmpdir/waitforport.pl $CONNECT_TEST_PORT # and run the user code run_connection_up echo "run_connection_up finished, exiting" ) 4>$pidsfile & start=`perl -e 'print time'` start_ssh_window sleep 1 end=`perl -e 'print time'` pids=`cat $pidsfile` if [ "x$pids" != x ] ; then echo "ssh window exited; killing $pids" kill -15 $pids else echo "ssh window exited; no subprocesses to kill" fi echo -n "link failed: "; date run_connection_down linklifetime=`expr $end - $start` if [ $linklifetime -gt 1800 ] then retries=0 echo "had success, link was up for $linklifetime secs" else retries=`expr $retries + 1` echo "link failed again after $linklifetime secs, retry $retries" fi if [ $retries -gt 3 -a $STOP_AND_ASK_ON_REPEATED_CONN_FAILURES = 1 ] then echo -n "waiting for user to respond: "; date waitforkey link failed fi done echo "oops! should never get here" 1>&2 exit 23 # never gets here ########################################################################### =head1 NAME mailtunnel - maintains SSH-encrypted tunnels to another host =head1 SYNOPSIS B =head1 DESCRIPTION This script is used to set up SSH-encrypted tunnels to another host, allowing you to pick up and send mail, and browse the web, without sending traffic in cleartext over a potentially-sniffed local net. It's a cheapo, works-everywhere, low-maintainance VPN. By default, the tunnels are as follows: - IMAP on localhost, port 8143 - SMTP on localhost, port 25 - access to remote WWW proxy (if it exists) on localhost, port 3128 - SOCKS on localhost, port 9050 (Wondering about port 9050? see http://www.peerfear.org/rss/permalink/2004/02/28/SSHSocksAndMozilla/ for details on why it's useful.) In addition, specific commands are run when the connection comes up or drops. By default, this starts and stops a fetchmail daemon. =head1 CONFIGURATION You will need to change the 'start_ssh_window' function to log in to *your* server instead of mine. In addition, there's a few other tweaks (choice of terminal app, commandlines run on connection up/down events, etc.) that can be changed. =head1 DETAILS When the connection drops, this script will quietly retry efficiently until it reappears. The SSH session used for the tunnels is created as an interactive session, with its own window, as a bonus (at the very least, this is handy if you need to type any passwords). However, if the internet connection 'flaps' up and down, the ssh session window may appear and disappear repeatedly, which can be annoying. To avoid this, the xterm or rxvt for the session is created with a distinctive app name and title. Your window manager should have a way to set policies for windows with certain names or titles; this is a good way to force that window to display on a specific desktop, so it doesn't open and close in your face while you're working on your 'main' desktop. =head1 AUTHOR Justin Mason, http://jmason.org =head1 LICENSE GPL =head1 VERSION Last modified: Dec 6 2004 jm =cut