golang DNS服务器的简单实现操作

简单的DNS服务器

提供一个简单的可以查询域名和反向查询的DNS服务器。

dig命令主要用来从 DNS 域名服务器查询主机地址信息。

查找www.baidu.com的ip (A记录):

命令:dig @127.0.0.1 www.baidu.com

根据ip查找对应域名 (PTR记录):

命令:dig @127.0.0.1 -x 220.181.38.150

源码 :

package mainimport (	"fmt"	"net"	"golang.org/x/net/dns/dnsmessage")func main() {	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})	if err != nil {		panic(err)	}	defer conn.Close()	fmt.Println("Listing ...")	for {		buf := make([]byte, 512)		_, addr, _ := conn.ReadFromUDP(buf)		var msg dnsmessage.Message		if err := msg.Unpack(buf); err != nil {			fmt.Println(err)			continue		}		go ServerDNS(addr, conn, msg)	}}// address booksvar (	addressBookOfA = map[string][4]byte{		"www.baidu.com.": [4]byte{220, 181, 38, 150},	}	addressBookOfPTR = map[string]string{		"150.38.181.220.in-addr.arpa.": "www.baidu.com.",	})// ServerDNS servefunc ServerDNS(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {	// query info	if len(msg.Questions) < 1 {		return	}	question := msg.Questions[0]	var (		queryTypeStr = question.Type.String()		queryNameStr = question.Name.String()		queryType    = question.Type		queryName, _ = dnsmessage.NewName(queryNameStr)	)	fmt.Printf("[%s] queryName: [%s]\n", queryTypeStr, queryNameStr)	// find record	var resource dnsmessage.Resource	switch queryType {	case dnsmessage.TypeA:		if rst, ok := addressBookOfA[queryNameStr]; ok {			resource = NewAResource(queryName, rst)		} else {			fmt.Printf("not fount A record queryName: [%s] \n", queryNameStr)			Response(addr, conn, msg)			return		}	case dnsmessage.TypePTR:		if rst, ok := addressBookOfPTR[queryName.String()]; ok {			resource = NewPTRResource(queryName, rst)		} else {			fmt.Printf("not fount PTR record queryName: [%s] \n", queryNameStr)			Response(addr, conn, msg)			return		}	default:		fmt.Printf("not support dns queryType: [%s] \n", queryTypeStr)		return	}	// send response	msg.Response = true	msg.Answers = append(msg.Answers, resource)	Response(addr, conn, msg)}// Response returnfunc Response(addr *net.UDPAddr, conn *net.UDPConn, msg dnsmessage.Message) {	packed, err := msg.Pack()	if err != nil {		fmt.Println(err)		return	}	if _, err := conn.WriteToUDP(packed, addr); err != nil {		fmt.Println(err)	}}// NewAResource A recordfunc NewAResource(query dnsmessage.Name, a [4]byte) dnsmessage.Resource {	return dnsmessage.Resource{		Header: dnsmessage.ResourceHeader{			Name:  query,			Class: dnsmessage.ClassINET,			TTL:   600,		},		Body: &dnsmessage.AResource{			A: a,		},	}}// NewPTRResource PTR recordfunc NewPTRResource(query dnsmessage.Name, ptr string) dnsmessage.Resource {	name, _ := dnsmessage.NewName(ptr)	return dnsmessage.Resource{		Header: dnsmessage.ResourceHeader{			Name:  query,			Class: dnsmessage.ClassINET,		},		Body: &dnsmessage.PTRResource{			PTR: name,		},	}}

补充:Golang自定义DNS Nameserver

某些情况下我们希望程序通过自定义Nameserver去查询域名,而不希望通过操作系统给定的Nameserver,本文介绍如何在Golang中实现自定义Nameserver。

DNS解析过程

Golang中一般通过net.Resolver的LookupHost(ctx context.Context, host string) (addrs []string, err error)去实现域名解析,

解析过程如下:

检查本地hosts文件是否存在解析记录,存在即返回解析地址

不存在即根据resolv.conf中读取的nameserver发起递归查询

nameserver不断的向上级nameserver发起迭代查询

nameserver最终返回查询结果给请求者

用户可以通过修改/etc/resolv.conf来添加特定的nameserver,但某些场景下我们不希望更改系统配置。比如在kubernetes中,作为sidecar服务需要通过service去访问其他集群内服务,必须更改dnsPolicy为ClusterFirst,但这可能会影响其他容器的DNS查询效率。

自定义Nameserver

在Golang中自定义Nameserver,需要我们自己实现一个Resolver,如果是httpClient需要自定义DialContext()

Resolver实现如下:

// 默认dialerdialer := &net.Dialer{  Timeout: 1 * time.Second,}// 定义resolverresolver := &net.Resolver{ Dial: func(ctx context.Context, network, address string) (net.Conn, error) {  return dialer.DialContext(ctx, "tcp", nameserver) // 通过tcp请求nameserver解析域名 },}

自定义Dialer如下:

