#!/bin/bash

# set up a socat udp tunnel automatically
# Sam Watkins <sam@ucm.dev>  Fri, 17 Apr 2009 01:55:06 +0800

# This script is pretty ugly and kludgy, please suggest me how to improve it!

# depends: apt-get install socat sudo ipmasq
# recommended: fsh  -  http://sam.ucm.dev/arcs/fsh/
# to use this script as is, you would need to setup passwordless sudo from your
# user to root on both boxes, e.g.:
#   visudo
#   %admin ALL = (ALL) NOPASSWD: ALL
# or
#   joe ALL = (ALL) NOPASSWD: ALL
# if your login is joe.

# In my case both hosts are running ipmasq, so they can act as gateways.
# remote host is a gateway so local host can talk to the internet via tun0.

# If you want to talk to the remote host through the tunnel, use
# the $REMOTE_TUN ip address, not the $REMOTE ip address
# ($REMOTE will still go via your normal internet interface).

# edit the config here:

VERBOSE=1
REMOTE="${REMOTE:-pireal}"
INTERNET_DEV=ppp0
TUNNEL_DEV=tun0
LOCAL_TUN=192.168.255.1
REMOTE_TUN=192.168.255.2
UDP_PORT=1234
MYIP_URL="http://sam.ucm.dev:82/cgi-bin/ip.cgi?"

# or use a config file:
CONFIG_FILE="$HOME/.socat-tunnel"

if [ -e "$CONFIG_FILE" ]; then
	source "$CONFIG_FILE"
fi

if [ "$1" = "-q" ]; then
	VERBOSE=
	exec >/dev/null 2>&1
	shift
elif [ "$1" = "-v" ]; then
	VERBOSE=1
	shift
fi

q() {
	"$@" >/dev/null 2>&1
}

v() {
	if [ -n "$VERBOSE" ]; then
		echo "  > $@" >&2
	fi
	"$@"
}

if [ -z "$SSH" ]; then
	if q which fsh; then SSH=fsh; fi
	SSH="${SSH:-ssh}"  # use SSH=fsh
fi

myip() {
	wget -q -O- "$MYIP_URL"
}

start() {
	q killall fshd fsh && sleep 1

	stop

	MY_IP=`myip`
	if [ -z "$MY_IP" ]; then
		echo >&2 "can't get our IP address"
		exit 1
	fi

	v sudo /i/ipmasq stop
	v $SSH $REMOTE "sudo /i/ipmasq stop"


	sleep 1

	v exec $SSH $REMOTE "sudo socat UDP-L:$UDP_PORT,RANGE=$MY_IP/32 TUN:$REMOTE_TUN/24,up" &

	sleep 1

	# wait for remote socat to start, this sucks, not so bad with fsh
	while true; do
		if v $SSH $REMOTE pidof socat; then break; fi
	done

	sleep 1

	v sudo socat UDP:$REMOTE:$UDP_PORT TUN:$LOCAL_TUN/24,up &

	# wait for the tun0 device to appear, this sucks too
	for A in `seq 1 10`; do
		q ifconfig $TUNNEL_DEV && break
		if [ "$A" = 10 ]; then
			echo >&2 "failed: $TUNNEL_DEV is not up"
			exit 1
		fi
		sleep 1
	done

	# make the tunnel the default route
	v sudo sh -c 'while route del default; do echo; done'
	v sudo route add $REMOTE dev $INTERNET_DEV
	v sudo route add default gw $REMOTE_TUN dev $TUNNEL_DEV

	sleep 1

	v $SSH $REMOTE 'sudo /i/ipmasq start &'
	v sudo /i/ipmasq start

	sleep 1

	check
}

stop() {
	v sudo route del $REMOTE_TUN
	v sudo route add default dev $INTERNET_DEV
	v $SSH $REMOTE sudo killall socat
	v sudo killall socat
}

check() {
	if v ping -c 1 -w 10 google.com; then
		echo >&2 "success: $TUNNEL_DEV seems to be working"
		return 0
	else
		echo >&2 "failed (possibly): $TUNNEL_DEV does not seem to be working"
		return 1
	fi
}

on() {
	v sudo route del default dev $INTERNET_DEV
	v sudo route add default dev $TUNNEL_DEV
}

off() {
	v sudo route del default dev $TUNNEL_DEV
	v sudo route add default dev $INTERNET_DEV
}

status() {
	R=`route -n | tail -n1`
	echo "$R"
	if echo "$R" | grep -v tun0 >/dev/null; then
		echo >&2 "tunnel $TUNNEL_DEV is not the default route"
	else
		ifconfig $TUNNEL_DEV && check && return 0
	fi
	return 1
}

case "$1" in
start|stop|on|off|status)
	$1
	;;
*)
	echo >&2 "usage: `basename $0` [-q|-v] start|stop|on|off|status"
	exit 1
	;;
esac
