首页
社区
课程
招聘
6
[原创]Linux自定义DNS server 做域名解析
发表于: 2012-4-5 17:55 11772

[原创]Linux自定义DNS server 做域名解析

2012-4-5 17:55
11772

我是搞网络的,发在这不合适的话麻烦斑竹了。

前段时间,项目遇到了这个需求,以前的代码用的都是gethostbyname等等之类的函数来做dns查询,而这些函数用的都是系统的默认文件里配置的dns服务器,经过调查发现,在Linux侧,主要是用resolver库解决的。这个库提供了一些函数接口,用法甚少,好像在bind库里有用到过。

在这里说下,要实现这么个机能,不一定非得用这个库,比如用host命令,也可以自定义dns server,用法如下:host -cClass –tType hostname server
第二种方法就是用resolver库提供的api,后面会解释怎么用。
第三种方法就是用udns库,Google下。现成的第三方库,多线程安全。
第四种方法就是自己做个dns查询包,发送到dns server的53端口,再收一个包,解析结果。代码量有点大,超出规模估计。

resolver库用的是一个全局的结构体_res,在用的时候,自己填充该结构体力度server list数组,调用相应的 函数就行。

_res结构体的参数决定了该库的行为,如等待时间,权威性,使用不使用tcp等等。根据所需,适量修改。

要注意的是,如果修改了该全局结构体,那么库函数gethostbyname等系列函数的行为也会受到影响。请慎思。

还有就是不是多线程安全。请做好同步处理。

windows下有DNSQUERY函数,很easy。

后来翻了翻glibc,发现_res._u.ext.sock是自定义的socket,如果自己创建的socket,帮到特定的interface,再利用advanced routing,哈哈,游刃有余啊。Linux的网络很是给力。

不多说了,上码。看完这个,自己实现个host命令应该不是问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * dns.c
 *
 *  Created on: Feb 8, 2012
 *      Author: Shaun
 */
 
 
#include "dns.h"
 
