golang使用DoH解析域名

按照RFC 8484 规范,DoH服务器支持GET或POST两种方式。
当使用GET方法,唯一的变量"dns"被赋值为base64url编码的DNS请求内容。

   These examples use a DoH service with a URI Template of
   "https://dnsserver.example.net/dns-query{?dns}" to resolve IN A
   records.

   The requests are represented as bodies with media type "application/
   dns-message".

   The first example request uses GET to request "www.example.com".

   :method = GET
   :scheme = https
   :authority = dnsserver.example.net
   :path = /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
   accept = application/dns-message

当使用POST方法,DNS查询消息被包含在HTTP 请求的body中,
header需要包含 Content-Type application/dns-message。


   The same DNS query for "www.example.com", using the POST method would
   be:

   :method = POST
   :scheme = https
   :authority = dnsserver.example.net
   :path = /dns-query
   accept = application/dns-message
   content-type = application/dns-message
   content-length = 33

   <33 bytes represented by the following hex encoding>
   00 00 01 00 00 01 00 00  00 00 00 00 03 77 77 77
   07 65 78 61 6d 70 6c 65  03 63 6f 6d 00 00 01 00
   01

DNS报文格式,具体请参看 RFC1035 ,本文不再赘述。

golang程序中dns报文封装推荐使用golang.org/x/net/dns/dnsmessage
下面先使用另一个库github.com/miekg/dns简要演示使用opendns DoH解析域名的GET和POST方式。

package main
import (
	"bytes"
	"encoding/base64"
	"fmt"
	"github.com/miekg/dns"
	"io"
	"net/http"
)
// https://datatracker.ietf.org/doc/html/rfc8484#section-4.1.1
func main() {
	rfc8484Get()
	rfc8484Post()

}
func rfc8484Get() {
	query := dns.Msg{}
	query.SetQuestion("www.github.com.", dns.TypeA)
	dsnReq, _ := query.Pack()
	base64Query := base64.RawURLEncoding.EncodeToString(dsnReq)
	client := &http.Client{}
	//GET请求必须具有 ?dns=查询参数,该参数带有采用 Base64Url编码的DNS消息
	req, err := http.NewRequest("GET", "https://doh.opendns.com/dns-query?dns="+base64Query, nil)
	if err != nil {
		fmt.Printf("Send query error, err:%v\n", err)
		return
	}
	req.Header.Add("Accept", "application/dns-message")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	bodyBytes, _ := io.ReadAll(resp.Body)
	response := dns.Msg{}
	response.Unpack(bodyBytes)
	fmt.Printf("Dns answer is :%v\n", response.String())
}

func rfc8484Post() {
	query := dns.Msg{}
	query.SetQuestion("www.github.com.", dns.TypeA)
	dsnReq, _ := query.Pack()
	client := &http.Client{}
	//POST请求正文为二进制DNS消息
	req, err := http.NewRequest("POST", "https://doh.opendns.com/dns-query", bytes.NewBuffer(dsnReq))
	if err != nil {
		fmt.Printf("Send query error, err:%v\n", err)
	}
	req.Header.Add("Accept", "application/dns-message")
	//POST头需要包含 Content-Type application/dns-message
	req.Header.Add("Content-Type", "application/dns-message")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	bodyBytes, _ := io.ReadAll(resp.Body)
	response := dns.Msg{}
	response.Unpack(bodyBytes)
	fmt.Printf("Dns answer is :%v\n", response.String())

}

上面response.String()直观显示了返回数据,下面再使用golang.org/x/net/dns/dnsmessage演示读取返回具体数据格式

package main

import (
	"bytes"
	"fmt"
	"golang.org/x/net/dns/dnsmessage"
	"io"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strings"
)

