Skip to content

Commit db96ad5

Browse files
added more bounds checks, removed fallback parsing, refactored creating of packet connection
1 parent 514b6c3 commit db96ad5

File tree

12 files changed

+220
-129
lines changed

12 files changed

+220
-129
lines changed

layers/arp.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,21 @@ func (ap *ARPPacket) UnmarshalBinary(data []byte) error {
167167
hoffset := 8 + ap.Hlen
168168
ap.SenderMAC = net.HardwareAddr(buf[8:hoffset])
169169
poffset := hoffset + ap.Plen
170+
if int(poffset) > len(buf) {
171+
return ErrSliceBounds
172+
}
170173
var ok bool
171174
ap.SenderIP, ok = netip.AddrFromSlice(buf[hoffset:poffset])
172175
if !ok {
173176
return fmt.Errorf("failed parsing sender IP address")
174177
}
178+
if int(poffset+ap.Hlen) > len(buf) {
179+
return ErrSliceBounds
180+
}
175181
ap.TargetMAC = net.HardwareAddr(buf[poffset : poffset+ap.Hlen])
182+
if int(poffset+ap.Hlen+ap.Plen) > len(buf) {
183+
return ErrSliceBounds
184+
}
176185
ap.TargetIP, ok = netip.AddrFromSlice(buf[poffset+ap.Hlen : poffset+ap.Hlen+ap.Plen])
177186
if !ok {
178187
return fmt.Errorf("failed parsing target IP address")

layers/dns.go

Lines changed: 86 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,29 @@ package layers
33
import (
44
"encoding/binary"
55
"encoding/hex"
6+
"encoding/json"
67
"fmt"
78
"net/netip"
89
"strings"
910
)
1011

11-
// TODO (shadowy-pycoder): add MarshalJSON
12-
1312
const headerSizeDNS = 12
1413

1514
type DNSFlags struct {
16-
Raw uint16
17-
QR uint8 // Indicates if the message is a query (0) or a reply (1).
18-
QRDesc string // Query (0) or Reply (1)
19-
OPCode uint8 // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
20-
OPCodeDesc string
21-
AA uint8 // Authoritative Answer, in a response, indicates if the DNS server is authoritative for the queried hostname.
22-
TC uint8 // TrunCation, indicates that this message was truncated due to excessive length.
23-
RD uint8 // Recursion Desired, indicates if the client means a recursive query.
24-
RA uint8 // Recursion Available, in a response, indicates if the replying DNS server supports recursion.
25-
Z uint8 // Zero, reserved for future use.
26-
AU uint8 // Indicates if answer/authority portion was authenticated by the server.
27-
NA uint8 // Indicates if non-authenticated data is accepatable.
28-
RCode uint8 // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
29-
RCodeDesc string
15+
Raw uint16 `json:"raw"`
16+
QR uint8 `json:"qr"` // Indicates if the message is a query (0) or a reply (1).
17+
QRDesc string `json:"qrdesc"` // Query (0) or Reply (1)
18+
OPCode uint8 `json:"opcode"` // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
19+
OPCodeDesc string `json:"opcodedesc"`
20+
AA uint8 `json:"aa"` // Authoritative Answer, in a response, indicates if the DNS server is authoritative for the queried hostname.
21+
TC uint8 `json:"tc"` // TrunCation, indicates that this message was truncated due to excessive length.
22+
RD uint8 `json:"rd"` // Recursion Desired, indicates if the client means a recursive query.
23+
RA uint8 `json:"ra"` // Recursion Available, in a response, indicates if the replying DNS server supports recursion.
24+
Z uint8 `json:"z"` // Zero, reserved for future use.
25+
AU uint8 `json:"au"` // Indicates if answer/authority portion was authenticated by the server.
26+
NA uint8 `json:"na"` // Indicates if non-authenticated data is accepatable.
27+
RCode uint8 `json:"rcode"` // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
28+
RCodeDesc string `json:"rcodedesc"`
3029
}
3130

3231
func (df *DNSFlags) String() string {
@@ -173,16 +172,16 @@ func rcdesc(rcode uint8) string {
173172
}
174173

175174
type DNSMessage struct {
176-
TransactionID uint16 // Used for matching response to queries.
177-
Flags *DNSFlags // Flags specify the requested operation and a response code.
178-
QDCount uint16 // Count of entries in the queries section.
179-
ANCount uint16 // Count of entries in the answers section.
180-
NSCount uint16 // Count of entries in the authority section.
181-
ARCount uint16 // Count of entries in the additional section.
182-
Questions []*QueryEntry
183-
AnswerRRs []*ResourceRecord
184-
AuthorityRRs []*ResourceRecord
185-
AdditionalRRs []*ResourceRecord
175+
TransactionID uint16 `json:"transaction-id"` // Used for matching response to queries.
176+
Flags *DNSFlags `json:"flags,omitempty"` // Flags specify the requested operation and a response code.
177+
QDCount uint16 `json:"questions-count"` // Count of entries in the queries section.
178+
ANCount uint16 `json:"answer-rrs-count"` // Count of entries in the answers section.
179+
NSCount uint16 `json:"authority-rrs-count"` // Count of entries in the authority section.
180+
ARCount uint16 `json:"additional-rrs-count"` // Count of entries in the additional section.
181+
Questions []*QueryEntry `json:"questions,omitempty"`
182+
AnswerRRs []*ResourceRecord `json:"answers,omitempty"`
183+
AuthorityRRs []*ResourceRecord `json:"authoritative-nameservers,omitempty"`
184+
AdditionalRRs []*ResourceRecord `json:"additional-records,omitempty"`
186185
}
187186

188187
func (d *DNSMessage) String() string {
@@ -313,15 +312,31 @@ func (d *DNSMessage) printRecords() string {
313312
if d.ARCount > 0 {
314313
sb.WriteString("- Additional records:\n")
315314
for _, rec := range d.AdditionalRRs {
316-
sb.WriteString(rec.String())
315+
sb.WriteString(strings.TrimSuffix(rec.String(), "\n"))
317316
}
318317
}
319-
return strings.TrimSuffix(sb.String(), "\n")
318+
return sb.String()
319+
}
320+
321+
type dnsMessageAlias DNSMessage
322+
323+
type dnsQueryWrapper struct {
324+
Query *dnsMessageAlias `json:"dns_query"`
325+
}
326+
type dnsReplyWrapper struct {
327+
Reply *dnsMessageAlias `json:"dns_reply"`
328+
}
329+
330+
func (d *DNSMessage) MarshalJSON() ([]byte, error) {
331+
if d.Flags.QR == 0 {
332+
return json.Marshal(&dnsQueryWrapper{Query: (*dnsMessageAlias)(d)})
333+
}
334+
return json.Marshal(&dnsReplyWrapper{Reply: (*dnsMessageAlias)(d)})
320335
}
321336

322337
type RecordClass struct {
323-
Name string
324-
Val uint16
338+
Name string `json:"name"`
339+
Val uint16 `json:"val"`
325340
}
326341

327342
func (c *RecordClass) String() string {
@@ -351,8 +366,8 @@ func className(cls uint16) string {
351366
}
352367

353368
type RecordType struct {
354-
Name string
355-
Val uint16
369+
Name string `json:"name"`
370+
Val uint16 `json:"val"`
356371
}
357372

358373
func (rt *RecordType) String() string {
@@ -391,12 +406,12 @@ func typeName(typ uint16) string {
391406
}
392407

393408
type ResourceRecord struct {
394-
Name string // Name of the node to which this record pertains.
395-
Type *RecordType // Type of RR in numeric form.
396-
Class *RecordClass // Class code.
397-
TTL uint32 // Count of seconds that the RR stays valid.
398-
RDLength uint16 // Length of RData field (specified in octets).
399-
RData fmt.Stringer // Additional RR-specific data.
409+
Name string `json:"name"` // Name of the node to which this record pertains.
410+
Type *RecordType `json:"record-type"` // Type of RR in numeric form.
411+
Class *RecordClass `json:"record-class"` // Class code.
412+
TTL uint32 `json:"ttl"` // Count of seconds that the RR stays valid.
413+
RDLength uint16 `json:"rdata-length"` // Length of RData field (specified in octets).
414+
RData fmt.Stringer `json:"rdata"` // Additional RR-specific data.
400415
}
401416

402417
func (rt *ResourceRecord) String() string {
@@ -453,9 +468,9 @@ func (rt *ResourceRecord) Summary() string {
453468
}
454469

455470
type QueryEntry struct {
456-
Name string // Name of the node to which this record pertains.
457-
Type *RecordType // Type of RR in numeric form.
458-
Class *RecordClass // Class code.
471+
Name string `json:"name"` // Name of the node to which this record pertains.
472+
Type *RecordType `json:"record-type"` // Type of RR in numeric form.
473+
Class *RecordClass `json:"record-class"` // Class code.
459474
}
460475

461476
func (qe *QueryEntry) String() string {
@@ -467,37 +482,37 @@ func (qe *QueryEntry) String() string {
467482
}
468483

469484
type RDataA struct {
470-
Address netip.Addr
485+
Address netip.Addr `json:"address"`
471486
}
472487

473488
func (d *RDataA) String() string {
474489
return fmt.Sprintf("Address: %s", d.Address)
475490
}
476491

477492
type RDataNS struct {
478-
NsdName string
493+
NsdName string `json:"ns"`
479494
}
480495

481496
func (d *RDataNS) String() string {
482497
return fmt.Sprintf("NS: %s", d.NsdName)
483498
}
484499

485500
type RDataCNAME struct {
486-
CName string
501+
CName string `json:"cname"`
487502
}
488503

489504
func (d *RDataCNAME) String() string {
490505
return fmt.Sprintf("CNAME: %s", d.CName)
491506
}
492507

493508
type RDataSOA struct {
494-
PrimaryNS string
495-
RespAuthorityMailbox string
496-
SerialNumber uint32
497-
RefreshInterval uint32
498-
RetryInterval uint32
499-
ExpireLimit uint32
500-
MinimumTTL uint32
509+
PrimaryNS string `json:"primary-nameserver"`
510+
RespAuthorityMailbox string `json:"responsible-authority-mailbox"`
511+
SerialNumber uint32 `json:"serial-number"`
512+
RefreshInterval uint32 `json:"refresh-interval"`
513+
RetryInterval uint32 `json:"retry-interval"`
514+
ExpireLimit uint32 `json:"expire-limit"`
515+
MinimumTTL uint32 `json:"minimum-ttl"`
501516
}
502517

503518
func (d *RDataSOA) String() string {
@@ -518,36 +533,36 @@ func (d *RDataSOA) String() string {
518533
}
519534

520535
type RDataMX struct {
521-
Preference uint16
522-
Exchange string
536+
Preference uint16 `json:"preference"`
537+
Exchange string `json:"exchange"`
523538
}
524539

525540
func (d *RDataMX) String() string {
526541
return fmt.Sprintf("MX: %d %s", d.Preference, d.Exchange)
527542
}
528543

529544
type RDataTXT struct {
530-
TxtData string
545+
TxtData string `json:"txt-data"`
531546
}
532547

533548
func (d *RDataTXT) String() string {
534549
return fmt.Sprintf("TXT: %s", d.TxtData)
535550
}
536551

537552
type RDataAAAA struct {
538-
Address netip.Addr
553+
Address netip.Addr `json:"address"`
539554
}
540555

541556
func (d *RDataAAAA) String() string {
542557
return fmt.Sprintf("Address: %s", d.Address)
543558
}
544559

545560
type RDataOPT struct {
546-
UDPPayloadSize uint16
547-
HigherBitsExtRCode uint8
548-
EDNSVer uint8
549-
Z uint16
550-
DataLen uint16
561+
UDPPayloadSize uint16 `json:"udp-payload-size"`
562+
HigherBitsExtRCode uint8 `json:"higer-bits-in-extended-rcode"`
563+
EDNSVer uint8 `json:"edns0-version"`
564+
Z uint16 `json:"z"`
565+
DataLen uint16 `json:"data-length"`
551566
}
552567

553568
func (d *RDataOPT) String() string {
@@ -565,8 +580,8 @@ func (d *RDataOPT) String() string {
565580
}
566581

567582
type SvcParamKey struct {
568-
Val uint16
569-
Desc string
583+
Val uint16 `json:"val"`
584+
Desc string `json:"desc"`
570585
}
571586

572587
// https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
@@ -608,9 +623,9 @@ func (spk *SvcParamKey) String() string {
608623
}
609624

610625
type SvcParam struct {
611-
Key *SvcParamKey
612-
Length uint16
613-
Value []byte // TODO: add proper parsing
626+
Key *SvcParamKey `json:"svc-param-key"`
627+
Length uint16 `json:"svc-param-value-length"`
628+
Value []byte `json:"svc-param-value"` // TODO: add proper parsing
614629
}
615630

616631
func newSvcParam(data []byte) (*SvcParam, []byte, error) {
@@ -639,10 +654,10 @@ func (sp *SvcParam) String() string {
639654
}
640655

641656
type RDataHTTPS struct {
642-
SvcPriority uint16
643-
Length int
644-
TargetName string
645-
SvcParams []*SvcParam
657+
SvcPriority uint16 `json:"svc-priority"`
658+
Length int `json:"length"`
659+
TargetName string `json:"target-name"`
660+
SvcParams []*SvcParam `json:"svc-params"`
646661
}
647662

648663
func (d *RDataHTTPS) printSvcParams() string {
@@ -668,7 +683,7 @@ func (d *RDataHTTPS) String() string {
668683
}
669684

670685
type RDataUnknown struct {
671-
Data string
686+
Data string `json:"data"`
672687
}
673688

674689
func (d *RDataUnknown) String() string {
@@ -681,7 +696,7 @@ func (d *RDataUnknown) String() string {
681696
func extractDomain(payload, tail []byte) (string, []byte, error) {
682697
// see https://brunoscheufler.com/blog/2024-05-12-building-a-dns-message-parser#domain-names
683698
var domainName string
684-
for {
699+
for len(tail) > 0 {
685700
blen := tail[0]
686701
if blen>>6 == 0b11 {
687702
if len(tail) < 2 {

layers/ethernet.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ func (ef *EthernetFrame) UnmarshalBinary(data []byte) error {
9898
ef.SrcMAC = net.HardwareAddr(buf[6:12])
9999
et := EtherType(binary.BigEndian.Uint16(buf[12:14]))
100100
etdesc := ethertypedesc(et)
101+
if etdesc == "Unknown" {
102+
return fmt.Errorf("failed determining Ethernet type")
103+
}
101104
ef.EtherType = &EthernetType{Val: et, Desc: etdesc}
102105
ef.Payload = buf[headerSizeEthernet:]
103106
ef.DstVendor = oui.VendorWithMAC(ef.DstMAC)
@@ -131,7 +134,7 @@ func ethertypedesc(et EtherType) string {
131134
case EtherTypeIPv6:
132135
etdesc = "IPv6"
133136
default:
134-
etdesc = ""
137+
etdesc = "Unknown"
135138
}
136139
return etdesc
137140
}

layers/http.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func (h *HTTPMessage) Parse(data []byte) error {
6464
if !bytes.Contains(buf, protohttp10) && !bytes.Contains(buf, protohttp11) {
6565
h.Request = nil
6666
h.Response = nil
67-
return nil
67+
return fmt.Errorf("message does not contain protocol")
6868
}
6969
reader := bufio.NewReader(bytes.NewReader(buf))
7070
if bytes.HasPrefix(buf, protohttp11) || bytes.HasPrefix(buf, protohttp10) {
@@ -90,7 +90,7 @@ func (h *HTTPMessage) NextLayer() Layer { return nil }
9090
func (h *HTTPMessage) Name() LayerName { return LayerHTTP }
9191

9292
type HTTPRequestWrapper struct {
93-
Request HTTPRequest `json:"http_request"`
93+
Request *HTTPRequest `json:"http_request"`
9494
}
9595

9696
type HTTPRequest struct {
@@ -103,7 +103,7 @@ type HTTPRequest struct {
103103
}
104104

105105
type HTTPResponseWrapper struct {
106-
Response HTTPResponse `json:"http_response"`
106+
Response *HTTPResponse `json:"http_response"`
107107
}
108108

109109
type HTTPResponse struct {
@@ -115,7 +115,7 @@ type HTTPResponse struct {
115115

116116
func (h *HTTPMessage) MarshalJSON() ([]byte, error) {
117117
if h.Request != nil {
118-
return json.Marshal(&HTTPRequestWrapper{Request: HTTPRequest{
118+
return json.Marshal(&HTTPRequestWrapper{Request: &HTTPRequest{
119119
Host: h.Request.Host,
120120
URI: h.Request.RequestURI,
121121
Method: h.Request.Method,
@@ -124,7 +124,7 @@ func (h *HTTPMessage) MarshalJSON() ([]byte, error) {
124124
Header: h.Request.Header,
125125
}})
126126
} else if h.Response != nil {
127-
return json.Marshal(&HTTPResponseWrapper{Response: HTTPResponse{
127+
return json.Marshal(&HTTPResponseWrapper{Response: &HTTPResponse{
128128
Proto: h.Response.Proto,
129129
Status: h.Response.Status,
130130
ContentLength: int(h.Response.ContentLength),

0 commit comments

Comments
 (0)