一个ldns的例子

        ldns是一个提供DNS解析的开源库,可以实现各种DNS服务器或者客户端的功能,最近项目中有用到,所以就记录一下,需求是这样的:nginx的upstream server中可以配置域名,nginx的upstream模块会解析出域名对应的IP,然后就跟配置IP的server一样了,但这样配置是死的,当域名的A记录改变时(在用DNS来做负载均衡时)就必须重载nginx配置文件才能生效,这样就得重启nginx的work进程,重启过程中可能丢掉一些连接,比较低效,所以我们就用了ldns来做域名解析,当发现A记录改变的时候,每个worker进程重新生成upstream server就可以了,不用重启进程,也不会丢连接,所以效果还不错。

        DNS记录中每条记录都有TTL(Time To Live),也就是过期时间,我们就可以根据这个TTL来设置定时器,当定时器过期时就做一次域名解析,解析之后就得到域名对应的CNAME和A记录,当A记录发生变化时再更新upstream server就可以了,这样就可以达到我们的目的了。

        先来看看最简单的dig吧:

root@jusse ~# dig www.baidu.com

; <<>> DiG 9.8.1-P1 <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57888
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 5, ADDITIONAL: 0

;; QUESTION SECTION:
;www.baidu.com.                 IN      A

;; ANSWER SECTION:
www.baidu.com.          648     IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       37      IN      A       115.239.210.27
www.a.shifen.com.       37      IN      A       115.239.211.112

;; AUTHORITY SECTION:
a.shifen.com.           934     IN      NS      ns3.a.shifen.com.
a.shifen.com.           934     IN      NS      ns1.a.shifen.com.
a.shifen.com.           934     IN      NS      ns5.a.shifen.com.
a.shifen.com.           934     IN      NS      ns2.a.shifen.com.
a.shifen.com.           934     IN      NS      ns4.a.shifen.com.

;; Query time: 3 msec
;; SERVER: 10.202.72.116#53(10.202.72.116)
;; WHEN: Wed Jan  7 21:37:35 2015
;; MSG SIZE  rcvd: 180

       其实主要看ANSWER SECTION这一段,底下是五个字段,第一个就是域名,第二个是TTL,第三个是协议类型(IN、CH、HS……),第四个是记录类型(A、AAAA、CNAME……),第五个是记录类型对应的结果。

       我们用ldns来实现这个最简单的dig:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <ldns/ldns.h>

static int
usage(FILE *fp, char *prog) {
    fprintf(fp, "%s server nameserver\n", prog);
    fprintf(fp, "  print out some information about server\n");
    return 0;
}

int
main(int argc, char *argv[])
{
    ldns_resolver   *res;
    ldns_rdf        *name;
    ldns_rdf        *nameserver;
    ldns_rdf        *rr_rdf;
    ldns_pkt        *p;
    ldns_rr_list    *info;
    ldns_rr         *rr;
    ldns_status     s;
    size_t          i;
    unsigned int    addr_ip;
    int             cname_ttl = 0;
    int             a_ttl = 0;
    char            str[16];

    if (argc < 2) {
        usage(stdout, argv[0]);
        exit(EXIT_FAILURE);
    } else {
        /* create a rdf from the command line arg */
        name = ldns_dname_new_frm_str(argv[1]);
        if (!name) {
            usage(stdout, argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    if (argc == 2) {
        /* create a new resolver from /etc/resolv.conf */
        s = ldns_resolver_new_frm_file(&res, NULL);
        if (s != LDNS_STATUS_OK) {
            ldns_rdf_deep_free(name);
            exit(EXIT_FAILURE);
        }
    } else {
        res = ldns_resolver_new();
        nameserver = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, argv[2]);//这里可以指定DNS服务器
        if (!nameserver) {
            printf("nameserver is NULL\n");
            exit(EXIT_FAILURE);
        }
        (void)ldns_resolver_push_nameserver(res, nameserver);
    }

    ldns_resolver_set_retry(res, 1); /* don't want to wait too long */

    p = ldns_resolver_query(res, name, LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, LDNS_RD);//做DNS查询
    if (p) {
        //ldns_pkt_print(stdout, p);
        info = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_CNAME, LDNS_SECTION_ANSWER);//获取CNAME

        if (info) {
            ldns_rr_list_print(stdout, info);//打印CNAME记录
            cname_ttl = ldns_rr_ttl(ldns_rr_list_rr(info, 0));//获取TTL方法
            //printf("cname_ttl: %d\n", cname_ttl);
            ldns_rr_list_deep_free(info);
        } else {
            printf(" *** version retrieval failed\n");
        }

        info = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_A, LDNS_SECTION_ANSWER);//获取A记录

        if (info) {
            for(i = 0; i < ldns_rr_list_rr_count(info); i++) {
                ldns_rr_print(stdout, ldns_rr_list_rr(info, i));//A记录可能有多个,所以这里循环获取
                rr = ldns_rr_list_rr(info, i);
                rr_rdf = ldns_rr_rdf(rr, 0);
                a_ttl = ldns_rr_ttl(rr);
                addr_ip = *(unsigned int *)ldns_rdf_data(rr_rdf);//获取DNS原始报文中的A记录,也就是IP

                if (inet_ntop(AF_INET, ldns_rdf_data(rr_rdf), str, sizeof(str))) {
                    //printf("ip: %s\t", str);
                }
                //printf("addr_ip: %u, ttl: %d\n", addr_ip, a_ttl);
            }

            //ldns_rr_list_print(stdout, info);
            ldns_rr_list_deep_free(info);
        } else {
            printf(" *** version retrieval failed\n");
        }

        ldns_pkt_free(p);
    } else {
        printf(" *** query failed\n");
    }

    //ldns_rdf_deep_free(name);//这行去掉,否则跟下一行代码导致重复释放内存而出现coredump
    ldns_resolver_deep_free(res);

    exit(EXIT_SUCCESS);
}

编译:

gcc -Wall -g -o my_dig ./my_dig.c -lldns

运行:

root@jusse ~/develop# ./my_dig www.baidu.com
www.baidu.com.  206     IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       266     IN      A       115.239.210.27
www.a.shifen.com.       266     IN      A       115.239.211.112
root@jusse ~/develop# ./my_dig www.baidu.com 8.8.8.8
www.baidu.com.  219     IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       299     IN      A       115.239.210.27
www.a.shifen.com.       299     IN      A       115.239.211.112

        ldns第一次接触,入门不光要从它的文档入手,而且更应该从它的例子和源码入手,上面这个例子就是在它的例子ldns-chaos上改写的,源码目录中examples还有很多例子,源码也不大,也比较清晰,有兴趣可以看看。


参考:

http://www.nlnetlabs.nl/projects/ldns/


(全文完)


  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值