package dns import "fmt" type RData interface { Type() uint16 Pack(p *packer) error Unpack(u *unpacker, length uint16) error String() string } type RR struct { Name Name Type uint16 Class uint16 TTL uint32 Data RData } func unpackRR(u *unpacker) (RR, error) { var rr RR var err error rr.Name, err = u.readName() if err != nil { return rr, err } rr.Type, err = u.readUint16() if err != nil { return rr, err } rr.Class, err = u.readUint16() if err != nil { return rr, err } rr.TTL, err = u.readUint32() if err != nil { return rr, err } rdlength, err := u.readUint16() if err != nil { return rr, err } rdStart := u.off if u.off+int(rdlength) > len(u.buf) { return rr, fmt.Errorf("dns: short rdata") } switch rr.Type { case TypeA: rr.Data = &A{} case TypeNS: rr.Data = &NS{} case TypeCNAME: rr.Data = &CNAME{} case TypeSOA: rr.Data = &SOA{} case TypePTR: rr.Data = &PTR{} case TypeMX: rr.Data = &MX{} case TypeTXT: rr.Data = &TXT{} case TypeAAAA: rr.Data = &AAAA{} case TypeOPT: opt := &OPT{ UDPSize: rr.Class, } opt.ExtRCode = uint8(rr.TTL >> 24) opt.Version = uint8(rr.TTL >> 16) // https://datatracker.ietf.org/doc/html/rfc3225 // https://datatracker.ietf.org/doc/html/rfc4035 opt.DO = rr.TTL&0x8000 != 0 opt.Z = uint16(rr.TTL & 0x7FFF) rr.Data = opt default: rr.Data = &Unknown{RRType: rr.Type} } if err := rr.Data.Unpack(u, rdlength); err != nil { return rr, err } if u.off != rdStart+int(rdlength) { return rr, fmt.Errorf("dns: rdata length mismatch") } return rr, nil } func (rr *RR) pack(p *packer) error { if err := p.writeName(rr.Name); err != nil { return err } p.writeUint16(rr.Type) p.writeUint16(rr.Class) p.writeUint32(rr.TTL) rdlenOff := len(p.buf) p.buf = append(p.buf, 0, 0) rdStart := len(p.buf) if err := rr.Data.Pack(p); err != nil { return err } rdlen := uint16(len(p.buf) - rdStart) p.buf[rdlenOff] = byte(rdlen >> 8) p.buf[rdlenOff+1] = byte(rdlen) return nil }