extern int errno;    /* general system errors */
 
 
int dnsquery(DNSQUERY *pdnsquery)
{
  union {
    HEADER hdr;       /*DNS query packet header, defined in resolv.h*/
    u_char buf[NS_PACKETSZ];/*default UDP packet size, usually 512 bytes, defined in arpa/nameser.h */
  } query, response;      /*query and response buffers*/
 
  int querylen, responselen;  /*buffer length*/
  int rrnum;          /*resource record number*/
  int packet_id;        /*packet identifier*/
 
  u_char *cp;         /*point to parse DNS packet*/
  ns_msg handle;        /*handle for response packet*/
  ns_rr rr;          /*expanded resource record*/
 
  /*check the params that passed in*/
  if (pdnsquery == NULL)
  {
    SYS_LOG("dnsquery:invalid params\n");
    return -1;
  }
  /*query default domain: "." */
  if (pdnsquery->domain == NULL)
  {
    pdnsquery->domain = ".";
  }
 
  /*
   * first, we use the res_init() to initialize the global _res struct
   * the lib will read the /etc/resolv.conf and fill the _res
   */
  res_init();
 
  /*
   * turn off the RES_DEFNAMES and RES_DNSRCH
   * the RES_RECURSE bit was on, in default
   */
  _res.options &= ~(RES_DNSRCH | RES_DEFNAMES);
 
  /*
   * turn on this bit, tell the resolver do not
   * try tcp If the name server response has the truncation bit set
   */
  _res.options |= RES_IGNTC;
  /*
   * store the server address in _res struct for later use
   if no server was given, use system default
   */
  if (pdnsquery->server != NULL)
  {
    if (inet_pton(AF_INET, pdnsquery->server, &(_res.nsaddr_list[0].sin_addr)) != 1)
    {
      SYS_LOG("inet_aton: %s\n", strerror(errno));
      return -1;
    }
  }
 
  /* only use one server */
  _res.nscount = 1;
 
  /*
   * _res.retrans is the base time-out period in seconds
   * between queries to the name servers
   */
  _res.retrans = 1;
 
  /*
   * number of retry times, default is 4,
   * _res.retrans is 1s,with 2 retries and we have only one address to query,
   * we'll wait at most 2 seconds
   */
  _res.retry = 2;
 
  /*
   * we make a DNS query packet with
   * specified domain
   */
  if ((querylen = res_mkquery(
            ns_o_query, /*Standard query*/
            pdnsquery->domain,    /*the domain to query*/
            ns_c_in,  /*Internet*/
            ns_t_a,    /*Host address*/
            (u_char *)NULL, /*always NULL*/
            0,      /*length of NULL*/
            (u_char *)NULL, /*always NULL*/
            (u_char *)&query, /*query buffer*/
            sizeof(query)  /*size of buffer*/
            )) < 0)
  {
    SYS_LOG("res_mkquery: %s\n", strerror(errno));
    return -1;
  }
 
  /*save the packet id*/
  packet_id = query.hdr.id;
 
  /*send the packet*/
  if ((responselen = res_send(
            (u_char *)&query, /*query buffer*/
            querylen,      /*true length*/
            (u_char *)&response,/*response buffer*/
            sizeof(response))) < 0) /*length*/
  {
    SYS_LOG("res_send: %s\n", strerror(errno));
    return -1;
  }
  /*check whether it is our packet*/
  if (response.hdr.id != packet_id)
  {
    SYS_LOG("not our packet\n");
    return -1;
  }
  /*
   * initialize a handle for this response, the handle will be
   * used later for extract data from the response buffer
   */
  if ((ns_initparse(response.buf, responselen, &handle)) < 0)
  {
    SYS_LOG("ns_initparse: %s\n", strerror(errno));
    return -1;
  }
 
  /*
   if the server response an error, log the error message
   * note: these error was caused by remote server,
   * so perror or h_error do not work
   */
  if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror)
  {
    switch (ns_msg_getflag(handle, ns_f_rcode))
    {
    case ns_r_formerr:
      SYS_LOG("ns_msg_getflag: format error\n");
      break;
    case ns_r_servfail:
      SYS_LOG("ns_msg_getflag: server failer\n");
      break;
    case ns_r_nxdomain:
      SYS_LOG("ns_msg_getflag: Name error.\n");
      break;
    case ns_r_notimpl:
      SYS_LOG("ns_msg_getflag: Unimplemented.\n");
      break;
    case ns_r_refused:
      SYS_LOG("ns_msg_getflag: Operation refused\n");
      break;
    default:
      SYS_LOG("ns_msg_getflag: Unexpected error\n");
      break;
    }
    return -1;
  }
 
  /*
   if the domain has alias, server will return us
   * all information, so iterate all items in handle.sections
   */
  for (rrnum = 0; rrnum <= ns_msg_count(handle, ns_s_an); rrnum++)
  {
    if (ns_parserr(&handle, ns_s_an, rrnum, &rr))
    {
      SYS_LOG("ns_parserr: %s\n", strerror(errno));
      return -1;
    }
    /*
     * we only care about the A name(Host address)
     */
    if (ns_rr_type(rr) == ns_t_a)
    {
      /*
       * now, make cp point to the real ip address
       */
      cp = (u_char *)ns_rr_rdata(rr);
 
      if (inet_ntop(AF_INET, cp, pdnsquery->addr, NS_MAXDNAME) != NULL)
      {
        /* success, */
        return 0;
      }
      else
      {
        SYS_LOG("inet_ntop:%s\n", strerror(errno));
        return -1;
      }
 
    }
 
  }
 
  return -1;
}

[注意]看雪招聘,专注安全领域的专业人才平台!

上传的附件:
收藏
免费 6
支持
分享
赞赏记录
参与人
雪币
留言
时间
伟叔叔
为你点赞~
2024-5-31 04:24
心游尘世外
为你点赞~
2024-5-27 01:00
QinBeast
为你点赞~
2024-4-24 01:00
飘零丶
为你点赞~
2024-3-27 01:10
shinratensei
为你点赞~
2024-1-28 04:56
PLEBFE
为你点赞~
2023-3-7 00:44
最新回复 (1)
雪    币: 1233
活跃值: (907)
能力值: ( LV12,RANK:750 )
在线值:
发帖
回帖
粉丝
2
自己实现DNS好像不难的说
2012-4-5 19:10
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册