2024-08-24 17:46:34 +03:00
|
|
|
package dnsProxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2024-08-25 00:50:34 +03:00
|
|
|
"io"
|
2024-08-24 17:46:34 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-08-25 00:50:34 +03:00
|
|
|
ErrInvalidDNSAddressResourceData = errors.New("invalid DNS address resource data")
|
2024-08-24 17:46:34 +03:00
|
|
|
)
|
|
|
|
|
2024-08-25 00:50:34 +03:00
|
|
|
func parseName(response []byte, pos int) (*Name, int, error) {
|
2024-08-24 17:46:34 +03:00
|
|
|
var nameParts []string
|
|
|
|
var jumped bool
|
|
|
|
var outPos int
|
|
|
|
responseLen := len(response)
|
|
|
|
|
|
|
|
for {
|
2024-08-25 00:50:34 +03:00
|
|
|
if responseLen < pos+1 {
|
|
|
|
return nil, pos, io.EOF
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
length := int(response[pos])
|
|
|
|
pos++
|
|
|
|
if length == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-08-26 19:10:40 +03:00
|
|
|
if length&0xC0 != 0 {
|
2024-08-25 00:50:34 +03:00
|
|
|
if responseLen < pos+1 {
|
|
|
|
return nil, pos, io.EOF
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
if !jumped {
|
|
|
|
outPos = pos + 1
|
|
|
|
}
|
|
|
|
pos = int(binary.BigEndian.Uint16(response[pos-1:pos+1]) & 0x3FFF)
|
|
|
|
jumped = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-08-25 00:50:34 +03:00
|
|
|
if responseLen < pos+length {
|
|
|
|
return nil, pos, io.EOF
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nameParts = append(nameParts, string(response[pos:pos+length]))
|
|
|
|
pos += length
|
|
|
|
}
|
|
|
|
|
|
|
|
if !jumped {
|
|
|
|
outPos = pos
|
|
|
|
}
|
2024-08-25 00:50:34 +03:00
|
|
|
return &Name{Parts: nameParts}, outPos, nil
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseResourceRecord(response []byte, pos int) (ResourceRecord, int, error) {
|
|
|
|
responseLen := len(response)
|
|
|
|
|
2024-08-25 00:50:34 +03:00
|
|
|
rhName, pos, err := parseName(response, pos)
|
|
|
|
if err != nil {
|
|
|
|
return nil, pos, fmt.Errorf("error while parsing DNS name: %w", err)
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
|
|
|
|
if responseLen < pos+10 {
|
2024-08-25 00:50:34 +03:00
|
|
|
return nil, pos, io.EOF
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rh := ResourceRecordHeader{
|
2024-08-25 00:50:34 +03:00
|
|
|
Name: *rhName,
|
2024-08-24 17:46:34 +03:00
|
|
|
Type: binary.BigEndian.Uint16(response[pos+0 : pos+2]),
|
|
|
|
Class: binary.BigEndian.Uint16(response[pos+2 : pos+4]),
|
|
|
|
TTL: binary.BigEndian.Uint32(response[pos+4 : pos+8]),
|
|
|
|
}
|
2024-08-25 00:50:34 +03:00
|
|
|
rdLen := int(binary.BigEndian.Uint16(response[pos+8 : pos+10]))
|
2024-08-24 17:46:34 +03:00
|
|
|
|
|
|
|
pos += 10
|
|
|
|
|
2024-08-25 00:50:34 +03:00
|
|
|
if responseLen < pos+rdLen {
|
|
|
|
return nil, pos, io.EOF
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
switch rh.Type {
|
|
|
|
case 1:
|
2024-08-25 00:50:34 +03:00
|
|
|
if rdLen != 4 {
|
2024-08-24 17:46:34 +03:00
|
|
|
return nil, pos, ErrInvalidDNSAddressResourceData
|
|
|
|
}
|
2024-08-25 00:50:34 +03:00
|
|
|
return Address{
|
|
|
|
ResourceRecordHeader: rh,
|
|
|
|
Address: response[pos+0 : pos+4],
|
|
|
|
}, pos + 4, nil
|
2024-08-24 17:46:34 +03:00
|
|
|
case 2:
|
2024-08-25 00:50:34 +03:00
|
|
|
var ns *Name
|
|
|
|
ns, pos, err = parseName(response, pos)
|
|
|
|
if err != nil {
|
|
|
|
return nil, pos, fmt.Errorf("error while parsing DNS resource record: %w", err)
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
return NameServer{
|
|
|
|
ResourceRecordHeader: rh,
|
2024-08-25 00:50:34 +03:00
|
|
|
NSDName: *ns,
|
2024-08-24 17:46:34 +03:00
|
|
|
}, pos, nil
|
|
|
|
case 5:
|
2024-08-25 00:50:34 +03:00
|
|
|
var cname *Name
|
|
|
|
cname, pos, err = parseName(response, pos)
|
|
|
|
if err != nil {
|
|
|
|
return nil, pos, fmt.Errorf("error while parsing DNS resource record: %w", err)
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
return CName{
|
|
|
|
ResourceRecordHeader: rh,
|
2024-08-25 00:50:34 +03:00
|
|
|
CName: *cname,
|
2024-08-24 17:46:34 +03:00
|
|
|
}, pos, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return Unknown{
|
|
|
|
ResourceRecordHeader: rh,
|
2024-08-25 00:50:34 +03:00
|
|
|
Data: response[pos+0 : pos+rdLen],
|
|
|
|
}, pos + rdLen, nil
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func ParseResponse(response []byte) (*Message, error) {
|
|
|
|
var err error
|
|
|
|
|
2024-08-25 00:50:34 +03:00
|
|
|
msg := new(Message)
|
|
|
|
|
2024-08-24 17:46:34 +03:00
|
|
|
responseLen := len(response)
|
|
|
|
if responseLen < 12 {
|
2024-08-25 00:50:34 +03:00
|
|
|
return msg, io.EOF
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
msg.ID = binary.BigEndian.Uint16(response[0:2])
|
|
|
|
|
|
|
|
flagsRAW := binary.BigEndian.Uint16(response[2:4])
|
|
|
|
msg.Flags = Flags{
|
|
|
|
QR: uint8(flagsRAW >> 15 & 0x1),
|
|
|
|
Opcode: uint8(flagsRAW >> 11 & 0xF),
|
|
|
|
AA: uint8(flagsRAW >> 10 & 0x1),
|
|
|
|
TC: uint8(flagsRAW >> 9 & 0x1),
|
|
|
|
RD: uint8(flagsRAW >> 8 & 0x1),
|
|
|
|
RA: uint8(flagsRAW >> 7 & 0x1),
|
|
|
|
Z1: uint8(flagsRAW >> 6 & 0x1),
|
|
|
|
Z2: uint8(flagsRAW >> 5 & 0x1),
|
|
|
|
Z3: uint8(flagsRAW >> 4 & 0x1),
|
|
|
|
RCode: uint8(flagsRAW >> 0 & 0xF),
|
|
|
|
}
|
|
|
|
|
|
|
|
qdCount := int(binary.BigEndian.Uint16(response[4:6]))
|
|
|
|
anCount := int(binary.BigEndian.Uint16(response[6:8]))
|
|
|
|
nsCount := int(binary.BigEndian.Uint16(response[8:10]))
|
|
|
|
arCount := int(binary.BigEndian.Uint16(response[10:12]))
|
|
|
|
|
|
|
|
pos := 12
|
|
|
|
|
|
|
|
msg.QD = make([]Question, qdCount)
|
|
|
|
for i := 0; i < qdCount; i++ {
|
2024-08-25 00:50:34 +03:00
|
|
|
var name *Name
|
|
|
|
name, pos, err = parseName(response, pos)
|
|
|
|
if err != nil {
|
|
|
|
return msg, fmt.Errorf("error while parsing DNS name: %w", err)
|
|
|
|
}
|
|
|
|
if responseLen < pos+4 {
|
|
|
|
return msg, io.EOF
|
|
|
|
}
|
2024-08-24 17:46:34 +03:00
|
|
|
msg.QD[i] = Question{
|
2024-08-25 00:50:34 +03:00
|
|
|
QName: *name,
|
2024-08-24 17:46:34 +03:00
|
|
|
QType: binary.BigEndian.Uint16(response[pos+0 : pos+2]),
|
|
|
|
QClass: binary.BigEndian.Uint16(response[pos+2 : pos+4]),
|
|
|
|
}
|
|
|
|
pos += 4
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.AN = make([]ResourceRecord, anCount)
|
|
|
|
for i := 0; i < anCount; i++ {
|
|
|
|
msg.AN[i], pos, err = parseResourceRecord(response, pos)
|
|
|
|
if err != nil {
|
2024-08-25 00:50:34 +03:00
|
|
|
return msg, fmt.Errorf("error while parsing AN record: %w", err)
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.NS = make([]ResourceRecord, nsCount)
|
|
|
|
for i := 0; i < nsCount; i++ {
|
|
|
|
msg.NS[i], pos, err = parseResourceRecord(response, pos)
|
|
|
|
if err != nil {
|
2024-08-25 00:50:34 +03:00
|
|
|
return msg, fmt.Errorf("error while parsing NS record: %w", err)
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.AR = make([]ResourceRecord, arCount)
|
|
|
|
for i := 0; i < arCount; i++ {
|
|
|
|
msg.AR[i], pos, err = parseResourceRecord(response, pos)
|
|
|
|
if err != nil {
|
2024-08-25 00:50:34 +03:00
|
|
|
return msg, fmt.Errorf("error while parsing AR record: %w", err)
|
2024-08-24 17:46:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg, nil
|
|
|
|
}
|