type Dialer struct { dialer     *net.Dialer resolver   *net.Resolver nameserver string}// NewDialer create a Dialer with user's nameserver.func NewDialer(dialer *net.Dialer, nameserver string) (*Dialer, error) { conn, err := dialer.Dial("tcp", nameserver) if err != nil {  return nil, err } defer conn.Close() return &Dialer{  dialer: dialer,  resolver: &net.Resolver{   Dial: func(ctx context.Context, network, address string) (net.Conn, error) {    return dialer.DialContext(ctx, "tcp", nameserver)   },  },  nameserver: nameserver, // 用户设置的nameserver }, nil}// DialContext connects to the address on the named network using// the provided context.func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil {  return nil, err } ips, err := d.resolver.LookupHost(ctx, host) // 通过自定义nameserver查询域名 for _, ip := range ips {    // 创建链接  conn, err := d.dialer.DialContext(ctx, network, ip+":"+port)  if err == nil {   return conn, nil  } } return d.dialer.DialContext(ctx, network, address)}

httpClient中自定义DialContext()如下:

ndialer, _ := NewDialer(dialer, nameserver)client := &http.Client{  Transport: &http.Transport{    DialContext:         ndialer.DialContext,    TLSHandshakeTimeout: 10 * time.Second,  },  Timeout: timeout,}

总结

通过以上实现可解决自定义Nameserver,也可以在Dailer中添加缓存,实现DNS缓存。

(0)

