admin管理员组

文章数量:1530281

一、说明

        我们经常在网上工作,对于互联网上的DNS真的需要了解一些,本文对一些DNS给出一些解释,但是更多的知识还远没有说清楚,期望有兴趣的读者查阅更多专业文章,本篇只是抛砖引玉罢了。

二、什么是 DNS?

        域名系统 (DNS) 是互联网的电话簿。人们通过域名在线访问信息,例如 nytimes 或 espn。Web 浏览器通过Internet 协议 (IP)地址进行交互。DNS 将域名转换为IP 地址,以便浏览器可以加载 Internet 资源。

        连接到互联网的每台设备都有一个唯一的 IP 地址,其他机器使用该地址来查找该设备。DNS 服务器消除了人类记忆 IP 地址(例如 192.168.1.1(在 IPv4 中))或更复杂的较新字母数字 IP 地址(例如 2400:cb00:2048:1::c629:d7a2(在 IPv6 中))的需要。

三、DNS 是如何工作的?

        DNS 解析过程涉及将主机名(例如 www.example)转换为计算机友好的 IP 地址(例如 192.168.1.1)。互联网上的每个设备都有一个 IP 地址,并且该地址对于查找适当的互联网设备是必要的 - 就像街道地址用于查找特定的家庭一样。当用户想要加载网页时,必须在用户在 Web 浏览器 (example) 中键入的内容与查找 example 网页所需的机器友好地址之间进行转换。

        为了了解 DNS 解析背后的过程,了解 DNS 查询必须在不同的硬件组件之间传递非常重要。对于 Web 浏览器,DNS 查找发生在“幕后”,除了初始请求之外,不需要与用户计算机进行任何交互。

四、加载网页涉及 4 个 DNS 服务器:

  • DNS 递归器- 递归器可以被视为图书管理员,他被要求去图书馆的某个地方查找特定的书籍。DNS 递归器是一种服务器,旨在通过 Web 浏览器等应用程序接收来自客户端计算机的查询。通常,递归程序负责发出额外的请求,以满足客户端的 DNS 查询。
  • 根名称服务器-根服务器是将人类可读的主机名转换(解析)为 IP 地址的第一步。它可以被认为是图书馆中的索引,指向不同书架 - 通常它用作其他更具体位置的参考。
  • TLD 名称服务器- 顶级域服务器 ( TLD ) 可以被视为图书馆中的特定书架。该名称服务器是搜索特定 IP 地址的下一步,它托管主机名的最后部分(在 example 中,TLD 服务器是“com”)。
  • 权威名称服务器- 这个最终名称服务器可以被认为是书架上的一本字典,其中特定名称可以翻译为其定义。权威域名服务器是域名服务器查询的最后一站。如果权威名称服务器有权访问所请求的记录,它会将所请求主机名的 IP 地址返回给发出初始请求的 DNS 递归者(图书馆员)。

 

五、权威DNS服务器

        简单来说,权威DNS服务器就是实际保存并负责DNS资源记录的服务器。这是 DNS 查找链底部的服务器,它将响应查询的资源记录,最终允许发出请求的 Web 浏览器到达访问网站或其他 Web 资源所需的 IP 地址。权威域名服务器可以满足来自其自身数据的查询,而无需查询其他来源,因为它是某些 DNS 记录的最终真实来源。

        值得一提的是,在查询子域名(例如 foo.example 或blog.cloudflare )的情况下,将在权威域名服务器之后的序列中添加一个额外的域名服务器,该域名服务器负责存储子域名的CNAME 记录。

        许多 DNS 服务与 Cloudflare 提供的服务之间存在一个关键区别。不同的 DNS 递归解析器(例如 Google DNS、OpenDNS)以及 Comcast 等提供商都维护 DNS 递归解析器的数据中心安装。这些解析器允许通过 DNS 优化的计算机系统的优化集群进行快速、轻松的查询,但它们与 Cloudflare 托管的域名服务器有根本的不同。

        Cloudflare 维护基础设施级名称服务器,这些服务器是互联网功能不可或缺的一部分。一个重要的例子是f-root 服务器网络,Cloudflare 部分负责托管。F 根是根级 DNS 名称服务器基础设施组件之一,每天负责数十亿个互联网请求。我们的Anycast 网络使我们处于独特的地位,可以在不中断服务的情况下处理大量 DNS 流量。

