使用Node.js的“native-dns”模块编写一个简单DNS代理服务器

native-dns模块是一个第三方node.js模块,不属于node核心模块,详见:

https://github.com/tjfontaine/node-dns

这个模块不是很火,不过用起来的确不错。下面就用它来做一个代理DNS。

DNSProxy.js

/**
 * @file DNSProxy.js
 *
 * @author YanWen <i@yanwen.email>
 * @class DNSProxy
 * @last_modified 2017-11-11
 *
 * NOTE:
 */

const dns = require('dns');
const dnsNative = require('native-dns');

const DNSProxy = class {
  constructor() {
    this.TTL = 60;
    this.DNS_SERVER = dns.getServers();
  }

  // Private
  __requestAName() {
    /**
     * TODO: Request data from the external DNS servers (Only A record)
     *
     * @param none
     * @return (server handler):object
     */
    return dnsNative.createServer().on('request', (req, res) => {
      const question = dnsNative.Question({
        name: req.question[0].name,
        type: 'A'
      });
      const timeStart = Date.now();

      dnsNative.Request({
        question: question,
        server: {
          address: this.DNS_SERVER[0],
          port: 53,
          type: 'udp'
        },
        timeout: 1000
      }).on('timeout', () => {
        console.log(DNSProxy.__logString(req, () => { return 'timeout in making request' }));

      }).on('message', (err, resMsg) => {
        this.__responseAName(req, res, resMsg);

      }).on('end', () => {
        console.log(DNSProxy.__logString(req, () => { return `finished processing after ${
          Date.now() - timeStart} ms`; }));

      }).send();
    });
  }

  __responseAName(req, res, ANameList) {
    /**
     * TODO: Reply to the client with the requested DNS data
     *
     * @param res:object, aNameList:list (A Name)
     * @return none
     */
    ANameList.answer.forEach((answer) => {
      answer && ((undefined) => {
        // Only for A records
        res.answer.push(dnsNative.A({
          name: req.question[0].name,
          address: answer.address,
          ttl: this.TTL
        }));
      })(undefined);
    });
    res.send();
  }

  static __logString(req, callback) {
    const address = req.address;

    return `Request from ${address.address}:${address.port} / ${
      address.family} / ${address.size} bytes - ${callback()}`;
  }

  // public
  listen(port) {
    this.__requestAName().on('error', (err, buff, req, res) => {
      console.log(err.stack);
    }).serve(port);
  }
}

module.exports = (() => { return DNSProxy })();

代码很清晰,在此就不做解释,直接调用listen方法就可以监听DNS-udp端口了。

下面是一个简单测试。

运行服务器,监听udp 15353端口,执行如下命令:

dig yanwen.blog @127.0.0.1 -p 15353

dig客户端输出:

...

;; QUESTION SECTION:
;yanwen.blog.                   IN      A

;; ANSWER SECTION:
yanwen.blog.            60      IN      A       47.74.144.173

...

服务器端输出:

Request from 127.0.0.1:51315 / IPv4 / 52 bytes - finished processing after 7 ms

不过这个简易服务器只能处理A NAME,当然它可以进行扩展以处理其他DNS记录。

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s