Discovering public IP programmatically
This may be the easiest way. Parse the output of the following commands:
- run a traceroute to find a router that is less than 3 hops out from your machine.
- run ping with the option to record the source route and parse the output. The first IP address in the recorded route is your public one.
For example, I am on a Windows machine, but the same idea should work from unix too.
> tracert -d www.yahoo.com
Tracing route to www-real.wa1.b.yahoo.com [69.147.76.15]
over a maximum of 30 hops:
1 <1 ms <1 ms <1 ms 192.168.14.203
2 * * * Request timed out.
3 8 ms 8 ms 9 ms 68.85.228.121
4 8 ms 8 ms 9 ms 68.86.165.234
5 10 ms 9 ms 9 ms 68.86.165.237
6 11 ms 10 ms 10 ms 68.86.165.242
The 68.85.228.121 is a Comcast (my provider) router. We can ping that:
> ping -r 9 68.85.228.121 -n 1
Pinging 68.85.228.121 with 32 bytes of data:
Reply from 68.85.228.121: bytes=32 time=10ms TTL=253
Route: 66.176.38.51 ->
68.85.228.121 ->
68.85.228.121 ->
192.168.14.203
Voila! The 66.176.38.51 is my public IP.
Python code to do this (hopefully works for py2 or py3):
#!/usr/bin/env python
def natIpAddr():
# Find next visible host out from us to the internet
hostList = []
resp, rc = execute("tracert -w 100 -h 3 -d 8.8.8.8") # Remove '-w 100 -h d' if this fails
for ln in resp.split('\n'):
if len(ln)>0 and ln[-1]=='\r': ln = ln[:-1] # Remove trailing CR
if len(ln)==0: continue
tok = ln.strip().split(' ')[-1].split('.') # Does last token look like a dotted IP address?
if len(tok)!=4: continue
hostList.append('.'.join(tok))
if len(hostList)>1: break # If we found a second host, bail
if len(hostList)<2:
print("!!tracert didn't work, try removing '-w 100 -h 3' options")
# Those options were to speed up tracert results
else:
resp, rc = execute("ping -r 9 "+hostList[1]+" -n 1")
ii = resp.find("Route: ")
if ii>0: return resp[ii+7:].split(' ')[0]
return none
def execute(cmd, showErr=True, returnStr=True):
import subprocess
if type(cmd)==str:
cmd = cmd.split(' ')
# Remove ' ' tokens caused by multiple spaces in str
cmd = [xx for xx in cmd if xx!='']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
if type(out)==bytes: # Needed for python 3 (stupid python)
out = out.decode()
try:
err = err.decode()
except Exception as ex:
err = "!!--"+str(type(ex))+"--!!"
if showErr and len(err)>0:
out += err
if returnStr and str(type(out))=="<type 'unicode'>":
# Trying to make 'out' be an ASCII string whether in py2 or py3, sigh.
out = out.encode() # Convert UNICODE (u'xxx') to string
return out, proc.returncode
if __name__ == "__main__":
print("(This could take 30 sec)")
print(natIpAddr())
Use it from the command line (on Windows) or from a python program:
import natIpAddr
myip = natIpAddr.natIpAddr()
print(myip)
I have made a program that connects to http://automation.whatismyip.com/n09230945.asp it is is written in D an getting someone else to tell you what they see your ip as is probably the most reliable way:
/*
Get my IP address
*/
import tango.net.http.HttpGet;
import tango.io.Stdout;
void main()
{
try
{
auto page = new HttpGet ("http://automation.whatismyip.com/n09230945.asp");
Stdout(cast(char[])page.read);
}
catch(Exception ex)
{
Stdout("An exception occurred");
}
}
Edit python code should be like:
from urllib import urlopen
print urlopen('http://automation.whatismyip.com/n09230945.asp').read()