在网络世界中,DNS(域名系统)在将人类友好的域名转换为计算机可以理解的IP地址方面发挥着关键作用。然而,设置和管理DNS服务器可能相当复杂,特别是当你需要一个既快速又灵活的解决方案时。这就是quick DNS的用武之地,它是一个轻量级且高效的DNS服务器,能够处理IPv4和IPv6查询,甚至可以在没有将地址绑定到接口的情况下工作。
概述
quick dns旨在提供一个非递归DNS服务器解决方案。它被设计为简单易设置和操作,即使在没有静态IP地址的环境中也能工作。服务器基于本地区域数据库响应DNS查询。
核心特性
- 支持IPv4和IPv6:能够处理IPv4和IPv6查询,使其成为现代网络的多功能选择。
- 非递归操作:它作为非递归服务器运行,意味着它不会将查询转发到其他DNS服务器,而是基于本地区域数据库响应。
- 可定制性:允许通过命令行参数进行定制,使管理员能够根据特定需求调整其行为。
- 效率:它旨在提供快速的DNS解析。
一个快速灵活的IPv4和IPv6非递归DNS服务器(C/C++代码实现)
int dns::build_error(const string &s)
{
err = "qdns::";
err += s;
if (errno) {
err += ": ";
err += strerror(errno);
}
return -1;
}
int dns::init(const map<string, string> &args)
{
if (args.count("laddr"))
io = new (nothrow) socket_provider();
else if (args.count("mon") > 0)
io = new (nothrow) usipp_provider();
if (!io)
return build_error("init: OOM");
if (io->init(args) < 0)
return build_error(string("init:") + io->why());
auto it = args.find("nxdomain");
if (it != args.end())
nxdomain = (strtoul(it->second.c_str(), NULL, 10) != 0);
if (args.count("resend") > 0)
resend = 1;
return 0;
}
int dns::loop()
{
if (!io)
return build_error("loop: no IO provider initialized");
string log = "";
int r = 0;
for (;;) {
client.pkt = "";
client.reply = "";
if (io->recv(client.pkt) < 0) {
cerr<<io->why()<<endl;
continue;
}
src = io->sender();
r = parse_packet(client.pkt, client.reply, log);
if (r == 0) {
// 返回0的回复等于重新发送的pkt
if (io->resend(client.reply) < 0) {
cerr<<src<<": "<<io->why()<<endl;
continue;
}
} else if (r > 0) {
if (io->reply(client.reply) < 0) {
cerr<<src<<": "<<io->why()<<endl;
continue;
}
} // in < 0 case, just log output
cout<<src<<": "<<log<<endl;
}
return 0;
}
int dns::parse_packet(const string &query, string &response, string &log)
{
using net_headers::dnshdr;
using net_headers::dns_type;
log = "invalid query";
response = "";
if (query.size() <= sizeof(dnshdr))
return -1;
const char *ptr = query.c_str(), *end_ptr = ptr + query.size();
dnshdr hdr;
memcpy(&hdr, query.c_str(), sizeof(dnshdr));
ptr += sizeof(dnshdr);
// dst端口53没有查询?
if (hdr.qr != 0 || hdr.opcode != 0)
return -1;
if (hdr.q_count != htons(1))
return -1;
// skip QNAME
auto qptr = ptr;
while (*ptr != 0 && ptr < end_ptr)
++ptr;
++ptr;
if (ptr + 2*sizeof(uint16_t) > end_ptr)
return -1;
uint16_t qtype = 0;
memcpy(&qtype, ptr, sizeof(qtype));
string qname = string(qptr, ptr - qptr);
string question = string(qptr, ptr + 2*sizeof(uint16_t) - qptr);
string fqdn = "";
if (qname2host(qname, fqdn) <= 0)
return -1;
switch (ntohs(qtype)) {
case dns_type::A:
log = "A? ";
break;
case dns_type::AAAA:
log = "AAAA? ";
break;
case dns_type::MX:
log = "MX? ";
break;
case dns_type::CNAME:
log = "CNAME? ";
break;
case dns_type::NS:
log = "NS? ";
break;
case dns_type::PTR:
log = "PTR? ";
break;
case dns_type::SRV:
log = "SRV? ";
break;
case dns_type::TXT:
log = "TXT? ";
break;
default:
char s[32];
snprintf(s, sizeof(s), "%d? ", ntohs(qtype));
log = s;
}
log += fqdn;
log += " -> ";
bool found_domain = 1;
auto it1 = exact_matches.find(make_pair(qname, qtype)), lit = it1;
if (lit == exact_matches.end()) {
string::size_type pos = string::npos, minpos = string::npos;
// 尝试找到最大的子字符串匹配
for (auto it2 = wild_matches.begin(); it2 != wild_matches.end(); ++it2) {
if ((pos = qname.find(it2->first.first