a go dns packet parser

Compare changes

Choose any two refs to compare.

+26
message.go
···
// Decode decodes a DNS packet.
func (m *Message) Decode(buf []byte) (err error) {
+
// gets checked when parsing additional records
+
m.HasEDNS = false
+
offset, err := m.Header.Decode(buf, 0)
if err != nil {
return fmt.Errorf("failed to decode message header: %w", err)
···
return fmt.Errorf("failed to decode additional record #%d: %w", i+1, err)
}
+
if rr.RType == OPTType {
+
opt, ok := rr.RData.(*OPT)
+
if !ok {
+
// this should never fail
+
return fmt.Errorf("unable to parse RData as OPT")
+
}
+
+
m.HasEDNS = true
+
m.ExtendedRCode = uint8(rr.TTL >> 24 & 0xFF000000)
+
m.EDNSVersion = uint8((rr.TTL & 0x00FF0000) >> 16)
+
m.EDNSFlags = uint16(rr.TTL & 0x0000FFFF)
+
m.EDNSOptions = opt.Options
+
m.UDPSize = uint16(rr.RClass)
+
}
+
m.Additional = append(m.Additional, rr)
}
+
if !m.HasEDNS {
+
m.ExtendedRCode = 0
+
m.EDNSVersion = 0
+
m.EDNSFlags = 0
+
m.EDNSOptions = make([]EDNSOption, 0)
+
m.UDPSize = 512 // default in rfc-1035 section 2.3.4.
+
}
+
return nil
}
+11
.golangci.toml
···
+
version = "2"
+
[linters.settings.govet]
+
enable_all = true
+
[linters.settings.staticcheck]
+
checks = ["all"]
+
[linters.settings.exhaustive]
+
check = ["switch"]
+
[linters.settings.exhaustruct]
+
[linters.settings.goconst]
+
[linters.settings.testifylint]
+
enable-all = true
+7
Justfile
···
+
format:
+
go fmt ./...
+
gofumpt -l -w $(fd .go)
+
+
verify:
+
go vet ./...
+
golangci-lint run
+1 -1
message_test.go
···
},
},
EDNSVersion: 0x64,
-
UDPSize: 0x4d0,
+
UDPSize: 0x4d0,
},
},
}
+6
README.md
···
# Magna
+
## Contributing
+
Dependencies:
+
- go 1.24
+
- just
+
- golint-ci
+
this is a go package for packing/unpacking dns packets.
> which we expect to be so popular that it would be a waste of wire space
+30 -1
resource_record.go
···
return nil, fmt.Errorf("A record: cannot encode non-IPv4 address %s", a.Address.String())
}
-
return append(bytes, a.Address.To4()...), nil
+
return append(bytes, ipv4...), nil
}
func (a A) String() string {
···
return fmt.Sprintf("%d %s", rt.Preference, rt.IntermediateHost)
}
+
func (a *AAAA) Decode(buf []byte, offset int, rdlength int) (int, error) {
+
bytes, offset, err := getSlice(buf, offset, rdlength)
+
if err != nil {
+
return offset, fmt.Errorf("AAAA record: failed to read address data: %w", err)
+
}
+
+
a.Address = net.IP(bytes)
+
if a.Address.To16() == nil {
+
return offset, fmt.Errorf("AAAA record: decoded data is not a valid IPv6 address: %v", bytes)
+
}
+
return offset, nil
+
}
+
+
func (a *AAAA) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) {
+
ipv6 := a.Address.To16()
+
// XXX: ipv6 encodes ipv4 so need to check its not ipv4
+
if a.Address.To4() != nil || ipv6 == nil {
+
return nil, fmt.Errorf("AAAA record: cannot encode non-IPv6 address %s", a.Address.String())
+
}
+
+
return append(bytes, ipv6...), nil
+
}
+
+
func (a AAAA) String() string {
+
return a.Address.String()
+
}
+
func (opt *OPT) Decode(buf []byte, offset int, rdlength int) (int, error) {
// s := offset
opt.Options = make([]EDNSOption, 0)
···
r.RData = &ISDN{}
case 21:
r.RData = &RT{}
+
case 28:
+
r.RData = &AAAA{}
case 41:
r.RData = &OPT{}
default:
+30
resource_record_test.go
···
})
}
}
+
+
func TestAAAARecord(t *testing.T) {
+
addr := net.ParseIP("2001:db8::1")
+
rdataBytes := []byte(addr)
+
aaaa := &AAAA{}
+
+
offset, err := aaaa.Decode([]byte{}, 0, 16)
+
assert.Error(t, err, "Decode should fail with empty buffer")
+
assert.True(t, errors.Is(err, &BufferOverflowError{}))
+
+
offset, err = aaaa.Decode(rdataBytes, 0, 16)
+
assert.NoError(t, err)
+
assert.Equal(t, 16, offset)
+
assert.Equal(t, addr, aaaa.Address)
+
+
_, err = aaaa.Decode([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 0, 15)
+
assert.Error(t, err)
+
assert.Contains(t, err.Error(), "AAAA record:")
+
+
aaaaEncode := &AAAA{Address: addr}
+
encoded := encodeRData(t, aaaaEncode)
+
assert.Equal(t, rdataBytes, encoded)
+
+
ipv4 := net.ParseIP("192.168.1.1")
+
aaaaEncodeInvalid := &AAAA{Address: ipv4}
+
_, err = aaaaEncodeInvalid.Encode([]byte{}, &map[string]uint16{})
+
+
assert.Error(t, err)
+
assert.Contains(t, err.Error(), "cannot encode non-IPv6 address")
+
}