Script reads present IP address from ADSL router

Update: A simpler and morre universal method to get your IP address:    http://oao.no/wpe/2014/10/script-reads-public-ip-address-from-adsl-router-using-unp/

I have a dynamic IP address ADSL subscription. This means that my public IP address changes now and then. If the address changes and I don’t know the new one, I cannot access my network from the outside. Address change detection also enables me to inform the dynamic IP name server I use ( http://freedns.afraid.org/ ) so my DNS entry will be updated. All this has to be done automatically in a script. There are servers around the Internet you can query for your present IP address (e.g. http://www.whatismyip.com/ ), but they usually have a limit on how often you can send queries.

The public IP address (often denoted WAN address) can be found somewhere on the router’s information page, e. g.:

IP address as seen on info page

This is easy for a script to pick up, but first the script must login to the server. In my case the router home page is at http://192.168.1.200/.

wan_2

All routers seems to have a login page looking somewhat like this. To enable the script to login I must first find out what information the web reader sends when I press the LOGIN button. Either I can study the html-source or use Wireshark for this. The html of the login page is:

wan_4

I guess the login form code is in  the /login.htm frame, so I go to http://192.168.1.200/login.htm and fetch the source code. The html code defines a form containing a number of INPUT fields and a submit button. The essential lines from the code are

<FORM ACTION=/cgi-bin/logi METHOD=POST NAME=LOGIN ONSUBMIT="stime()">
<INPUT TYPE=HIDDEN VALUE="@" NAME=rc>
<INPUT TYPE=PASSWORD NAME=PS SIZE=9 MAXLENGTH=9>
<INPUT TYPE=HIDDEN VALUE="login" NAME=rd>
<INPUT TYPE=HIDDEN NAME=TC VALUE=123>
</TD>
</TR>
<TR>
<TD>&nbsp;</TD>
<TD>
<INPUT TYPE=SUBMIT VALUE="LOGIN">

This form has the name LOGIN, will call the java script function stime() when the submit-button is clicked, POST the form data and execute a cgi script in the router. Four values will be posted: rc,PS, rd and TC. rc is just a kind of mark, PS is the password, rd is some label for the data set and TC date information. The password will be truncated to 9 characters. The java script function called is

function stime() {
var n = new Date();
document.forms[0].TC.value=n.getTime()/1000-n.getTimezoneOffset()*60;
return true;
}

This function sets the TC field value to the present time (seconds since 1970) corrected for the time zone, this value is probably used within the router to update the real time clock. The FORM data are transmitted URL-encoded, so when my (old) password is 3x[w54K and the time is 13060424414.903 we will have rc=%40 ('@'), PS=3x%5Bw54K, rd=login and TC=13060424414.903. This results in the POST name/value pair string

rc=%40&PS=3x%5Bw54K&rd=login&TC=13060424414.903&

We can check this by using Wireshark ( http://www.wireshark.org/ ) and start collecting data just before we hit the LOGIN-button

Wireshark traffic log

Wireshark traffic log

We see the POST message and the POST string going to the router.

Now, we are ready to write our script. We will first do the login and then search for the IP address in the status page html code. Going to 192.168.1.200/status.htm and looking through the html source I see this line:

wan_5

So by seraching for the “WAN IP:”  part I can find this line. I use a regular expression for this: “WAN.+IP:.*$” which say search for a string starting with “WAN“, followed by at least one character,  “IP:” and any number of characters till the line ends. I use the MULTILINE option so the $ means end of line and not end of string, which is the full html source.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import time
import os
import sys
from subprocess import Popen, PIPE
import urllib2
import re
import urllib
def login():
    # login: responds with a file containing main.html, or #incorrect
    # password' or document.LOGIN.PS.focus(); or 404
    tc= time.time()
    passwd= "3x[w54K"
    f= {}
    f["PS"]= passwd[:9]
    post= "rc=%%40&%s&rd=login&TC=%d.000"%(urllib.urlencode(f), tc)
    url= "http://192.168.1.200/cgi-bin/logi"
    print "url: ", url
    print "post: ", post
    try:
        # send the post data, expect "main.htm" in the page returned
        f=urllib2.urlopen(url, post).read().find("/main.htm")
        if f >= 0: return 0 #OK
        return -1
    except urllib2.URLError:
        return -2

def pickIp(statuspage, regexp): 
    try:
        # read the page
        f=urllib2.urlopen(stauspage, None).read()
        #seach for the WAN address line
        line= regexp.search(f).group().strip()
        # split the line at white spaces, 
        # the IP will be the last element in the list
        ip= line.split()[-1]
        if len(ip)>=7:
            return ip
        else:
            # No IP found
            return -1
    except urllib2.URLError:
        # exception while reading from router
        return -2

# Try to login 
r= login() 
if r < 0: 
    errormsg(r) 
# login OK; proceed to find the IP 
statuspage= "http://192.168.1.200/status.htm " 
regexp= re.compile(r"WAN.+IP:.*$", re.MULTILINE)
ip= pickIp(statuspage, regexp)
print "IP: ", ip

This script must obviously be adapted for your router to work, but it looks like this pattern is followed by most routers. I could have used urlencode for all the url parametres, but this router seems to accept the parameters in only the given sequence, by using urlencode for all I lose the control of the parameter sequence.

When you get the IP address you can check if it changed since the last query, and if so inform your dynamic IP DNS service about it.

As this script only accesses your router there is no limit to how often you can run it. You may adapt the script to first login and then run the pickIp function in a infinite loop, sleeping perhaps for 10 seconds after each query. I have combined this with my previous script to both inform my DNS provider and send me a tweet when the address changes.

 

 

0 Responses to “Script reads present IP address from ADSL router”


  • No Comments

Leave a Reply