xedlock

SSH + iTerm + SRV Wrapper

Apr 14th, 2013
302
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.83 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. ##
  4. ## Purpose: This script does a few different things before it opens an SSH session. First, it
  5. ## uses the magical DNS SRV records to make sure I'm connecting to the correct host and correct
  6. ## port for the host I'm trying to actually reach (I have 5-6 hosts behind firewalls, using port
  7. ## mappings, and this prevents me from having to hardcode those port-mappings in my ~/.ssh/config,
  8. ## since the firewall will randomly change those mappings around -- but it at least updates the
  9. ## SRV DNS records when it does.  Secondly, it uses some of the nifty features of iTerm2 for Mac
  10. ## that lets me use escape codes to set the color of the window tabs and the background color
  11. ## of the window to help me find things quicker (when I've got 40 windows open across three
  12. ## displays, this makes a huge difference).
  13. ##
  14.  
  15. ## Disclaimer: I suck at bash. I should have written this in Perl.  Use at your own risk, or at least
  16. ## understand completely what the script does before using it.
  17.  
  18. ## You could use a shell alias to call it, but I kept having weird problems with zsh and autocompletion
  19. ## when I did that, so I ended up renaming this script 'ssh' and putting it in my personal ~/bin
  20. ## directory, which conveniently is in my PATH before /usr/bin/.  Yes, that's kind of dangerous, so
  21. ## you probably shouldn't do that. ;)
  22.  
  23. ## so here we go. please keep your giggling to yourself...
  24.  
  25. # I'm only pre-initializing these variables because I may append stuff to them later
  26. # and don't need bash getting all uppity if they're missing.
  27.  
  28. ARGS=""
  29. PORT=""
  30. USER=""
  31. HOST=""
  32. CMD=""
  33.  
  34. MYNAME=$(basename $0)
  35.  
  36. # if we're called as 'xsh', it means we want something different. see down below for more.
  37. if [[ $MYNAME == "xsh" ]]; then
  38.     SCREEN=1
  39. else
  40.     SCREEN=0
  41. fi
  42.  
  43. # loop through the arguments. this is faster/easier than getopt, in my opinion
  44. # since i'm only looking for a few specific things
  45.  
  46. while [[ -n "$@" ]]; do
  47.  
  48.     case $1 in
  49.         "-o")
  50.             shift
  51.             ARGS="${ARGS} -o $1"
  52.             shift
  53.             ;;
  54.         "-p")
  55.             shift
  56.             PORT=$1
  57.             shift
  58.             ;;
  59.         "-l")
  60.             shift
  61.             USER=$1
  62.             shift
  63.             ;;
  64.         *@*)
  65.             USER=${1%@*}
  66.             HOST=${1#*@}
  67.             shift
  68.             ;;
  69.         *.*)
  70.             [[ -z $HOST ]] && HOST=$1
  71.             shift
  72.             ;;
  73.         */*)
  74.             CMD=$1
  75.             shift
  76.             ;;
  77.         *\ *)
  78.             CMD=$1
  79.             shift
  80.             ;;
  81.         *)
  82.             [[ -n $ARGS ]] && ARGS="${ARGS} "
  83.             ARGS="${ARGS} ${1}"
  84.             shift
  85.             ;;
  86.     esac
  87. done
  88.  
  89. ## now the main purpose of this script, look up the damned DNS SRV record to find out where I should actually
  90. ## be connecting to get to SSH on $HOST
  91.  
  92. # SSH on the Mac used to do this automatically, then people complained and Apple removed the patch, so now
  93. # we have to look it up ourselves; no big deal, it only takes a fraction of a second.
  94.  
  95. # let's first save the HOST since we may be nuking it; I want to use the original hostname to calculate the
  96. # colors
  97. ORIGHOST=$HOST
  98.  
  99. # ... except .local hosts won't have SRV DNS records...
  100. if [[ ${HOST} =~ \.local ]]; then
  101.     HOST=${HOST}
  102.     PORT=""
  103. else
  104.     # search domains be damned; besides, I usually type in FQDN's anyway due to work VPN
  105.     # the dot tremendously speeds things up so the resolver doesn't waste time jumping to every DNS server
  106.     # it can find
  107.     if [[ $HOST =~ \.$ ]]; then
  108.         LOOKUP=${HOST}
  109.     else
  110.         LOOKUP=${HOST}.
  111.     fi
  112.  
  113.     # now some horriffic piping
  114.     SRV_LOOKUP=$( host -s 1 -t SRV _ssh._tcp.${LOOKUP} 2> /dev/null | grep "has SRV record" | sort -n -k 5,6 | cut -d " " -f 7- | head -n1 )
  115.     if [[ -z $SRV_LOOKUP ]]; then
  116.         # no SRV record; let SSH or .ssh/config figure out what port
  117.         SRV=0
  118.         PORT=""
  119.     else
  120.         # and we now have our destination host and destination port
  121.         PORT=${SRV_LOOKUP%\ *}
  122.         HOST=${SRV_LOOKUP#*\ }
  123.     fi
  124. fi
  125.  
  126. # because of our . shortcut, we sometimes end up with a host ending with a dot; that messes up the matching
  127. # in ~/.ssh/config, so let's strip it off here
  128. if [[ $HOST =~ (.*)\.$ ]]; then
  129.     HOST=${BASH_REMATCH[1]}
  130. fi
  131.  
  132. # Now the color stuff comes into play, which only works if you're using iTerm2
  133. if [[ $TERM_PROGRAM == "iTerm.app" ]]; then
  134.  
  135.     # base colors; I went through the 'safe' HTML color chart and found as many bright colors as different as
  136.     # possible from one another.  I'm colorblind, so my results may be horrible for someone else.
  137.     COLORS=( 0000FF 8A2BE2 A52A2A DEB887 5F9EA0 7FFF00 D2691E FF7F50 6495ED DC143C 00008B 008B8B B8860B A9A9A9 A9A9A9 006400 BDB76B 8B008B 556B2F FF8C00 9932CC 8B0000 E9967A 483D8B 2F4F4F 2F4F4F 00CED1 9400D3 FF1493 00BFFF 696969 696969 1E90FF B22222 228B22 FF00FF FFD700 DAA520 808080 808080 008000 ADFF2F FF69B4 CD5C5C 4B0082 7CFC00 00FF00 32CD32 FF00FF 800000 66CDAA 0000CD BA55D3 9370D8 3CB371 7B68EE 00FA9A 48D1CC C71585 191970 000080 808000 6B8E23 FFA500 FF4500 DA70D6 D87093 CD853F DDA0DD 800080 FF0000 BC8F8F 4169E1 8B4513 FA8072 F4A460 2E8B57 A0522D 6A5ACD 708090 00FF7F 4682B4 D2B48C 008080 FF6347 FFFF00 9ACD32 )
  138.  
  139.     # this list changes a lot, so.. count 'em
  140.     COLOR_COUNT=${#COLORS[@]}
  141.  
  142.     # now to get some consistency (same color for each host), let's md5 the hostname, convert a fragment of the md5 to decimal...
  143.     # uses name CRC because I was originally using plain CRC32, but I kept getting numbers that were too close to one another
  144.     if [[ -x /sbin/md5 ]]; then
  145.         CRC=$( echo ${ORIGHOST}${USER}${PORT} | md5 )
  146.         CRC=$( printf "%d" 0x${CRC:0:6} )
  147.     fi
  148.  
  149.     # goofy math to get a number between 1 and $COLOR_COUNT
  150.     RVAL=$(( $CRC % $COLOR_COUNT ))
  151.  
  152.     # and get that hex color from the array
  153.     THECOLOR=${COLORS[$RVAL]}
  154.  
  155.     # now split that color apart into decimal 0-255
  156.     RED=$( printf "%d" 0x${THECOLOR:0:2} )
  157.     GRN=$( printf "%d" 0x${THECOLOR:2:2} )
  158.     BLU=$( printf "%d" 0x${THECOLOR:4:2} )
  159.  
  160.     # now calculate the window background color; I use 10% of the color because it's barely visible,
  161.     # doesn't screw with the normal ANSI/256 colors, but is visible enough to quickly pick out windows for
  162.     # the same destination across three monitors
  163.     PERCENT=10
  164.  
  165.     # gotta use bc and printf, since bash can't do floats. this is about the point I should have stopped this
  166.     # project and rewritten it in perl... but... here we go.
  167.  
  168.     REDBG=$( printf "%0.0f" $( echo "scale=2; $RED * ($PERCENT / 100)" | bc -l ) )
  169.     GRNBG=$( printf "%0.0f" $( echo "scale=2; $GRN * ($PERCENT / 100)" | bc -l ) )
  170.     BLUBG=$( printf "%0.0f" $( echo "scale=2; $GRN * ($PERCENT / 100)" | bc -l ) )
  171.  
  172.     # now we convert it back into hex, since iTerm is a little inconsistent here (needs hex one place, decimal in another)
  173.     printf "\033]Ph%02x%02x%02x\033\\" $REDBG $GRNBG $BLUBG
  174.  
  175.     # now we set the window frame and/or tab colors
  176.     printf "\033]6;1;bg;red;brightness;%d\a" $RED
  177.     printf "\033]6;1;bg;green;brightness;%d\a" $GRN
  178.     printf "\033]6;1;bg;blue;brightness;%d\a" $BLU
  179.  
  180. fi
  181.  
  182. # build up the known SSH arguments
  183. if [[ -n $USER ]]; then
  184.     ARGS="-l $USER ${ARGS}"
  185. fi
  186. if [[ -n $PORT ]]; then
  187.     ARGS="-p $PORT ${ARGS}"
  188. fi
  189.  
  190. # if we started this script as 'xsh', we're going to launch a persistent screen session on the remote
  191. # host that this particular session can reconnect to (should I get disconnected). Ideally this would
  192. # use mosh, but mosh's scrollback support sucks dog butt and annoys me...  don't forget the '-tt', or
  193. # ssh will go "f-u, you don't have a TTY" and disconnect.
  194.  
  195. if [[ $SCREEN -eq 1 ]]; then
  196.     CMD="screen -a -d -RR -S $ITERM_SESSION_ID -e^Bb -i -U -T screen-256color $CMD"
  197.     ARGS="$ARGS -tt"
  198. fi
  199.  
  200. ###########################################################################################################
  201.  
  202. # have to do a little weirdness here and trap exits, since I need the terminal returned to normal if
  203. # I (for some reason) start to hit ^C before the ssh session is created.. so I use a function and a
  204. # trap to clean things up and exit the script from here on out...
  205.  
  206. suicide() {
  207.     EC=$?
  208.     if [[ $TERM_PROGRAM == "iTerm.app" ]]; then
  209.         # tab colors to default
  210.         printf '\033]6;1;bg;*;default\a'
  211.         # background color to black
  212.         printf "\033]Ph000000\033\\"
  213.     fi
  214.  
  215.     # I really have no idea what most of the exit codes mean, and I'm not in the mood to go
  216.     # searching through the OpenSSH source to find out.
  217.     case $EC in
  218.         130)
  219.             echo "SSH exited with exit code 130, because you hit ^C. You hurt its feelings. :("
  220.             ;;
  221.         0)
  222.             # no need to tell them anything. SSH is happy.
  223.             ;;
  224.         *)
  225.             echo "SSH exited with exit code ${EC}."
  226.             ;;
  227.     esac
  228.     exit $EC
  229. }
  230. trap suicide exit
  231.  
  232. ###########################################################################################################
  233.  
  234. # because dropbox likes to mess with my directory permissions, this is necessary. i used to
  235. # do a 'stat' to see if it was really needed, but it turns out it's just faster to blindly apply the
  236. # changes than do a stat then change them...
  237.  
  238. chmod 700 $HOME/.ssh > /dev/null 2>&1
  239. chmod 600 $HOME/.ssh/* > /dev/null 2>&1
  240.  
  241. # now let's call the real SSH... with all of my parameters...
  242. /usr/bin/ssh $ARGS $HOST $CMD
  243. EC=$?
  244.  
  245. # this 'exit' ends up looping back to my 'suicide' function above
  246. exit $EC
Advertisement
Add Comment
Please, Sign In to add comment