纯nodejs简易实现DNS解析服务器

版权声明:转载请注明出处:http://blog.csdn.net/hursing https://blog.csdn.net/hursing/article/details/81236290

dns-server

A DNS server app written by pure Nodejs for develop and test use. Only support IPv4 now.

To resolve specified domain name to specified IP using keyword-matching algorithm. Useful for frontend developer and tester when involved into a website project.

纯Nodejs写的本地DNS服务器,供开发测试使用。

前端做网站时,本地开发通常是用nginx或nodejs做服务器,然后在浏览器中使用IP地址(127.0.0.1或localhost)来打开网站。但有些功能需要有域名(或二级域名)才能工作,那么就可以用这个工具来做本地解析,对符合关键字的域名查询指向指定的IP。

效果图 effect

请留意各图中的url地址,实际上hursing.com不存在的。Please look at the url and know that the hursing.com does not exist actually.

sample

sample

sample

nslookup

使用方法 How to use

按照图示,找到原来的DNS服务器地址,并改成127.0.0.1。Follow the steps, find the original dns server and change it to 127.0.0.1.

Mac

修改前 before change

这里写图片描述

修改后 after change:

这里写图片描述

Windows

查看原来的dns服务器地址 view the original dns server:

这里写图片描述

修改前 before change:

这里写图片描述

修改后 after change:

这里写图片描述

启动dns服务 launch dns server

打开 openindex.js:

这里写图片描述

对上面的代码做修改 modify the code

  • fallbackServer改成原来的DNS服务器地址 change fallbackServer to your original dns server address
  • domain改成你想解析的域名关键字 change domain to the keyword of your expected domain name
  • targetIp改成你想解析到的IP地址 change targetIp to the resolved IP

然后运行 then run node index.js。mac上需要 on mac you have to run sudo node index.js

这里写图片描述

实现原理 implement

DNS protocol: https://tools.ietf.org/html/rfc1035

具体请看源码: https://github.com/hursing/dns-server,也就100行代码而已。

可以做更多 You can do more

  • 加入配置文件,可匹配多个关键字 use configuration file for matching multiple keywords
  • 增加命令行参数,不用在源码修改关键字 add command line args support to avoid modifying keyword in source code

源码

以Github的为最新,这里贴一贴供参考

// https://github.com/hursing/dns-server

// The regular expression keyword in domain name.
const domain = /hursing/
// When keyword matched, resolve to this IP.
const targetIp = '127.0.0.1'
// When keyword not matched, use the fallback dns server to resolve.
const fallbackServer = '10.0.0.1'

const dgram = require('dgram')

const server = dgram.createSocket('udp4')

function copyBuffer(src, offset, dst) {
  for (let i = 0; i < src.length; ++i) {
    dst.writeUInt8(src.readUInt8(i), offset + i)
  }
}

function resolve(msg, rinfo) {
  const queryInfo = msg.slice(12)
  const response = Buffer.alloc(28 + queryInfo.length)
  let offset = 0
  const id = msg.slice(0, 2)
  copyBuffer(id, 0, response)  // Transaction ID
  offset += id.length
  response.writeUInt16BE(0x8180, offset)  // Flags
  offset += 2
  response.writeUInt16BE(1, offset)  // Questions
  offset += 2
  response.writeUInt16BE(1, offset)  // Answer RRs
  offset += 2
  response.writeUInt32BE(0, offset)  // Authority RRs & Additional RRs
  offset += 4
  copyBuffer(queryInfo, offset, response)
  offset += queryInfo.length
  response.writeUInt16BE(0xC00C, offset)  // offset to domain name
  offset += 2
  const typeAndClass = msg.slice(msg.length - 4)
  copyBuffer(typeAndClass, offset, response)
  offset += typeAndClass.length
  response.writeUInt32BE(600, offset)  // TTL, in seconds
  offset += 4
  response.writeUInt16BE(4, offset)  // Length of IP
  offset += 2
  targetIp.split('.').forEach(value => {
    response.writeUInt8(parseInt(value), offset)
    offset += 1
  })
  // console.log(response.toString('hex'))
  server.send(response, rinfo.port, rinfo.address, (err) => {
    if (err) {
      console.log(err)
      server.close()
    }
  })
}

function forward(msg, rinfo) {
  const client = dgram.createSocket('udp4')
  client.on('error', (err) => {
    console.log(`client error:\n${err.stack}`)
    client.close()
  })
  client.on('message', (fbMsg, fbRinfo) => {
    server.send(fbMsg, rinfo.port, rinfo.address, (err) => {
      err && console.log(err)
    })
    client.close()
  })
  client.send(msg, 53, fallbackServer, (err) => {
    if (err) {
      console.log(err)
      client.close()
    }
  })
}

function parseHost(msg) {
  let num = msg.readUInt8(0)
  let offset = 1
  let host = ""
  while (num !== 0) {
    host += msg.slice(offset, offset + num).toString()
    offset += num
    num = msg.readUInt8(offset)
    offset += 1
    if (num !== 0) {
      host += '.'
    }
  }
  return host
}

server.on('message', (msg, rinfo) => {
  // console.log(msg.toString('hex'))
  const host = parseHost(msg.slice(12))
  console.log(`receive query: ${host}`)
  if (domain.test(host)) {
    resolve(msg, rinfo)
  } else {
    forward(msg, rinfo)
  }
})

server.on('error', (err) => {
  console.log(`server error:\n${err.stack}`)
  server.close()
})

server.on('listening', () => {
  const address = server.address()
  console.log(`server listening ${address.address}:${address.port}`)
})

// On linux or Mac, run node with sudo. Because port 53 is lower then 1024.
server.bind(53)

猜你喜欢

转载自blog.csdn.net/hursing/article/details/81236290