我在路由器后面,我需要一个简单的命令来发现我的公共 IP(而不是谷歌搜索我的 IP 并单击一个结果)
是否有任何标准协议?我听说过 STUN 但我不知道如何使用它?
PS我打算写一个简短的python脚本来做
我在路由器后面,我需要一个简单的命令来发现我的公共 IP(而不是谷歌搜索我的 IP 并单击一个结果)
是否有任何标准协议?我听说过 STUN 但我不知道如何使用它?
PS我打算写一个简短的python脚本来做
这可能是最简单的方法。解析以下命令的输出:
例如,我在一台 Windows 机器上,但同样的想法也应该适用于 unix。
> 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
68.85.228.121 是 Comcast(我的提供商)路由器。我们可以ping通:
> 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
瞧!66.176.38.51 是我的公共 IP。
执行此操作的 Python 代码(希望适用于 py2 或 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())
从命令行(在 Windows 上)或 python 程序中使用它:
import natIpAddr
myip = natIpAddr.natIpAddr()
print(myip)
我制作了一个连接到http://automation.whatismyip.com/n09230945.asp的程序, 它是用 D 编写的,让别人告诉你他们看到你的 ip 是什么,这可能是最可靠的方法:
/*
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");
}
}
编辑python代码应该是这样的:
from urllib import urlopen
print urlopen('http://automation.whatismyip.com/n09230945.asp').read()
我喜欢ipify.org:
$ curl api.ipify.org
167.220.196.42
$ curl "api.ipify.org?format=json"
{"ip":"167.220.196.42"}
针对 www.whatsmyip.org 是不礼貌的。他们请求不要在页面上这样做。
只有与目标处于同一 NAT 级别的系统才能看到相同的 IP。例如,您的应用程序可能位于多层 NAT 之后(当您远离 IP 过剩的美国时,这种情况发生得更多)。
STUN确实是最好的方法。一般来说,您应该计划在您的应用程序可以要求的某个地方运行(STUN)服务器:不要硬编码其他人的服务器。您必须编写代码以发送一些特定的消息,如 rfc5389 中所述。
我建议好好阅读和相关链接。 http://www.ietf.org/html.charters/behave-charter.html
您可能更喜欢查看 IPv6 和 Teredo,以确保您始终拥有 IPv6 访问权限。(有人告诉我,Microsoft Vista 让这变得非常简单)
每当我想这样做时,我都会抓取whatismyip.org。当您访问该站点时,它会为您提供纯文本公共 IP。干净利落。
只需让您的脚本访问该站点并读取 IP。
我不知道您是否在帖子中暗示了这一点,但是不可能从您自己的计算机上获取您的公共 IP。它必须来自外部来源。
2013 年编辑:此站点现在返回图像而不是文本,因此对此没有用处。
正如一些人提到的,STUN确实是正确的解决方案。
pynat
这是一个使用Python 模块的代码示例:
>>> import pynat
>>> pynat.get_ip_info()
('UDP Firewall', '192.0.2.2', 54320)
编辑: curlmyip.com 不再可用。(感谢 maxywb)
原帖:
在撰写本文时,curmyip.com 有效。从命令行:
curl curlmyip.com
这是一个第三方网站,几年后可能会也可能不会。但就目前而言,这似乎很简单,也很中肯。
您最简单的方法可能是询问网络外部的某个服务器。
要记住的一件事是,不同的目的地可能会为您看到不同的地址。路由器可能是多宿主的。实际上,这正是问题开始的地方。
如果网络有一个在网关上运行的 UpNp 服务器,您可以与网关通信并询问您的外部 IP 地址。
要获取您的外部 IP,您可以使用特殊主机名“myip.opendns.com”对 opendns 服务器进行 dns 查询:
from subprocess import check_output
ip = check_output(["dig", "+short", "@resolver1.opendns.com",
"myip.opendns.com"]).decode().strip()
在 Windows 上,您可以尝试nslookup
.
Python stdlib 中没有允许指定自定义 dns 服务器的 dns 模块。您可以使用第三方库,例如Twisted进行 dns 查询:
from twisted.internet import task # $ pip install twisted
from twisted.names.client import Resolver
from twisted.python.util import println
def main(reactor):
opendns_resolvers = [("208.67.222.222", 53), ("208.67.220.220", 53)]
resolver = Resolver(servers=opendns_resolvers, reactor=reactor)
# use magical hostname to get our public ip
return resolver.getHostByName('myip.opendns.com').addCallback(println)
task.react(main)
这是相同的使用dnspython
库:
import dns.resolver # $ pip install dnspython
resolver = dns.resolver.Resolver(configure=False)
resolver.nameservers = ["208.67.222.222", "208.67.220.220"]
print(resolver.query('myip.opendns.com')[0])
这是asyncio
使用aiodns
库的变体:
$ pipenv install aiodns
$ pipenv run python -m asyncio
...
>>> import asyncio
>>> import aiodns # pip install aiodns
>>> resolver = aiodns.DNSResolver()
>>> resolver.nameservers = "208.67.222.222", "208.67.220.220"
>>> await resolver.query("myip.opendns.com", "A")
[<ares_query_a_result> host=192.0.2.2, ttl=0]
获取 IPv6 地址:
>>> resolver.nameservers = "2620:119:35::35", "2620:119:53::53"
>>> await resolver.query("myip.opendns.com", "AAAA")
curl api.infoip.io
- 完整的细节
curl api.infoip.io/ip
- 只是IP地址
curl api.infoip.io/country
- 只是国家名称
......还有更多相同的
我正在与您分享我无需使用外部 API 即可检索服务器公共 IP 地址的方法(这当然是一个安全风险)
INTERFACE=`ip route get 8.8.8.8 | grep 8.8.8.8 | cut -d' ' -f5`
HOSTIP=`ifconfig $INTERFACE | grep "inet " | awk -F'[: ]+' '{ print $4 }'`
或者如果您愿意,可以在 python 中:
import subprocess
public_ip = subprocess.check_output(["ifconfig `ip route get 8.8.8.8 | grep 8.8.8.8 | cut -d' ' -f5` | grep \'inet \' | awk -F'[: ]+' '{ print $4 }'"], shell=True)
它是这样工作的:
ifconfig
从该接口的条目中greps 公共 IP以下是一些支持 IPv4 和 IPv6 的公共服务:
curl http://icanhazip.com
curl http://www.trackip.net/ip
curl https://ipapi.co/ip
curl http://api6.ipify.org
curl http://www.cloudflare.com/cdn-cgi/trace
curl http://checkip.dns.he.net
以下似乎目前仅支持 IPv4:
curl http://bot.whatismyipaddress.com
curl http://checkip.dyndns.org
curl http://ifconfig.me
curl http://ip-api.com
curl http://api.infoip.io/ip
以编程方式进行 HTTP 调用很容易。因此,所有这些都应该相对容易使用,并且您可以尝试多个不同的 URL,以防万一失败。
Duck Duck Go 根据他们自己的页面在这里免费访问他们的 API: https ://duckduckgo.com/api
如果您想要您的 IP 地址,请点击以下 URL: http ://api.duckduckgo.com/?q=my+ip&format=json
这将返回一个 JSON 对象。该Answer
属性有一个人类可读的字符串,其中包含您的 IP 地址。例子:
{
...
"Answer": "Your IP address is ww.xx.yyy.zzz in <a href=\"http://open.mapquest.com/?q=aaaaa(bbbbb)\">aaaaa(bbbbb)</a>"
...
}
split()[4]
如果您认为可以安全地假设该字符串永远不会更改或者您愿意需要定期修复它,则可以使用 来从该字符串中提取 IP 地址。
或者,如果您想要一个更面向未来的方法,您可以遍历返回的所有内容split()
并返回第一项是 IP 地址。请参阅此处以验证 IP 地址:
如何在 Python 中验证 IP 地址?
另一种厚颜无耻的方式:如果您的路由器可以选择在DynDNS上更新其网络 IP ,您可以通过以下方式获得自己的 IP:
IP=`resolveip -s myalias.dyndns-home.com`
现在托管您自己的“无服务器”API 非常便宜。所有主要的云提供商都为此提供服务。例如,使用 Google Cloud Functions 只需:
exports.requestIP = (req, res) => {
res.status(200).send(req.ip)
}
这种方法可能比使用上面的公共服务更可靠,您可以在函数名称中添加一个长随机字符串以使其保持私有。