From 2ea9b9b854551a07d85935e6208532fa783341ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 08:37:02 +0000 Subject: [PATCH] Bump github.com/gopacket/gopacket from 1.7.0 to 1.7.1 Bumps [github.com/gopacket/gopacket](https://github.com/gopacket/gopacket) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/gopacket/gopacket/releases) - [Commits](https://github.com/gopacket/gopacket/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: github.com/gopacket/gopacket dependency-version: 1.7.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../gopacket/gopacket/layers/ague_var0.go | 14 +- .../gopacket/gopacket/layers/arp.go | 14 +- .../gopacket/gopacket/layers/ctp.go | 17 + .../gopacket/gopacket/layers/dhcpv4.go | 5 + .../gopacket/gopacket/layers/diameter.go | 5 + .../gopacket/gopacket/layers/dns.go | 649 ++++++++++++++---- .../gopacket/gopacket/layers/dot11.go | 4 + .../gopacket/gopacket/layers/erspan2.go | 5 + .../gopacket/gopacket/layers/etherip.go | 5 + .../gopacket/gopacket/layers/fddi.go | 5 + .../gopacket/gopacket/layers/gtp.go | 3 + .../gopacket/gopacket/layers/ipsec.go | 6 + .../gopacket/gopacket/layers/lcm.go | 7 + .../gopacket/gopacket/layers/linux_sll.go | 7 +- .../gopacket/gopacket/layers/linux_sll2.go | 5 + .../gopacket/gopacket/layers/mpls.go | 8 + .../gopacket/gopacket/layers/pflog.go | 5 +- .../gopacket/gopacket/layers/pktap.go | 4 + .../gopacket/gopacket/layers/ppp.go | 24 +- .../gopacket/gopacket/layers/pppoe.go | 12 +- .../gopacket/gopacket/layers/prism.go | 13 +- .../gopacket/gopacket/layers/radiotap.go | 4 + .../gopacket/gopacket/layers/rudp.go | 15 +- .../gopacket/gopacket/layers/sctp.go | 26 +- .../gopacket/gopacket/layers/sflow.go | 9 + .../gopacket/gopacket/layers/sip.go | 7 + .../gopacket/gopacket/layers/tcp.go | 22 +- .../gopacket/gopacket/layers/tls_handshake.go | 59 +- .../gopacket/gopacket/layers/udplite.go | 5 + .../gopacket/gopacket/layers/vrrp.go | 10 + vendor/modules.txt | 2 +- 33 files changed, 826 insertions(+), 156 deletions(-) diff --git a/go.mod b/go.mod index 478e9aa3..543234ac 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.7 require ( github.com/gdamore/tcell/v2 v2.13.10 - github.com/gopacket/gopacket v1.7.0 + github.com/gopacket/gopacket v1.7.1 github.com/jpillora/sizestr v1.0.0 github.com/mattn/go-sqlite3 v1.14.48 github.com/navidys/tvxwidgets v0.14.0 diff --git a/go.sum b/go.sum index 27dd2ea3..16d9afb4 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oX github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopacket/gopacket v1.7.0 h1:GdmF8ytGnjtSvyy30CTZhIwX1ybDWH3Q0MNK0blIKzA= -github.com/gopacket/gopacket v1.7.0/go.mod h1:QKowPlTLrQU2rqV5C5I14Aoaid3l8da3kbddibc/Wgk= +github.com/gopacket/gopacket v1.7.1 h1:1C7/wrJ5HyiEAYDtStJHQk4rV0ChpanZDV9+3Ov3gaM= +github.com/gopacket/gopacket v1.7.1/go.mod h1:QKowPlTLrQU2rqV5C5I14Aoaid3l8da3kbddibc/Wgk= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/vendor/github.com/gopacket/gopacket/layers/ague_var0.go b/vendor/github.com/gopacket/gopacket/layers/ague_var0.go index ea256878..201c7552 100644 --- a/vendor/github.com/gopacket/gopacket/layers/ague_var0.go +++ b/vendor/github.com/gopacket/gopacket/layers/ague_var0.go @@ -72,12 +72,22 @@ func (l AGUEVar0) CanDecode() gopacket.LayerClass { } // DecodeFromBytes extracts our header data from a serialized packet. -func (l *AGUEVar0) DecodeFromBytes(data []byte, _ gopacket.DecodeFeedback) error { +func (l *AGUEVar0) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return errors.New("AGUE var0 packet too small") + } l.Version = data[0] >> 6 l.C = data[0]&0x20 != 0 l.Protocol = IPProtocol(data[1]) l.Flags = (uint16(data[2]) << 8) | uint16(data[3]) - hlen := data[0] & 0x1f + // hlen counts 32-bit extension words; compute the bound in int so it cannot + // wrap past len(data). + hlen := int(data[0] & 0x1f) + if 4+hlen > len(data) { + df.SetTruncated() + return errors.New("AGUE var0 extensions exceed packet length") + } l.Extensions = data[4 : 4+hlen] l.Data = data[4+hlen:] return nil diff --git a/vendor/github.com/gopacket/gopacket/layers/arp.go b/vendor/github.com/gopacket/gopacket/layers/arp.go index 64120253..a89542e3 100644 --- a/vendor/github.com/gopacket/gopacket/layers/arp.go +++ b/vendor/github.com/gopacket/gopacket/layers/arp.go @@ -49,15 +49,17 @@ func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { arp.HwAddressSize = data[4] arp.ProtAddressSize = data[5] arp.Operation = binary.BigEndian.Uint16(data[6:8]) - arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize - if len(data) < int(arpLength) { + hwAddressSize := int(arp.HwAddressSize) + protAddressSize := int(arp.ProtAddressSize) + arpLength := 8 + 2*hwAddressSize + 2*protAddressSize + if len(data) < arpLength { df.SetTruncated() return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength) } - arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize] - arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize] - arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize] - arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize] + arp.SourceHwAddress = data[8 : 8+hwAddressSize] + arp.SourceProtAddress = data[8+hwAddressSize : 8+hwAddressSize+protAddressSize] + arp.DstHwAddress = data[8+hwAddressSize+protAddressSize : 8+2*hwAddressSize+protAddressSize] + arp.DstProtAddress = data[8+2*hwAddressSize+protAddressSize : arpLength] arp.Contents = data[:arpLength] arp.Payload = data[arpLength:] diff --git a/vendor/github.com/gopacket/gopacket/layers/ctp.go b/vendor/github.com/gopacket/gopacket/layers/ctp.go index 8b43cb80..639c9311 100644 --- a/vendor/github.com/gopacket/gopacket/layers/ctp.go +++ b/vendor/github.com/gopacket/gopacket/layers/ctp.go @@ -8,6 +8,7 @@ package layers import ( "encoding/binary" + "errors" "fmt" "github.com/gopacket/gopacket" @@ -71,6 +72,10 @@ func (c *EthernetCTPReply) LayerType() gopacket.LayerType { func (c *EthernetCTPReply) Payload() []byte { return c.Data } func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 2 { + p.SetTruncated() + return errors.New("EthernetCTP packet too small") + } c := &EthernetCTP{ SkipCount: binary.LittleEndian.Uint16(data[:2]), BaseLayer: BaseLayer{data[:2], data[2:]}, @@ -85,9 +90,17 @@ func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error { // decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP // layer type to decode next, then decodes based on that. func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 2 { + p.SetTruncated() + return errors.New("EthernetCTP function type too small") + } function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2])) switch function { case EthernetCTPFunctionReply: + if len(data) < 4 { + p.SetTruncated() + return errors.New("EthernetCTP reply too small") + } reply := &EthernetCTPReply{ Function: function, ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]), @@ -98,6 +111,10 @@ func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) er p.SetApplicationLayer(reply) return nil case EthernetCTPFunctionForwardData: + if len(data) < 8 { + p.SetTruncated() + return errors.New("EthernetCTP forward data too small") + } forward := &EthernetCTPForwardData{ Function: function, ForwardAddress: data[2:8], diff --git a/vendor/github.com/gopacket/gopacket/layers/dhcpv4.go b/vendor/github.com/gopacket/gopacket/layers/dhcpv4.go index 1f1e623e..cd44a607 100644 --- a/vendor/github.com/gopacket/gopacket/layers/dhcpv4.go +++ b/vendor/github.com/gopacket/gopacket/layers/dhcpv4.go @@ -132,6 +132,11 @@ func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error d.Operation = DHCPOp(data[0]) d.HardwareType = LinkType(data[1]) d.HardwareLen = data[2] + if d.HardwareLen > 16 { + // The BOOTP chaddr field is fixed at 16 bytes (data[28:44]); a larger + // hardware length would read past it (and 28+HardwareLen wraps in uint8). + return fmt.Errorf("DHCPv4 hardware address length %d exceeds 16", d.HardwareLen) + } d.RelayHops = data[3] d.Xid = binary.BigEndian.Uint32(data[4:8]) d.Secs = binary.BigEndian.Uint16(data[8:10]) diff --git a/vendor/github.com/gopacket/gopacket/layers/diameter.go b/vendor/github.com/gopacket/gopacket/layers/diameter.go index a06a46ca..00836276 100644 --- a/vendor/github.com/gopacket/gopacket/layers/diameter.go +++ b/vendor/github.com/gopacket/gopacket/layers/diameter.go @@ -128,6 +128,11 @@ func (d *Diameter) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) erro // Message Length is 24 bits (bytes 1-3) d.MessageLength = uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if d.MessageLength < 20 { + // The length includes the 20-byte header; a smaller value would make + // the data[20:MessageLength] AVP slice below have out-of-order bounds. + return fmt.Errorf("diameter message length %d below 20-byte header", d.MessageLength) + } if uint32(len(data)) < d.MessageLength { return fmt.Errorf("diameter message truncated: expected %d bytes, got %d", d.MessageLength, len(data)) } diff --git a/vendor/github.com/gopacket/gopacket/layers/dns.go b/vendor/github.com/gopacket/gopacket/layers/dns.go index a4db29eb..f194b5c6 100644 --- a/vendor/github.com/gopacket/gopacket/layers/dns.go +++ b/vendor/github.com/gopacket/gopacket/layers/dns.go @@ -7,6 +7,7 @@ package layers import ( + "bytes" "encoding/binary" "errors" "fmt" @@ -267,6 +268,19 @@ func (doc DNSOpCode) String() string { // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // DNS contains data from a single Domain Name Service packet. +// +// DNS name fields (such as DNSQuestion.Name and DNSResourceRecord.Name) hold +// names in dotted presentation form. When a packet decoded by this layer is +// re-serialized without changing a name field, the original wire label +// boundaries are preserved, so a label that legitimately contains a literal +// dot (for example a DNS-SD instance label "foo.bar") round-trips correctly. +// +// A name field that is newly constructed or changed is instead parsed as a +// presentation name, where a dot separates labels. To embed a literal dot, +// backslash, or arbitrary byte in a single label, use the escapes "\.", "\\", +// or "\DDD" (three decimal digits). A decoded name copied verbatim into a +// different field loses its preserved boundaries and is re-parsed this way, so +// any literal dot in it must be escaped first. type DNS struct { BaseLayer @@ -428,65 +442,285 @@ func b2i(b bool) int { return 0 } -func recSize(rr *DNSResourceRecord) int { +// dnsNameLabels is the sequence of raw wire labels (each without its length +// octet) that a DNS name decoded to. It is recorded only when a name needs its +// exact label boundaries preserved across a decode -> serialize round trip. +type dnsNameLabels [][]byte + +// dnsNameMeta preserves one decoded DNS name's wire label boundaries together +// with a snapshot of the decoded presentation bytes. Serialization reproduces +// the exact labels only while the public name field still equals orig; once the +// caller changes the name, the metadata is treated as stale. +type dnsNameMeta struct { + labels dnsNameLabels + orig []byte +} + +// dnsRecordNameMeta holds the preserved name metadata for one resource record. +// It is allocated lazily, only when a decoded name actually contains a label +// boundary that the dotted presentation form cannot represent, so the common +// case costs a single nil pointer rather than a metadata field per name. A +// record carries at most its owner name plus the one or two DNS names in its +// RDATA, so rdata holds the single RDATA name (NS, CNAME, PTR, MX, SRV, the +// NAPTR Replacement, the SVCB/HTTPS Target, the RRSIG SignerName, or the SOA +// MName), and rdata2 holds the SOA RName. +type dnsRecordNameMeta struct { + name dnsNameMeta + rdata dnsNameMeta + rdata2 dnsNameMeta +} + +// newDNSNameMeta builds metadata for a decoded name. Call it only when labels +// is non-nil (the name needs boundary preservation). +func newDNSNameMeta(name []byte, labels dnsNameLabels) dnsNameMeta { + return dnsNameMeta{labels: labels, orig: bytes.Clone(name)} +} + +// dnsLabelNeedsPreservation reports whether a wire label cannot be represented +// unambiguously in the dotted presentation Name. '.' and '\' are the only +// metacharacters of presentation form: the label separator and the escape +// introducer (see encodeDNSPresentationName). A label containing either would be +// mis-split or mis-escaped if flattened into Name and re-encoded, so its exact +// wire boundaries must be preserved. Every other byte round-trips literally. +func dnsLabelNeedsPreservation(label []byte) bool { + for _, b := range label { + if b == '.' || b == '\\' { + return true + } + } + return false +} + +// collectDNSWireLabels reads the literal wire labels of a name in data[offset:end]. +// It stops at the first compression pointer or out-of-range length, returning the +// labels gathered so far; the caller appends any pointed-to labels separately. +func collectDNSWireLabels(data []byte, offset, end int) dnsNameLabels { + var labels dnsNameLabels + for offset < end { + if offset >= len(data) || data[offset]&0xc0 != 0 { + return labels + } + next := offset + int(data[offset]) + 1 + if next > end || next > len(data) { + return labels + } + labels = append(labels, bytes.Clone(data[offset+1:next])) + offset = next + } + return labels +} + +// encodeDNSPresentationName encodes a presentation-form DNS name (where '.' +// separates labels and '\' introduces an escape) into wire format. When data +// is non-nil it writes the encoding starting at offset; when data is nil it only +// measures. Either way it returns the wire size, so a caller sizes a buffer with +// data == nil and then fills it with identical logic: one source of truth for +// the grammar, with no separate size/write passes to keep in sync. +// +// Recognized escapes are \. \\ and \DDD (three decimal digits, value <= 255); +// any other \X is preserved literally as backslash plus X. +func encodeDNSPresentationName(name []byte, data []byte, offset int) (int, error) { + start := offset + if len(name) == 0 || (len(name) == 1 && name[0] == '.') { + if data != nil { + data[offset] = 0x00 + } + return 1, nil + } + labelOffset := offset // reserved slot for the current label's length octet + offset++ + labelLen := 0 + lastWasSeparator := false + for i := 0; i < len(name); i++ { + c := name[i] + if c == '.' { + if labelLen > 63 { + return 0, errDNSNameTooLong + } + if data != nil { + data[labelOffset] = byte(labelLen) + } + labelOffset = offset + offset++ + labelLen = 0 + lastWasSeparator = true + continue + } + if c == '\\' { + if i+1 >= len(name) { + return 0, errDNSNameInvalidIndex + } + next := name[i+1] + switch { + case next == '.' || next == '\\': + if data != nil { + data[offset] = next + } + offset++ + labelLen++ + i++ + case next >= '0' && next <= '9': + if i+3 >= len(name) || name[i+2] < '0' || name[i+2] > '9' || name[i+3] < '0' || name[i+3] > '9' { + return 0, errDNSNameInvalidIndex + } + v := int(name[i+1]-'0')*100 + int(name[i+2]-'0')*10 + int(name[i+3]-'0') + if v > 255 { + return 0, errDNSNameInvalidIndex + } + if data != nil { + data[offset] = byte(v) + } + offset++ + labelLen++ + i += 3 + default: + if data != nil { + data[offset] = c + data[offset+1] = next + } + offset += 2 + labelLen += 2 + i++ + } + lastWasSeparator = false + continue + } + if data != nil { + data[offset] = c + } + offset++ + labelLen++ + lastWasSeparator = false + } + if labelLen > 63 { + return 0, errDNSNameTooLong + } + if !lastWasSeparator { + if data != nil { + data[labelOffset] = byte(labelLen) + } + } else { + offset = labelOffset + } + if data != nil { + data[offset] = 0x00 + } + size := offset + 1 - start + if size > 255 { + return 0, errDNSNameTooLong + } + return size, nil +} + +func dnsNameLabelsSize(labels dnsNameLabels) (int, error) { + size := 1 + for _, label := range labels { + if len(label) > 63 { + return 0, errDNSNameTooLong + } + size += 1 + len(label) + if size > 255 { + return 0, errDNSNameTooLong + } + } + return size, nil +} + +// usePreservedDNSLabels reports whether the preserved wire labels in m should be +// used to encode name: only when metadata exists, recorded labels, and the public +// name field still matches the decoded snapshot (i.e. the caller did not change it). +func usePreservedDNSLabels(name []byte, m *dnsNameMeta) bool { + return m != nil && m.labels != nil && bytes.Equal(name, m.orig) +} + +func dnsNameSize(name []byte, m *dnsNameMeta) (int, error) { + if usePreservedDNSLabels(name, m) { + return dnsNameLabelsSize(m.labels) + } + return encodeDNSPresentationName(name, nil, 0) +} + +func recSize(rr *DNSResourceRecord) (int, error) { switch rr.Type { case DNSTypeA: - return 4 + return 4, nil case DNSTypeAAAA: - return 16 + return 16, nil case DNSTypeNS: - return len(rr.NS) + 2 + return dnsNameSize(rr.NS, rr.rdataMeta()) case DNSTypeCNAME: - return len(rr.CNAME) + 2 + return dnsNameSize(rr.CNAME, rr.rdataMeta()) case DNSTypePTR: - return len(rr.PTR) + 2 + return dnsNameSize(rr.PTR, rr.rdataMeta()) case DNSTypeSOA: - return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20 + mNameSize, err := dnsNameSize(rr.SOA.MName, rr.rdataMeta()) + if err != nil { + return 0, err + } + rNameSize, err := dnsNameSize(rr.SOA.RName, rr.rdata2Meta()) + if err != nil { + return 0, err + } + return mNameSize + rNameSize + 20, nil case DNSTypeMX: - return 2 + len(rr.MX.Name) + 2 + nameSize, err := dnsNameSize(rr.MX.Name, rr.rdataMeta()) + if err != nil { + return 0, err + } + return 2 + nameSize, nil case DNSTypeTXT: l := len(rr.TXTs) for _, txt := range rr.TXTs { l += len(txt) } - return l + return l, nil case DNSTypeSRV: - return 6 + len(rr.SRV.Name) + 2 + nameSize, err := dnsNameSize(rr.SRV.Name, rr.rdataMeta()) + if err != nil { + return 0, err + } + return 6 + nameSize, nil case DNSTypeNAPTR: - return 4 + 1 + len(rr.NAPTR.Flags) + 1 + len(rr.NAPTR.Service) + 1 + len(rr.NAPTR.Regexp) + len(rr.NAPTR.Replacement) + 2 + replacementSize, err := dnsNameSize(rr.NAPTR.Replacement, rr.rdataMeta()) + if err != nil { + return 0, err + } + return 4 + 1 + len(rr.NAPTR.Flags) + 1 + len(rr.NAPTR.Service) + 1 + len(rr.NAPTR.Regexp) + replacementSize, nil case DNSTypeURI: - return 4 + len(rr.URI.Target) + return 4 + len(rr.URI.Target), nil case DNSTypeOPT: l := len(rr.OPT) * 4 for _, opt := range rr.OPT { l += len(opt.Data) } - return l + return l, nil case DNSTypeRRSIG: - return rr.RRSIG.size() + return rr.RRSIG.size(rr.rdataMeta()) case DNSTypeDNSKEY: - return rr.DNSKEY.size() + return rr.DNSKEY.size(), nil case DNSTypeSVCB, DNSTypeHTTPS: - return rr.SVCB.size() + return rr.SVCB.size(rr.rdataMeta()) } - return 0 + return 0, nil } -func computeSize(recs []DNSResourceRecord) int { +func computeSize(recs []DNSResourceRecord) (int, error) { sz := 0 for _, rr := range recs { - v := len(rr.Name) - - if v == 0 { - sz += v + 11 - } else { - sz += v + 12 + v, err := dnsNameSize(rr.Name, rr.ownerMeta()) + if err != nil { + return 0, err } + sz += v + 10 - sz += recSize(&rr) + rSz, err := recSize(&rr) + if err != nil { + return 0, err + } + sz += rSz } - return sz + return sz, nil } // SerializeTo writes the serialized form of this layer into the @@ -494,11 +728,27 @@ func computeSize(recs []DNSResourceRecord) int { func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { dsz := 0 for _, q := range d.Questions { - dsz += len(q.Name) + 6 + qSize, err := dnsNameSize(q.Name, q.nameMeta) + if err != nil { + return err + } + dsz += qSize + 4 + } + answersSize, err := computeSize(d.Answers) + if err != nil { + return err + } + dsz += answersSize + authoritiesSize, err := computeSize(d.Authorities) + if err != nil { + return err } - dsz += computeSize(d.Answers) - dsz += computeSize(d.Authorities) - dsz += computeSize(d.Additionals) + dsz += authoritiesSize + additionalsSize, err := computeSize(d.Additionals) + if err != nil { + return err + } + dsz += additionalsSize bytes, err := b.PrependBytes(12 + dsz) if err != nil { @@ -521,7 +771,10 @@ func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt off := 12 for _, qd := range d.Questions { - n := qd.encode(bytes, off) + n, err := qd.encode(bytes, off) + if err != nil { + return err + } off += n } @@ -558,18 +811,19 @@ func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt const maxRecursionLevel = 255 -func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) { +func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, dnsNameLabels, int, error) { if level > maxRecursionLevel { - return nil, 0, errMaxRecursion + return nil, nil, 0, errMaxRecursion } else if offset >= len(data) { - return nil, 0, errDNSNameOffsetTooHigh + return nil, nil, 0, errDNSNameOffsetTooHigh } else if offset < 0 { - return nil, 0, errDNSNameOffsetNegative + return nil, nil, 0, errDNSNameOffsetNegative } start := len(*buffer) index := offset + var labels dnsNameLabels if data[index] == 0x00 { - return nil, index + 1, nil + return nil, labels, index + 1, nil } loop: for data[index] != 0x00 { @@ -585,12 +839,18 @@ loop: */ index2 := index + int(data[index]) + 1 if index2-offset > 255 { - return nil, 0, errDNSNameTooLong + return nil, nil, 0, errDNSNameTooLong } else if index2 < index+1 || index2 > len(data) { - return nil, 0, errDNSNameInvalidIndex + return nil, nil, 0, errDNSNameInvalidIndex } + label := data[index+1 : index2] *buffer = append(*buffer, '.') - *buffer = append(*buffer, data[index+1:index2]...) + *buffer = append(*buffer, label...) + if labels != nil { + labels = append(labels, bytes.Clone(label)) + } else if dnsLabelNeedsPreservation(label) { + labels = collectDNSWireLabels(data, offset, index2) + } index = index2 case 0xc0: @@ -614,39 +874,49 @@ loop: - a sequence of labels ending with a pointer */ if index+2 > len(data) { - return nil, 0, errDNSPointerOffsetTooHigh + return nil, nil, 0, errDNSPointerOffsetTooHigh } offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff) if offsetp > len(data) { - return nil, 0, errDNSPointerOffsetTooHigh + return nil, nil, 0, errDNSPointerOffsetTooHigh } // This looks a little tricky, but actually isn't. Because of how // decodeName is written, calling it appends the decoded name to the // current buffer. We already have the start of the buffer, then, so // once this call is done buffer[start:] will contain our full name. - _, _, err := decodeName(data, offsetp, buffer, level+1) + pointedName, pointedLabels, _, err := decodeName(data, offsetp, buffer, level+1) if err != nil { - return nil, 0, err + return nil, nil, 0, err + } + if pointedLabels != nil { + if labels == nil { + labels = collectDNSWireLabels(data, offset, index) + } + labels = append(labels, pointedLabels...) + } else if labels != nil && len(pointedName) > 0 { + for _, lbl := range bytes.Split(pointedName, []byte{'.'}) { + labels = append(labels, bytes.Clone(lbl)) + } } index++ // pointer is two bytes, so add an extra byte here. break loop /* EDNS, or other DNS option ? */ case 0x40: // RFC 2673 - return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)", + return nil, nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)", data[index], index) case 0x80: - return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)", + return nil, nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)", data[index], index) } if index >= len(data) { - return nil, 0, errDNSIndexOutOfRange + return nil, nil, 0, errDNSIndexOutOfRange } } if len(*buffer) <= start { - return (*buffer)[start:], index + 1, nil + return (*buffer)[start:], labels, index + 1, nil } - return (*buffer)[start+1:], index + 1, nil + return (*buffer)[start+1:], labels, index + 1, nil } // DNSQuestion wraps a single request (question) within a DNS query. @@ -654,10 +924,14 @@ type DNSQuestion struct { Name []byte Type DNSType Class DNSClass + + // nameMeta preserves the wire label boundaries of a decoded Name; it is set + // only when Name contains a label with a literal dot or backslash. See DNS. + nameMeta *dnsNameMeta } func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { - name, endq, err := decodeName(data, offset, buffer, 1) + name, labels, endq, err := decodeName(data, offset, buffer, 1) if err != nil { return 0, err } @@ -667,18 +941,25 @@ func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback } q.Name = name + if labels != nil { + meta := newDNSNameMeta(name, labels) + q.nameMeta = &meta + } q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) return endq + 4, nil } -func (q *DNSQuestion) encode(data []byte, offset int) int { - noff := encodeName(q.Name, data, offset) - nSz := noff - offset +func (q *DNSQuestion) encode(data []byte, offset int) (int, error) { + nSz, err := encodeDNSName(q.Name, q.nameMeta, data, offset) + if err != nil { + return 0, err + } + noff := offset + nSz binary.BigEndian.PutUint16(data[noff:], uint16(q.Type)) binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class)) - return nSz + 4 + return nSz + 4, nil } // DNSResourceRecord @@ -731,11 +1012,49 @@ type DNSResourceRecord struct { // Undecoded TXT for backward compatibility TXT []byte + + // names preserves wire label boundaries for this record's owner and RDATA + // names; allocated lazily, only when a name needs it. See DNS and dnsRecordNameMeta. + names *dnsRecordNameMeta +} + +// ensureNameMeta returns the record's preserved-name metadata block, allocating it +// on first use. Only call it when a decoded name actually needs preservation. +func (rr *DNSResourceRecord) ensureNameMeta() *dnsRecordNameMeta { + if rr.names == nil { + rr.names = &dnsRecordNameMeta{} + } + return rr.names +} + +// ownerMeta, rdataMeta, and rdata2Meta return the preserved metadata for the +// owner name, the single RDATA name (or SOA MName), and the SOA RName. They +// return nil when no preservation metadata was recorded, which the size and +// encode helpers treat as "use presentation form". +func (rr *DNSResourceRecord) ownerMeta() *dnsNameMeta { + if rr.names == nil { + return nil + } + return &rr.names.name +} + +func (rr *DNSResourceRecord) rdataMeta() *dnsNameMeta { + if rr.names == nil { + return nil + } + return &rr.names.rdata +} + +func (rr *DNSResourceRecord) rdata2Meta() *dnsNameMeta { + if rr.names == nil { + return nil + } + return &rr.names.rdata2 } // decode decodes the resource record, returning the total length of the record. func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { - name, endq, err := decodeName(data, offset, buffer, 1) + name, labels, endq, err := decodeName(data, offset, buffer, 1) if err != nil { return 0, err } @@ -745,6 +1064,9 @@ func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeF } rr.Name = name + if labels != nil { + rr.ensureNameMeta().name = newDNSNameMeta(name, labels) + } rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8]) @@ -764,34 +1086,42 @@ func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeF return endq + 10 + int(rr.DataLength), nil } -func encodeName(name []byte, data []byte, offset int) int { - l := 0 - for i := range name { - if name[i] == '.' { - data[offset+i-l] = byte(l) - l = 0 - } else { - // skip one to write the length - data[offset+i+1] = name[i] - l++ - } +func encodeDNSNameLabels(labels dnsNameLabels, data []byte, offset int) (int, error) { + size, err := dnsNameLabelsSize(labels) + if err != nil { + return 0, err } - - if len(name) == 0 { - data[offset] = 0x00 // terminal - return offset + 1 + start := offset + for _, label := range labels { + data[offset] = byte(len(label)) + offset++ + copy(data[offset:], label) + offset += len(label) } + data[offset] = 0x00 + if offset+1-start != size { + return 0, errDNSNameInvalidIndex + } + return size, nil +} - // length for final portion - data[offset+len(name)-l] = byte(l) - data[offset+len(name)+1] = 0x00 // terminal - return offset + len(name) + 2 +// encodeDNSName writes name into data at offset, returning the wire size. If the +// preserved wire labels in m still match name (an unchanged decoded name), they +// are re-emitted exactly; otherwise name is parsed as presentation form. +func encodeDNSName(name []byte, m *dnsNameMeta, data []byte, offset int) (int, error) { + if usePreservedDNSLabels(name, m) { + return encodeDNSNameLabels(m.labels, data, offset) + } + return encodeDNSPresentationName(name, data, offset) } func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) { - noff := encodeName(rr.Name, data, offset) - nSz := noff - offset + nSz, err := encodeDNSName(rr.Name, rr.ownerMeta(), data, offset) + if err != nil { + return 0, err + } + noff := offset + nSz binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type)) binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class)) @@ -803,14 +1133,27 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria case DNSTypeAAAA: copy(data[noff+10:], rr.IP) case DNSTypeNS: - encodeName(rr.NS, data, noff+10) + if _, err = encodeDNSName(rr.NS, rr.rdataMeta(), data, noff+10); err != nil { + return 0, err + } case DNSTypeCNAME: - encodeName(rr.CNAME, data, noff+10) + if _, err = encodeDNSName(rr.CNAME, rr.rdataMeta(), data, noff+10); err != nil { + return 0, err + } case DNSTypePTR: - encodeName(rr.PTR, data, noff+10) + if _, err = encodeDNSName(rr.PTR, rr.rdataMeta(), data, noff+10); err != nil { + return 0, err + } case DNSTypeSOA: - noff2 := encodeName(rr.SOA.MName, data, noff+10) - noff2 = encodeName(rr.SOA.RName, data, noff2) + n1, err := encodeDNSName(rr.SOA.MName, rr.rdataMeta(), data, noff+10) + if err != nil { + return 0, err + } + n2, err := encodeDNSName(rr.SOA.RName, rr.rdata2Meta(), data, noff+10+n1) + if err != nil { + return 0, err + } + noff2 := noff + 10 + n1 + n2 binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial) binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh) binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry) @@ -818,7 +1161,9 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum) case DNSTypeMX: binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference) - encodeName(rr.MX.Name, data, noff+12) + if _, err = encodeDNSName(rr.MX.Name, rr.rdataMeta(), data, noff+12); err != nil { + return 0, err + } case DNSTypeTXT: noff2 := noff + 10 for _, txt := range rr.TXTs { @@ -830,7 +1175,9 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority) binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight) binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port) - encodeName(rr.SRV.Name, data, noff+16) + if _, err = encodeDNSName(rr.SRV.Name, rr.rdataMeta(), data, noff+16); err != nil { + return 0, err + } case DNSTypeNAPTR: binary.BigEndian.PutUint16(data[noff+10:], rr.NAPTR.Order) binary.BigEndian.PutUint16(data[noff+12:], rr.NAPTR.Preference) @@ -844,7 +1191,9 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria data[noff2] = byte(len(rr.NAPTR.Regexp)) copy(data[noff2+1:], rr.NAPTR.Regexp) noff2 += 1 + len(rr.NAPTR.Regexp) - encodeName(rr.NAPTR.Replacement, data, noff2) + if _, err = encodeDNSName(rr.NAPTR.Replacement, rr.rdataMeta(), data, noff2); err != nil { + return 0, err + } case DNSTypeURI: binary.BigEndian.PutUint16(data[noff+10:], rr.URI.Priority) binary.BigEndian.PutUint16(data[noff+12:], rr.URI.Weight) @@ -858,17 +1207,24 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria noff2 += 4 + len(opt.Data) } case DNSTypeRRSIG: - rr.RRSIG.encode(data, noff+10) + if err = rr.RRSIG.encode(rr.rdataMeta(), data, noff+10); err != nil { + return 0, err + } case DNSTypeDNSKEY: rr.DNSKEY.encode(data, noff+10) case DNSTypeSVCB, DNSTypeHTTPS: - rr.SVCB.encode(data, noff+10) + if _, err = rr.SVCB.encode(rr.rdataMeta(), data, noff+10); err != nil { + return 0, err + } default: return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type) } // DataLength - dSz := recSize(rr) + dSz, err := recSize(rr) + if err != nil { + return 0, err + } binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz)) if opts.FixLengths { @@ -950,32 +1306,32 @@ func decodeOPTs(data []byte, offset int) ([]DNSOPT, error) { return allOPT, nil } -func decodeSVCB(data []byte, offset int, buffer *[]byte) (DNSSVCB, error) { +func decodeSVCB(data []byte, offset int, buffer *[]byte) (DNSSVCB, dnsNameLabels, error) { var svcb DNSSVCB end := len(data) if offset == end { - return svcb, fmt.Errorf("DNSSVCB record is empty") + return svcb, nil, fmt.Errorf("DNSSVCB record is empty") } if offset+3 > end { - return svcb, fmt.Errorf("DNSSVCB record is of length %d, it should be at least length 3", end-offset) + return svcb, nil, fmt.Errorf("DNSSVCB record is of length %d, it should be at least length 3", end-offset) } priority := binary.BigEndian.Uint16(data[offset:]) - target, ofs, err := decodeName(data, offset+2, buffer, 1) + target, labels, ofs, err := decodeName(data, offset+2, buffer, 1) if err != nil { - return svcb, err + return svcb, nil, err } var params []DNSSvcParam for ofs < end { - if offset+4 > end { - return svcb, fmt.Errorf("DNSSVCB record truncated in SvcParams") + if ofs+4 > end { + return svcb, nil, fmt.Errorf("DNSSVCB record truncated in SvcParams") } key := DNSSvcParamKey(binary.BigEndian.Uint16(data[ofs:])) l := int(binary.BigEndian.Uint16(data[ofs+2:])) if ofs+4+l > end { - return svcb, fmt.Errorf("DNSSVCB record truncated in SvcParams") + return svcb, nil, fmt.Errorf("DNSSVCB record truncated in SvcParams") } params = append(params, DNSSvcParam{ Key: key, @@ -988,7 +1344,7 @@ func decodeSVCB(data []byte, offset int, buffer *[]byte) (DNSSVCB, error) { Priority: priority, Target: target, Params: params, - }, nil + }, labels, nil } func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error { @@ -1005,30 +1361,42 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte } rr.TXTs = txts case DNSTypeNS: - name, _, err := decodeName(data, offset, buffer, 1) + name, labels, _, err := decodeName(data, offset, buffer, 1) if err != nil { return err } rr.NS = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypeCNAME: - name, _, err := decodeName(data, offset, buffer, 1) + name, labels, _, err := decodeName(data, offset, buffer, 1) if err != nil { return err } rr.CNAME = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypePTR: - name, _, err := decodeName(data, offset, buffer, 1) + name, labels, _, err := decodeName(data, offset, buffer, 1) if err != nil { return err } rr.PTR = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypeSOA: - name, endq, err := decodeName(data, offset, buffer, 1) + name, labels, endq, err := decodeName(data, offset, buffer, 1) if err != nil { return err } rr.SOA.MName = name - name, endq, err = decodeName(data, endq, buffer, 1) + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } + name, labels, endq, err = decodeName(data, endq, buffer, 1) if err != nil { return err } @@ -1036,6 +1404,9 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte return errors.New("SOA too small") } rr.SOA.RName = name + if labels != nil { + rr.ensureNameMeta().rdata2 = newDNSNameMeta(name, labels) + } rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4]) rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8]) rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12]) @@ -1046,11 +1417,14 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte return errors.New("MX too small") } rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2]) - name, _, err := decodeName(data, offset+2, buffer, 1) + name, labels, _, err := decodeName(data, offset+2, buffer, 1) if err != nil { return err } rr.MX.Name = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypeURI: if len(rr.Data) < 4 { return errors.New("URI too small") @@ -1065,11 +1439,14 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2]) rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4]) rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6]) - name, _, err := decodeName(data, offset+6, buffer, 1) + name, labels, _, err := decodeName(data, offset+6, buffer, 1) if err != nil { return err } rr.SRV.Name = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypeNAPTR: if len(data) < offset+4 { return errors.New("NAPTR too small") @@ -1111,11 +1488,14 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte rr.NAPTR.Regexp = data[offset : offset+regexpLen] offset += regexpLen // Decode Replacement (domain-name) - name, _, err := decodeName(data, offset, buffer, 1) + name, labels, _, err := decodeName(data, offset, buffer, 1) if err != nil { return err } rr.NAPTR.Replacement = name + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(name, labels) + } case DNSTypeOPT: allOPT, err := decodeOPTs(data, offset) if err != nil { @@ -1123,21 +1503,27 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte } rr.OPT = allOPT case DNSTypeRRSIG: - err := rr.RRSIG.decode(data, offset) + labels, err := rr.RRSIG.decode(data, offset) if err != nil { return err } + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(rr.RRSIG.SignerName, labels) + } case DNSTypeDNSKEY: err := rr.DNSKEY.decode(data, offset) if err != nil { return err } case DNSTypeSVCB, DNSTypeHTTPS: - svcb, err := decodeSVCB(data, offset, buffer) + svcb, labels, err := decodeSVCB(data, offset, buffer) if err != nil { return err } rr.SVCB = svcb + if labels != nil { + rr.ensureNameMeta().rdata = newDNSNameMeta(svcb.Target, labels) + } } return nil } @@ -1183,14 +1569,13 @@ type DNSSVCB struct { Params []DNSSvcParam } -func (svcb DNSSVCB) size() int { +func (svcb DNSSVCB) size(m *dnsNameMeta) (int, error) { // Target. - sz := len(svcb.Target) - if sz == 0 { - sz++ - } else { - sz += 2 + targetSize, err := dnsNameSize(svcb.Target, m) + if err != nil { + return 0, err } + sz := targetSize // Priority. sz += 2 @@ -1198,7 +1583,7 @@ func (svcb DNSSVCB) size() int { for _, param := range svcb.Params { sz += param.size() } - return sz + return sz, nil } func (svcb DNSSVCB) String() string { @@ -1206,13 +1591,19 @@ func (svcb DNSSVCB) String() string { svcb.Priority, string(svcb.Target), svcb.Params) } -func (svcb DNSSVCB) encode(data []byte, offset int) { +func (svcb DNSSVCB) encode(m *dnsNameMeta, data []byte, offset int) (int, error) { + start := offset binary.BigEndian.PutUint16(data[offset:], svcb.Priority) - offset = encodeName(svcb.Target, data, offset+2) + n, err := encodeDNSName(svcb.Target, m, data, offset+2) + if err != nil { + return 0, err + } + offset += 2 + n for _, param := range svcb.Params { offset = param.encode(data, offset) } + return offset - start, nil } // DNSSvcParamKey defines SVCB service parameter keys. @@ -1297,9 +1688,13 @@ type DNSRRSIG struct { SignerName, Signature []byte } -func (rrsig DNSRRSIG) size() int { - // 18 bytes for the fixed fields, 2 bytes for the first Label Length, and ending 0x00 byte. - return 18 + len(rrsig.SignerName) + 2 + len(rrsig.Signature) +func (rrsig DNSRRSIG) size(m *dnsNameMeta) (int, error) { + // 18 bytes for the fixed fields, plus the wire-encoded signer name and the signature. + signerSize, err := dnsNameSize(rrsig.SignerName, m) + if err != nil { + return 0, err + } + return 18 + signerSize + len(rrsig.Signature), nil } func (rrsig DNSRRSIG) String() string { @@ -1328,11 +1723,12 @@ func (rrsig DNSRRSIG) String() string { // / Signature / // / / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -func (rrsig *DNSRRSIG) decode(data []byte, offset int) error { +func (rrsig *DNSRRSIG) decode(data []byte, offset int) (dnsNameLabels, error) { if len(data) < offset+18 { - return errors.New("RRSIG too small") + return nil, errors.New("RRSIG too small") } var err error + var labels dnsNameLabels rrsig.TypeCovered = DNSType(binary.BigEndian.Uint16(data[offset:])) rrsig.Algorithm = DNSSECAlgorithm(data[offset+2]) rrsig.Labels = data[offset+3] @@ -1340,18 +1736,18 @@ func (rrsig *DNSRRSIG) decode(data []byte, offset int) error { rrsig.Expiration = binary.BigEndian.Uint32(data[offset+8:]) rrsig.Inception = binary.BigEndian.Uint32(data[offset+12:]) rrsig.KeyTag = binary.BigEndian.Uint16(data[offset+16:]) - _, offset, err = decodeName(data, offset+18, &rrsig.SignerName, 1) + _, labels, offset, err = decodeName(data, offset+18, &rrsig.SignerName, 1) if len(rrsig.SignerName) > 1 { rrsig.SignerName = rrsig.SignerName[1:] // Remove leading '.' } if err != nil { - return err + return nil, err } rrsig.Signature = data[offset:] - return nil + return labels, nil } -func (rrsig DNSRRSIG) encode(data []byte, offset int) { +func (rrsig DNSRRSIG) encode(m *dnsNameMeta, data []byte, offset int) error { binary.BigEndian.PutUint16(data[offset:], uint16(rrsig.TypeCovered)) data[offset+2] = uint8(rrsig.Algorithm) data[offset+3] = rrsig.Labels @@ -1359,8 +1755,13 @@ func (rrsig DNSRRSIG) encode(data []byte, offset int) { binary.BigEndian.PutUint32(data[offset+8:], rrsig.Expiration) binary.BigEndian.PutUint32(data[offset+12:], rrsig.Inception) binary.BigEndian.PutUint16(data[offset+16:], rrsig.KeyTag) - offset += encodeName(rrsig.SignerName, data[offset+18:], 0) + 18 + n, err := encodeDNSName(rrsig.SignerName, m, data, offset+18) + if err != nil { + return err + } + offset += 18 + n copy(data[offset:], rrsig.Signature) + return nil } // DNSSECAlgorithm common values diff --git a/vendor/github.com/gopacket/gopacket/layers/dot11.go b/vendor/github.com/gopacket/gopacket/layers/dot11.go index 0f92748d..3005904d 100644 --- a/vendor/github.com/gopacket/gopacket/layers/dot11.go +++ b/vendor/github.com/gopacket/gopacket/layers/dot11.go @@ -1631,6 +1631,10 @@ func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.Decod } m.Info = data[offset+4 : offset+int(m.Length)] } else if m.ID == 255 { + if len(data) < offset+1 { + df.SetTruncated() + return fmt.Errorf("extension element size < %d", offset+1) + } m.ExtensionID = Dot11InformationElementExtId(data[offset]) if err := checkOffsetLength(offset+1, offset+int(m.Length), &df); err != nil { return err diff --git a/vendor/github.com/gopacket/gopacket/layers/erspan2.go b/vendor/github.com/gopacket/gopacket/layers/erspan2.go index dae2b30c..57029346 100644 --- a/vendor/github.com/gopacket/gopacket/layers/erspan2.go +++ b/vendor/github.com/gopacket/gopacket/layers/erspan2.go @@ -8,6 +8,7 @@ package layers import ( "encoding/binary" + "errors" "github.com/gopacket/gopacket" ) @@ -34,6 +35,10 @@ func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPAN // DecodeFromBytes decodes the given bytes into this layer. func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { erspan2Length := 8 + if len(data) < erspan2Length { + df.SetTruncated() + return errors.New("ERSPAN II header too short") + } erspan2.Version = data[0] & 0xF0 >> 4 erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF erspan2.CoS = data[2] & 0xE0 >> 5 diff --git a/vendor/github.com/gopacket/gopacket/layers/etherip.go b/vendor/github.com/gopacket/gopacket/layers/etherip.go index d290e303..2fd31858 100644 --- a/vendor/github.com/gopacket/gopacket/layers/etherip.go +++ b/vendor/github.com/gopacket/gopacket/layers/etherip.go @@ -8,6 +8,7 @@ package layers import ( "encoding/binary" + "errors" "github.com/gopacket/gopacket" ) @@ -24,6 +25,10 @@ func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP } // DecodeFromBytes decodes the given bytes into this layer. func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return errors.New("EtherIP packet too small") + } e.Version = data[0] >> 4 e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff e.BaseLayer = BaseLayer{data[:2], data[2:]} diff --git a/vendor/github.com/gopacket/gopacket/layers/fddi.go b/vendor/github.com/gopacket/gopacket/layers/fddi.go index a99bdd00..f7104808 100644 --- a/vendor/github.com/gopacket/gopacket/layers/fddi.go +++ b/vendor/github.com/gopacket/gopacket/layers/fddi.go @@ -7,6 +7,7 @@ package layers import ( + "errors" "net" "github.com/gopacket/gopacket" @@ -29,6 +30,10 @@ func (f *FDDI) LinkFlow() gopacket.Flow { } func decodeFDDI(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 13 { + p.SetTruncated() + return errors.New("FDDI packet too small") + } f := &FDDI{ FrameControl: FDDIFrameControl(data[0] & 0xF8), Priority: data[0] & 0x07, diff --git a/vendor/github.com/gopacket/gopacket/layers/gtp.go b/vendor/github.com/gopacket/gopacket/layers/gtp.go index f2bc5464..73e10a06 100644 --- a/vendor/github.com/gopacket/gopacket/layers/gtp.go +++ b/vendor/github.com/gopacket/gopacket/layers/gtp.go @@ -80,6 +80,9 @@ func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error if g.ExtensionHeaderFlag { extensionFlag := true for extensionFlag { + if int(cIndex) >= dLen { + return fmt.Errorf("GTP packet too small: %d bytes", dLen) + } extensionType := uint8(data[cIndex-1]) extensionLength := uint(data[cIndex]) if extensionLength == 0 { diff --git a/vendor/github.com/gopacket/gopacket/layers/ipsec.go b/vendor/github.com/gopacket/gopacket/layers/ipsec.go index 91fc8450..e9da0d87 100644 --- a/vendor/github.com/gopacket/gopacket/layers/ipsec.go +++ b/vendor/github.com/gopacket/gopacket/layers/ipsec.go @@ -50,6 +50,12 @@ func (i *IPSecAH) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error i.Seq = binary.BigEndian.Uint32(data[8:12]) i.ActualLength = (int(i.HeaderLength) + 2) * 4 + if i.ActualLength < 12 { + // Authentication data starts at byte 12; a shorter header would make + // the data[12:ActualLength] slice below have out-of-order bounds. + df.SetTruncated() + return errors.New("AH packet ActualLength < 12") + } if len(data) < i.ActualLength { df.SetTruncated() return errors.New("Truncated AH packet < ActualLength") diff --git a/vendor/github.com/gopacket/gopacket/layers/lcm.go b/vendor/github.com/gopacket/gopacket/layers/lcm.go index 960e6de2..433adab6 100644 --- a/vendor/github.com/gopacket/gopacket/layers/lcm.go +++ b/vendor/github.com/gopacket/gopacket/layers/lcm.go @@ -136,6 +136,13 @@ func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if lcm.Magic == LCMFragmentedHeaderMagic { lcm.Fragmented = true + // The fragmented header adds size(4) + fragOffset(4) + fragNumber(2) + + // totalFragments(2) = 12 bytes on top of the 8 already consumed. + if len(data) < offset+12 { + df.SetTruncated() + return errors.New("LCM fragmented header truncated") + } + lcm.PayloadSize = binary.BigEndian.Uint32(data[offset : offset+4]) offset += 4 diff --git a/vendor/github.com/gopacket/gopacket/layers/linux_sll.go b/vendor/github.com/gopacket/gopacket/layers/linux_sll.go index e1d05cae..76534e91 100644 --- a/vendor/github.com/gopacket/gopacket/layers/linux_sll.go +++ b/vendor/github.com/gopacket/gopacket/layers/linux_sll.go @@ -87,8 +87,13 @@ func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) er sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2])) sll.AddrType = binary.BigEndian.Uint16(data[2:4]) sll.AddrLen = binary.BigEndian.Uint16(data[4:6]) + // The address field is a fixed 8-byte window at offset 6; anything larger is + // invalid and would otherwise wrap the uint16 addition below. + if sll.AddrLen > 8 { + return fmt.Errorf("Linux SLL invalid address length %d", sll.AddrLen) + } - sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6]) + sll.Addr = net.HardwareAddr(data[6 : 6+int(sll.AddrLen)]) sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16])) sll.BaseLayer = BaseLayer{data[:16], data[16:]} diff --git a/vendor/github.com/gopacket/gopacket/layers/linux_sll2.go b/vendor/github.com/gopacket/gopacket/layers/linux_sll2.go index 7b37e747..c904b551 100644 --- a/vendor/github.com/gopacket/gopacket/layers/linux_sll2.go +++ b/vendor/github.com/gopacket/gopacket/layers/linux_sll2.go @@ -166,6 +166,11 @@ func (sll *LinuxSLL2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) e sll.ARPHardwareType = ARPHardwareType(binary.BigEndian.Uint16(data[8:10])) sll.PacketType = LinuxSLL2PacketType(data[10]) sll.AddrLength = data[11] + // The address field is a fixed 8-byte window at offset 12, so a larger + // declared length would reslice past its capacity. + if sll.AddrLength > 8 { + return fmt.Errorf("Linux SLL2 invalid address length %d", sll.AddrLength) + } sll.Addr = data[12:20] sll.Addr = sll.Addr[:sll.AddrLength] sll.BaseLayer = BaseLayer{data[:20], data[20:]} diff --git a/vendor/github.com/gopacket/gopacket/layers/mpls.go b/vendor/github.com/gopacket/gopacket/layers/mpls.go index e8e3eefd..764a9d2b 100644 --- a/vendor/github.com/gopacket/gopacket/layers/mpls.go +++ b/vendor/github.com/gopacket/gopacket/layers/mpls.go @@ -36,6 +36,10 @@ func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS } type ProtocolGuessingDecoder struct{} func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 1 { + p.SetTruncated() + return errors.New("Unable to guess protocol of empty packet data") + } switch data[0] { // 0x40 | header_len, where header_len is at least 5. case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f: @@ -56,6 +60,10 @@ func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) err var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{} func decodeMPLS(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 4 { + p.SetTruncated() + return errors.New("MPLS packet too small") + } decoded := binary.BigEndian.Uint32(data[:4]) mpls := &MPLS{ Label: decoded >> 12, diff --git a/vendor/github.com/gopacket/gopacket/layers/pflog.go b/vendor/github.com/gopacket/gopacket/layers/pflog.go index 2daf4557..85f54e11 100644 --- a/vendor/github.com/gopacket/gopacket/layers/pflog.go +++ b/vendor/github.com/gopacket/gopacket/layers/pflog.go @@ -40,9 +40,10 @@ type PFLog struct { } func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { - if len(data) < 60 { + // Direction is read from data[60], so 61 bytes are required. + if len(data) < 61 { df.SetTruncated() - return errors.New("PFLog data less than 60 bytes") + return errors.New("PFLog data less than 61 bytes") } pf.Length = data[0] pf.Family = ProtocolFamily(data[1]) diff --git a/vendor/github.com/gopacket/gopacket/layers/pktap.go b/vendor/github.com/gopacket/gopacket/layers/pktap.go index a2619074..19cdf09e 100644 --- a/vendor/github.com/gopacket/gopacket/layers/pktap.go +++ b/vendor/github.com/gopacket/gopacket/layers/pktap.go @@ -119,6 +119,10 @@ func (p *PktapV1) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error } p.HeaderLength = binary.LittleEndian.Uint32(data[0:4]) + if int64(p.HeaderLength) > int64(len(data)) { + df.SetTruncated() + return fmt.Errorf("pktap header length %d exceeds packet length %d", p.HeaderLength, len(data)) + } if p.HeaderLength < 156 { return fmt.Errorf("pktap v1 header length mismatch: got %d", p.HeaderLength) } diff --git a/vendor/github.com/gopacket/gopacket/layers/ppp.go b/vendor/github.com/gopacket/gopacket/layers/ppp.go index 0a66074b..0b9e654b 100644 --- a/vendor/github.com/gopacket/gopacket/layers/ppp.go +++ b/vendor/github.com/gopacket/gopacket/layers/ppp.go @@ -39,11 +39,29 @@ func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow } func decodePPP(data []byte, p gopacket.PacketBuilder) error { ppp := &PPP{} offset := 0 - if data[0] == 0xff && data[1] == 0x03 { - offset = 2 - ppp.HasPPTPHeader = true + if len(data) < 1 { + p.SetTruncated() + return errors.New("PPP packet too small") + } + if data[0] == 0xff { + if len(data) < 2 { + p.SetTruncated() + return errors.New("PPP packet too small") + } + if data[1] == 0x03 { + offset = 2 + ppp.HasPPTPHeader = true + } + } + if len(data) < offset+1 { + p.SetTruncated() + return errors.New("PPP packet too small") } if data[offset]&0x1 == 0 { + if len(data) < offset+2 { + p.SetTruncated() + return errors.New("PPP packet too small") + } if data[offset+1]&0x1 == 0 { return errors.New("PPP has invalid type") } diff --git a/vendor/github.com/gopacket/gopacket/layers/pppoe.go b/vendor/github.com/gopacket/gopacket/layers/pppoe.go index 549224d1..6e3d6128 100644 --- a/vendor/github.com/gopacket/gopacket/layers/pppoe.go +++ b/vendor/github.com/gopacket/gopacket/layers/pppoe.go @@ -8,6 +8,7 @@ package layers import ( "encoding/binary" + "fmt" "github.com/gopacket/gopacket" ) @@ -29,6 +30,10 @@ func (p *PPPoE) LayerType() gopacket.LayerType { // decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516). func decodePPPoE(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 6 { + p.SetTruncated() + return fmt.Errorf("PPPoE packet length %d too short", len(data)) + } pppoe := &PPPoE{ Version: data[0] >> 4, Type: data[0] & 0x0F, @@ -36,7 +41,12 @@ func decodePPPoE(data []byte, p gopacket.PacketBuilder) error { SessionId: binary.BigEndian.Uint16(data[2:4]), Length: binary.BigEndian.Uint16(data[4:6]), } - pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]} + payloadEnd := 6 + int(pppoe.Length) + if len(data) < payloadEnd { + p.SetTruncated() + return fmt.Errorf("PPPoE packet length %d too short, %d total bytes expected", len(data), payloadEnd) + } + pppoe.BaseLayer = BaseLayer{data[:6], data[6:payloadEnd]} p.AddLayer(pppoe) return p.NextDecoder(pppoe.Code) } diff --git a/vendor/github.com/gopacket/gopacket/layers/prism.go b/vendor/github.com/gopacket/gopacket/layers/prism.go index b075ac0a..c7931ea3 100644 --- a/vendor/github.com/gopacket/gopacket/layers/prism.go +++ b/vendor/github.com/gopacket/gopacket/layers/prism.go @@ -113,10 +113,21 @@ type PrismHeader struct { func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader } func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + // The fixed part of the header (code, length, device name) is 24 bytes. + if len(data) < 24 { + df.SetTruncated() + return ErrPrismExpectedMoreData + } m.Code = binary.LittleEndian.Uint16(data[0:4]) m.Length = binary.LittleEndian.Uint16(data[4:8]) + // Length must cover the fixed header and be backed by real bytes; otherwise + // the slices below run past the end and the value subtraction underflows. + if int(m.Length) < 24 || int(m.Length) > len(data) { + df.SetTruncated() + return ErrPrismExpectedMoreData + } m.DeviceName = string(data[8:24]) - m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]} + m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:]} switch m.Code { case PrismType1MessageCode: diff --git a/vendor/github.com/gopacket/gopacket/layers/radiotap.go b/vendor/github.com/gopacket/gopacket/layers/radiotap.go index c26776a2..c2549394 100644 --- a/vendor/github.com/gopacket/gopacket/layers/radiotap.go +++ b/vendor/github.com/gopacket/gopacket/layers/radiotap.go @@ -1365,6 +1365,10 @@ func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) erro // and expects all fields are packed in the first it_present. // Extended bitmap will be just ignored. offset += 4 + if offset+4 > dataLen { + df.SetTruncated() + return errors.New("RadioTap present bitmap extends beyond data") + } m.Present = append(m.Present, RadioTapPresent(binary.LittleEndian.Uint32(data[offset:offset+4]))) } offset += 4 // move past the previous present bitmap diff --git a/vendor/github.com/gopacket/gopacket/layers/rudp.go b/vendor/github.com/gopacket/gopacket/layers/rudp.go index 5c3e7694..33f15ade 100644 --- a/vendor/github.com/gopacket/gopacket/layers/rudp.go +++ b/vendor/github.com/gopacket/gopacket/layers/rudp.go @@ -42,6 +42,10 @@ type RUDPHeaderEACK struct { func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP } func decodeRUDP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 18 { + p.SetTruncated() + return fmt.Errorf("RUDP packet length %d too short", len(data)) + } r := &RUDP{ SYN: data[0]&0x80 != 0, ACK: data[0]&0x40 != 0, @@ -61,8 +65,17 @@ func decodeRUDP(data []byte, p gopacket.PacketBuilder) error { return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength) } hlen := int(r.HeaderLength) * 2 + if len(data) < hlen { + p.SetTruncated() + return fmt.Errorf("RUDP packet length %d too short, %d header bytes expected", len(data), hlen) + } + payloadEnd := hlen + int(r.DataLength) + if len(data) < payloadEnd { + p.SetTruncated() + return fmt.Errorf("RUDP packet length %d too short, %d total bytes expected", len(data), payloadEnd) + } r.Contents = data[:hlen] - r.Payload = data[hlen : hlen+int(r.DataLength)] + r.Payload = data[hlen:payloadEnd] r.VariableHeaderArea = data[18:hlen] headerData := r.VariableHeaderArea switch { diff --git a/vendor/github.com/gopacket/gopacket/layers/sctp.go b/vendor/github.com/gopacket/gopacket/layers/sctp.go index 30571b8e..0871f942 100644 --- a/vendor/github.com/gopacket/gopacket/layers/sctp.go +++ b/vendor/github.com/gopacket/gopacket/layers/sctp.go @@ -46,6 +46,13 @@ func (s *SCTP) TransportFlow() gopacket.Flow { } func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error { + if len(data) == 0 { + return nil + } + if len(data) < 4 { + p.SetTruncated() + return errors.New("invalid SCTP chunk length") + } chunkType := SCTPChunkType(data[0]) return chunkType.Decode(data, p) } @@ -120,12 +127,21 @@ func roundUpToNearest4(i int) int { } func decodeSCTPChunk(data []byte) (SCTPChunk, error) { + if len(data) < 4 { + return SCTPChunk{}, errors.New("invalid SCTP chunk length") + } length := binary.BigEndian.Uint16(data[2:4]) if length < 4 { return SCTPChunk{}, errors.New("invalid SCTP chunk length") } actual := roundUpToNearest4(int(length)) ct := SCTPChunkType(data[0]) + if ct == SCTPChunkTypeData && length < 16 { + return SCTPChunk{}, errors.New("invalid SCTP data chunk length") + } + if len(data) < actual { + return SCTPChunk{}, errors.New("SCTP chunk length exceeds remaining packet length") + } // For SCTP Data, use a separate layer for the payload delta := 0 @@ -144,11 +160,17 @@ func decodeSCTPChunk(data []byte) (SCTPChunk, error) { } func decodeSCTPDataChunk(data []byte) (SCTPChunk, error) { - length := binary.BigEndian.Uint16(data[2:4]) - if length < 4 { + if len(data) < 4 { return SCTPChunk{}, errors.New("invalid SCTP chunk length") } + length := binary.BigEndian.Uint16(data[2:4]) + if length < 16 { + return SCTPChunk{}, errors.New("invalid SCTP data chunk length") + } actual := roundUpToNearest4(int(length)) + if len(data) < actual { + return SCTPChunk{}, errors.New("SCTP data chunk length exceeds remaining packet length") + } ct := SCTPChunkType(data[0]) return SCTPChunk{ diff --git a/vendor/github.com/gopacket/gopacket/layers/sflow.go b/vendor/github.com/gopacket/gopacket/layers/sflow.go index e2f7cabf..19d67ad0 100644 --- a/vendor/github.com/gopacket/gopacket/layers/sflow.go +++ b/vendor/github.com/gopacket/gopacket/layers/sflow.go @@ -302,8 +302,17 @@ func (s SFlowIPType) Length() int { func (s *SFlowDatagram) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { var agentAddressType SFlowIPType + if len(data) < 8 { + df.SetTruncated() + return errors.New("SFlow datagram too short") + } data, s.DatagramVersion = data[4:], binary.BigEndian.Uint32(data[:4]) data, agentAddressType = data[4:], SFlowIPType(binary.BigEndian.Uint32(data[:4])) + // agent address + subAgentID + sequence + uptime + sampleCount = agentAddr + 16. + if len(data) < agentAddressType.Length()+16 { + df.SetTruncated() + return errors.New("SFlow datagram too short for agent address and header") + } data, s.AgentAddress = data[agentAddressType.Length():], data[:agentAddressType.Length()] data, s.SubAgentID = data[4:], binary.BigEndian.Uint32(data[:4]) data, s.SequenceNumber = data[4:], binary.BigEndian.Uint32(data[:4]) diff --git a/vendor/github.com/gopacket/gopacket/layers/sip.go b/vendor/github.com/gopacket/gopacket/layers/sip.go index 0e7719c9..725c3dab 100644 --- a/vendor/github.com/gopacket/gopacket/layers/sip.go +++ b/vendor/github.com/gopacket/gopacket/layers/sip.go @@ -427,6 +427,13 @@ func (s *SIP) ParseHeader(header []byte) (err error) { // multiline headers must begin by SP or TAB if header[0] == '\t' || header[0] == ' ' { + // A continuation line is only meaningful if a header has already been + // parsed. Without this check lastHeaderParsed is still "", the map + // lookup yields a nil slice, and the index below evaluates to [-1]. + if len(s.Headers[s.lastHeaderParsed]) == 0 { + return fmt.Errorf("SIP header continuation line with no preceding header") + } + header = bytes.TrimSpace(header) s.Headers[s.lastHeaderParsed][len(s.Headers[s.lastHeaderParsed])-1] += fmt.Sprintf(" %s", string(header)) return diff --git a/vendor/github.com/gopacket/gopacket/layers/tcp.go b/vendor/github.com/gopacket/gopacket/layers/tcp.go index 07583268..7c710e42 100644 --- a/vendor/github.com/gopacket/gopacket/layers/tcp.go +++ b/vendor/github.com/gopacket/gopacket/layers/tcp.go @@ -346,9 +346,23 @@ OPTIONS: opt.OptionLength = 1 case TCPOptionKindMultipathTCP: tcp.Multipath = true + // The subtype nibble lives in data[2], so three bytes are the + // minimum any MPTCP option can occupy. + if len(data) < 3 { + df.SetTruncated() + return fmt.Errorf("Invalid MPTCP option. Length %d less than 3", len(data)) + } opt.OptionLength = data[1] - if opt.OptionLength <= 0 { + if opt.OptionLength < 3 { return fmt.Errorf("MPTCP bad option length %d", opt.OptionLength) + } else if int(opt.OptionLength) > len(data) { + // Bound the declared length against the bytes actually present + // before any subtype dispatch. The per-subtype checks below only + // compare OptionLength against the expected constant for that + // subtype, so without this every fixed-offset slice below is + // reachable past the end of the options region. + df.SetTruncated() + return fmt.Errorf("Invalid MPTCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data)) } opt.OptionMultipath = MPTCPSubtype(data[2] >> 4) switch opt.OptionMultipath { @@ -404,6 +418,12 @@ OPTIONS: } } case MPTCPSubtypeDSS: + // The flags byte is read before the length can be computed from + // it, so it needs its own bound; 4 is the smallest value + // optionMptcpDsslen can return. + if opt.OptionLength < 4 { + return fmt.Errorf("DSS bad option length %d", opt.OptionLength) + } opt.OptionMPTCPDss = &Dss{ F: data[3]&0x10 != 0, m: data[3]&0x08 != 0, diff --git a/vendor/github.com/gopacket/gopacket/layers/tls_handshake.go b/vendor/github.com/gopacket/gopacket/layers/tls_handshake.go index 7c3a68b0..43c49e0f 100644 --- a/vendor/github.com/gopacket/gopacket/layers/tls_handshake.go +++ b/vendor/github.com/gopacket/gopacket/layers/tls_handshake.go @@ -102,6 +102,18 @@ type TLSHandshakeRecord struct { } func (t *TLSHandshakeRecordClientHello) decodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + // The record framing hands us a slice whose length is the TLS record body + // but whose capacity may span the rest of the packet buffer. The original + // parser read up to that capacity; re-slice to it so the bounds checks + // below reflect the bytes actually available and can never read past the + // underlying buffer (which is what caused the panics this guards against). + data = data[:cap(data)] + + // Fixed prefix: type(1) + length(3) + version(2) + random(32) + sessionIDLen(1). + if len(data) < 39 { + df.SetTruncated() + return errors.New("TLS ClientHello too short") + } t.HandshakeType = data[0] d := make([]byte, 4) for k, v := range data[1:4] { @@ -111,15 +123,42 @@ func (t *TLSHandshakeRecordClientHello) decodeFromBytes(data []byte, df gopacket t.ProtocolVersion = TLSVersion(binary.BigEndian.Uint16(data[4:6])) t.Random = data[6:38] t.SessionIDLength = data[38] - t.SessionID = data[39 : 39+t.SessionIDLength] - t.CipherSuitsLength = binary.BigEndian.Uint16(data[39+t.SessionIDLength : 39+t.SessionIDLength+2]) - t.CipherSuits = data[39+t.SessionIDLength+2 : (39 + uint16(t.SessionIDLength) + 2 + t.CipherSuitsLength)] - t.CompressionMethodsLength = data[(39 + uint16(t.SessionIDLength) + 2 + t.CipherSuitsLength)] - t.CompressionMethods = data[(39+uint16(t.SessionIDLength)+2+t.CipherSuitsLength)+1 : (39+uint16(t.SessionIDLength)+2+t.CipherSuitsLength)+1+uint16(t.CompressionMethodsLength)] - t.ExtensionsLength = binary.BigEndian.Uint16(data[(39+uint16(t.SessionIDLength)+2+t.CipherSuitsLength)+1+uint16(t.CompressionMethodsLength) : (39+uint16(t.SessionIDLength)+2+t.CipherSuitsLength)+1+uint16(t.CompressionMethodsLength)+2]) + + // All subsequent offsets are computed in int to avoid uint16 overflow, + // and each variable-length field is bounds-checked before it is read. + pos := 39 + int(t.SessionIDLength) + if len(data) < pos+2 { + df.SetTruncated() + return errors.New("TLS ClientHello truncated in session ID") + } + t.SessionID = data[39:pos] + t.CipherSuitsLength = binary.BigEndian.Uint16(data[pos : pos+2]) + pos += 2 + + if len(data) < pos+int(t.CipherSuitsLength)+1 { + df.SetTruncated() + return errors.New("TLS ClientHello truncated in cipher suites") + } + t.CipherSuits = data[pos : pos+int(t.CipherSuitsLength)] + pos += int(t.CipherSuitsLength) + t.CompressionMethodsLength = data[pos] + pos++ + + if len(data) < pos+int(t.CompressionMethodsLength)+2 { + df.SetTruncated() + return errors.New("TLS ClientHello truncated in compression methods") + } + t.CompressionMethods = data[pos : pos+int(t.CompressionMethodsLength)] + pos += int(t.CompressionMethodsLength) + t.ExtensionsLength = binary.BigEndian.Uint16(data[pos : pos+2]) + pos += 2 // extract extension data - data = data[((39 + uint16(t.SessionIDLength) + 2 + t.CipherSuitsLength) + 1 + uint16(t.CompressionMethodsLength) + 2) : ((39+uint16(t.SessionIDLength)+2+t.CipherSuitsLength)+1+uint16(t.CompressionMethodsLength)+2)+t.ExtensionsLength] + if len(data) < pos+int(t.ExtensionsLength) { + df.SetTruncated() + return errors.New("TLS ClientHello truncated in extensions") + } + data = data[pos : pos+int(t.ExtensionsLength)] t.Extensions = data for len(data) > 0 { if len(data) < 4 { @@ -193,10 +232,14 @@ func (t *TLSHandshakeRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df if t.isEncryptedHandshakeMessage(h, data) { return nil } + if len(data) < 1 { + df.SetTruncated() + return errors.New("TLS handshake record too short") + } handshakeType := data[0] switch handshakeType { case TLSHandshakeClientHello: - t.ClientHello.decodeFromBytes(data, df) + return t.ClientHello.decodeFromBytes(data, df) case TLSHandshakeClientKeyExchange: t.ClientKeyChange.decodeFromBytes(data, df) default: diff --git a/vendor/github.com/gopacket/gopacket/layers/udplite.go b/vendor/github.com/gopacket/gopacket/layers/udplite.go index 6566e17b..88cded79 100644 --- a/vendor/github.com/gopacket/gopacket/layers/udplite.go +++ b/vendor/github.com/gopacket/gopacket/layers/udplite.go @@ -9,6 +9,7 @@ package layers import ( "encoding/binary" + "errors" "github.com/gopacket/gopacket" ) @@ -26,6 +27,10 @@ type UDPLite struct { func (u *UDPLite) LayerType() gopacket.LayerType { return LayerTypeUDPLite } func decodeUDPLite(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 8 { + p.SetTruncated() + return errors.New("UDPLite packet too small") + } udp := &UDPLite{ SrcPort: UDPLitePort(binary.BigEndian.Uint16(data[0:2])), sPort: data[0:2], diff --git a/vendor/github.com/gopacket/gopacket/layers/vrrp.go b/vendor/github.com/gopacket/gopacket/layers/vrrp.go index b512e6f8..1caf53a0 100644 --- a/vendor/github.com/gopacket/gopacket/layers/vrrp.go +++ b/vendor/github.com/gopacket/gopacket/layers/vrrp.go @@ -91,6 +91,10 @@ type VRRPv2 struct { func (v *VRRPv2) LayerType() gopacket.LayerType { return LayerTypeVRRP } func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("Not a valid VRRP packet. Packet length is too small.") + } v.BaseLayer = BaseLayer{Contents: data[:len(data)]} v.Version = data[0] >> 4 // high nibble == VRRP version. We're expecting v2 @@ -108,6 +112,11 @@ func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error if v.CountIPAddr < 1 { return errors.New("VRRPv2 number of IP addresses is not valid.") } + addressEnd := 8 + 4*int(v.CountIPAddr) + if len(data) < addressEnd { + df.SetTruncated() + return errors.New("VRRPv2 packet too short for IP address count.") + } v.AuthType = VRRPv2AuthType(data[4]) v.AdverInt = uint8(data[5]) @@ -116,6 +125,7 @@ func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error // populate the IPAddress field. The number of addresses is specified in the v.CountIPAddr field // offset references the starting byte containing the list of ip addresses offset := 8 + v.IPAddress = nil for i := uint8(0); i < v.CountIPAddr; i++ { v.IPAddress = append(v.IPAddress, data[offset:offset+4]) offset += 4 diff --git a/vendor/modules.txt b/vendor/modules.txt index 63772e35..1b39ca21 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -140,7 +140,7 @@ github.com/google/pprof/profile # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/gopacket/gopacket v1.7.0 +# github.com/gopacket/gopacket v1.7.1 ## explicit; go 1.25.0 github.com/gopacket/gopacket github.com/gopacket/gopacket/endian