我能否使用客户端Javascript执行DNS查找(主机名到IP地址)?

112

我想使用客户端JavaScript进行DNS查找(主机名到IP地址),并从客户端计算机上查看。这是否可能?


9
这个问题的许多答案似乎都建议在服务器端执行分辨率。根据使用情况,这可能不足够。例如,如果您要查找的服务正在使用GSLB,则基于用户所在地,它可能返回不同的IP地址;因此,服务器端代码收到的响应很可能与浏览器接收到的响应不同。话虽如此,对于那些关心这种差异的人,我还没有其他解决方案。 - Ilan Rabinovitch
16个回答

74

编辑: 这个问题让我很痒,所以我在谷歌应用引擎上搭建了一个JSONP服务,可以返回客户端的IP地址。使用方法:

<script type="application/javascript">
function getip(json){
  alert(json.ip); // alerts the ip address
}
</script>

<script type="application/javascript" src="http://jsonip.appspot.com/?callback=getip"> </script>

太棒了,不需要服务器代理。


纯JS无法实现。如果您有一个在同一域下打印它的服务器脚本,您可以发送XMLHttpRequest来读取它。


5
你能发布你的网络服务源代码吗?这样就可以运行一个实例了。 - dysbulic
35
很抱歉,我不得不投下反对票,因为我认为它并没有真正回答原问题。他们只是想要一个标准的DNS查询,而不是用户的公共IP。 - Simon East
1
你的网址似乎无法访问。 - Jens Timmerman

60

我知道这个问题很久以前就被问过了,但是我想提供一个更新的答案。

DNS over HTTPS (DoH)

您可以通过支持它的DNS解析程序将DNS查询发送到HTTPS上。 DOH的标准在RFC 8484中描述。

这与所有其他答案建议的类似,只是DoH实际上是DNS协议通过HTTPS。它还是一个“拟议中”的Internet标准,正在变得非常流行。例如,一些主要的浏览器支持它或计划支持它(Chrome、Edge、Firefox),微软正在将其构建到他们的操作系统中。

DoH的目的之一是:

允许Web应用程序通过现有的浏览器API以一种符合CORS(跨源资源共享)的安全方式访问DNS信息

有一个专门为Web应用程序从中进行DNS查找而制作的开源工具叫做dohjs。它执行如RFC 8484所述的DNS over HTTPS(DoH)wireformat查询。它支持GET和POST方法。

完整披露:我是dohjs的贡献者。

这里还有一个类似的JavaScript库 - https://github.com/sc0Vu/doh-js-client。我个人没有使用过这个库,但我认为它也可以在客户端使用。

DNS over HTTPS JSON APIs

如果您不想麻烦于DNS wireformat,Google和Cloudflare都提供了DNS over HTTPS的JSON API。

使用Google的JSON DOH API查找example.com的示例Javascript代码:

var response = await fetch('https://dns.google/resolve?name=example.com');
var json = await response.json();
console.log(json);

使用Wireformat的DOH GET和POST的RFC示例

