本章我们主要介绍一下ngx_resolver.h头文件,其主要是实现对nginx中涉及到的域名的解析。

1. 相关宏定义

#include <ngx_core.h>


#ifndef _NGX_RESOLVER_H_INCLUDED_
#define _NGX_RESOLVER_H_INCLUDED_


//由域名转换成IP地址
#define NGX_RESOLVE_A         1

//查询规范名称
#define NGX_RESOLVE_CNAME     5

//将IP地址转换成域名
#define NGX_RESOLVE_PTR       12
#define NGX_RESOLVE_MX        15
#define NGX_RESOLVE_TXT       16
#if (NGX_HAVE_INET6)
#define NGX_RESOLVE_AAAA      28
#endif

//根据服务名查询目标服务器的域名
#define NGX_RESOLVE_SRV       33
#define NGX_RESOLVE_DNAME     39

#define NGX_RESOLVE_FORMERR   1
#define NGX_RESOLVE_SERVFAIL  2


// 表示并未指定domain域,一般表示不需要进行DNS查询
#define NGX_RESOLVE_NXDOMAIN  3
#define NGX_RESOLVE_NOTIMP    4
#define NGX_RESOLVE_REFUSED   5
#define NGX_RESOLVE_TIMEDOUT  NGX_ETIMEDOUT


#define NGX_NO_RESOLVER       (void *) -1

#define NGX_RESOLVER_MAX_RECURSION    50

2. ngx_resolver_connection_t数据结构

typedef struct {
    ngx_connection_t         *udp;
    ngx_connection_t         *tcp;
    struct sockaddr          *sockaddr;
    socklen_t                 socklen;
    ngx_str_t                 server;
    ngx_log_t                 log;
    ngx_buf_t                *read_buf;
    ngx_buf_t                *write_buf;
    ngx_resolver_t           *resolver;
} ngx_resolver_connection_t;

本数据结构用于表示resolver实例与DNS服务器的一条连接。下面简单介绍一下各字段的含义:

  • udp: 代表resolver与DNS服务器的udp连接

  • tcp: 代表resolver与DNS服务器的tcp连接

  • sockaddr: 存放DNS服务器的IP地址

  • socklen: DNS服务器IP地址的长度

  • server: DNS服务器IP地址的字符串表示形式

  • log: 与当前connection关联的日志对象

  • read_buf: 当前connection的读取缓冲(用于TCP)

  • write_buf: 当前connection的写缓冲(用于TCP)

  • resolver: 当前connection对象所属的resolver

2. ngx_resolver_srv_t数据结构

typedef struct {
    ngx_str_t                 name;
    u_short                   priority;
    u_short                   weight;
    u_short                   port;
} ngx_resolver_srv_t;

在进行NGX_RESOLVE_SRV查询时,返回目标服务器的相关信息。下面简要介绍一下各字段的含义(rfc2782):

  • name: 目标服务器的域名信息

  • priority: 优先级。一般值越低,优先级越高

  • weight: 目标服务器所占用的权重

  • port: 该不服务在目标服务器上所占用的端口

3. ngx_resolver_srv_name_t数据结构

typedef struct {
    ngx_str_t                 name;
    u_short                   priority;
    u_short                   weight;
    u_short                   port;

    ngx_resolver_ctx_t       *ctx;

    ngx_uint_t                naddrs;
    ngx_addr_t               *addrs;
} ngx_resolver_srv_name_t;

本数据结构用于表示某一个域名(name)下所指定的服务的IP地址,用于服务查询。下面我们简要介绍一下各字段的含义:

  • name: 服务对应的域名

  • priority: 服务在name域名下的优先级

  • weight: 权重

  • port: 对应的端口

  • ctx: 所关联的ngx_resolver_ctx_t上下文

  • naddrs: ip地址个数

  • addrs: 所返回的IP地址

4. ngx_resolver_node_t数据结构

typedef struct {
    ngx_rbtree_node_t         node;
    ngx_queue_t               queue;

    /* PTR: resolved name, A: name to resolve */
    u_char                   *name;

#if (NGX_HAVE_INET6)
    /* PTR: IPv6 address to resolve (IPv4 address is in rbtree node key) */
    struct in6_addr           addr6;
#endif

    u_short                   nlen;
    u_short                   qlen;

    u_char                   *query;
#if (NGX_HAVE_INET6)
    u_char                   *query6;
#endif

    union {
        in_addr_t             addr;		//表示单个IP地址
        in_addr_t            *addrs;	//表示IP地址的数组
        u_char               *cname;	//用于表示查询返回的规范名称(canonical name)
        ngx_resolver_srv_t   *srvs;		//用于SRV查询时,返回的目标服务器的相关信息
    } u;

    u_char                    code;
    u_short                   naddrs;
    u_short                   nsrvs;
    u_short                   cnlen;

#if (NGX_HAVE_INET6)
    union {
        struct in6_addr       addr6;
        struct in6_addr      *addrs6;
    } u6;

    u_short                   naddrs6;
#endif

    time_t                    expire;
    time_t                    valid;
    uint32_t                  ttl;

    unsigned                  tcp:1;
#if (NGX_HAVE_INET6)
    unsigned                  tcp6:1;
#endif

    ngx_uint_t                last_connection;

    ngx_resolver_ctx_t       *waiting;
} ngx_resolver_node_t;

