From 0c817c4a825694685f9f911cf7f4392142836b39 Mon Sep 17 00:00:00 2001 From: brewlin <97404667@qq.com> Date: Fri, 6 Mar 2020 16:44:39 +0800 Subject: [PATCH] [dns] #support dns lookup --- README.md | 2 +- cmd/application/dns/main.go | 22 +++- protocol/application/dns/endopoint.go | 36 ++++--- protocol/application/dns/query.go | 15 +++ protocol/application/dns/rsp.go | 57 ++++++++++ protocol/header/dns.go | 138 +++++++++++++++++++------ protocol/transport/udp/client/write.go | 2 +- 7 files changed, 222 insertions(+), 50 deletions(-) create mode 100644 protocol/application/dns/query.go create mode 100644 protocol/application/dns/rsp.go diff --git a/README.md b/README.md index d67887d..dca61c4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ## @application 应用层 - [x] [http](http://wiki.brewlin.com/wiki/net-protocol/index/) - [x] [websocket](http://wiki.brewlin.com/wiki/net-protocol/index/) -- [ ] [dns](http://wiki.brewlin.com/wiki/net-protocol/index/) +- [x] [dns](http://wiki.brewlin.com/wiki/net-protocol/index/) ## @transport 传输层 diff --git a/cmd/application/dns/main.go b/cmd/application/dns/main.go index e4d0ba4..d2a6832 100644 --- a/cmd/application/dns/main.go +++ b/cmd/application/dns/main.go @@ -3,13 +3,27 @@ package main import ( "fmt" "github.com/brewlin/net-protocol/protocol/application/dns" + "github.com/brewlin/net-protocol/protocol/header" ) func main() { - dns := dns.NewEndpoint("www.baidu.com") - ir,err := dns.Resolve(); - fmt.Println(err) - fmt.Println(string(ir)) + d := dns.NewEndpoint("www.baidu.com") + fmt.Println("DNS lookuphost : www.baidu.com") + defer d.Close() + + ir,err := d.Resolve(); + if err != nil { + fmt.Println(err) + return + } + for _,v := range *ir { + switch v.Type { + case header.A: + fmt.Println("A(host name) :",v.Address) + case header.CNAME: + fmt.Println("CNAME (alias name):",v.Address) + } + } } diff --git a/protocol/application/dns/endopoint.go b/protocol/application/dns/endopoint.go index 8760719..ff22753 100644 --- a/protocol/application/dns/endopoint.go +++ b/protocol/application/dns/endopoint.go @@ -1,9 +1,9 @@ package dns import ( + _ "github.com/brewlin/net-protocol/pkg/logging" "github.com/brewlin/net-protocol/protocol/header" "github.com/brewlin/net-protocol/protocol/transport/udp/client" - _ "github.com/brewlin/net-protocol/pkg/logging" ) var gid uint16 = 0x0010 @@ -11,32 +11,42 @@ type Endpoint struct { ID uint16 Domain string + //req data + req *header.DNS + resp *header.DNS + + answer *[]header.DNSResource + c *client.Client } //NewEndpoint +//support single domain query func NewEndpoint(domain string)*Endpoint{ id := gid + 1 return &Endpoint{ Domain:domain, c:client.NewClient("8.8.8.8",53), + //c:client.NewClient("114.114.114.114",53), ID:id, } } //Resolve -func (e *Endpoint) Resolve()([]byte,error){ +func (e *Endpoint) Resolve() ( *[]header.DNSResource,error ) { + h := header.DNS(make([]byte,12)) h.Setheader(e.ID) - h.SetQdcount(1) - h.SetAncount(0) - h.SetNscount(0) - h.SetQAcount(0) - h.SetDomain(e.Domain) - h.SetQuestion(1,1) - e.c.Connect() - e.c.Write(h) - return e.c.Read() - - + h.SetCount(1,0,0,0) + h.SetQuestion(e.Domain,1,1) + e.req = &h + return e.sendQuery() +} +//GetResp() +func (e *Endpoint) GetResp() *header.DNS{ + return e.resp +} +//Close close +func (e *Endpoint) Close(){ + e.c.Close() } diff --git a/protocol/application/dns/query.go b/protocol/application/dns/query.go new file mode 100644 index 0000000..e58fb10 --- /dev/null +++ b/protocol/application/dns/query.go @@ -0,0 +1,15 @@ +package dns + +import "github.com/brewlin/net-protocol/protocol/header" + +//sendQuery udp query dns +func (e *Endpoint) sendQuery () ( *[]header.DNSResource ,error ) { + + if err := e.c.Connect();err != nil { + return nil,err + } + if err := e.c.Write(*e.req) ; err != nil { + return nil,err + } + return e.parseResp() +} \ No newline at end of file diff --git a/protocol/application/dns/rsp.go b/protocol/application/dns/rsp.go new file mode 100644 index 0000000..e9f6af1 --- /dev/null +++ b/protocol/application/dns/rsp.go @@ -0,0 +1,57 @@ +package dns + +import ( + "github.com/brewlin/net-protocol/protocol/header" + "strconv" + "strings" +) + +//parseResp +//解析响应 +func (e *Endpoint) parseResp() (*[]header.DNSResource,error){ + rsp,err := e.c.Read() + if err != nil { + return nil,err + } + p := header.DNS(rsp) + e.resp = &p + e.answer = p.GetAnswer(e.Domain) + return e.parseAnswer() +} + +func (e *Endpoint) parseAnswer()(*[]header.DNSResource,error){ + for i := 0; i < len(*e.answer) ; i++ { + switch (*e.answer)[i].Type { + case header.A: + (*e.answer)[i].Address = e.parseAName((*e.answer)[i].RData) + case header.CNAME: + (*e.answer)[i].Address = e.parseCName((*e.answer)[i].RData) + } + } + return e.answer,nil +} +func (e *Endpoint)parseAName(rd []byte) string { + res := []string{} + for _,v := range rd { + res = append(res,strconv.Itoa(int(v))) + } + return strings.Join(res,".") +} + +func (e *Endpoint)parseCName(rd []byte) (res string) { + + for{ + l := int(rd[0]) + if l >= len(rd){ + res += ".com" + return + } + rd = rd[1:] + res += string(rd[0:l]) + rd = rd[l:] + if len(rd) == 0 { + return + } + + } +} diff --git a/protocol/header/dns.go b/protocol/header/dns.go index 296e630..0d63b85 100644 --- a/protocol/header/dns.go +++ b/protocol/header/dns.go @@ -6,17 +6,32 @@ import ( "strings" ) -//DNSResourceType resource type 表示资源类型 -type DNSResourceType uint16 - -//DNSop 表示dns header 操作码 -type DNSop uint16 //ADCOUNT question 实体数量占2个字节 //ANCOUNT answer 资源数量占2个字节 //NSCOUNT authority 部分包含的资源数量 2byte //ARCOUNT additional 部分包含的资源梳理 2byte +//DNSResourceType resource type 表示资源类型 +type DNSResourceType uint16 + +const ( + A DNSResourceType = iota + 1 // name = hostname value = ipaddress + NS //name = ,value = dns hostname + MD + MF + CNAME //name = hostname + SOA + MB + MG + MR + NULL + WKS + PTR + HINFO + MINFO + MX +) const ( ID = 0 @@ -24,64 +39,125 @@ const ( QDCOUNT = 4 ANCOUNT = 6 NSCOUNT = 8 - QACOUNT = 10 + ARCOUNT = 10 DOMAIN = 12 ) - +//DNSQuestion type DNSQuestion struct { QuestionType uint16 QuestionClass uint16 } +//DNSResource ansower,authority,additional +type DNSResource struct { + Name uint16 + Type DNSResourceType + Class uint16 + TTL uint32 + RDlen uint16 + RData []byte + Address string +} + + //DNS 报文的封装 type DNS []byte +//GetId +func (d DNS) GetId() uint16 { + return binary.BigEndian.Uint16(d[ID:OP]) +} +//GetQDCount +func (d DNS) GetQDCount()uint16 { + return binary.BigEndian.Uint16(d[QDCOUNT:QDCOUNT+2]) +} +//GetANCount +func (d DNS) GetANCount()uint16{ + return binary.BigEndian.Uint16(d[ANCOUNT:ANCOUNT + 2]) +} +//GetNSCount +func (d DNS) GetNSCount() uint16 { + return binary.BigEndian.Uint16(d[NSCOUNT:NSCOUNT + 2]) +} +//GetQACount +func (d DNS) GetARCount () uint16 { + return binary.BigEndian.Uint16(d[ARCOUNT:ARCOUNT + 2]) +} + +//GetAnswer +func (d DNS) GetAnswer(domain string) *[]DNSResource { + //answer 起始地址 + asLen := DOMAIN + len(d.getDomain(domain)) + 4 + + answer := []DNSResource{} + for i := 0; i < (int(d.GetANCount() + d.GetNSCount() + d.GetARCount())) ;i ++ { + rs := DNSResource{} + //判断是不是指针 pointer地址 + if checkP := d[asLen]; checkP >> 6 == 3 { + //pointer := (d[asLen] & 0x3F << 8) + d[asLen+1] + rs.Name = binary.BigEndian.Uint16(d[asLen:asLen+2]) + asLen += 2 + rs.Type = DNSResourceType(binary.BigEndian.Uint16(d[asLen:asLen+2])) + asLen += 2 + rs.Class = binary.BigEndian.Uint16(d[asLen:asLen+2]) + asLen += 2 + rs.TTL = binary.BigEndian.Uint32(d[asLen:asLen+4]) + asLen += 4 + rs.RDlen = binary.BigEndian.Uint16(d[asLen:asLen+2]) + asLen += 2 + rs.RData = d[asLen:asLen+int(rs.RDlen)] + asLen += int(rs.RDlen) + answer = append(answer,rs) + } + } + return &answer +} + //Setheader func (d DNS) Setheader(id uint16){ d.setID(id) d.setFlag(0,0,0,0,1,0,0) } -//SetID -func (d DNS)setID(id uint16){ - //set id - binary.BigEndian.PutUint16(d[ID:], id) -} -//SetQdcount -func (d DNS)SetQdcount(qd uint16){ - +//SetCount +func (d DNS) SetCount(qd,an,ns,qa uint16) { + //SetQdcount binary.BigEndian.PutUint16(d[QDCOUNT:], qd) -} -//SetAncount -func (d DNS)SetAncount(an uint16){ + //SetAncount binary.BigEndian.PutUint16(d[ANCOUNT:] ,an) -} -//SetNscount -func (d DNS)SetNscount(ns uint16){ + //SetNscount binary.BigEndian.PutUint16(d[NSCOUNT:],ns) + //SetQAcount + binary.BigEndian.PutUint16(d[ARCOUNT:],qa) } -//SetQAcount -func (d DNS)SetQAcount(qa uint16){ - binary.BigEndian.PutUint16(d[QACOUNT:],qa) +//setID +func (d DNS)setID(id uint16){ + //set id + binary.BigEndian.PutUint16(d[ID:], id) } -//SetDomain -func (d *DNS)SetDomain(domain string) { +//setDomain +func (d *DNS)getDomain(domain string) []byte { var ( buffer bytes.Buffer segments []string = strings.Split(domain, ".") ) - binary.Write(&buffer,binary.BigEndian,*d) - for _, seg := range segments { binary.Write(&buffer, binary.BigEndian, byte(len(seg))) binary.Write(&buffer, binary.BigEndian, []byte(seg)) } binary.Write(&buffer, binary.BigEndian, byte(0x00)) - *d = buffer.Bytes() - //return buffer.Bytes() + return buffer.Bytes() } -func (d *DNS)SetQuestion(qtype,qclass uint16){ +//SetQuestion query field +//domain url +//qtype type +//qclass class +func (d *DNS)SetQuestion(domain string,qtype,qclass uint16){ + for _,b := range d.getDomain(domain) { + *d = append((*d),b) + } + //d.setDomain(domain) q := DNSQuestion{ QuestionType: qtype, QuestionClass: qclass, diff --git a/protocol/transport/udp/client/write.go b/protocol/transport/udp/client/write.go index c3f0e20..2073494 100644 --- a/protocol/transport/udp/client/write.go +++ b/protocol/transport/udp/client/write.go @@ -5,7 +5,7 @@ import ( tcpip "github.com/brewlin/net-protocol/protocol" ) //Write -func (c *Client) Write(buf []byte) error { +func (c *Client) Write(buf []byte) *tcpip.Error { v := buffer.View(buf) for{ _,ch,err := c.ep.Write(tcpip.SlicePayload(v),