No problem, here is an improved script with some added copyright boilerplate.
I used the same license as the main package; let me know if you'd prefer
something else.
--Barak.
#! /bin/bash
### Script to set up an iodine tunnel route traffic through it
###
### Copyright 2008 Barak A. Pearlmutter <[EMAIL PROTECTED]>
###
### License: MIT
###
### Permission to use, copy, modify, and distribute this software for
### any purpose with or without fee is hereby granted, provided that
### the above copyright notice and this permission notice appear in
### all copies.
###
### THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
### WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
### WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
### AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
### CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
### LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
### NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
### CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
## Cause script to bail immediately on failed command
set -e
## Options for user to set.
## Minimal customization: put the two lines
## subdomain=your.tunnel.sub.domain
## passed=password_for_that_tunnel
## in the file /etc/default/iodine-client.
echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
if [ -r ${iodine_client_rc} ]; then
. ${iodine_client_rc}
else
echo WARNING: Cannot read ${iodine_client_rc}
fi
if [ -z ${subdomain} ]; then
read -p "DNS tunnel DNS subdomain: " subdomain
fi
if [ -z ${subdomain} ]; then
echo ERROR: Must set subdomain.
exit 1
fi
if [ -z ${passwd} ]; then
read -p "Password for DNS tunnel over ${subdomain}: " passwd
fi
## This is a host name used for testing DNS and for pinging
echo "${testhost:=slashdot.org}" > /dev/null
## Set if local network should be taken down and then up
echo "${bounce_localnet:=true}" > /dev/null
## Set for testing network availability via ping at various points
echo "${test_ping_localnet:=true}" > /dev/null
echo "${test_ping_tunnel:=true}" > /dev/null
echo "${test_ping_final:=true}" > /dev/null
## Set if the script cannot find and then incorrectly guesses the
## local network router
echo "${default_router}" > /dev/null
## Set if script uses the wrong hardware interface
echo "{interface}" > /dev/null
## Set if the script should continue even if a command fails.
## Used to test script when running as non-root.
if [ $(whoami) = root ]; then
echo "${continue_on_error:=false}" > /dev/null
else
echo "${continue_on_error:=true}" > /dev/null
fi
## DEBIAN PACKAGES TO INSTALL: these are needed to run this script
## iodine (for /usr/sbin/iodine)
## iproute (for /bin/ip)
## ipcalc (for /usr/bin/ipcalc)
## dnsutils (for /usr/bin/dig)
## fping (for /usr/bin/fping)
## The default tunnel MTU is 1024.
## If local DNS server restricts to 512 byte packets then do this:
# ifconfig ${d} mtu 220
## TO DO
## - avoid double ping when DNS server and local router are the same
## - option to not kill existing iodine DNS tunnels, in case there
## are meant to be more than one
## - sanify check whether default_router is on local network
echo ==== Creating IP-over-DNS tunnel over local network connection...
## Find a network interface
if [ -z ${interface} ]; then
interface=$(tail --lines=+3 /proc/net/wireless \
| head -1 | tr -d : | awk '{print $1}')
fi
if [ -z ${interface} ]; then
interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \
| head -1 | awk '{print $1}')
fi
if [ -z ${interface} ]; then
echo ERROR: No network interface found.
exit 1
fi
echo ==== Local network interface: ${interface}
## Down any existing DNS tunnel (wish there were "approved" way to do this)
echo ==== Killing existing DNS tunnels...
if killall --quiet --wait --verbose --signal HUP iodine; then
sleep 2
fi
## Stabilize local network
if ${bounce_localnet}; then
echo ==== Bouncing local network connection...
ifdown --force ${interface} || true
ifup ${interface} || ${continue_on_error}
fi
## Fetch some information about the local network
addr=$(ip -4 addr show dev ${interface} scope global \
| tail -1 | awk '{print $2}')
prefix_len=$(echo ${addr} | sed 'sX^.*/XX')
local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}')
echo ==== Local address: ${addr}
echo ==== Local network: ${local_net}
router=$(ip -4 route list dev ${interface} \
| awk '$1=="default" {print $3}' | head -1)
if [ -z ${router} ]; then
## This can happen when the default local route is already deleted
if [ -z ${default_router} ]; then
echo WARNING: no default route, guessing local router IP address.
## Minimum address on local net is usually right
router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}')
else
echo WARNING: no default route, using configured default router.
## But sometimes need to hardwire...
router=${default_router}
fi
fi
echo ==== Local network router: ${router}
## Test DNS service
testhost_ip=$(dig +short -t A -q ${testhost})
if [ -z ${testhost_ip} ]; then
echo WARNING: Failure on DNS lookup of ${testhost}.
fi
## fetch DNS servers
nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf)
if [ -n "${nameservers}" ]; then
echo ==== DNS servers: ${nameservers}
else
echo ERROR: No DNS servers found.
exit 1
fi
## Test if local network is up
if ${test_ping_localnet}; then
echo ==== Ping test of local network router and DNS servers...
fping -C1 ${router} ${nameservers} \
|| echo WARNING: Ping test failed.
fi
## Add point-to-point routes for any non-local DNS servers
for n in ${nameservers}; do
n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print
$2}')
if [ "${n_net}" != "${local_net}" ]; then
echo ==== Adding point-to-point route for DNS server ${n}
ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
fi
done
## Bring up DNS tunnel
echo ==== Creating IP-over-DNS tunnel...
iodine -P ${passwd} ${subdomain} || ${continue_on_error}
## Find DNS tunnel interface
tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1)
if [ -z "${tunnel_interface}" ]; then
echo WARNING: Cannot find DNS tunnel interface, using default.
tunnel_interface=dns0
fi
echo ==== DNS tunnel interface: ${tunnel_interface}
## Figure out router at other end of tunnel, assuming router uses final octet .1
## (There should be some way to get this information out of iodine, since
## it *prints* it as it sets up the tunnel, so it does know it.)
tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \
| awk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1)
if [ -z ${tunnel_remote} ]; then
echo ERROR: Cannot find DNS tunnel remote endpoint.
${continue_on_error}
## set something random if debugging
echo WARNING: Confabulating DNS tunnel remote endpoint.
tunnel_remote=192.168.253.1
fi
echo ==== DNS tunnel remote endpoint: ${tunnel_remote}
if ${test_ping_tunnel}; then
echo ==== Ping test of local router, nameserver, and DNS tunnel...
fping -C1 ${router} ${nameservers} ${tunnel_remote} \
|| echo WARNING: Ping test failed.
fi
## Modify routing table to send trafic via DNS tunnel
echo ==== Setting default route through DNS tunnel...
## Remove default route via local router
ip -4 route del default via ${router} || ${continue_on_error}
## Add default via tunnel
ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
## Test if all is well
if ${test_ping_final}; then
echo ==== Ping test of local router, nameserver, DNS tunnel, external test
host...
fping -C1 ${router} ${nameservers} ${tunnel_remote}
${testhost_ip:-${testhost}} \
|| echo WARNING: Ping test failed.
fi