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.
使用方法 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服务器地址 changefallbackServer
to your original dns server address - 把
domain
改成你想解析的域名关键字 changedomain
to the keyword of your expected domain name - 把
targetIp
改成你想解析到的IP地址 changetargetIp
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)