基于 Netty DNS Codec编写DNS代理

356 阅读1分钟

Netty提供了丰富的编解码器方便了网络应用开发,我们可以基于Netty提供的DNS编解码器来做内网的DNS解析同时也能代理公网的域名解析,我们将来不再需要本地HOST文件来维护内网的域名解析处理。

1、DNS代理实现处理流程

我们通过对本地DNS(A记录)请求的拦截直接返回DNS响应结果,如果未在本地DNS做解析处理那么将请求转发给公网DNS服务做解析,在做DNS代理时我们处理DNS请求时需要将本地发起的DNS RequestId设置到DNS请求中,这样操作相当于实现了从本地发起DNS解析的请求。

sequenceDiagram
DNS解析请求->>DNSServer: DNS解析请求(A记录)
DNSServer ->> DNSServer : 本地解析记录存在返回
DNSServer->>DNS解析请求: 本地DNS(A记录响应)
DNSServer ->> DNSRemoteServer : 公网DNS(A记录)请求
DNSRemoteServer ->> DNSRemoteServer : 公网DNS请求响应
DNSRemoteServer ->> DNSServer : 公网DNS请求响应
DNSServer ->> DNS解析请求 : DNS响应

DNS请求判断处理

@Override  
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery query) throws Exception {  
 this.dnsProxy.setClientSender(query.sender());  
 this.dnsProxy.setClientRecipient(query.recipient());  
 DefaultDnsQuestion dnsQuestion = query.recordAt(DnsSection.QUESTION);  
 log.info("查询的域名:{},ID:{}",dnsQuestion.name(),query.id());  
 ILocalDnsService localDnsService=SpringUtil.getBean(ILocalDnsService.class);  
byte[] dnsRecord=localDnsService.dnsResolver(dnsQuestion.name(),query.recipient().getAddress().getHostAddress());  
if(dnsRecord[0]!=0){  
 DatagramDnsResponse response = new DatagramDnsResponse(query.recipient(),  query.sender(), query.id());  
response.setId(query.id());  
 response.addRecord(DnsSection.QUESTION, dnsQuestion);  
 ByteBuf buf=ctx.channel().alloc().buffer().writeBytes(dnsRecord);  
 DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(dnsQuestion.name(), DnsRecordType.A, 10, buf);  
 response.addRecord(DnsSection.ANSWER, queryAnswer);  
 ctx.writeAndFlush(response);  
}else{  
 this.dnsProxy.sendDnsQuery(query);  
}  
}

DNS转发RemoteServer处理

处理过程也相当简单只是将DatagramDnsResponse重新封装成客户端返回的DatagramDnsResponse即可。

protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramDnsResponse response) throws Exception{  
if(this.clientChannel!=null){  
DatagramDnsResponse newResponse = new DatagramDnsResponse(dnsProxy.getClientRecipient(),dnsProxy.getClientSender(), response.id());  
  
for(int i=0;i<response.count(DnsSection.QUESTION);i++){  
try{  
DnsRecord dnsRecord=response.recordAt(DnsSection.QUESTION,i);  
newResponse.addRecord(DnsSection.QUESTION,dnsRecord);  
}catch (Exception e){  
break;  
}  
}  
for(int i=0;i<response.count(DnsSection.ANSWER);i++){  
try{  
DnsRecord dnsRecord=response.recordAt(DnsSection.ANSWER,i);  
newResponse.addRecord(DnsSection.ANSWER,dnsRecord);  
}catch (Exception e){  
break;  
}  
}  
for(int i=0;i<response.count(DnsSection.AUTHORITY);i++){  
try{  
DnsRecord dnsRecord=response.recordAt(DnsSection.AUTHORITY,i);  
newResponse.addRecord(DnsSection.AUTHORITY,dnsRecord);  
}catch (Exception e){  
break;  
}  
}  
this.clientChannel.writeAndFlush(newResponse);  
}  
}

DNS代理本地测试方式

测试本地域名解析

nslookup -query=a www.test123.com DNS服务IP

测试远程域名解析

nslookup -query=a www.baidu.com DNS服务IP