下面简要介绍一下各字段的含义:

  • node: 作为红黑树的一个节点存在,将ngx_resolver_node_t数据结构作为红黑树的一个节点来进行管理

  • queue: 作为一个辅助元素,用于实现ngx_resolver_t中的超时队列。

  • name: 当执行的是DNS A类型查询时,本字段保存的是需要解析的域名; 当执行的是DNS PTR类型的查询时,本字段保存的是逆解析后得到的域名

  • nlen: 用于指明name的长度

  • qlen: 用于指明query的长度

  • query: 用于存放当前DNS的查询请求报文

  • query6: 用于存放当前DNS的查询请求报文(适用于IPv6, 当前我们并不支持NGX_HAVE_INET6宏定义)

  • u: 用于保存当前DNS查询到的地址信息(IPv4)。

  • naddrs: 用于指明当前解析到的u.addrs的个数。 -1表示未开始解析,或解析出错。

  • nsrvs: 用于指明当前解析大的u.srvs的个数。 -1表示未开始解析,或解析出错

  • cnlen: 用于指明u.cname的长度

  • u6: 用于保存当前DNS查询到的地址信息(IPv6)

  • naddrs6:当前DNS解析到的IPv6地址信息的个数。 -1表示未开始解析,或解析出错

  • expire: 用于指示当前节点的过期时刻,主要是用于控制超时队列使用,适时的淘汰ngx_resolver_tname_rbtreesrv_rbtreeaddr_rbtree中的节点,对应的超时队列分别是ngx_resolver_t中的name_expire_queue、srv_expire_queue或addr_expire_queue;

  • valid: 用于指示当前节点的有效期时刻。注意: 本字段含义上与expire相似,但expire主要用于控制超时队列。

  • ttl: 用于保存DNS返回的TTL时间

  • last_connection: 用于表示适用resolver连接数组中的哪一个连接(这里采用round-robin算法)

  • waiting: 用于指示当前节点所对应的ngx_resolver_ctx_t类型的等待链表

5. ngx_resolver_t数据结构

struct ngx_resolver_s {
    /* has to be pointer because of "incomplete type" */
    ngx_event_t              *event;
    void                     *dummy;
    ngx_log_t                *log;

    /* event ident must be after 3 pointers as in ngx_connection_t */
    ngx_int_t                 ident;

    /* simple round robin DNS peers balancer */
    ngx_array_t               connections;
    ngx_uint_t                last_connection;

    ngx_rbtree_t              name_rbtree;
    ngx_rbtree_node_t         name_sentinel;

    ngx_rbtree_t              srv_rbtree;
    ngx_rbtree_node_t         srv_sentinel;

    ngx_rbtree_t              addr_rbtree;
    ngx_rbtree_node_t         addr_sentinel;

    ngx_queue_t               name_resend_queue;
    ngx_queue_t               srv_resend_queue;
    ngx_queue_t               addr_resend_queue;

    ngx_queue_t               name_expire_queue;
    ngx_queue_t               srv_expire_queue;
    ngx_queue_t               addr_expire_queue;

#if (NGX_HAVE_INET6)
    ngx_uint_t                ipv6;                 /* unsigned  ipv6:1; */
    ngx_rbtree_t              addr6_rbtree;
    ngx_rbtree_node_t         addr6_sentinel;
    ngx_queue_t               addr6_resend_queue;
    ngx_queue_t               addr6_expire_queue;
#endif

    time_t                    resend_timeout;
    time_t                    tcp_timeout;
    time_t                    expire;
    time_t                    valid;

    ngx_uint_t                log_level;
};

ngx_resolver_t数据结构用于表示nginx中的一个DNS解析器。下面简单介绍一下各字段的含义:

  • event: 与当前resolver关联的event, 一般作为定时事件来执行resend_queue中相应的任务

  • log: 与当前resolver关联的日志对象

  • connections: 是一个ngx_resolver_connection_t类型的数组,数组的每一个元素代表与DNS的一条连接。这里涉及到两个方面,首先一个resolver可能对应多个不同域名(或IP)的DNS, 其次即使是同一个DNS域名,也可能对应多个IP地址, 因此这里需要用数组来保存。

  • last_connection:采用round-robin算法时,用于控制采用连接数组中的哪一个连接

  • name_rbtree: 用于保存从DNS查询到的域名到IP地址的映射的红黑树,

  • name_sentinel: name_rbtree红黑树的叶子终节点

  • srv_rbtree: 用于保存从DNS查询到的服务名到IP地址的映射的红黑树(实际上,当前程序中并未有任何地方使用到此功能)

  • srv_sentinel: srv_rbtree红黑树的叶子终节点

  • addr_rbtree: 用于保存从DNS逆查询得到的IP地址到域名的映射的红黑树。红黑树中的value是ngx_resolver_node_t结构

  • addr_sentinel: addr_rbtree红黑树的叶子终节点

  • name_resend_queue:一般作为name查询服务类型ngx_resolver_node_t的waiting context所关联事件的resend队列

  • srv_resend_queue: 一般作为srv查询服务类型ngx_resolver_node_t的waiting context所关联事件的resend队列

  • addr_resend_queue: 一般作为addr查询服务类型ngx_resolver_node_t的waiting context所关联事件的resend队列

  • name_expire_queue: 用于控制name_rbtree中节点超时的队列

  • srv_expire_queue: 用于控制srv_rbtree中节点超时的队列

  • addr_expire_queue; 用于控制addr_rbtree中节点超时的队列

  • IPv6相关

