@@ -25,6 +25,7 @@ const (
2525 EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
2626 EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914)
2727 EDNS0REPORTING = 0x12 // EDNS0 reporting (See RFC 9567)
28+ EDNS0ZONEVERSION = 0x13 // EDNS0 Zone Version (See RFC 9660)
2829 EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
2930 EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
3031 _DO = 1 << 15 // DNSSEC OK
@@ -63,6 +64,8 @@ func makeDataOpt(code uint16) EDNS0 {
6364 return new (EDNS0_ESU )
6465 case EDNS0REPORTING :
6566 return new (EDNS0_REPORTING )
67+ case EDNS0ZONEVERSION :
68+ return new (EDNS0_ZONEVERSION )
6669 default :
6770 e := new (EDNS0_LOCAL )
6871 e .Code = code
@@ -78,17 +81,16 @@ type OPT struct {
7881
7982func (rr * OPT ) String () string {
8083 s := "\n ;; OPT PSEUDOSECTION:\n ; EDNS: version " + strconv .Itoa (int (rr .Version ())) + "; "
84+ s += "flags:"
8185 if rr .Do () {
82- if rr .Co () {
83- s += "flags: do, co; "
84- } else {
85- s += "flags: do; "
86- }
87- } else {
88- s += "flags:; "
86+ s += " do"
8987 }
90- if rr .Hdr .Ttl & 0x7FFF != 0 {
91- s += fmt .Sprintf ("MBZ: 0x%04x, " , rr .Hdr .Ttl & 0x7FFF )
88+ if rr .Co () {
89+ s += " co"
90+ }
91+ s += "; "
92+ if z := rr .Z (); z != 0 {
93+ s += fmt .Sprintf ("MBZ: 0x%04x, " , z )
9294 }
9395 s += "udp: " + strconv .Itoa (int (rr .UDPSize ()))
9496
@@ -132,6 +134,8 @@ func (rr *OPT) String() string {
132134 s += "\n ; ESU: " + o .String ()
133135 case * EDNS0_REPORTING :
134136 s += "\n ; REPORT-CHANNEL: " + o .String ()
137+ case * EDNS0_ZONEVERSION :
138+ s += "\n ; ZONEVERSION: " + o .String ()
135139 }
136140 }
137141 return s
@@ -313,41 +317,54 @@ type EDNS0_SUBNET struct {
313317func (e * EDNS0_SUBNET ) Option () uint16 { return EDNS0SUBNET }
314318
315319func (e * EDNS0_SUBNET ) pack () ([]byte , error ) {
316- b := make ([]byte , 4 )
317- binary .BigEndian .PutUint16 (b [0 :], e .Family )
318- b [2 ] = e .SourceNetmask
319- b [3 ] = e .SourceScope
320320 switch e .Family {
321321 case 0 :
322322 // "dig" sets AddressFamily to 0 if SourceNetmask is also 0
323323 // We might don't need to complain either
324324 if e .SourceNetmask != 0 {
325325 return nil , errors .New ("bad address family" )
326326 }
327+ b := make ([]byte , 4 )
328+ b [3 ] = e .SourceScope
329+ return b , nil
327330 case 1 :
328331 if e .SourceNetmask > net .IPv4len * 8 {
329332 return nil , errors .New ("bad netmask" )
330333 }
331- if len (e .Address .To4 ()) != net .IPv4len {
334+ ip4 := e .Address .To4 ()
335+ if len (ip4 ) != net .IPv4len {
332336 return nil , errors .New ("bad address" )
333337 }
334- ip := e .Address .To4 ().Mask (net .CIDRMask (int (e .SourceNetmask ), net .IPv4len * 8 ))
335338 needLength := (e .SourceNetmask + 8 - 1 ) / 8 // division rounding up
336- b = append (b , ip [:needLength ]... )
339+ b := make ([]byte , 4 + needLength )
340+ binary .BigEndian .PutUint16 (b [0 :], e .Family )
341+ b [2 ] = e .SourceNetmask
342+ b [3 ] = e .SourceScope
343+ if needLength > 0 {
344+ ip := ip4 .Mask (net .CIDRMask (int (e .SourceNetmask ), net .IPv4len * 8 ))
345+ copy (b [4 :], ip [:needLength ])
346+ }
347+ return b , nil
337348 case 2 :
338349 if e .SourceNetmask > net .IPv6len * 8 {
339350 return nil , errors .New ("bad netmask" )
340351 }
341352 if len (e .Address ) != net .IPv6len {
342353 return nil , errors .New ("bad address" )
343354 }
344- ip := e .Address .Mask (net .CIDRMask (int (e .SourceNetmask ), net .IPv6len * 8 ))
345355 needLength := (e .SourceNetmask + 8 - 1 ) / 8 // division rounding up
346- b = append (b , ip [:needLength ]... )
356+ b := make ([]byte , 4 + needLength )
357+ binary .BigEndian .PutUint16 (b [0 :], e .Family )
358+ b [2 ] = e .SourceNetmask
359+ b [3 ] = e .SourceScope
360+ if needLength > 0 {
361+ ip := e .Address .Mask (net .CIDRMask (int (e .SourceNetmask ), net .IPv6len * 8 ))
362+ copy (b [4 :], ip [:needLength ])
363+ }
364+ return b , nil
347365 default :
348366 return nil , errors .New ("bad address family" )
349367 }
350- return b , nil
351368}
352369
353370func (e * EDNS0_SUBNET ) unpack (b []byte ) error {
@@ -906,3 +923,48 @@ func (e *EDNS0_REPORTING) unpack(b []byte) error {
906923 e .AgentDomain = domain
907924 return nil
908925}
926+
927+ // EDNS0_ZONEVERSION implements the EDNS0 Zone Version option (RFC 9660).
928+ type EDNS0_ZONEVERSION struct {
929+ // always EDNS0ZONEVERSION (19)
930+ Code uint16
931+ // An unsigned 1-octet Label Count indicating
932+ // the number of labels for the name of the zone that VERSION value refers to.
933+ LabelCount uint8
934+ // An unsigned 1-octet type number distinguishing the format and meaning of version.
935+ // 0 SOA-SERIAL, 1-245 Unassigned, 246-255 Reserved for private use, see RFC 9660.
936+ Type uint8
937+ // An opaque octet string conveying the zone version data (VERSION).
938+ Version string
939+ }
940+
941+ func (e * EDNS0_ZONEVERSION ) Option () uint16 { return EDNS0ZONEVERSION }
942+ func (e * EDNS0_ZONEVERSION ) String () string { return e .Version }
943+ func (e * EDNS0_ZONEVERSION ) copy () EDNS0 {
944+ return & EDNS0_ZONEVERSION {e .Code , e .LabelCount , e .Type , e .Version }
945+ }
946+ func (e * EDNS0_ZONEVERSION ) pack () ([]byte , error ) {
947+ b := []byte {
948+ // first octet label count
949+ e .LabelCount ,
950+ // second octet is type
951+ e .Type ,
952+ }
953+ if len (e .Version ) > 0 {
954+ b = append (b , []byte (e .Version )... )
955+ }
956+ return b , nil
957+ }
958+ func (e * EDNS0_ZONEVERSION ) unpack (b []byte ) error {
959+ if len (b ) < 2 {
960+ return ErrBuf
961+ }
962+ e .LabelCount = b [0 ]
963+ e .Type = b [1 ]
964+ if len (b ) > 2 {
965+ e .Version = string (b [2 :])
966+ } else {
967+ e .Version = ""
968+ }
969+ return nil
970+ }
0 commit comments