六、DNS 查找的步骤是什么?

在大多数情况下,DNS 涉及将域名转换为适当的 IP 地址。要了解此过程的工作原理,有助于跟踪 DNS 查找从 Web 浏览器传输到 DNS 查找过程并再次返回的路径。我们来看看步骤吧。

注意:DNS 查找信息通常会缓存在查询计算机本地或远程 DNS 基础设施中。DNS 查找通常有 8 个步骤。缓存 DNS 信息后,会跳过 DNS 查找过程中的步骤,从而加快速度。下面的示例概述了未缓存任何内容时的所有 8 个步骤。

DNS 查找的 8 个步骤:

一旦 DNS 查找的 8 个步骤返回了 example 的 IP 地址,浏览器就可以发出对该网页的请求:

  1. 用户在 Web 浏览器中输入“example”,查询就会传输到 Internet 并由 DNS 递归解析器接收。
  2. 然后解析器查询 DNS 根名称服务器 (.)。
  3. 然后,根服务器使用顶级域 (TLD) DNS 服务器(例如 或 )的地址响应解析器,该服务器存储其域的信息。搜索 example 时,我们的请求指向 TLD。
  4. 然后解析程序向 TLD 发出请求。
  5. 然后,TLD 服务器使用域名称服务器的 IP 地址 example 进行响应。
  6. 最后,递归解析器向域的名称服务器发送查询。
  7. 然后 example 的 IP 地址从名称服务器返回到解析器。
  8. 然后,DNS 解析器使用最初请求的域的 IP 地址响应 Web 浏览器。
  9. 浏览器向 IP 地址发出HTTP请求。
  10. 该 IP 上的服务器返回要在浏览器中呈现的网页(步骤 10)。

七、什么是 DNS 解析器?

DNS解析器是DNS查找的第一站,它负责处理发出初始请求的客户端。解析器启动一系列查询,最终将 URL 转换为必要的 IP 地址。

注意:典型的未缓存 DNS 查找将涉及递归查询和迭代查询。

区分递归 DNS查询和递归 DNS 解析器非常重要。查询是指向 DNS 解析器发出的需要解析查询的请求。DNS 递归解析器是接受递归查询并通过发出必要的请求来处理响应的计算机。

八、DNS 查询有哪些类型?

        在典型的 DNS 查找中,会发生三种类型的查询。通过使用这些查询的组合,DNS 解析的优化过程可以减少行驶距离。在理想情况下,缓存的记录数据将可用,从而允许 DNS 名称服务器返回非递归查询。

3 种类型的 DNS 查询:
  1. 递归查询- 在递归查询中,DNS 客户端要求 DNS 服务器(通常是 DNS 递归解析器)使用所请求的资源记录或错误消息(如果解析器找不到该记录)来响应客户端。
  2. 迭代查询- 在这种情况下,DNS 客户端将允许 DNS 服务器返回其所能返回的最佳答案。如果查询的 DNS 服务器没有与查询名称匹配的内容,它将返回对较低级别的域命名空间具有权威性的 DNS 服务器的引用。然后 DNS 客户端将查询引用地址。此过程将继续使用查询链中的其他 DNS 服务器,直到发生错误或超时。
  3. 非递归查询- 通常,当 DNS 解析器客户端向 DNS 服务器查询它有权访问的记录时,就会发生这种情况,因为它对该记录具有权威性,或者该记录存在于其缓存内。通常,DNS 服务器会缓存 DNS 记录,以防止额外的带宽消耗和上游服务器的负载。

