03 基于 java udp 做一个dns服务器 和 一个dns代理服务器

前言

这个也是 来自于一个朋友的需求

最终的目的是实现一个 dns 代理服务器, 当然 这本质也是一个 dns 服务器

并且 dns 代理服务器是依赖于 一个 dns 服务器的, 因此 顺便给一个 dns 服务器的 demo

这里 主要是 基于 udp 的一个 dns 请求, 响应数据的交互

dns 服务器

一个基础的 dns 服务器 demo 如下

为了方便测试, 可以将 端口改为 53, 然后使用 System.out.println 之类的进行调试

复制代码
package com.hx.test15;

import org.xbill.DNS.*;

import java.io.ByteArrayOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;

/**
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2024-07-02 22:33
 */
public class Test25SimpleDnsServer {

    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(8053);
        System.out.println("DNS Server listening on port 8053...");

        while (true) {
            byte[] receiveData = new byte[512];
            DatagramPacket requestPacket = new DatagramPacket(receiveData, receiveData.length);
            socket.receive(requestPacket);

            byte[] data = requestPacket.getData();
            int offset = requestPacket.getOffset();
            int length = requestPacket.getLength();

            Message request = new Message(ByteBuffer.wrap(data, offset, length));
            Message response = handleRequest(request);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DNSOutput out = new DNSOutput();
            baos.write(out.toByteArray());
            baos.write(response.toWire());
            byte[] responseData = baos.toByteArray();

            DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length,
                    requestPacket.getAddress(), requestPacket.getPort());
            socket.send(responsePacket);
        }
    }

    private static Message handleRequest(Message request) throws Exception {
        Message response = new Message(request.getHeader().getID());
        response.getHeader().setFlag(Flags.QR);
        response.getHeader().setOpcode(Opcode.QUERY);

        Record question = request.getQuestion();
        if (question.getName().toString().startsWith("www.abc.com") && question.getType() == Type.A) {
            ARecord answer = new ARecord(question.getName(), DClass.IN, 300, InetAddress.getByName("172.0.0.2"));
            response.addRecord(answer, Section.ANSWER);
        } else {
            // apply default
        }

        return response;
    }


}

最终实现效果如下, dns 服务器中仅仅配置了 "www.abc.com" -> "172.0.0.2"

因此 www.abc.com 能够拿到查询结果, www.baidu.com 拿不到查询结果

dns 代理服务器

主需求是实现一个 dns 的代理服务器

实现如下, 无非就是 代理发送一次 dns 的请求 到目标 dns 服务器, 拿到响应之后 响应给客户端

复制代码
package com.hx.test15;

import org.xbill.DNS.Message;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;

/**
 * Test25JavaDnsForwardServer
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2024-07-02 22:10
 */
public class Test25JavaDnsForwardServer {

    public static void main(String[] args) throws IOException {
        DatagramSocket serverSocket = new DatagramSocket(53);
        System.out.println("DNS Proxy Server listening on port 53...");

        while (true) {
            byte[] receiveData = new byte[512];
            DatagramPacket requestPacket = new DatagramPacket(receiveData, receiveData.length);
            serverSocket.receive(requestPacket);

            Message request = readMessage(requestPacket);
            Message response = proxyRequest(request);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write(response.toWire());
            byte[] responseData = baos.toByteArray();

            DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length,
                    requestPacket.getAddress(), requestPacket.getPort());
            serverSocket.send(responsePacket);
        }
    }

    private static Message readMessage(DatagramPacket packet) throws IOException {
        byte[] data = packet.getData();
        int offset = packet.getOffset();
        int length = packet.getLength();
        return new Message(ByteBuffer.wrap(data, offset, length));
    }

    private static Message proxyRequest(Message request) throws IOException {
        DatagramSocket upstreamSocket = new DatagramSocket();
        InetAddress upstreamAddress = InetAddress.getByName("127.0.0.1");
        int upstreamPort = 8053;

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(request.toWire());
        byte[] requestData = baos.toByteArray();
        DatagramPacket upstreamRequest = new DatagramPacket(requestData, requestData.length, upstreamAddress, upstreamPort);
        upstreamSocket.send(upstreamRequest);

        byte[] upstreamResponseData = new byte[512];
        DatagramPacket upstreamResponse = new DatagramPacket(upstreamResponseData, upstreamResponseData.length);
        upstreamSocket.receive(upstreamResponse);

        Message response = new Message(ByteBuffer.wrap(upstreamResponseData, upstreamResponse.getOffset(), upstreamResponse.getLength()));
        upstreamSocket.close();
        return response;
    }


}

最终实现效果如下, dns 服务器中仅仅配置了 "www.abc.com" -> "172.0.0.2"

因此 www.abc.com 能够拿到查询结果, www.baidu.com 拿不到查询结果

如果 关闭 目标dns服务, 代理服务器会阻塞住

完

相关推荐
LK_071 天前
【FastAPI】基础篇 01|开始之前:API、HTTP 与 FastAPI 到底是什么
网络协议·http·fastapi
阿星_1 天前
pydio登陆不了分析问题
网络协议·http·https
Joy T1 天前
Gzip 压缩原理与实践:HTTP 响应压缩、性能收益与工程配置
网络·网络协议·http
Java成神之路-2 天前
TCP Socket 与 UDP Socket
tcp/ip·计算机网络·udp
博客zhu虎康3 天前
实现局域网P2P通信:内网发送消息
网络·网络协议·p2p
xixingzhe24 天前
AI 对话打字机效果:SSE 和 HTTP 的区别与适用场景
网络·网络协议·http
叼烟扛炮4 天前
Linux 学习笔记(五十一):数据链路层与 ARP 协议——局域网寻址与黑客骗局
linux·网络·网络协议·php·tcpdump
沫璃染墨4 天前
从零入门计算机网络系列(十四):TCP报文与字节流本质——从数据封装到报文边界
linux·服务器·网络·网络协议·tcp/ip·tcp
byte轻骑兵4 天前
【LMP】规范精讲[13]: 参数的户口本、公钥的集装箱与出厂的兜底值
人工智能·网络协议·人机交互·lmp·蓝牙链路管理
流浪0014 天前
计算机网络篇17:传输层——TCP 超时重传、三次握手与四次挥手,TIME_WAIT 为什么要等 2MSL
网络协议·tcp/ip·计算机网络·三次握手·四次挥手·超时重传