这是RFC给出的GET和POST的示例(请参见https://www.rfc-editor.org/rfc/rfc8484#section-4.1.1):

GET示例:

第一个示例请求使用GET请求“www.example.com”。

:method = GET
:scheme = https
:authority = dnsserver.example.net
:path = /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
accept = application/dns-message



POST示例:

使用POST方法相同的DNS查询“www.example.com”如下:

:method = POST
:scheme = https
:authority = dnsserver.example.net
:path = /dns-query
accept = application/dns-message
content-type = application/dns-message
content-length = 33

<以下是表示33个字节的十六进制编码> 00 00 01 00 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00 01

其他查询DOH的地方

您可以在以下几个地方找到一些支持DNS over HTTPS的公共DNS解析器列表:

以上资源中,我认为Curl的wiki上的列表和DNSCrypt列表可能是最完整和最经常更新的。Curl的页面还包括一系列用于DoH的开源工具(服务器、代理、客户端库等)列表。


1
所有这些服务(例如-https://cloudflare-dns.com/dns-query?)是否受浏览器内的同源策略阻止?或者在浏览器中运行的JS代码可以随意访问这些DoH服务吗? - jschultz410
是的,它们都受到CORS的限制,因此您的使用体验可能会因所连接的服务器而异。如果需要,您可以使用允许任何内容的CORS代理。 - kimbo

40

在 JavaScript 标准库中没有主机或 IP 地址的概念。因此,您需要访问某些外部服务来查找主机名。

我建议托管一个 CGI-BIN 程序,该程序可以查找主机名的 IP 地址,并通过 JavaScript 访问它。


29
cgi-bin?那可太老派了。我喜欢它! - Andrew Hedges
11
这是写作时的情况(2008年)是正确的。六年后,情况已经发生了改变:请参见我在同一页中关于WebRTC的评论。(不幸的是,谷歌搜索解决IP地址问题时仍会指向此帖子,这可能会让人们走上错误的方向。) - earizon
3
@earizon - 你的回答是针对不同的问题 - 如何发现自己的私有IP地址。 - Gene Vayngrib
通过云端的CGI,问题在于发现内部网络主机IP是不可能从外部进行的。你需要在本地机器或内部网络上使用本地服务。 - Tzahi Fadida
1
有一个新的互联网标准提议,允许您通过HTTPS发送DNS查询(请参见此答案https://dev59.com/6HVD5IYBdhLWcg3wDXJ3#58299823)。实际上,它基本上与cgi-bin脚本的工作方式相同 :)(除了它已经被IETF标准化,并且有很多大公司支持它) - kimbo

32

虽然已经很晚了,但我猜很多人仍然通过“Google Airlines”来到这里。一种现代方法是使用WebRTC,它不需要服务器支持。

https://hacking.ventures/local-ip-discovery-with-html5-webrtc-security-and-privacy-risk/

下面的代码是从http://net.ipcalf.com/复制&粘贴的。

// NOTE: window.RTCPeerConnection is "not a constructor" in FF22/23
var RTCPeerConnection = /*window.RTCPeerConnection ||*/ window.webkitRTCPeerConnection || window.mozRTCPeerConnection;

if (RTCPeerConnection) (function () {
    var rtc = new RTCPeerConnection({iceServers:[]});
    if (window.mozRTCPeerConnection) {      // FF needs a channel/stream to proceed
        rtc.createDataChannel('', {reliable:false});
    };  

    rtc.onicecandidate = function (evt) {
        if (evt.candidate) grepSDP(evt.candidate.candidate);
    };  
    rtc.createOffer(function (offerDesc) {
        grepSDP(offerDesc.sdp);
        rtc.setLocalDescription(offerDesc);
    }, function (e) { console.warn("offer failed", e); }); 


    var addrs = Object.create(null);
    addrs["0.0.0.0"] = false;
    function updateDisplay(newAddr) {
        if (newAddr in addrs) return;
        else addrs[newAddr] = true;
        var displayAddrs = Object.keys(addrs).filter(function (k) { return addrs[k]; }); 
        document.getElementById('list').textContent = displayAddrs.join(" or perhaps ") || "n/a";
    }   

    function grepSDP(sdp) {
        var hosts = []; 
        sdp.split('\r\n').forEach(function (line) { // c.f. http://tools.ietf.org/html/rfc4566#page-39
            if (~line.indexOf("a=candidate")) {     // http://tools.ietf.org/html/rfc4566#section-5.13
                var parts = line.split(' '),        // http://tools.ietf.org/html/rfc5245#section-15.1
                    addr = parts[4],
                    type = parts[7];
                if (type === 'host') updateDisplay(addr);
            } else if (~line.indexOf("c=")) {       // http://tools.ietf.org/html/rfc4566#section-5.7
                var parts = line.split(' '), 
                    addr = parts[2];
                updateDisplay(addr);
            }   
        }); 
    }   
})(); else {
    document.getElementById('list').innerHTML = "<code>ifconfig | grep inet | grep -v inet6 | cut -d\" \" -f2 | tail -n1</code>";
    document.getElementById('list').nextSibling.textContent = "In Chrome and Firefox your IP should display automatically, by the power of WebRTCskull.";
}   

22
的确,这是一个之前不存在于WebRTC中的新功能 - 可以发现你自己的IP地址。但@noahjacobson问了一个不同的问题 - 从JavaScript中通过主机名进行DNS查找IP。 - Gene Vayngrib
2
非常有趣,这是一个漏洞或设计缺陷,无论如何,在某个时候它都会被纠正,因此不适合长期项目。 - e-info128

12

托管的JSONP版本非常好用,但它似乎在晚上(东部时间)大多数时候超出了其资源限制,因此我不得不创建自己的版本。

以下是我使用PHP实现的方法:

<?php
header('content-type: application/json; charset=utf-8');

$data = json_encode($_SERVER['REMOTE_ADDR']);
echo $_GET['callback'] . '(' . $data . ');';
?>

然后JavaScript代码与以前完全相同,只是不再是数组:

<script type="application/javascript">
function getip(ip){
    alert('IP Address: ' + ip);
}
</script>

<script type="application/javascript" src="http://www.anotherdomain.com/file.php?callback=getip"> </script>

就是这么简单!

顺便提一下:如果您在任何公共环境中使用此功能,请务必清理您的 $_GET!


谢谢tcole!正是我想要的 :) - jClark
1
等一下,为什么要使用 $_GET?就像你说的这是一个漏洞。难道不能只使用:echo 'getip(' . $data . ');';吗? - deweydb
如果您控制客户端和服务器,则可以这样做。如果不是,那么tcole是使用可配置函数名称设置JSONP回调的适当方法(这非常标准)。 - James M. Greene
10
很抱歉,我必须给它投反对票,因为我认为它实际上并没有回答原问题。他们只是想要一个标准的DNS查找,而不是用户的公共IP地址。 - Simon East
2
@SimonEast 呵呵。你修改了一个7年前的问题。做任何你需要满足自己的事情吧;-) - tcole
3
我只是编辑了原问题,以使其更清晰。最近我也在研究同一个问题,但由于某种原因,这里的大多数答案实际上并不是原发帖人所问的内容,应该发布在另一个问题下。 - Simon East

4

有一个第三方服务,提供了一个适用于CORS的REST API,可以从浏览器中执行DNS查找 - https://exana.io/tools/dns/


1
这是否受到浏览器的同源策略阻止?或者CORS是否允许任何浏览器内的JS代码通过? - jschultz410

2

我知道这是一个老问题,但我的解决方案可能有助于其他人。

我发现使这个过程变得容易的 JSON(P) 服务并不会永远持续下去,但以下 JavaScript 在我写作时效果很好。

<script type="text/javascript">function z (x){ document.getElementById('y').innerHTML=x.query }</script>
<script type='text/javascript' src='http://ip-api.com/json/zero.eu.org?callback=z'></script>

以上代码会在页面上写下服务器的IP地址,但是通过修改“zero.eu.org”为其他域名可以查找任何IP地址。

你可以在我的网页http://meon.zero.eu.org/上看到这个功能。


我不明白如何使用此代码找到自己的IP地址:<!--#echo var="REMOTE_ADDR"-->,根据您的网站。 - George Carlin
这是大多数Web服务器上可用的标准“echo”功能。请参见:https://www.google.co.uk/search?q='%3C!--%23echo+var%3D%22REMOTE_ADDR%22--%3E'&newwindow=1&num=100 - Neville Hillyer
1
这可能是唯一一个正确解决原问题的答案,做得好。不幸的是,它没有遵循“从客户端电脑看到”的部分,这可能(或可能不)是一个重要的要求。 - Simon East
@NevilleHillyer - 你的回答忽略了一个特定方面的原因,那就是浏览器要求 ip-api.com 执行 DNS 查找并通过 JSONP 返回结果。虽然这在许多情况下可能运行良好,但如果您正在解析的主机名使用地理 DNS 解析,则世界各地的不同位置可能会看到从查找返回的不同 IP 地址。因此,客户端看到的 IP 可能与 ip-api.com 看到的 IP 不同。将该注释添加到您的答案中可能值得一试。 - Simon East
1
@Simon - 很好的观点,但由于通常客户端JS是由服务器提供的,因此作者/服务器所有者很可能已经意识到这个DNS限制 - 这可能会成为使用第三方服务器的作者的问题。正如在这里的帖子中所指出的,现有技术难以满足这一问题的所有约束条件。我的输入旨在传达我在我的服务器上找到的最实用的解决方案。 - Neville Hillyer
显示剩余2条评论

2
有一个名为DNS-JS.com的javascript库可以实现此功能,它可以将域名解析成IP地址。最初的回答。
DNS.Query("dns-js.com",
    DNS.QueryType.A,
    function(data) {
        console.log(data);
});

2
仍然不是从客户端的角度。该库向 https://www.dns-js.com/api.aspx 发出请求以获取 IP 地址,然后在服务器端解析 DNS。 - hostingutilities.com

1

许多人说你需要使用外部服务并调用它。这只会从服务器角度获取DNS解析结果。

如果这已经足够,并且您只需要DNS解析,您可以使用以下Docker容器:

https://github.com/kuralabs/docker-webaiodns

终端点:

[GET] /ipv6/[domain]: 对指定的域名进行 DNS 解析并返回关联的 IPv6 地址。

 {
     "addresses": [
         "2a01:91ff::f03c:7e01:51bd:fe1f"
     ]
 }

[GET] /ipv4/[domain]: 执行给定域名的DNS解析并返回关联的IPv4地址。

 {
     "addresses": [
         "139.180.232.162"
     ]
 }

我的建议是,您应该设置您的Web服务器以反向代理到容器上的特定端点,在您的服务器上提供您的Javascript,并使用您的标准Javascript Ajax函数进行调用。

0

由于某种原因,在Firefox 64 beta中不存在 browser,所以我想知道它是否被删除了。 - Kevin Ghadyani
5
仅适用于WebExtensions。另外请注意,它需要“dns”权限,并且脚本不应作为内容脚本运行(因为在那里将无法公开使用browser.dns)。 - Saturnus
@Saturnus,这对于Firefox扩展程序很好用。有没有办法以某种方式对Chrome扩展程序进行相同的操作? - drk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接