一、什么是DNS
DNS(Domain Name System)是互联网中的一个命名系统,用于将域名与IP地址相互映射。借助DNS,用户只需要输入易于记忆和理解的域名,即可访问到远程主机。
DNS是一个分布式的数据库系统,同时也是一个应用层协议。可提供两种服务:解析和逆向解析。解析将域名转换成IP地址,逆向解析将IP地址转换成域名。
二、DNS在哪一层
DNS作为一个应用层协议,位于OSI七层模型中的应用层。DNS的数据传输使用的是TCP和UDP协议,TCP常用于大数据传输,UDP常用于小数据传输,例如DNS的查询。
三、DNS的工作原理
DNS的工作可以分为两个阶段:递归查询和缓存查询。
1.递归查询
当用户查询一个域名时,本地域名服务器(Local DNS Server)会先查询本地缓存是否存在该域名对应的IP地址。如果存在,则直接将IP地址返回给用户。如果不存在,则本地域名服务器需要向根域名服务器(Root DNS Server)查询该域名对应的顶级域名服务器(TLD DNS Server)的IP地址。
然后本地域名服务器会向TLD DNS Server发送一个查询请求,询问负责该域名的下一级域名服务器的IP地址。例如,www.example.com的顶级域名是.com,则TLD DNS Server需要返回负责.com域名解析的域名服务器IP地址。
本地域名服务器再向负责.com域名解析的域名服务器发送询问请求,询问www.example.com对应的IP地址。域名服务器查询自己的缓存。如果缓存中有该域名对应的IP地址,则返回,否则继续向上级域名服务器查询,直到返回该域名对应的IP地址。
最后,本地域名服务器将查询结果返回给用户,并将结果缓存,以备下次查询时使用。
2.缓存查询
缓存查询指的是本地域名服务器在递归查询过程中,将查询结果缓存起来,以便在下次查询时能够更快地返回结果。缓存可以设置生存时间,超过生存时间就需要重新从根域名服务器查询。
四、DNS的代码示例
以下是一个使用Node.js实现的简单DNS服务器,它可以响应简单的DNS查询请求。
const dns = require('dns'); const dgram = require('dgram'); const server = dgram.createSocket('udp4'); server.on('error', (err) => { console.log(`server error:\n${err.stack}`); server.close(); }); server.on('message', (msg, rinfo) => { console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`); const domain = msg.slice(12, msg.length - 5); dns.lookup(domain, (err, address) => { if (err) { console.log('lookup error:', err); return; } const response = Buffer.alloc(16); response.writeUInt16BE(msg.readUInt16BE(0), 0); // 事务ID response.writeUInt16BE(0x8180, 2); // 标准响应,无递归和权威答案 response.writeUInt16BE(msg.readUInt16BE(4), 4); // 问题数量 response.writeUInt16BE(1, 6); // 回答数量 response.writeUInt16BE(0, 8); // 权威域名数量 response.writeUInt16BE(0, 10); // 额外信息数量 response.write(msg.slice(12, msg.length)); // 问题部分 response.writeUInt16BE(0xc00c, response.length - 4); // 回答部分域名偏移量 response.writeUInt16BE(1, response.length - 2); // 回答部分类型 response.writeUInt16BE(1, response.length); // 回答部分类 response.writeUInt32BE(0, response.length + 2); // 回答部分缓存时间 response.writeUInt16BE(4, response.length + 6); // 回答部分数据长度 const addressParts = address.split('.'); for (let i = 0; i < addressParts.length; i++) { response.writeUInt8(parseInt(addressParts[i]), response.length + 8 + i); // 回答部分数据 } server.send(response, rinfo.port, rinfo.address); }); }); server.on('listening', () => { const address = server.address(); console.log(`server listening ${address.address}:${address.port}`); }); server.bind(53);