I was once told that when learning another (non-programming) language that you cannot say that you are fluent in it until you stop converting what you are hearing / what to say to / from your native tongue. I think the same applies to programming languages; you do not know it until you can use it to solve any problem. Of course, if the only tool you have is a hammer…

A bit of background before we get to code. I have a 14 yr old daughter who at the best of times is negligent of her homework and who is, shall we say, less than street smart. Yesterday she got her nana’s old computer for her room which is a scenario which could compound the two previous problems so we wanted to be able to control when the machine was live onto the internet. I thought about teaching my wife how to log into the router and control the access lists, but that would involve lots of steps she would have to remember so I figured I would have to automate it.

My first crack at it was to use Selenium but it got foiled by the username/password window which basic auth presents. And the old school trick of putting the credentials in the url doesn’t work at all in IE (on purpose) and in Firefox it still pops up the box, just with the values prepopulated. So that wasn’t going to work.

Reaching into the toolbox I pulled out Python. I had tried a number of months ago to this task using python but overengineered it using the httplib module and abandoned it. Sure, I could cart around a session, but for this task it was overkill. Since all I needed to do was POST a specifically constructed request to the router to control whether the filter is on or off it was 10 minutes of coding to whip together something that almost works and the rest of the hour to debug it (I wasn’t setting the authentication realm properly and there was a status code that apparently means something which I wasn’t setting correctly either).

Before using the script there is some setup work on the router that needs to be done. First, you need to create the filter that you want to be controlling. Second, you need to associate the filter to the machine you are trying to blackhole. I did this via the nic’s mac address so I could still have the machine use dhcp. And third, tweak the script to suit your environment. Specifically the router_host and encoded_password variables and the f_name and f_id keys in options. encoded_password is just a base64 representation of the router’s password if anyone (such as Meagan) is snooping for the password to gain access to the router they won’t see it. Note: this doesn’t work in a household of programmers.

import urllib2, sys, base64, urllib

router_host = "192.168.5.1"
security_page = "http://%s/apply.cgi" % router_host
encoded_password = "cnViYXJi"

options = {"submit_button": "Filters",
           "change_action": "",
           "submit_type": "save",
           "action": "Apply",
           "blocked_service": "",
           "filter_web": "",
           "filter_policy": "",
           "f_status": "2", # 0 for disable, 1 or 2 for enable
           "f_id": "3", # filter id
           "f_status1": "enable",
           "f_name": "Meagan", # filter name
           "f_status2": "deny",
           "day_all": "1",
           "time_all": "1",
           "allday": "",
           "blocked_service0": "None",
           "blocked_service1": "None",
           "host0": "",
           "host1": "",
           "host2": "",
           "host3": "",
           "url0": "",
           "url1": "",
           "url2": "",
           "url3": "",
           "url4": "",
           "url5": ""}

if sys.argv[1] == "on":
    options["f_status1"] = "enable"
else:
    options["f_status1"] = "disable"
    options["f_status"] = "0"

auth_handler = urllib2.HTTPBasicAuthHandler()
auth_handler.add_password("downstairs", router_host, "", base64.decodestring(encoded_password))
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)

try:
    f = urllib2.urlopen(security_page, urllib.urlencode(options))
    if f.read().find("Settings are successful.") == -1:
        print "Settings were not successful"
    else:
        print "Settings were successful"
except urllib2.HTTPError, e:
    print e.msg

In addition to the script, there are 2 batch scripts which are used to actually control this as the behavior is determined by a parameter and I wanted this to work by just clicking a single icon on the desktop. Here is the one for turning off the filter.

c:\python25\python.exe router.py off

One side benefit of this solution is that I could quite easily turn on DDNS and remote management of the router and I would be able to control her access from work if necessary by only having to change a single variable.