DNS Protocol Golang implementation

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

The implementation of the DNS client is still using the go language, which is most familiar after all. My implementation is simply the implementation of the sending of DNS requests, parsing the response content of these functions, for multi-threaded concurrency and other mechanisms are not considered.

I first refer to the "Computer network", the above mentioned that the DNS request is to send a UDP packet, and then I was naïve, directly to the domain name of the string with UDP sent. The result is no response.

Later on an honest analysis of the agreement, the analysis of the agreement, please refer to the previous article, "DNS protocol analysis."

The domain name server chooses the 233.5.5.5,53 port provided by Ali. Details can be found in the Ali DNS official website.

Let's talk about the method of Golang sending UDP datagrams. All languages this piece seems to be about the same length, I will not introduce more.

service := "223.5.5.5:53"udpAddr, err := net.ResolveUDPAddr("udp", service)checkError(err)conn, err := net.DialUDP("udp", nil, udpAddr)checkError(err)_, err = conn.Write(out.Pack())checkError(err)buf := []byte{}buf = make([]byte, 512)n, err := conn.Read(buf[0:])checkError(err)

Then is to send and receive the message body to be used, it consists of a transationid, Flags, query number questions, answer is the number of responses and query question. Query question is made up of domain names, categories, and classifications. The query result answer consists of the query domain name, record type, classification, data length, and primaryname. These structures can be defined as:

type DnsMsg struct {Id                                 uint16Bits                               uint16Qdcount, Ancount, Nscount, Arcount uint16Questions                          []dnsQuestionAnswers                            []dnsAnswer}type dnsQuestion struct {Name   string `net:"domain-name"`Qtype  uint16Qclass uint16}type dnsAnswer struct {Name   uint16Qtype  uint16Qclass uint16QLive  uint32QLen   uint16CName  string `net:"domain-name"`}

The DNS protocol is two bytes, all with uint16 processing, question inside the name and answer inside the definition of the CNAME should be defined as []byte , but in order to facilitate viewing, first defined as a string, the last encoding time to process the line.

With structs, you can assign values to these structures and then encode them into bytecode for sending. The encoding method is as follows:

  func (this *dnsmsg) Pack () []byte {bs: = make ([]byte,) binary. Bigendian.putuint16 (Bs[0:2], this. ID) binary. Bigendian.putuint16 (Bs[2:4], this. Bits) binary. Bigendian.putuint16 (Bs[4:6], uint16 (Len (this. Questions))) binary. Bigendian.putuint16 (Bs[6:8], this. Ancount) binary. Bigendian.putuint16 (Bs[8:10], this. Nscount) binary. Bigendian.putuint16 (Bs[10:12], this. Arcount) ds: = Strings. Split (this. Questions[0]. Name, ".") For _, D: = Range ds {bs = append (BS, Byte (Len (d))) bs = append (BS, []byte (d) ...)} BS = append (BS, 0) Temp: = Make ([]byte, 2) binary. Bigendian.putuint16 (temp, this.) Questions[0]. Qtype) bs = append (BS, temp ...) Binary. Bigendian.putuint16 (temp, this.) Questions[0]. Qclass) bs = append (BS, temp ...) Return BS}  

The DNS header is a fixed 12 bytes, so bs := make([]byte, 12) 12 bytes of space are requested first. Next, populate these bytes. is to turn the inside of the structure uint16 []byte . This will use the binary package for conversion. The last is to populate the question, to convert the domain name. The conversion of the domain name inside . , changed to its later part of the length of the information, that is, www.cyeam.com replace 3www5cyeam3com0 . After the conversion is complete, you will also add a decimal to indicate the end. Finally, add the type and classification of the request.

After all these requests succeed, print the result, finally can see something: a bunch of garbled inside some letters, can see out is my domain name CDN address.

Finally, the response content is parsed. The response and request format are the same, and the difference is that there is more response than the request.

