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。
请留意各图中的url地址,实际上hursing.com
不存在的。Please look at the url and know that the hursing.com
does not exist actually.
按照图示,找到原来的DNS服务器地址,并改成127.0.0.1
。Follow the steps, find the original dns server and change it to 127.0.0.1
.
修改前 before change
修改后 after change:
查看原来的dns服务器地址 view the original dns server:
修改前 before change:
修改后 after change:
打开 openindex.js
:
对上面的代码做修改 modify the code
fallbackServer
改成原来的DNS服务器地址 change fallbackServer
to your original dns server addressdomain
改成你想解析的域名关键字 change domain
to the keyword of your expected domain nametargetIp
改成你想解析到的IP地址 change targetIp
to the resolved IP然后运行 then run node index.js
。mac上需要 on mac you have to run sudo node index.js
。
DNS protocol: https://tools.ietf.org/html/rfc1035
具体请看源码: https://github.com/hursing/dns-server,也就100行代码而已。
以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)