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.comTracing 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 1Pinging 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 pythondef 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.returncodeif __name__ == "__main__": print("(This could take 30 sec)") print(natIpAddr())
Use it from the command line (on Windows) or from a python program:
import natIpAddrmyip = natIpAddr.natIpAddr()print(myip)
I have made a program that connects to http://automation.whatismyip.com/n09230945.aspit 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 urlopenprint urlopen('http://automation.whatismyip.com/n09230945.asp').read()
I like the ipify.org:
- it's free to use (even programmatically and allows even heavy traffic)
- the response contains only the IP address without any garbage (no need for parsing)
- you can also request a response in JSON
- works for both IPv4 and IPv6
- it's hosted in a cloud
- it's open-source
$ curl api.ipify.org167.220.196.42$ curl "api.ipify.org?format=json"{"ip":"167.220.196.42"}