【问题标题】:How can I do DNS lookups in Python, including referring to /etc/hosts?如何在 Python 中进行 DNS 查找,包括引用 /etc/hosts?
【发布时间】:2011-02-17 19:31:26
【问题描述】:

dnspython 可以很好地完成我的 DNS 查找,但它完全忽略了 /etc/hosts 的内容。

是否有一个 python 库调用可以做正确的事情?即首先检查etc/hosts,否则只回退到DNS查找?

【问题讨论】:

  • 我为此创建了一个问题:github.com/rthalley/dnspython/issues/149
  • dnspython 不会实现这个。对于简单的正向查找,使用建议的socket.gethostbyname,对于更复杂的查询,使用 dnspython。

标签: python dns


【解决方案1】:

我不确定您是要自己进行 DNS 查找,还是只需要主机的 ip。如果你想要后者,

/!\ socket.gethostbyname 已弃用,首选socket.getaddrinfo

来自man gethostbyname

gethostbyname*()、gethostbyaddr*()、[...] 函数已过时。应用程序应使用 getaddrinfo(3)、getnameinfo(3),

import socket
print(socket.gethostbyname('localhost')) # result from hosts file
print(socket.gethostbyname('google.com')) # your os sends out a dns query

【讨论】:

  • 有谁知道这个查找缓存在哪个级别?在 Python 中?还是操作系统?还是 DNS 服务器?
  • @Simon Python 和操作系统都没有缓存。这取决于所涉及的任何 DNS 服务器是否缓存。 – 一般而言:DNS 仅由应用程序本身缓存,或由解析链中的解析 DNS 服务器缓存。
  • @Jochen “localhost”是否来自主机文件取决于配置!
  • @RobertSiemer 抱歉迟到的评论:结果可能被本地解析器缓存。 Unix 机器上的 nscdnslcd 可以做到这一点。它也可以由配置为缓存的本地名称服务器缓存(从前的常见设置。现在可能没有那么多)。不幸的是,这不是一个直截了当的“不”回答。这些东西很少。 :)
  • 这只会返回一个地址,不是吗?因此,如果您有一个 dns 循环,这将不会公开与主机名关联的所有地址。
【解决方案2】:

Python 中的正常名称解析工作正常。为什么你需要 DNSpython。只需使用socketgetaddrinfo,它遵循为您的操作系统配置的规则(在Debian 上,它遵循/etc/nsswitch.conf

>>> print(socket.getaddrinfo('google.com', 80))
[(10, 1, 6, '', ('2a00:1450:8006::63', 80, 0, 0)), (10, 2, 17, '', ('2a00:1450:8006::63', 80, 0, 0)), (10, 3, 0, '', ('2a00:1450:8006::63', 80, 0, 0)), (10, 1, 6, '', ('2a00:1450:8006::68', 80, 0, 0)), (10, 2, 17, '', ('2a00:1450:8006::68', 80, 0, 0)), (10, 3, 0, '', ('2a00:1450:8006::68', 80, 0, 0)), (10, 1, 6, '', ('2a00:1450:8006::93', 80, 0, 0)), (10, 2, 17, '', ('2a00:1450:8006::93', 80, 0, 0)), (10, 3, 0, '', ('2a00:1450:8006::93', 80, 0, 0)), (2, 1, 6, '', ('209.85.229.104', 80)), (2, 2, 17, '', ('209.85.229.104', 80)), (2, 3, 0, '', ('209.85.229.104', 80)), (2, 1, 6, '', ('209.85.229.99', 80)), (2, 2, 17, '', ('209.85.229.99', 80)), (2, 3, 0, '', ('209.85.229.99', 80)), (2, 1, 6, '', ('209.85.229.147', 80)), (2, 2, 17, '', ('209.85.229.147', 80)), (2, 3, 0, '', ('209.85.229.147', 80))]

【讨论】:

  • 添加转换步骤会很好。 addrs = [ str(i[4][0]) for i in socket.getaddrinfo(name, 80) ] 给了我 ips 列表。
  • 我的请求结果多次返回同一个ip地址;我们可以通过对 alex 的建议进行简单更改来解决这个问题:addrs = { str(i[4][0]:i for i in socket.getaddrinfo(name, 80) } 返回一个包含唯一 ips 作为键的 dict,其余结果配对。
【解决方案3】:

听起来您不想自己解析 dns(这可能是错误的命名法)dnspython 似乎是一个独立的 dns 客户端,可以理解的是它会忽略您的操作系统,因为它绕过了操作系统的实用程序。

我们可以查看一个名为 getent 的 shell 实用程序来了解(debian 11 类似)操作系统如何为程序解析 dns,这可能是所有使用套接字实现的类 *nix 系统的标准。

查看man getent的“主机”部分,其中提到了getaddrinfo的使用,我们可以将其视为man getaddrinfo

要在 python 中使用它,我们必须从数据结构中提取一些信息

.

import socket

def get_ipv4_by_hostname(hostname):
    # see `man getent` `/ hosts `
    # see `man getaddrinfo`

    return list(
        i        # raw socket structure
            [4]  # internet protocol info
            [0]  # address
        for i in 
        socket.getaddrinfo(
            hostname,
            0  # port, required
        )
        if i[0] is socket.AddressFamily.AF_INET  # ipv4

        # ignore duplicate addresses with other socket types
        and i[1] is socket.SocketKind.SOCK_RAW  
    )

print(get_ipv4_by_hostname('localhost'))
print(get_ipv4_by_hostname('google.com'))

【讨论】:

    【解决方案4】:
    list( map( lambda x: x[4][0], socket.getaddrinfo( \
         'www.example.com.',22,type=socket.SOCK_STREAM)))
    

    为您提供 www.example.com 的地址列表。 (ipv4 和 ipv6)

    【讨论】:

      【解决方案5】:

      此代码适用于返回可能属于特定 URI 的所有 IP 地址。由于许多系统现在处于托管环境(AWS/Akamai/等)中,系统可能会返回多个 IP 地址。 lambda 是从@Peter Silva “借来的”。

      def get_ips_by_dns_lookup(target, port=None):
          '''
              this function takes the passed target and optional port and does a dns
              lookup. it returns the ips that it finds to the caller.
      
              :param target:  the URI that you'd like to get the ip address(es) for
              :type target:   string
              :param port:    which port do you want to do the lookup against?
              :type port:     integer
              :returns ips:   all of the discovered ips for the target
              :rtype ips:     list of strings
      
          '''
          import socket
      
          if not port:
              port = 443
      
          return list(map(lambda x: x[4][0], socket.getaddrinfo('{}.'.format(target),port,type=socket.SOCK_STREAM)))
      
      ips = get_ips_by_dns_lookup(target='google.com')
      

      【讨论】:

      • '{}.'.format(target) 有什么用? example.comexample.com. 似乎都可以正常工作。
      • @MatthewD.Scholefield '{}.'.format(target) 构造可以很容易地替换为{target},但我想保留@Peter Silver 提出的原始解决方案。
      【解决方案6】:

      我发现这种方法可以将扩展为 IP 列表的 DNS RR 主机名扩展为成员主机名列表:

      #!/usr/bin/python
      
      def expand_dnsname(dnsname):
          from socket import getaddrinfo
          from dns import reversename, resolver
          namelist = [ ]
          # expand hostname into dict of ip addresses
          iplist = dict()
          for answer in getaddrinfo(dnsname, 80):
              ipa = str(answer[4][0])
              iplist[ipa] = 0
          # run through the list of IP addresses to get hostnames
          for ipaddr in sorted(iplist):
              rev_name = reversename.from_address(ipaddr)
              # run through all the hostnames returned, ignoring the dnsname
              for answer in resolver.query(rev_name, "PTR"):
                  name = str(answer)
                  if name != dnsname:
                      # add it to the list of answers
                      namelist.append(name)
                      break
          # if no other choice, return the dnsname
          if len(namelist) == 0:
              namelist.append(dnsname)
          # return the sorted namelist
          namelist = sorted(namelist)
          return namelist
      
      namelist = expand_dnsname('google.com.')
      for name in namelist:
          print name
      

      当我运行它时,会列出几个 1e100.net 主机名:

      【讨论】:

        猜你喜欢
        • 2017-03-14
        • 2015-07-10
        • 2021-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多