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

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

2012-4-5 17:55
11528
我是搞网络的,发在这不合适的话麻烦斑竹了。

前段时间,项目遇到了这个需求,以前的代码用的都是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命令应该不是问题。

/*
 * 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;
}



头文件
/*
 * dns.h
 *
 *  Created on: Feb 8, 2012
 *      Author: Shaun
 */

#ifndef VPNFRM_UTILITY_H_
#define VPNFRM_UTILITY_H_

#include <sys/types.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <resolv.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

#define VPNFRM_UTILITY_DEBUG

#define UNIX_PATH_MAX    108
#define VPNFRM_UTILITY_ADDRLEN 1024

typedef struct _dns {
  char *domain;
  char *server;
  char addr[NS_MAXDNAME];
} DNSQUERY, *PDNSQUERY;


int dnsquery(DNSQUERY *dnsquery);


#ifndef VPNFRM_UTILITY_DEBUG
#define SYS_LOG(format, ...)
#else
#define SYS_LOG(format, ...) \
  do { fprintf(stderr, format, ## __VA_ARGS__ );} while (0)
#endif



#endif /* VPNFRM_UTILITY_H_ */




main.c,测试文件
/*
 * main.c
 *
 *  Created on: Feb 8, 2012
 *      Author: Shaun
 */

#include "dns.h"

int main(int argc, char *argv[])
{
  DNSQUERY query;

  if (argc < 2)
  {
    printf("Usage:%s domain [server]\n", argv[0]);
    return -1;
  }
  if (argv[1] != NULL)
  {
    query.domain = argv[1];
  }
  else
  {
    query.domain = NULL;
  }

  if (argv[2] != NULL)
  {
    query.server = argv[2];
  }
  else
  {
    query.server = NULL;
  }

  if (dnsquery(&query) == 0)
    printf("%s has the address:%s\n", query.domain, query.addr);
  else
    printf("vpnfrm_dnsquery error\n");



  return 0;
}


编译:gcc -odns main.c dns.c -lresolv
运行:
[root@localhost dns]# ./dns bbs.pediy.com 
bbs.pediy.com has the address:219.232.241.55
[root@localhost dns]# ./dns bbs.pediy.com 172.28.181.10 //指定dns server
bbs.pediy.com has the address:219.232.241.55

[ATTACH]dns.rar[/ATTACH]

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (1)
雪    币: 1233
活跃值: (907)
能力值: ( LV12,RANK:750 )
在线值:
发帖
回帖
粉丝
2
自己实现DNS好像不难的说
2012-4-5 19:10
0
游客
登录 | 注册 方可回帖
返回
//