@@ -3,30 +3,29 @@ package layers
3
3
import (
4
4
"encoding/binary"
5
5
"encoding/hex"
6
+ "encoding/json"
6
7
"fmt"
7
8
"net/netip"
8
9
"strings"
9
10
)
10
11
11
- // TODO (shadowy-pycoder): add MarshalJSON
12
-
13
12
const headerSizeDNS = 12
14
13
15
14
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"`
30
29
}
31
30
32
31
func (df * DNSFlags ) String () string {
@@ -173,16 +172,16 @@ func rcdesc(rcode uint8) string {
173
172
}
174
173
175
174
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"`
186
185
}
187
186
188
187
func (d * DNSMessage ) String () string {
@@ -313,15 +312,31 @@ func (d *DNSMessage) printRecords() string {
313
312
if d .ARCount > 0 {
314
313
sb .WriteString ("- Additional records:\n " )
315
314
for _ , rec := range d .AdditionalRRs {
316
- sb .WriteString (rec .String ())
315
+ sb .WriteString (strings . TrimSuffix ( rec .String (), " \n " ))
317
316
}
318
317
}
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 )})
320
335
}
321
336
322
337
type RecordClass struct {
323
- Name string
324
- Val uint16
338
+ Name string `json:"name"`
339
+ Val uint16 `json:"val"`
325
340
}
326
341
327
342
func (c * RecordClass ) String () string {
@@ -351,8 +366,8 @@ func className(cls uint16) string {
351
366
}
352
367
353
368
type RecordType struct {
354
- Name string
355
- Val uint16
369
+ Name string `json:"name"`
370
+ Val uint16 `json:"val"`
356
371
}
357
372
358
373
func (rt * RecordType ) String () string {
@@ -391,12 +406,12 @@ func typeName(typ uint16) string {
391
406
}
392
407
393
408
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.
400
415
}
401
416
402
417
func (rt * ResourceRecord ) String () string {
@@ -453,9 +468,9 @@ func (rt *ResourceRecord) Summary() string {
453
468
}
454
469
455
470
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.
459
474
}
460
475
461
476
func (qe * QueryEntry ) String () string {
@@ -467,37 +482,37 @@ func (qe *QueryEntry) String() string {
467
482
}
468
483
469
484
type RDataA struct {
470
- Address netip.Addr
485
+ Address netip.Addr `json:"address"`
471
486
}
472
487
473
488
func (d * RDataA ) String () string {
474
489
return fmt .Sprintf ("Address: %s" , d .Address )
475
490
}
476
491
477
492
type RDataNS struct {
478
- NsdName string
493
+ NsdName string `json:"ns"`
479
494
}
480
495
481
496
func (d * RDataNS ) String () string {
482
497
return fmt .Sprintf ("NS: %s" , d .NsdName )
483
498
}
484
499
485
500
type RDataCNAME struct {
486
- CName string
501
+ CName string `json:"cname"`
487
502
}
488
503
489
504
func (d * RDataCNAME ) String () string {
490
505
return fmt .Sprintf ("CNAME: %s" , d .CName )
491
506
}
492
507
493
508
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"`
501
516
}
502
517
503
518
func (d * RDataSOA ) String () string {
@@ -518,36 +533,36 @@ func (d *RDataSOA) String() string {
518
533
}
519
534
520
535
type RDataMX struct {
521
- Preference uint16
522
- Exchange string
536
+ Preference uint16 `json:"preference"`
537
+ Exchange string `json:"exchange"`
523
538
}
524
539
525
540
func (d * RDataMX ) String () string {
526
541
return fmt .Sprintf ("MX: %d %s" , d .Preference , d .Exchange )
527
542
}
528
543
529
544
type RDataTXT struct {
530
- TxtData string
545
+ TxtData string `json:"txt-data"`
531
546
}
532
547
533
548
func (d * RDataTXT ) String () string {
534
549
return fmt .Sprintf ("TXT: %s" , d .TxtData )
535
550
}
536
551
537
552
type RDataAAAA struct {
538
- Address netip.Addr
553
+ Address netip.Addr `json:"address"`
539
554
}
540
555
541
556
func (d * RDataAAAA ) String () string {
542
557
return fmt .Sprintf ("Address: %s" , d .Address )
543
558
}
544
559
545
560
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"`
551
566
}
552
567
553
568
func (d * RDataOPT ) String () string {
@@ -565,8 +580,8 @@ func (d *RDataOPT) String() string {
565
580
}
566
581
567
582
type SvcParamKey struct {
568
- Val uint16
569
- Desc string
583
+ Val uint16 `json:"val"`
584
+ Desc string `json:"desc"`
570
585
}
571
586
572
587
// https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
@@ -608,9 +623,9 @@ func (spk *SvcParamKey) String() string {
608
623
}
609
624
610
625
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
614
629
}
615
630
616
631
func newSvcParam (data []byte ) (* SvcParam , []byte , error ) {
@@ -639,10 +654,10 @@ func (sp *SvcParam) String() string {
639
654
}
640
655
641
656
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"`
646
661
}
647
662
648
663
func (d * RDataHTTPS ) printSvcParams () string {
@@ -668,7 +683,7 @@ func (d *RDataHTTPS) String() string {
668
683
}
669
684
670
685
type RDataUnknown struct {
671
- Data string
686
+ Data string `json:"data"`
672
687
}
673
688
674
689
func (d * RDataUnknown ) String () string {
@@ -681,7 +696,7 @@ func (d *RDataUnknown) String() string {
681
696
func extractDomain (payload , tail []byte ) (string , []byte , error ) {
682
697
// see https://brunoscheufler.com/blog/2024-05-12-building-a-dns-message-parser#domain-names
683
698
var domainName string
684
- for {
699
+ for len ( tail ) > 0 {
685
700
blen := tail [0 ]
686
701
if blen >> 6 == 0b11 {
687
702
if len (tail ) < 2 {
0 commit comments