九、什么是 DNS 缓存?DNS 缓存发生在哪里?

        缓存的目的是将数据临时存储在某个位置,从而提高数据请求的性能和可靠性。DNS 缓存涉及将数据存储在更靠近请求客户端的位置,以便可以更早地解析 DNS 查询,并避免 DNS 查找链下游的其他查询,从而缩短加载时间并减少带宽/CPU 消耗。DNS 数据可以缓存在多个位置,每个位置都将在由生存时间 ( TTL)确定的设定时间内存储 DNS 记录。

浏览器 DNS 缓存

现代 Web 浏览器默认设计为将 DNS 记录缓存一段时间。这里的目的很明显;DNS 缓存离 Web 浏览器越近,检查缓存并向 IP 地址发出正确请求所需的处理步骤就越少。当请求 DNS 记录时,浏览器缓存是检查所请求记录的第一个位置。

在 Chrome 中,您可以通过转至 chrome://net-internals/#dns 查看 DNS 缓存的状态。

操作系统 (OS) 级 DNS 缓存

操作系统级 DNS 解析器是 DNS 查询离开您的计算机之前的第二个也是最后一个本地停止点。操作系统内用于处理此查询的进程通常称为“存根解析器”或 DNS 客户端。当存根解析器从应用程序获取请求时,它首先检查自己的缓存以查看是否有该记录。如果没有,它就会在本地网络外部向 Internet 服务提供商 (ISP) 内部的 DNS 递归解析器发送 DNS 查询(设置了递归标志)。

当 ISP 内部的递归解析器收到 DNS 查询时,与之前的所有步骤一样,它还会检查请求的主机到 IP 地址的转换是否已存储在其本地持久层内。

递归解析器还具有附加功能,具体取决于其缓存中的记录类型:

  1. 如果解析器没有A 记录,但有权威域名服务器的NS 记录,它将直接查询这些域名服务器,绕过 DNS 查询中的几个步骤。此快捷方式可防止从根名称服务器和 名称服务器(在我们搜索 example 中)进行查找,并有助于更快地解析 DNS 查询。
  2. 如果解析器没有 NS 记录,它将向 TLD 服务器(在我们的例子中为 )发送查询,跳过根服务器。
  3. 万一解析器没有指向 TLD 服务器的记录,它将查询根服务器。此事件通常在 DNS 缓存被清除后发生。

了解Cloudflare DNS与其他 DNS 提供商的区别。

十、DNS常规查询方法

10.1、本地查询

        主机保存有近期的DNS查询记录,这里面主要包含两块内容。一是hosts文件,文件保存在客户机系统盘中,文件路径是Windows/system32/drivers/etc/。另外一个是客户机的高速缓存,可以用ipconfig/displaydns查看。

        如果主机发起DNS查询,首先查询hosts文件,然后在查询DNS缓存。如果hosts文件被恶意程序篡改,那么上网将异常,甚至还会打开不良网页。

        很明显,本地缓存不会有http://qq的DNS记录。因此,主机向本地DNS服务器发起查询。

10.2、直接查询

        本地DNS服务器是192.168.16.1,这是一个家庭路由器,本地DNS缓存里也不会有相应的DNS记录,因为它并不负责解析http://qq。因此,本地DNS服务器必须将查询请求转发至转发器。这个转发器即家庭路由器WAN口内设置的DNS地址,一般会有主备两个。

10.3、迭代查询

        转发器按照域名级别高低,先后查询根服务器、域服务器、http://qq域服务器,最终得到授权应答。这个查询过程即迭代查询。

10.4、递归查询

        转发器将相应的查询结果返回至本地DNS服务器192.168.16.1,本地DNS服务器将查询结果返回至主机,最终得出http://qq的ns记录。

10.5 参考代码