func main() {
	query := dnsmessage.Message{
		Header: dnsmessage.Header{
			ID:               uint16(rand.Intn(65535) + 1), // Unique identifier for the query
			Response:         false,                        // This is a query, not a response
			RecursionDesired: true,                         // Ask for recursive resolution
		},
		Questions: []dnsmessage.Question{
			{
				Name:  dnsmessage.MustNewName("www.github.com."), // Domain name to query
				Type:  dnsmessage.TypeA,                          // Query type (A record)
				Class: dnsmessage.ClassINET,                      // Internet class
			},
		},
	}

	queryBytes, err := query.Pack()
	if err != nil {
		fmt.Println("Failed to pack DNS query:", err)
		return
	}

	client := &http.Client{}
	//POST请求正文为二进制DNS消息
	req, err := http.NewRequest("POST", "https://doh.opendns.com/dns-query", bytes.NewBuffer(queryBytes))
	if err != nil {
		fmt.Printf("Send query error, err:%v\n", err)
		return
	}
	req.Header.Add("Accept", "application/dns-message")
	//POST头需要包含 Content-Type application/dns-message
	req.Header.Add("Content-Type", "application/dns-message")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	bodyBytes, _ := io.ReadAll(resp.Body)
	var dnsResponse dnsmessage.Message
	err = dnsResponse.Unpack(bodyBytes)
	if err != nil {
		fmt.Println("Failed to unpack DNS response:", err)
		os.Exit(1)
	}

	// 遍历响应资源记录
	for _, answer := range dnsResponse.Answers {
		switch answer.Header.Type {
		case dnsmessage.TypeA:
			fmt.Println("Type A Record:")
			fmt.Println("IP Address:", net.IP(answer.Body.(*dnsmessage.AResource).A[:]).String())
		case dnsmessage.TypeNS:
			fmt.Println("Type NS Record:")
			fmt.Println("Name Server:", answer.Body.(*dnsmessage.NSResource).NS.String())
		case dnsmessage.TypeCNAME:
			fmt.Println("Type CNAME Record:")
			fmt.Println("Canonical Name:", answer.Body.(*dnsmessage.CNAMEResource).CNAME.String())
		case dnsmessage.TypeSOA:
			fmt.Println("Type SOA Record:")
			soa := answer.Body.(*dnsmessage.SOAResource)
			fmt.Println("Primary Name Server:", soa.NS.String())
			fmt.Println("Responsible Person:", soa.MBox.String())
			fmt.Println("Serial Number:", soa.Serial)
			fmt.Println("Refresh Interval:", soa.Refresh)
			fmt.Println("Retry Interval:", soa.Retry)
			fmt.Println("Expire Limit:", soa.Expire)
			fmt.Println("Minimum TTL:", soa.MinTTL)
		case dnsmessage.TypePTR:
			fmt.Println("Type PTR Record:")
			fmt.Println("Pointer Domain Name:", answer.Body.(*dnsmessage.PTRResource).PTR.String())
		case dnsmessage.TypeMX:
			fmt.Println("Type MX Record:")
			mx := answer.Body.(*dnsmessage.MXResource)
			fmt.Println("Mail Exchange:", mx.MX.String())
			fmt.Println("Preference:", mx.Pref)
		case dnsmessage.TypeTXT:
			fmt.Println("Type TXT Record:")
			fmt.Println("Text Data:", strings.Join(answer.Body.(*dnsmessage.TXTResource).TXT, ""))
		case dnsmessage.TypeAAAA:
			fmt.Println("Type AAAA Record:")
			fmt.Println("IPv6 Address:", net.IP(answer.Body.(*dnsmessage.AAAAResource).AAAA[:]))
		case dnsmessage.TypeSRV:
			fmt.Println("Type SRV Record:")
			srv := answer.Body.(*dnsmessage.SRVResource)
			fmt.Println("Priority:", srv.Priority)
			fmt.Println("Weight:", srv.Weight)
			fmt.Println("Port:", srv.Port)
			fmt.Println("Target:", srv.Target.String())
		case dnsmessage.TypeOPT:
			fmt.Println("Type OPT Record:")
			// 一般情况下,OPT记录用于 DNS 扩展,不是常规资源记录。
		default:
			fmt.Println("Unhandled record type:", answer.Header.Type)
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/604233.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ethercat :推荐一个不错的ethercat主从站开源项目

一、引言 最近在研究EtherCAT,也极有兴趣想要搞通整个底层协议&#xff0c;将来有机会搞自己的软件EtherCAT产品。这里推荐一个不错的开源项目&#xff0c;与志同道合的朋友共同学习。 Ethercat-master 主站地址&#xff1a;https://github.com/OpenEtherCATsociety/SOEM Eth…

记一次DNS故障导致用户无法充值的问题(上)

背景&#xff1a; 刚刚过去了五一劳动节&#xff0c;回来后一上班接到客服运营团队反馈的节日期间的问题&#xff0c;反馈有部分用户无法充值。拿到的反馈资料有&#xff1a; 无法充值操作视频、问题时间、手机机型、手机网络情况。 1、从视频中看到用户点击支付后没有任何反…

DRF视图基类使用方法

【 一 】drf之请求 请求对象Request 【 0 】前言 ​ 在 Python 中&#xff0c;通常通过 request 对象来处理 HTTP 请求&#xff0c;尤其是在 web 开发中&#xff0c;比如使用 Django、Flask 等框架时会经常接触到这个对象。request 对象是框架提供的&#xff0c;用于封装客户…

[附源码]秦时明月6.2魔改版_搭建架设教程_附GM工具_安卓苹果

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 演示视频 秦时明…

stack的使用

1.栈的定义 我们可以看到模板参数里面有一个容器适配器 &#xff0c;什么是适配器&#xff1f;比如充电器就叫做电源适配器&#xff0c;用在做转换&#xff0c;对电压进行相关的转换适配我们的设备。栈&#xff0c;队列不是自己直接管理数据&#xff0c;是让其他容器管理数据&a…

缓存雪崩、击穿、击穿

缓存雪崩&#xff1a; 就是大量数据在同一时间过期或者redis宕机时&#xff0c;这时候有大量的用户请求无法在redis中进行处理&#xff0c;而去直接访问数据库&#xff0c;从而导致数据库压力剧增&#xff0c;甚至有可能导致数据库宕机&#xff0c;从而引发的一些列连锁反应&a…

【linux】进程概念|task_struct|getpid|getppid

目录 ​编辑 1.进程的概念 进程的基本概念 进程与程序的主要区别 进程的特征 进程的状态 描述进程—PCB task_struct中的内容 查看进程 1.创建一个进程&#xff0c;运行以下代码 通过系统调用获取进程标示符 getpid()以及getppid() 1.进程的概念 进程的基本概念…

JRT失控处理打印和演示

基于JRT完备的脚本化和打印基础&#xff0c;基于JRT的业务可以轻松的实现想要的打效果&#xff0c;这次以质控图的失控处理打印和月报打印来分享基于JRT的打印业务实现。 演示视频链接 失控报告打印 失控处理打印的虚拟M import JRT.Core.DataGrid.GridDto; import JRT.Co…

微信小程序开发-数据事件绑定

&#x1f433;简介 数据绑定 数据绑定是一种将小程序中的数据与页面元素关联起来的技术&#xff0c;使得当数据变化时&#xff0c;页面元素能够自动更新。这通常使用特定的语法&#xff08;如双大括号 {{ }}&#xff09;来实现&#xff0c;以便在页面上展示动态数据。 事件绑…

分布式与一致性协议之ZAB协议(八)

ZAB协议 如何实现读操作 相比写操作&#xff0c;读操作的处理要简单很多&#xff0c;因为接收到度请求的节点只需要查询本地数据&#xff0c;然后响应数据给客户端就可以了。读操作的核心流程如图所示。 1.跟随者在FollowerRequestProcessor.processRequest()中接收到度请求…

Python深度学习基于Tensorflow(6)神经网络基础

文章目录 使用Tensorflow解决XOR问题激活函数正向传播和反向传播解决过拟合权重正则化Dropout正则化批量正则化 BatchNormal权重初始化残差连接 选择优化算法传统梯度更新算法动量算法NAG算法AdaGrad算法RMSProp算法Adam算法如何选择优化算法 使用tf.keras构建神经网络使用Sequ…

射频无源器件之电桥

一. 电桥的定义及作用 电桥主要用于实现微波大功率功放系统的功率合成分配,信号采集等功能,被广泛应用于中国及全球4G/5G基站、5G网络覆盖、北斗导航天线、车载高精度导航(无人驾驶)天线等。可将信号分成有相位差的两路,90度电桥相位差90,180度电桥相位差180。 常说的3d…

【CSS】认识CSS选择器及各选择器对应的用法

目录 一、什么是CSS&#xff1f; 二、CSS 选择器 1. 标签选择器 2. 类选择器 3. ID选择器 4. 通配符选择器 5. 复合选择器 一、什么是CSS&#xff1f; CSS(Cascading Style Sheet)&#xff0c;层叠样式表。它与 HTML&#xff08;超文本标记语言&#xff09;一起使用&am…

c++11 的explicit关键字 -- 显示构建对象

概述: 在平时我们定义一个类&#xff0c;然后创建类对象可以有多种方式&#xff0c;主要分为两种: 声明一个Student类: class Student { public: Student(int age) { m_age age; } private: int m_age; }; 显示构建(explicit) Student s1(5); //…

全栈开发之路——前端篇(5)组件间通讯和接口等知识补充

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 辅助文档&…

WPF 图片显示某一部分区域

效果图&#xff1a; 代码&#xff1a; <Image Width"32"HorizontalAlignment"Right"Height"32"Source"../../Resources/Images/BLUEWOLF.jpg"><Image.Clip><PathGeometry><PathFigure StartPoint"32,32&quo…

重写muduo之Thread、EventLoopThread、EventLoopThreadPool

目录 1、概述 2、Thread 2.1 Thread.h 3、EventLoopThread 3.1 EventLoopThread.h 3.2 EventLoopThread.cc 4、 EventLoopThreadPool 4.1 EventLoopThreadPool.h 4.2 EventLoopThreadPool.cc 1、概述 管理事件循环线程的调度的 打包了一个EventLoop和线程&#xff0c;…

在ubuntu虚拟机中手动安装VMware Tools(VMware Workstation 17 player)

可参考官方文档&#xff1a;在 Linux 虚拟机中手动安装 VMware Tools 以下列出我在安装过程中遇见的问题&#xff1a; 1、“安装VMware Tools”选项为灰&#xff0c;无法选中 原因是VMware Tools的安装包镜像在Player的安装目录下&#xff0c;需要在虚拟机启动的时候加载这个…

【数字经济】上市公司供应链数字化数据(2000-2022)

数据来源&#xff1a; 时间跨度&#xff1a;2000-2022年 数据范围&#xff1a;各上市企业 数据指标&#xff1a; 样例数据&#xff1a; 参考文献&#xff1a;[1]刘海建,胡化广,张树山,等.供应链数字化的绿色创新效应[J].财经研究,2023,49(03):4-18. 下载链接&#xff1a;https:…

关系型数据库MySQL开发要点之多表查询2024详解

多表查询 准备测试数据 -- 部门管理 create table tb_dept(id int unsigned primary key auto_increment comment 主键ID,name varchar(10) not null unique comment 部门名称,create_time datetime not null comment 创建时间,update_time datetime not null comment 修改时…
最新文章