#if (NGX_HAVE_INET6)
    ngx_uint_t                ipv6;                 /* unsigned  ipv6:1; */
    ngx_rbtree_t              addr6_rbtree;
    ngx_rbtree_node_t         addr6_sentinel;
    ngx_queue_t               addr6_resend_queue;
    ngx_queue_t               addr6_expire_queue;
#endif

在当前,我们暂未支持NGX_HAVE_INET6这一宏定义。这里除了协议版本与ipv4不同外,字段含义与ipv4中对应字段均相同。

  • resend_timeout: 用于指示请求重发的超时时间

  • tcp_timeout: 代表与DNS服务连接的TCP超时时间

  • expire: 用于指示当前resolver缓存DNS解析结果的时间,这样就可以不需要每一次请求都去请求DNS

  • valid: 用于指明resolver的有效事件

注意: 上面介绍的DNS逆地址解析,一般只会在邮件模块用到

6. ngx_resolver_ctx_t数据结构

typedef struct ngx_resolver_ctx_s  ngx_resolver_ctx_t;
struct ngx_resolver_ctx_s {
    ngx_resolver_ctx_t       *next;
    ngx_resolver_t           *resolver;
    ngx_resolver_node_t      *node;

    /* event ident must be after 3 pointers as in ngx_connection_t */
    ngx_int_t                 ident;

    ngx_int_t                 state;
    ngx_str_t                 name;
    ngx_str_t                 service;

    time_t                    valid;
    ngx_uint_t                naddrs;
    ngx_resolver_addr_t      *addrs;
    ngx_resolver_addr_t       addr;
    struct sockaddr_in        sin;

    ngx_uint_t                count;
    ngx_uint_t                nsrvs;
    ngx_resolver_srv_name_t  *srvs;

    ngx_resolver_handler_pt   handler;
    void                     *data;
    ngx_msec_t                timeout;

    ngx_uint_t                quick;  /* unsigned  quick:1; */
    ngx_uint_t                recursion;
    ngx_event_t              *event;
};

本数据结构用于表示resolver对象实例所关联的上下文对象。下面简单介绍一下各字段的含义:

  • next: 用于指示ngx_resolver_ctx_t链表中的下一个上下文对象;

  • resolver: 本上下文对象所关联的resolver(注意: 本身ngx_resolver_t并不直接与ctx关联,而是通过ngx_resolver_t中相应的节点来关联的)

  • node: 本上下文所关联的ngx_resolver_node_t数据结构。

  • state: 用于指示当前上下文对DNS域名解析,或DNS逆解析的状态

  • name: DNS A类型解析时保存的是将要解析的域名; DNS PTR类型解析时,保存的是IP地址逆解析到的域名

  • service: 用于保存要解析的服务名称

  • valid: 当前context的有效期时间

  • addr: 当前需要进行DNS逆查询的IP地址(即IP地址到域名的映射)

  • count: 用于保存当前未处理完的srvs的个数。(srv表示将服务名转换为IP地址,有时可能不能直接转换,会先返回规范化的名称,然后我们需要再根据返回的规范化名称再去查询DNS,以返回IP地址。此处用于指明当前我们未处理的规范化名称个数)

  • nsrvs: 当前ctx所要解析的srvs的个数

  • srvs: 当前ctx所要解析的规范化服务名称(canonical name)

  • handler: 本context对象绑定的回调函数

  • data: 一般用于保存handler()回调函数的参数

  • timeout: 当前context的超时时间,主要用于event事件的超时管理

  • quick: 一般情况下,当我们并不需要调用DNS服务器进行解析时会将本字段设置为1,这时直接调用ngx_resolver_ctx_t的handler回调函数即可。

  • recursion: 因为DNS解析时可能会产生递归,这里用于记录递归的次数。超过了一定的次数之后,一般可以认为出现了问题,此时不应该继续查询DNS让递归一直进行下去了

下面我们给出nginx resolver各数据结构的一个整体图景:

ngx-resolver-pic



[参看]

  1. Nginx的DNS解析过程

  2. nginx关于域名解析的源码分析

  3. nginx resolver

  4. Nginx displayed by LXR