dnslookup.h
#include <QDnsLookup>
#include <QHostAddress>

//! [0]

struct DnsQuery
{
    DnsQuery() : type(QDnsLookup::A) {}

    QDnsLookup::Type type;
    QHostAddress nameServer;
    QString name;
};

//! [0]

class DnsManager : public QObject
{
    Q_OBJECT

public:
    DnsManager();
    void setQuery(const DnsQuery &q) { query = q; }

public slots:
    void execute();
    void showResults();

private:
    QDnsLookup *dns;
    DnsQuery query;
};
dnslookup.cpp

#include "dnslookup.h"

#include <QCoreApplication>
#include <QDnsLookup>
#include <QHostAddress>
#include <QStringList>
#include <QTimer>
#include <QCommandLineParser>
#include <QCommandLineOption>

#include <stdio.h>

static int typeFromParameter(const QString &type)
{
    if (type == "a")
        return QDnsLookup::A;
    if (type == "aaaa")
        return QDnsLookup::AAAA;
    if (type == "any")
        return QDnsLookup::ANY;
    if (type == "cname")
        return QDnsLookup::CNAME;
    if (type == "mx")
        return QDnsLookup::MX;
    if (type == "ns")
        return QDnsLookup::NS;
    if (type == "ptr")
        return QDnsLookup::PTR;
    if (type == "srv")
        return QDnsLookup::SRV;
    if (type == "txt")
        return QDnsLookup::TXT;
    return -1;
}

//! [0]

enum CommandLineParseResult
{
    CommandLineOk,
    CommandLineError,
    CommandLineVersionRequested,
    CommandLineHelpRequested
};

CommandLineParseResult parseCommandLine(QCommandLineParser &parser, DnsQuery *query, QString *errorMessage)
{
    parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
    const QCommandLineOption nameServerOption("n", "The name server to use.", "nameserver");
    parser.addOption(nameServerOption);
    const QCommandLineOption typeOption("t", "The lookup type.", "type");
    parser.addOption(typeOption);
    parser.addPositionalArgument("name", "The name to look up.");
    const QCommandLineOption helpOption = parser.addHelpOption();
    const QCommandLineOption versionOption = parser.addVersionOption();

    if (!parser.parse(QCoreApplication::arguments())) {
        *errorMessage = parser.errorText();
        return CommandLineError;
    }

    if (parser.isSet(versionOption))
        return CommandLineVersionRequested;

    if (parser.isSet(helpOption))
        return CommandLineHelpRequested;

    if (parser.isSet(nameServerOption)) {
        const QString nameserver = parser.value(nameServerOption);
        query->nameServer = QHostAddress(nameserver);
        if (query->nameServer.isNull() || query->nameServer.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
            *errorMessage = "Bad nameserver address: " + nameserver;
            return CommandLineError;
        }
    }

    if (parser.isSet(typeOption)) {
        const QString typeParameter = parser.value(typeOption);
        const int type = typeFromParameter(typeParameter.toLower());
        if (type < 0) {
            *errorMessage = "Bad record type: " + typeParameter;
            return CommandLineError;
        }
        query->type = static_cast<QDnsLookup::Type>(type);
    }

    const QStringList positionalArguments = parser.positionalArguments();
    if (positionalArguments.isEmpty()) {
        *errorMessage = "Argument 'name' missing.";
        return CommandLineError;
    }
    if (positionalArguments.size() > 1) {
        *errorMessage = "Several 'name' arguments specified.";
        return CommandLineError;
    }
    query->name = positionalArguments.first();

    return CommandLineOk;
}

//! [0]

DnsManager::DnsManager()
    : dns(new QDnsLookup(this))
{
    connect(dns, &QDnsLookup::finished, this, &DnsManager::showResults);
}

void DnsManager::execute()
{
    // lookup type
    dns->setType(query.type);
    if (!query.nameServer.isNull())
        dns->setNameserver(query.nameServer);
    dns->setName(query.name);
    dns->lookup();
}

