Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
380 views
in Technique[技术] by (71.8m points)

python - Discovering public IP programmatically

I'm behind a router, I need a simple command to discover my public ip (instead of googling what's my ip and clicking one the results)

Are there any standard protocols for this? I've heard about STUN but I don't know how can I use it?

P.S. I'm planning on writing a short python script to do it

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This may be the easiest way. Parse the output of the following commands:

  1. run a traceroute to find a router that is less than 3 hops out from your machine.
  2. 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('
'):
    if len(ln)>0 and ln[-1]=='
': 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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...