#!/bin/bash # # open-wifi-auto-connect.sh: Auto connect to open (and online) WIFI(s). # This script scanns for free WIFIs and connects to the one with the best quality. # It checks if the WIFI/access point is really online. # If no open and online WIFI could be connected or after a disconnect it # restarts with a new and fast scan for WIFIs, to make the PC nearly always # online. # To avoid problems with duplicate ESSIDs and hidden ESSIDs this script uses # MACs. # # ---------------------------------------------------------------------------- # "THE BEERWARE LICENSE" (Revision 44): # Dr. Rolf Freitag (rolf dot freitag at email dot de) wrote this file. # As long as you retain this notice you can do whatever # the GPL (GNU Public License version 3) allows with this stuff. # If you think this stuff is worth it, you can send me money via # paypal, and get a contribution receipt if you wish, or if we met some day # you can buy me a beer in return. # ---------------------------------------------------------------------------- # # Version 2010-07-24 # # set -u : Stop the script when a variable isn't set (add -x for debugging) set -u echo "List of availible WIFI devices (if any):" #tail -n +3 /proc/net/wireless iwconfig 2>/dev/null | grep "ESSID" | cut -d" " -f1 if [ $# -ne 1 ] then echo "Error: Not exact one parameter (the WIFI device which you can find via iwconfig); exiting." exit 1 fi # device used for wifi (usually wlan0): first argument DEVICE="$1" # check if the device can be found ifconfig "$DEVICE" >/dev/null 2>/dev/null if [ $? -ne 0 ]; then echo "Error: Device $DEVICE not found (ifconfig $DEVICE returned not zero); exiting." exit 1 fi # Make sure the script is run as root. #if [ $EUID -ne 0 ]; then if [ $GROUPS -ne 0 ]; then echo "Error: This script must be run from group root, but your group is $GROUPS; exiting." exit 1 fi ########## Lockfile Part ##################### sleeptime="1" # sleeptime for creating the lockfile retries="10" # default number of retries of creating the lockfile: 10, should be > locktimeout*sleeptime locktimeout="5" # default timeout : 5 s. The lockfile will be removed # by force after locktimeout seconds have passed since the lock- # file was last modified/created. Lockfile is clock skew immune. lockdir="/var/tmp" # directory for the lock file # Eleminate the optional bash call with sed and get this process name from basename. this_process="$(basename "$(ps -p $$ -o cmd= | sed 's/^[^ ]*bash //')")" lockfile="$lockdir/.lockfile.$this_process" # (hidden) lockfile name # remove parameters lockfile="`echo "$lockfile" | cut -d" " -f1`" # ascertain whether we have lockf or lockfile system apps check () { if [ -z "$(which lockfile | grep -v '^no ')" ] ; then echo "$0 failed: 'lockfile' utility not found in PATH." >&2 exit 1 fi } # make lockifle lock () { typeset -i pid=0 # check if a lockfile is present if [ -f "$lockfile" ]; then # check the PID in the lockfile pid="$(cat "$lockfile")" if [ $pid -eq 0 ]; then echo "Could not read a valid PID from the lockfile." echo "Trying to remove that lockfile" echo "$lockfile" echo "." rm -f "$lockfile" else if kill -0 $pid 2> /dev/null; then echo "The locking executable with pid $pid and lockfile \""$lockfile"\" appears to be already running." # check if the process with the found UID if [ $(ps -p $pid -o uid=) == $UID ] ; then echo "The locking process has been created from the same user $UID which is running this script; exiting." exit 1 else echo "The locking process has been created from the different user" echo $(ps -p $pid -o uid=) echo "; the user (UID) of this script is $UID." # If you want to (try to) kill the blocking process, uncomment the following 3 lines. echo "Try to kill this locking process." kill -9 $pid rm -f "$lockfile" echo "Done killing and lockfile deletion." # Maybe in the line before the next fi you should send an email to root@localhost that a user tried (or maybe caused) # a DOS attack and that the blocking process (here undocumented because already killed) was killed. fi else echo "The locking executable with pid $pid has completed or was killed without cleaning up its lockfile" echo "or the locking executable has another name than this script or it is run by an other user;" echo "removing that lockfile" echo "$lockfile" echo "." rm -f "$lockfile" fi fi else echo "No old lock file found (ok)." fi # (try to) create the lockfile; wait if ! lockfile -$sleeptime -r $retries -l $locktimeout "$lockfile" 2> /dev/null; then echo "$0: Failed: Couldn't create lockfile in time" >&2 exit 1 fi chmod u+rw "$lockfile" # store the pid echo $$ > "$lockfile" chmod u-wx "$lockfile" # A trap to delete the lockfile when the script gets killed by SIGHUP SIGINT or SIGTERM. # In many cases, e. g. a kernel hangup, this does not work and the checks above are necessary. #trap "rm -f $lockfile; exit" SIGHUP SIGINT SIGTERM } # cleanup unlock () { rm -f "$lockfile" } #################### "main" ############################## # lockfile: first check, then lock check lock # kill other stuff which uses the device /etc/init.d/network-manager stop 2>/dev/null # set the device offline for configuration ifconfig "$DEVICE" down # set a random MAC ran=$(cat /proc/interrupts | md5sum) MAC=00:0$[$RANDOM%6]:${ran:0:2}:${ran:3:2}:${ran:5:2}:${ran:7:2} echo "init: switching MAC to $MAC" ifconfig "$DEVICE" promisc ifconfig "$DEVICE" hw ether $MAC # disable the ESSID checking (ESSID promiscuous), no key iwconfig "$DEVICE" channel auto iwconfig "$DEVICE" essid any iwconfig "$DEVICE" key off # set auto parameters with maximum speed of 1 Mbit/s (half duplex; slow # down to speed up), for maximum sensitivity, maximum range and maximum # immunity to interference because SNR and bit rate are complementary. iwconfig "$DEVICE" txpower auto #iwconfig "$DEVICE" commit # Set a trasmission power of 500 mW, which is ok e. g. in the USA and works # with many cards. The value set here should be (smaller than or) equal to the # maximum of your card. iwconfig "$DEVICE" txpower 500mW 2>/dev/null iwconfig "$DEVICE" rate 1M auto iwconfig "$DEVICE" frag auto # set client mode iwconfig "$DEVICE" mode managed #iwconfig "$DEVICE" commit # bring the device up ifconfig "$DEVICE" up # Power on iwconfig "$DEVICE" power on 2>/dev/null # create temporary (hidden) working directory and change into that directory #TMPDIR0="/tmp/.$0.$$.$RANDOM.DIR1.if.txt" #mkdir "$TMPDIR0" TMPDIR0=`mktemp -d -p /tmp ."$RANDOM"_XXX` cd "$TMPDIR0" # bash trap function for cleanup at exit (executed e. g. when CTRL-C is pressed) # Signals: 1/HUP, 2/INT, 3/QUIT, 9/KILL, 15/TERM, ERR, EXIT trap bashtrap 2 9 15 EXIT bashtrap() { cd - rm -f /var/lib/dhcp*/dhclient.leases rm -rf "$TMPDIR0" unlock exit 0 } # create temporary files #TMPFILE0="$TMPDIR0/.$0.$$.$RANDOM.0.if.txt" #TMPFILE0=`tempfile -d "$TMPDIR0"` # tempfile is not part of the coreutils TMPFILE0=`mktemp --tmpdir="$TMPDIR0" ."$RANDOM"_XXX` TMPFILE1=`mktemp --tmpdir="$TMPDIR0" ."$RANDOM"_XXX` # variables for open WIFI count (0...), etc. typeset -i OPENCOUNT=0 typeset -i CLOSEDCOUNT=0 typeset -i CELLCOUNT=0 typeset -i i=0 typeset -i j=0 typeset -i k=0 typeset -i l=0 typeset -i m=0 typeset -i pi=0 typeset -i pj=0 declare -a APMAC declare -a OPENCELLNUMBER declare -a CHANNEL declare -a ESSID typeset -i deadline_counter=0 typeset -i loop_counter=0 typeset -i connected=0 typeset -i flag=0 typeset -i SCANNUMBER=0 typeset -i counter0=0 typeset -i counter1=0 # start of the endless loop with scanning, looking for open WIFIs, testing/using while [ 1 == 1 ] do OPENCOUNT=0 # Change the MAC before every scan to a random and valid MAC by limiting the second byte to 5. # Because modern WIFI cards usually have higher bytes, this ensures another and random MAC. ran=$(head /dev/urandom | md5sum) MAC=00:0$[$RANDOM%6]:${ran:0:2}:${ran:3:2}:${ran:5:2}:${ran:7:2} echo "switching MAC to $MAC" ifconfig "$DEVICE" down ifconfig "$DEVICE" promisc ifconfig "$DEVICE" hw ether $MAC # clear the arp cache #ip neigh flush all 2>/dev/null ifconfig "$DEVICE" up # scan and get a list of (open) wifi points echo "Scan number $SCANNUMBER, scanning ..." SCANNUMBER=$[$SCANNUMBER +1] iwlist "$DEVICE" scanning > "$TMPFILE0" 2>/dev/null # remove the first line of the scan output (with "Scan completed") i=`wc -l < "$TMPFILE0"` i=$[$i -1] tail -n $i "$TMPFILE0" > "$TMPFILE1" # get the number of open WIFIs cat "$TMPFILE1" | grep "Encryption key:off" > "$TMPFILE0" OPENCOUNT=`wc -l < "$TMPFILE0"` # get the number of closed WIFIs cat "$TMPFILE1" | grep "Encryption key:on" > "$TMPFILE0" CLOSEDCOUNT=`wc -l < "$TMPFILE0"` echo "Found $OPENCOUNT open WIFI(s) and $CLOSEDCOUNT closed WIFI(s)." # get the total number of WIFIs (Cells) cat "$TMPFILE1" | grep "Encryption key:" > "$TMPFILE0" CELLCOUNT=`wc -l < "$TMPFILE0"` # Split the scan output into one file per cell; the files are xx1, xx2, ... csplit --digits=1 -k "$TMPFILE1" '/Cell/' {99} 2> /dev/null > /dev/null # print the WIFI data echo "List of WIFI(s) with Channel, Encryption, Quality, Signal Level, MAC, ESSID:" i=1 # number for the first open WIFI MAC for loop_counter in $(seq 1 $CELLCOUNT) do echo -n "`cat xx$loop_counter | awk '/Channel:/{ print $1 }'`" echo -n -e "\t`cat xx$loop_counter | awk '/Encryption/{ print $2 }' | cut -d":" -f2`" echo -n -e "\t`cat xx$loop_counter | awk '/Quality/{ print $1}'`" echo -n " `cat xx$loop_counter | awk '/Quality/{ print $3}'`" echo -n " `cat xx$loop_counter | awk '/Address/{ print $5 }'`" echo " `cat xx$loop_counter | awk '/ESSID/{ print $1 }'`" done # if no open WIFI found: continue (make a new scan) if [ $OPENCOUNT -eq 0 ] then continue fi # Now we have at minimum one open WIFI. # Create the list of open WIFIs (OPENCELLNUMBER[1]...OPENCELLNUMBER[$OPENCOUNT]) i=1 # first number of the first open WIFI for loop_counter in $(seq 1 $CELLCOUNT) do cat xx$loop_counter | grep "Encryption key:off" 2>&1 >/dev/null if [ $? -eq 0 ]; then OPENCELLNUMBER[$i]=$loop_counter i=$[$i +1] fi done # Sort the list with swapsort by quality: highest/best first. # This takes n*(n-1)/2 comparisons (and maybe swaps), e. g. 45 for n=10, 499500 for n=1000, # so with really lots of open WIFIs you should use something really faster, like GPU-Quicksort or GPUSort. for i in $(seq 1 $[$OPENCOUNT -1]) do l=${OPENCELLNUMBER[$i]} qi=`cat xx$l | awk '/Quality/{ print $1}' | cut -d"=" -f2 | cut -d"/" -f1` for j in $(seq $[$i +1] $OPENCOUNT) do m=${OPENCELLNUMBER[$j]} qj=`cat xx$m | awk '/Quality/{ print $1}' | cut -d"=" -f2 | cut -d"/" -f1` if [ $qi -lt $qj ]; then OPENCELLNUMBER[$i]=$m # swap OPENCELLNUMBER[$j]=$l # swap fi done done # Now the strongest WIFI is at OPENCELLNUMBER[1], the weakest at OPENCELLNUMBER[$OPENCOUNT] # put the data of the open WIFIs into arrays for i in $(seq 1 $OPENCOUNT) do l=${OPENCELLNUMBER[$i]} APMAC[$i]=`cat xx$l | awk '/Address/{ print $5 }'` CHANNEL[$i]=`cat xx$l | awk '/Channel:/{ print $1 }' | cut -d ":" -f 2` foo=`cat xx$l | awk '/ESSID:/{ print $1 }' | cut -d ":" -f 2 | cut -c 2-128` ESSID[$i]=${foo%?} done # now the MACs of the open WIFIs are at APMAC[1]...APMAC[$OPENCOUNT] # Check/use the list of open WIFI(s) for loop_counter in $(seq 1 $OPENCOUNT) do deadline_counter=0 connected=0 echo "Checking the open WIFI with MAC ${APMAC[$loop_counter]}, Channel ${CHANNEL[$loop_counter]}, ESSID ${ESSID[$loop_counter]}" while [ $deadline_counter -lt 2 ] # give a connection try at minimum two chances do if [ $connected -eq 0 ]; then # if not connected # kill the now outdated dhcpcd dhcpcd -k 2>/dev/null # check termination of dhcpcd if [ -f /var/run/dhcpcd-"$DEVICE".pid ] then kill `cat /var/run/dhcpcd-"$DEVICE".pid` rm -f /var/run/dhcpcd-"$DEVICE".pid fi # kill the now outdated dhclient if [ -f /var/run/dhclient.pid ] then #dhclient -r kill `cat /var/run/dhclient.pid` rm -f /var/run/dhclient.pid fi killall dhclient 2>&1 >/dev/null rm -f /var/lib/dhcp*/dhclient.leases # Connect iwconfig "$DEVICE" mode managed ap "${APMAC[$loop_counter]}" channel "${CHANNEL[$loop_counter]}" essid ${ESSID[$loop_counter]} # iwconfig commit # DHCP configuration: use dhcpcd if availible, dhclient else # if command -s -v dhcpcd type -P dhcpcd if [ $? -eq 0 ] then # dhcpcd with 20 s timeout (default 60) dhcpcd -t 20 "$DEVICE" else dhclient -1 "$DEVICE" fi # Check if we can be connected: Because dhclient always returns 0, check if we got an ip ifconfig "$DEVICE" | grep "inet " if [ $? -ne 0 ] then echo "DHCP got no ip." type -P dhcpcd if [ $? -ne 0 ] then dhclient -r rm -f /var/lib/dhcp*/dhclient.leases fi deadline_counter=$[$deadline_counter +1] connected=0 continue fi connected=1 # we should now be connected to the access point fi echo -n "Connectet: " iwconfig "$DEVICE" | grep -i "quality" # check the internet connection first by DNS lookups, than test downloads counter0=0 # DNS lookup for DNS root nameserver D foo=`dig -p 53 +time=5 -4 +short +noidentify terp.umd.edu` if [ "$foo" == "128.8.10.90" ] then counter0=$[$counter0 +1] # ok counting fi # DNS lookup for dns.msftncsi.com, see http://technet.microsoft.com/en-us/library/cc766017%28WS.10%29.aspx foo=`dig -p 53 +time=5 -4 +short +noidentify dns.msftncsi.com` if [ "$foo" == "131.107.255.255" ] then counter0=$[$counter0 +1] fi if [[ ($counter0 -ne 0) || ($connected -ne 0) ]] # at minimum one DNS lookup was successfull or we were online before; we may be online then echo "DNS lookup check: $counter0 DNS lookup(s) successfull!" counter1=0 # test download section USER_AGENT=Mozilla/4.0\ \(compatible\;\ MSIE\ 7.0\;\ Windows\ NT\ 6.0\;\ SLCC1\;\ .NET\ CLR\ 2.0.50727\;\ .NET\ CLR\ 3.0.04506\) # download the small google logo rm -f file wget -np --proxy=off --timeout=5 --tries=1 --user-agent="$USER_AGENT" --header="Accept-Encoding: deflate, gzip" --output-document=file http://www.google.com/images/logo_sm.gif 2>/dev/null # check the download echo "46d45b3b923c33542f82722f6bfdda53bd485be5 file" > file.sha1 sha1sum -c file.sha1 2>/dev/null counter1=$[$counter1 +$?] # error counting # download the SM test file rm -f file wget -np --proxy=off --timeout=5 --tries=1 --user-agent="$USER_AGENT" --header="Accept-Encoding: deflate, gzip" --output-document=file http://www.msftncsi.com/ncsi.txt 2>/dev/null # check the download echo "33bf88d5b82df3723d5863c7d23445e345828904 file" > file.sha1 sha1sum -c file.sha1 2>/dev/null counter1=$[$counter1 +$?] if [ $counter1 -ge 2 ] # all test downloads failed then echo "all test downloads failed" # increase the deadline counter if we were not connected before or # if we were connected but also all DNS lookups failed if [ $connected -eq 0 ] then deadline_counter=$[$deadline_counter +1] else if [ $counter0 -eq 0 ] then deadline_counter=$[$deadline_counter +1] else # at minimum one dns lookup was successfull; we are half online sleep 1 # wait a second before the next checks fi fi else # we are online; wait before the next check echo "Online!" sleep 10 fi else # offline; no DNS lookup was successfull and we were not online before deadline_counter=$[$deadline_counter +1] echo "offline" fi done # the actual open WIFI failed the online test two times; loop to the next deadline_counter=0 connected=0 echo "Not connectet" done # go to the next open WIFI (or make a new scan) done # while [ 1 = 1 ], end of the endless loop exit 0