Sunday, January 24, 2010

Hostname mangling

Here's some bash code I wrote today for navigating my home network (the names have been changed to protect the innocent). I have three computers that live on my LAN, and I've instructed my router's DHCP server to assign each computer a fixed IP address. Thus, baley=192.168.1.10, gladia=192.168.1.11, and fastolfe=192.168.1.12. The goal is that I'd like to be able to access any of these computers from within the LAN, as well as from the outside (via a dyndns, sdh.yi.org).

First, I set up port forwarding on the router (essid solaria) to forward 2210 to baley, 2211 to gladia, and 2212 to fastolfe. I then added the respective ports to each host's /etc/ssh/sshd_config file (in addition to the regularly-scheduled port 22). Ideally I could simply add these names to my /etc/hosts file and be done with it, but because of the port issue, it's not quite that simple. Also, I have a different user name on fastolfe (steve) than I have on the others (sdh), and I'd rather not have to mess with that.

The solution I came up with is shell functions. I define ssh and scp to be functions, and parse the arguments to find any instances of these hostnames. I then do some work to figure out which LAN I'm on, so that I can short-circuit the router if possible. Without further ado, here's the functions:
WIRELESS=wlan0
function essid {
iwconfig $WIRELESS |
perl -ne 'print $1 if /ESSID:"([^"]*)"/' 2> /dev/null
}
function ssh {
local args=()
while [ -n "$*" ]; do
case "$1" in
baley)
case "$(essid)" in
solaria)
args=("${args[@]}" sdh@192.168.0.10) ;;
*)
args=("${args[@]}" -p 2210 sdh.yi.org) ;;
esac ;;
gladia)
case "$(essid)" in
solaria)
args=("${args[@]}" sdh@192.168.0.11) ;;
*)
args=("${args[@]}" -p 2211 sdh.yi.org) ;;
esac ;;
fastolfe)
case "$(essid)" in
solaria)
args=("${args[@]}" steve@192.168.0.12) ;;
*)
args=("${args[@]}" -p 2212 sdh.yi.org) ;;
esac ;;
*) args=("${args[@]}" "$1") ;;
esac
shift
done
command ssh "${args[@]}"
}
function scp {
local args=()
while [ -n "$*" ]; do
local arg="$1"
case "$arg" in
baley:*)
case "$(essid)" in
solaria)
arg="${arg/baley/sdh@192.168.0.10}" ;;
*)
arg="${arg/baley/sdh@sdh.yi.org}"
args=('-P' '2210' "${args[@]}") ;;
esac ;;
gladia:*)
case "$(essid)" in
solaria)
arg="${arg/gladia/sdh@192.168.0.11}" ;;
*)
arg="${arg/gladia/sdh@sdh.yi.org}"
args=('-P' '2211' "${args[@]}") ;;
esac ;;
fastolfe:*)
case "$(essid)" in
solaria)
arg="${arg/fastolfe/steve@192.168.0.12}" ;;
*)
arg="${arg/fastolfe/steve@sdh.yi.org}"
args=('-P' '2212' "${args[@]}") ;;
esac ;;
esac
args=("${args[@]}" "$arg")
shift
done
command scp "${args[@]}"
}
Now I can throw this onto any shell account I use (with appropriate modifications to the WIRELESS variable) and source it in my .bashrc to access any of my computers as if it had its own public IP address.