The DNS header 12 bytes and the request inside is exactly the same, back to parse on it. Note that two points, the first two bytes of the response and the first two bytes of the request are exactly the same, and if not, the request is ignored. Also, the requested time ancount is 0, this represents the number of responses, in the response will be found in the number of results.

The question of the query request will also be in the response, so it needs to be parsed. There may be multiple question when requesting a query, which is handled here. Start processing from the 13th byte:

i := 13for ; j < int(res.Qdcount); j++ {domain_count := int(buf[i-1])question := dnsQuestion{}for buf[i] != 0 {if domain_count > 0 {question.Name += string(buf[i:i+domain_count]) + "."i += domain_countdomain_count = 0} else {domain_count = int(buf[i])i++}}i++question.Name = strings.TrimRight(question.Name, ".")question.Qtype = binary.BigEndian.Uint16(buf[i : i+2])question.Qclass = binary.BigEndian.Uint16(buf[i+2 : i+4])i += 4res.Questions[j] = question}

The rest of the stuff is answer. The first 5 parts are fixed lengths, 2, 2, 2, 4, 2, respectively.

answer := dnsAnswer{}answer.Name = binary.BigEndian.Uint16(buf[i : i+2])i += 2answer.Qtype = binary.BigEndian.Uint16(buf[i : i+2])i += 2answer.Qclass = binary.BigEndian.Uint16(buf[i : i+2])i += 2answer.QLive = binary.BigEndian.Uint32(buf[i : i+4])i += 4answer.QLen = binary.BigEndian.Uint16(buf[i : i+2])i += 2

The CNAME behind the

is key, and parsing is done by type. One type is cname type, aka Alias, my domain name www.cyeam.com The alias for the is vm68h.x.incapdns.net , which is my CDN. There is also a real IP, the return alias is not much use, IP to solve the problem, so answer inside the second is to return this alias of the real Ip149.126.77.152 . So one by the string processing, one by the number processing. The classification in answer explains the parsing type, if the value is 1, the description is a record, which is IP, if the value is 5, is cname record. Parse the answer code as follows:

if answer.Qtype == dnsTypeCNAME {domain_count := int(buf[i])i++for buf[i] != 0 {if domain_count > 0 {answer.CName += string(buf[i:i+domain_count]) + "."i += domain_countdomain_count = 0} else {domain_count = int(buf[i])i++}}i++answer.CName = strings.TrimRight(answer.CName, ".")} else if answer.Qtype == dnsTypeA {for m := 0; m < int(answer.QLen); m++ {answer.CName += fmt.Sprintf("%d.", buf[i+m])}answer.CName = strings.TrimRight(answer.CName, ".")}

The answer also contains the length information of the result, which is used to iterate through the resulting content.

The complete request is as follows:

service := "223.5.5.5:53"udpAddr, err := net.ResolveUDPAddr("udp", service)checkError(err)conn, err := net.DialUDP("udp", nil, udpAddr)checkError(err)question := dnsQuestion{"www.cyeam.com", dnsTypeA, dnsClassINET}out := DnsMsg{}out.Id = 2015out.Bits |= _RDout.Questions = append(out.Questions, question)fmt.Println(out.Pack())_, err = conn.Write(out.Pack())checkError(err)buf := []byte{}buf = make([]byte, 512)n, err := conn.Read(buf[0:])checkError(err)fmt.Println(buf[0:n])fmt.Println(out.Unpack(buf[0:n]))os.Exit(0)

After parsing the completed structure, the following results are printed:

&{2015 33152 1 2 0 0 [{www.cyeam.com 1 1}] [{49164 5 1 30 22 vm68h.x.incapdns.net} {49195 1 1 30 4 149.126.77.152}]}

The data packets that are involved can be accessed from here. The complete code can be consulted here.

Reference documents

    1. "Computer Network (fifth edition)" Shehiren
    2. What is does a DNS request look like? -ServerFault

Original link: DNS protocol Golang Implementation, reproduced please specify the source!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.