mirror of
https://github.com/andwati/dns-resolver.git
synced 2026-10-18 02:32:52 +00:00
113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
package dns
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
type DNSAnswer struct {
|
|
Name string
|
|
Type uint16
|
|
Class uint16
|
|
TTL uint32
|
|
Data string
|
|
}
|
|
|
|
func parseHeader(data []byte) DNSHeader {
|
|
return DNSHeader{
|
|
ID: binary.BigEndian.Uint16(data[0:2]),
|
|
Flags: binary.BigEndian.Uint16(data[2:4]),
|
|
QDCount: binary.BigEndian.Uint16(data[4:6]),
|
|
ANCount: binary.BigEndian.Uint16(data[6:8]),
|
|
NSCount: binary.BigEndian.Uint16(data[8:10]),
|
|
ARCount: binary.BigEndian.Uint16(data[10:12]),
|
|
}
|
|
}
|
|
|
|
func parseName(data []byte, offset int) (string, int) {
|
|
name := ""
|
|
for {
|
|
length := int(data[offset])
|
|
if length == 0 {
|
|
offset++
|
|
break
|
|
}
|
|
|
|
if length&0xc0 == 0xc0 {
|
|
pointer := binary.BigEndian.Uint16(data[offset:offset+2]) & 0x3FF
|
|
offset = offset + 2
|
|
partial, _ := parseName(data, int(pointer))
|
|
return name + partial, offset
|
|
}
|
|
|
|
offset++
|
|
name += string(data[offset:offset+length]) + "."
|
|
offset += length
|
|
}
|
|
|
|
return name[:len(name)-1], offset
|
|
}
|
|
|
|
func ParseFullResponse(response []byte) ([]DNSAnswer, []DNSAnswer, []DNSAnswer, error) {
|
|
if len(response) < 12 {
|
|
return nil, nil, nil, fmt.Errorf("response too short to contain header")
|
|
}
|
|
|
|
header := parseHeader(response[:12])
|
|
if header.Flags&0x8000 == 0 { // Check QR bit
|
|
return nil, nil, nil, fmt.Errorf("not a response")
|
|
}
|
|
|
|
offset := 12
|
|
|
|
for i := 0; i < int(header.QDCount); i++ {
|
|
_, newOffset := parseName(response, offset)
|
|
offset = newOffset + 4
|
|
}
|
|
|
|
answers := []DNSAnswer{}
|
|
for i := 0; i < int(header.ANCount); i++ {
|
|
answer, newOffset := parseResourceRecord(response, offset)
|
|
answers = append(answers, answer)
|
|
offset = newOffset
|
|
}
|
|
authorities := []DNSAnswer{}
|
|
for i := 0; i < int(header.NSCount); i++ {
|
|
authority, newOffset := parseResourceRecord(response, offset)
|
|
authorities = append(authorities, authority)
|
|
offset = newOffset
|
|
}
|
|
|
|
additionals := []DNSAnswer{}
|
|
for i := 0; i < int(header.ARCount); i++ {
|
|
additional, newOffset := parseResourceRecord(response, offset)
|
|
additionals = append(additionals, additional)
|
|
offset = newOffset
|
|
}
|
|
|
|
return answers, authorities, additionals, nil
|
|
}
|
|
|
|
func parseResourceRecord(data []byte, offset int) (DNSAnswer, int) {
|
|
name, newOffset := parseName(data, offset)
|
|
offset = newOffset
|
|
|
|
answer := DNSAnswer{
|
|
Name: name,
|
|
Type: binary.BigEndian.Uint16(data[offset : offset+2]),
|
|
Class: binary.BigEndian.Uint16(data[offset+2 : offset+4]),
|
|
TTL: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
|
|
}
|
|
dataLen := binary.BigEndian.Uint16(data[offset+8 : offset+10])
|
|
offset += 10
|
|
|
|
if answer.Type == QTypeA && dataLen == 4 {
|
|
answer.Data = fmt.Sprintf("%d.%d.%d.%d", data[offset], data[offset+1], data[offset+2], data[offset+3])
|
|
} else if answer.Type == QTypeCNAME || answer.Type == QTypeNS {
|
|
answer.Data, _ = parseName(data, offset)
|
|
}
|
|
offset += int(dataLen)
|
|
|
|
return answer, offset
|
|
}
|