相关推荐

  • 详解如何热重启golang服务器

    服务端代码经常需要升级,对于线上系统的升级常用的做法是,通过前端的负载均衡(如nginx)来保证升级时至少有一个服务可用,依次(灰度)升级. 而另一种更方便的方法是在应用上做热重启,直接升级应用而不停服务. 原理 热重启的原理非常简单,但是涉及到一些系统调用以及父子进程之间文件句柄的传递等等细节比较多. 处理过程分为以下几个步骤: 监听信号(USR2) 收到信号时fork子进程(使用相同的启动命令),将服务监听的socket文件描述符传递给子进程 子进程监听父进程的socket,这个时候父进程和

  • golang-gin-mgo高并发服务器搭建教程

    gin-mgo服务器搭建 该服务器实现简单接收请求并将请求参数封装存储在mongodb数据库中,本文将讲述gin-mgo的使用方法. 项目完整代码地址: https://github.com/wayne-yhp/golang-gin-mgo gin web框架使用介绍 首先获取gin框架依赖 go get gopkg.in/gin-gonic/gin.v1 func main() { server = gin.Default() app.server.GET("/do", IndexR

  • golang项目如何上线部署到Linu服务器(方法详解)

    Go作为Google2009年推出的语言,其被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言. 对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率.它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了. 到现在Go的开发已经是完全开放的,并且拥有一个活跃的社区. 下面看下golang项目如何上线部署到Linu服务器上. windows服务器 先本地编译 go build main.go 编译后会在同级目录生成可执行文件

  • golang文件服务器的两种方式(可以访问任何目录)

    一.方法1: 主要用到的方法是http包的FileServer,参数很简单,就是要路由的文件夹的路径. package main import ( "fmt" "net/http" ) func main() { http.Handle("/", http.FileServer(http.Dir("./"))) e := http.ListenAndServe(":8080", nil) fmt.Print

  • golang HTTP 服务器 处理 日志/Stream流的操作

    目前,我开发 HTTP 服务, 用的是 beego框架, 方便了很多. 但是, 有时候,还是会遇到一些 特殊的场景. 比如: 过滤日志. 这应该是一种典型的stream,同时数据量也适中, 不会有人,为了这个, 就用一些很重的框架. 可以这样直观的描述这个 逻辑 其他组件 产生 log || \ / 我的组件,业务处理 || \ / 用户, http client 这种情景下, 有几个特殊点: 1. 难以用 string,或者 byte 数组 收集数据 2. 数据Source 端,不断的有数据产

  • golang搭建静态web服务器的实现方法

    我胡汉三又回来啦.好久没发文了,为保持平台上的活跃度,我今天就分享下个刚学到的知识,使用golang搭建静态web服务器,亲测可用,附代码! 使用过golang语言的程序猿都应该知道,在使用golang开发的时候,我们是不需要诸如iis,apache,nginx,kangle等服务器支持的. 为什么呢? 原因是,golang的net/http包中已经提供了HTTP的客户端与服务端实现方案. 网上言论都说golang不适合做web开发,相对php.java..net.nodejs等各类后端语言来说

  • golang DNS服务器的简单实现操作

    简单的DNS服务器 提供一个简单的可以查询域名和反向查询的DNS服务器. dig命令主要用来从 DNS 域名服务器查询主机地址信息. 查找www.baidu.com的ip (A记录): 命令:dig @127.0.0.1 www.baidu.com 根据ip查找对应域名 (PTR记录): 命令:dig @127.0.0.1 -x 220.181.38.150 源码 : package mainimport ( "fmt" "net" "golang.org

  • 图文详解添加DNS服务器的操作步骤

    用于安装DNS服务器的计算机必须使用静态IP地址,如172.16.16.99.系统下载安装完DNS服务器组件后,单击"开始"-管理工具"-"DNS"命令,就能打开"dnsmgmt"控制台,如图1所示.在该控制台中,可以完成DNS服务器的设置工作.在WindowsServer2003系统中默认的是将本地计算机作为DNS服务器的硬件设备,即将本地计算机的IP地址或名称指定给DNS服务器,图2是将YL服务器指定给[)NS服务器. "

  • C#利用WMI操作DNS服务器(可远程操作,需要相应权限)

    using System; using System.Collections.Generic; using System.Text; using System.Data; namespace Yaosansi {     class Test     {         static void Main()         {             MyDnsTEST();         } /// <summary>     /// MyDnsTEST功能测试   /// C#利用WMI

  • Python写的一个简单DNS服务器实例

    因为突然有个邪恶的想法,想在自己的Android平板上面搭建一个DNS服务器,因为平板上之前安装过SL4A和Python的解释器,也想继续学学Python因此,就打算用Python实现了. 在Google上面找了一下,Python实现的DNS,没找到我所希望的答案,因此就决定自己来实现了. 现在所实现的没什么高深的,只是能够对A记录查询进行简单的匹配和回复. 实现的代码如下: 复制代码 代码如下: '''Created on 2012-10-15 @author: RobinTang''' im

  • win2003 使用DNS服务器实现负载均衡

    解决方法有很多,如使用Windows2000或WindowsServer2003提供网络负载均衡服务,但该服务的设置非常复杂.而通过DNS服务器实现网络负载均衡则是一种比较简单的方法. 笔者以企业网中的Web服务器为例来介绍一下如何使用DNS服务器实现网络负载均衡.为了提高域名为"www.jb51.net"的网站的访问量,在企业网中部署三台内容相同的Web服务器,它们提供相同的服务,但每台服务器的IP地址都不一样.下面对企业网中的DNS服务器进行设置来实现三台Web服务器共同承担客户对

  • 三步 用TreeWalk架DNS就这么简单

    自制本地DNS服务器为宽带提速 我们在安装宽带网时,ISP(因特网服务供应商)通常都会提供几个DNS服务器地址,然后在Internet的TCP/IP协议设置窗口中进行设置.目前,由DNS引起的网络问题相信大家都曾遇到过,例如网页打开的速度过慢,某些网站无法访问,或者IE浏览器中页面不能显示等等.要解决这一系列问题,除了更换DNS地址外,还有另一种解决办法.请看下文. 认识DNS DNS是Domain Name System的英文缩写,译成中文就是"域名系统".我们要对某一个网站进行访问

  • 浅谈golang二进制bit位的常用操作

    golang作为一热门的兼顾性能 效率的热门语言,相信很多人都知道,在编程语言排行榜上一直都是很亮眼,作为一门强类型语言,二进制位的操作肯定是避免不了的,数据的最小的单位也就是位,尤其是网络中封包.拆包,读取二进制文件等用的特别广泛, 所以学好golang二进制bit位的常用操作还是很必要的,而且很多运算尤其是乘法除法运算,CPU效率是很低的,这时候可以二进制操作代替,不多说了,上干货! package main import ( "fmt" "github.com/imro

  • DNS服务器中创建正向查找区域并在该区域下创建主机记录

    创建正向查找区域ynkm.com,并在该区域下创建主机记录Webserver,ynkm.com.操作步骤如下: ①打开"(Insmgmt"控制台窗口,选择要创建正向查找区域的DNS服务器,然后在菜单栏中选择"操作"-"新建区域"命令,如图1所示. ②弹出如图2所示的"新建区域向导"对话框,该向导将引导用户创建新区域,单击"下一步"按钮. ③弹出如图3所示的对话框,选杼要创建的区域类型.选中"主要区

  • RHE5服务器管理 搭建DNS服务器步骤说明[图文]

    一.DNS主要配置文件 /etc/hosts-主机的一个列表文件-包含(本地网络中)已知主机的一个列表如果系统的IP不是动态生成,就可以使用它,对于简单的主机名解析(点分表示法/etc/host.conf-转换程序控制文件-告诉网络域名服务器如何查找主机(通常是/etc/hosts,然后就是域名服务器,可通过netconf对其进行更改)/etc/resolv.conf-转换程序配置文件-在配置程序请求BIND域名查询服务查询主机名称时,必须告诉程序使用哪个域名服务器和IP地址来完成这个任务 二.

  • DNS服务器管理与配置技巧浅谈

    人们或许都有这样一个困惑:计算机在网络上通讯时本来只能识别如"123.123.123.123"之类的数字地址,那么为什么当我们打开浏览器,在地址栏中输入域名dns.qy.com.cn后,就能看到我们所需要的页面呢? 其实,只是一个IP地址和域名相互"翻译"的过程.前者得建立一个指向相应IP地址的域名映射记录;对于后者,此记录已经建立并且在生效了.而这种"翻译"记录的建立,则需要用到同一种被称之为"DNS服务器"的计算机. DN

随机推荐