当前位置: 首页 > news >正文

用 Go 手搓一个内网 DNS 服务器:从此告别 IP 地址,用域名畅游家庭网络!

“记不住 192.168.1.103?那就给它起个名字!”

大家好,我是你们的"家庭网络摆渡人"。今天不聊 5G,也不谈元宇宙,咱们干一件特别"复古"但又超实用的事——在内网搭一个自己的 DNS 服务器!

你可能会问:DNS 是啥?能吃吗?

别急,先想象一个场景:

你有一台 NAS(网络存储),IP 是 192.168.1.100
一台 Git 服务器,IP 是 192.168.1.101
还有一台打印机,IP 是 192.168.1.102
每次想访问它们,你都得输入一串数字……是不是有点像在背电话号码?而且一旦 IP 变了,全家设备都得重新配置。

有没有办法像访问 baidu.com 一样,用 nas.local、git.home 这种好记的名字?

答案是:有!而且只需要几十行 Go 代码 + 一个神奇的第三方库!

🧠 DNS 是什么?30 秒科普

DNS(Domain Name System)就是互联网的"电话簿"。

你输入 www.qq.com → DNS 告诉你它对应的 IP 是 111.161.64.48
没有 DNS,你就得记住成千上万个 IP,那画面太美我不敢看 😅
而在内网,我们也可以建一个"小电话簿",只管我们自己家的设备。

🛠 我们用什么工具?

Go 语言 + 一个超好用的开源库:miekg/dns

这个库由 DNS 领域的大神 Miek Gieben 开发,功能强大、文档清晰,连 Kubernetes 的 CoreDNS 都用它!

安装它?一行命令搞定:

go get github.com/miekg/dns

🧪 核心思路:自定义 + 转发

我们的 DNS 服务器要做两件事:

  1. 如果查询的是我"认识"的域名(比如 nas.local),直接返回对应的内网 IP。
  2. 如果是其他域名(比如 baidu.com、github.com),就转发给真正的 DNS(比如你家的路由器 192.168.1.1)去查。

这就叫 "权威 + 递归"混合模式——听起来高大上,其实逻辑超简单!

💻 全部源码奉上(带中文日志!)

package mainimport ("fmt""log""net""github.com/miekg/dns"
)// 上游 DNS 服务器(你的路由器或者公网 DNS 地址)
var upstreamDNS = "192.168.1.1:53"// 自定义解析的域名映射(注意:域名必须以点 . 结尾!)
var customRecords = map[string]string{"a.local.": "192.168.1.100","b.local.": "192.168.1.101","c.git.":   "218.95.11.11",// 你可以继续添加:// "printer.home.": "192.168.1.102",// "camera.lan.":   "192.168.1.103",
}func main() {addr := ":53" // 监听 UDP 53 端口(标准 DNS 端口)server := &dns.Server{Addr: addr, Net: "udp"}dns.HandleFunc(".", handleDNSRequest)fmt.Printf("正在 %s 启动 DNS 服务器,其他请求将转发至 %s...\n", addr, upstreamDNS)if err := server.ListenAndServe(); err != nil {log.Fatalf("启动服务器失败:%v\n", err)}
}func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {if len(r.Question) == 0 {return}q := r.Question[0]// 检查是否命中自定义域名if ipStr, ok := customRecords[q.Name]; ok && q.Qtype == dns.TypeA {fmt.Printf("自定义解析:%s -> %s\n", q.Name, ipStr)m := new(dns.Msg)m.SetReply(r)rr := &dns.A{Hdr: dns.RR_Header{Name:   q.Name,Rrtype: dns.TypeA,Class:  dns.ClassINET,Ttl:    60, // 缓存 60 秒},A: net.ParseIP(ipStr).To4(),}m.Answer = append(m.Answer, rr)if err := w.WriteMsg(m); err != nil {log.Printf("发送自定义响应失败:%v\n", err)}return}// 未命中?转发给上游 DNS!fmt.Printf("正在将 %s 的查询请求转发至 %s\n", q.Name, upstreamDNS)client := new(dns.Client)resp, _, err := client.Exchange(r, upstreamDNS)if err != nil {log.Printf("转发查询请求失败:%v\n", err)m := new(dns.Msg)m.SetRcode(r, dns.RcodeServerFailure)w.WriteMsg(m)return}if err := w.WriteMsg(resp); err != nil {log.Printf("发送转发响应失败:%v\n", err)}
}

🚀 怎么用?

第一步:编译运行

go build -o mydns main.go
sudo ./mydns  # 需要 root 权限才能监听 53 端口!

💡 小技巧:开发时可以用 :1053 端口避免权限问题,用 dig @localhost -p 1053 a.local 测试。

第二步:让设备使用你的 DNS

有两种方式:

✅ 方式一:改路由器 DHCP 设置(推荐!)

  • 登录路由器后台(通常是 192.168.1.1)
  • 找到 DHCP 设置 → DNS 服务器
  • 把主 DNS 改成你运行程序的那台机器的 IP(比如 192.168.1.50)
  • 保存后,所有新连接的设备都会自动用你的 DNS!

✅ 方式二:手动改设备 DNS

  • Windows / macOS / 手机:在 Wi-Fi 设置里手动指定 DNS 为 192.168.1.50