void DnsManager::showResults()
{
    if (dns->error() != QDnsLookup::NoError)
        printf("Error: %i (%s)\n", dns->error(), qPrintable(dns->errorString()));

    // CNAME records
    const QList<QDnsDomainNameRecord> cnameRecords = dns->canonicalNameRecords();
    for (const QDnsDomainNameRecord &record : cnameRecords)
        printf("%s\t%i\tIN\tCNAME\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value()));

    // A and AAAA records
    const QList<QDnsHostAddressRecord> aRecords = dns->hostAddressRecords();
    for (const QDnsHostAddressRecord &record : aRecords) {
        const char *type = (record.value().protocol() == QAbstractSocket::IPv6Protocol) ? "AAAA" : "A";
        printf("%s\t%i\tIN\t%s\t%s\n", qPrintable(record.name()), record.timeToLive(), type, qPrintable(record.value().toString()));
    }

    // MX records
    const QList<QDnsMailExchangeRecord> mxRecords = dns->mailExchangeRecords();
    for (const QDnsMailExchangeRecord &record : mxRecords)
        printf("%s\t%i\tIN\tMX\t%u %s\n", qPrintable(record.name()), record.timeToLive(), record.preference(), qPrintable(record.exchange()));

    // NS records
    const QList<QDnsDomainNameRecord> nsRecords = dns->nameServerRecords();
    for (const QDnsDomainNameRecord &record : nsRecords)
        printf("%s\t%i\tIN\tNS\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value()));

    // PTR records
    const QList<QDnsDomainNameRecord> ptrRecords = dns->pointerRecords();
    for (const QDnsDomainNameRecord &record : ptrRecords)
        printf("%s\t%i\tIN\tPTR\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value()));

    // SRV records
    const QList<QDnsServiceRecord> srvRecords = dns->serviceRecords();
    for (const QDnsServiceRecord &record : srvRecords)
        printf("%s\t%i\tIN\tSRV\t%u %u %u %s\n", qPrintable(record.name()), record.timeToLive(), record.priority(), record.weight(), record.port(), qPrintable(record.target()));

    // TXT records
    const QList<QDnsTextRecord> txtRecords = dns->textRecords();
    for (const QDnsTextRecord &record : txtRecords) {
        QStringList values;
        const QList<QByteArray> dnsRecords = record.values();
        for (const QByteArray &ba : dnsRecords)
            values << "\"" + QString::fromLatin1(ba) + "\"";
        printf("%s\t%i\tIN\tTXT\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(values.join(' ')));
    }

    QCoreApplication::instance()->quit();
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

//! [1]
    QCoreApplication::setApplicationVersion(QT_VERSION_STR);
    QCoreApplication::setApplicationName(QCoreApplication::translate("QDnsLookupExample", "DNS Lookup Example"));
    QCommandLineParser parser;
    parser.setApplicationDescription(QCoreApplication::translate("QDnsLookupExample", "An example demonstrating the class QDnsLookup."));
    DnsQuery query;
    QString errorMessage;
    switch (parseCommandLine(parser, &query, &errorMessage)) {
    case CommandLineOk:
        break;
    case CommandLineError:
        fputs(qPrintable(errorMessage), stderr);
        fputs("\n\n", stderr);
        fputs(qPrintable(parser.helpText()), stderr);
        return 1;
    case CommandLineVersionRequested:
        printf("%s %s\n", qPrintable(QCoreApplication::applicationName()),
               qPrintable(QCoreApplication::applicationVersion()));
        return 0;
    case CommandLineHelpRequested:
        parser.showHelp();
        Q_UNREACHABLE();
    }
//! [1]

    DnsManager manager;
    manager.setQuery(query);
    QTimer::singleShot(0, &manager, SLOT(execute()));

    return app.exec();
}

本文标签: 你对dns