第三步:享受域名自由!

现在,在任何设备上:

ping a.local      # → 192.168.1.100
curl http://b.local
ssh user@nas.local  # 如果你加了 nas.local.

是不是瞬间感觉家里设备"活"起来了?😎

⚠️ 注意事项(避坑指南)

  1. 域名必须带结尾的点!

    • a.local. ✅
    • a.local ❌(DNS 协议要求 FQDN 以点结尾)
  2. 别用真实公网域名!

    • 比如别写 “baidu.com.”,否则你可能再也打不开百度了……
    • 建议用 .local、.home、.lan、.internal 等私有后缀。
  3. 确保你的 DNS 服务器一直在线

    • 如果它挂了,且设备只配置了这一个 DNS,可能导致"上不了网"。
    • 所以保留"转发到路由器"的逻辑非常重要!
  4. 防火墙别拦着 UDP 53 端口

    • 确保内网其他设备能访问这台机器的 53 端口。

🌈 结语:你也可以成为"内网上帝"

通过这个小项目,你不仅:

  • 理解了 DNS 的基本原理(查询 → 响应 / 转发)
  • 学会了用 Go 操作 DNS 协议
  • 还顺手打造了一个实用的家庭网络工具
  • 更重要的是——你再也不用记 IP 了!

📌 源码已附上,快去试试吧!

✨ 小彩蛋:如果你把这段代码跑在树莓派上,再配上 UPS 电源,你就拥有了一个 7x24 小时在线的"家庭域名管家"!

往期部分文章列表

  • 我用Go写了个华容道游戏,曹操终于不用再求关羽了!
  • 用 Go 接口把 Excel 变成数据库:一个疯狂但可行的想法
  • 穿墙术大揭秘:用 Go 手搓一个"内网穿透"神器!
  • 布隆过滤器(go):一个可能犯错但从不撒谎的内存大师
  • 自由通讯的魔法:Go从零实现UDP/P2P 聊天工具
  • Go语言实现的简易远程传屏工具:让你的屏幕「飞」起来
  • 当你的程序学会了"诈尸":Go 实现 Windows 进程守护术
  • 验证码识别API:告别收费接口,迎接免费午餐
  • 用 Go 给 Windows 装个"顺风耳":两分钟写个录音小工具
  • 无奈!我用go写了个MySQL服务
  • 使用 Go + govcl 实现 Windows 资源管理器快捷方式管理器
  • 用 Go 手搓一个 NTP 服务:从"时间混乱"到"精准同步"的奇幻之旅
  • 用 Go 手搓一个 Java 构建工具:当 IDE 不在身边时的自救指南
  • 深入理解 Windows 全局键盘钩子(Hook):拦截 Win 键的 Go 实现
  • 用 Go 语言实现《周易》大衍筮法起卦程序
  • Go 语言400行代码实现 INI 配置文件解析器:支持注释、转义与类型推断
  • 高性能 Go 语言带 TTL 的内存缓存实现:精确过期、自动刷新、并发安全
  • Golang + OpenSSL 实现 TLS 安全通信:从私有 CA 到动态证书加载
http://www.dtcms.com/a/544670.html

相关文章:

  • Rust async/await 语法糖的展开原理:从表象到本质
  • Rust 零拷贝技术:从所有权到系统调用的性能优化之道
  • 浪潮服务器装linux系统步骤
  • 视频网站服务器带宽需要多少?视频网站服务器配置要求
  • 《嵌入式硬件(十八):基于IMX6ULL的ADC操作》
  • 注册网站发财的富豪北京公司如何做网站
  • 仓颉语言异常捕获机制深度解析
  • 基于SAP.NET Core Web APP(MVC)的医疗记录管理系统完整开发指南
  • 咖啡网站建设设计规划书wordpress修改首页网址导航
  • C#WPF UI路由事件:事件冒泡与隧道机制
  • 神经网络时序预测融合宏观变量的ETF动态止盈系统设计与实现
  • 分布式Session会话实现方案
  • Java创建【线程池】的方法
  • 相机直播,HDMI线怎么选择
  • 做外贸哪些国外网站可以推广上海中学地址
  • HFSS微带线仿真
  • 推荐常州微信网站建设网站友链怎么做
  • 多模态的大模型文本分类模型代码(二)——模型初步运行
  • 强化特权用户监控,守护Active Directory核心安全
  • Kafka Consumer 消费流程详解
  • 安全守护者:防爆外壳在气体传感器领域的关键应用
  • 【JavaEE初阶】网络经典面试题小小结
  • 以太网多参量传感器:构筑工业安全与环境稳定的“数据堡垒”
  • pinia-storeToRefs方法
  • 基于用户的协同过滤算法理解
  • jsp书城网站开发中国建设银行重庆网站首页
  • 郑州网站建设公司排名湖南省城乡住房建设厅网站
  • 蓝牙钥匙 第4次 蓝牙协议栈深度剖析:从物理层到应用层的完整架构解析
  • 口腔健康系统|口腔医疗|基于java和小程序的口腔健康系统小程序设计与实现(源码+数据库+文档)
  • FANUC发那科焊接机器人薄板焊接节气