summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-04-01 05:02:42 +0700
committerShulhan <ms@kilabit.info>2024-04-12 06:28:16 +0700
commit6361b4a088ee67e34887430b4a57f330c08d15b3 (patch)
tree76e6f3ebd3b922c26ff49c31359370cdef9ef821
parent77b2da671a29eff58925c13ea0e690c3977698a5 (diff)
downloadpakakeh.go-6361b4a088ee67e34887430b4a57f330c08d15b3.tar.xz
lib/dns: fix packing and unpacking OPT record
The RDATA in OPT records can contains zero or _more_ options. Previously, we only handle unpacking and packing one option, now we handle multiple options.
-rw-r--r--_doc/RFC_6891_EDNS0.adoc76
-rw-r--r--_doc/index.adoc2
-rw-r--r--lib/dns/message.go34
-rw-r--r--lib/dns/message_test.go54
-rw-r--r--lib/dns/rdata_opt.go80
-rw-r--r--lib/dns/rdata_opt_var.go16
-rw-r--r--lib/dns/resource_record.go28
-rw-r--r--lib/dns/testdata/message/UnpackMessage_OPT_test.txt171
8 files changed, 388 insertions, 73 deletions
diff --git a/_doc/RFC_6891_EDNS0.adoc b/_doc/RFC_6891_EDNS0.adoc
new file mode 100644
index 00000000..966cbff3
--- /dev/null
+++ b/_doc/RFC_6891_EDNS0.adoc
@@ -0,0 +1,76 @@
+= Extension Mechanisms for DNS - EDNS(0)
+:toc:
+:sectlinks:
+
+The
+https://datatracker.ietf.org/doc/html/rfc6891[RFC 6891]
+define the pseudo resource record (RR) or meta RR for DNS named OPT.
+
+The OPT record provides an extension to DNS, nicknamed as "EDNS(0)", which
+was previously called "EDNS0" specified in
+https://datatracker.ietf.org/doc/html/rfc2671/[RFC 2671].
+
+The OPT RR has RR type 41 (0x21).
+
+The OPT record can only be added to the additional section of DNS response.
+
+== Implementation requirements
+
+OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master
+files.
+
+When an OPT RR is included within any DNS message, it MUST be the
+only OPT RR in that message.
+If a query message with more than one OPT RR is received, a FORMERR (format
+error with response code (RCODE) value 1) MUST be returned.
+
+== Record format
+
+The OPT RR changes the definition of CLASS and TTL from normal DNS RR.
+
+----
++--------+
+| NAME | ; 2-octets, MUST be 0 (an empty label).
++--------+
+| TYPE | ; 16-bit unsigned integer, with value 0x0029 (or 41)
++--------+
+| CLASS | ; 16-bit unsigned integer, requester's UDP payload size.
++--------+
+| TTL | ; 32-bit unsigned integer, extended RCODE and flags.
+| |
++--------+
+| RDLEN | ; 16-bit unsigned integer, length of RDATA.
++--------+
+/ RDATA / ; Arbitrary length based on RDLEN.
++--------+
+----
+
+Inside the TTL, the extended RCODE and flags define as below,
+
+----
++----------------+
+| EXTENDED-RCODE | 1-octet, the extended RCODE.
++----------------+
+| VERSION | 1-octet, version of implementation.
++----------------+
+| DO | 1-bit.
++----------------+
+| Z | 15-bit, zero bits.
++----------------+
+----
+
+Note that EXTENDED-RCODE value 0 indicates that an unextended RCODE is in
+use.
+
+The RDATA contains zero or more options as a pair of code-value in the
+following format,
+
+----
++---------------+
+| OPTION-CODE | ; 2-octets.
++---------------+
+| OPTION-LENGTH | ; 2-octets, the length of value in octets.
++---------------+
+/ OPTION-VALUE / ; Arbitrary length of value based on OPTION-LENGTH;
++---------------+
+----
diff --git a/_doc/index.adoc b/_doc/index.adoc
index 7db30f9a..5f71f567 100644
--- a/_doc/index.adoc
+++ b/_doc/index.adoc
@@ -102,6 +102,8 @@ SPF::
DNS::
+
--
+* link:RFC_6891_EDNS0.html[RFC 6891: Extension Mechanisms for DNS (EDNS(0))^]
+
* link:RFC_9460__SVCB_and_HTTP_RR.html[RFC 9460 Service Binding and
Parameter Specification via the DNS (SVCB and HTTPS Resource Records)]
--
diff --git a/lib/dns/message.go b/lib/dns/message.go
index 7a43a7c8..a35473b0 100644
--- a/lib/dns/message.go
+++ b/lib/dns/message.go
@@ -730,37 +730,15 @@ func (msg *Message) packAAAA(rr *ResourceRecord) {
}
}
+// packOPT pack the RDLEN and RDATA of OPT record.
func (msg *Message) packOPT(rr *ResourceRecord) {
- var (
- rrOPT, _ = rr.Value.(*RDataOPT)
- off = uint(len(msg.packet))
-
- n uint16
- )
-
- // Reserve two octets for rdlength.
- msg.packet = libbytes.AppendUint16(msg.packet, 0)
+ var rrOPT *RDataOPT
- if rrOPT.Length == 0 {
- return
- }
-
- // Pack OPT rdata
- msg.packet = libbytes.AppendUint16(msg.packet, rrOPT.Code)
-
- // Values of less than 512 bytes MUST be treated as equal to 512
- // bytes (RFC6891 P11).
- if rrOPT.Length < 512 {
- msg.packet = libbytes.AppendUint16(msg.packet, 512)
- } else {
- msg.packet = libbytes.AppendUint16(msg.packet, rrOPT.Length)
- }
+ rrOPT, _ = rr.Value.(*RDataOPT)
- msg.packet = append(msg.packet, rrOPT.Data[:rrOPT.Length]...)
-
- // Write rdlength.
- n = 4 + rrOPT.Length
- libbytes.WriteUint16(msg.packet, off, n)
+ var rdata = rrOPT.pack()
+ msg.packet = libbytes.AppendUint16(msg.packet, uint16(len(rdata)))
+ msg.packet = append(msg.packet, rdata...)
}
func (msg *Message) packSVCB(rr *ResourceRecord) {
diff --git a/lib/dns/message_test.go b/lib/dns/message_test.go
index bd8ead34..9d833389 100644
--- a/lib/dns/message_test.go
+++ b/lib/dns/message_test.go
@@ -2078,6 +2078,59 @@ func TestUnpackMessage(t *testing.T) {
}
}
+func TestUnpackMessage_OPT(t *testing.T) {
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/message/UnpackMessage_OPT_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var listCase = []string{
+ `cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:00`,
+ `cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:01`,
+ }
+
+ var (
+ tcase string
+ stream []byte
+ msg *Message
+ bbuf bytes.Buffer
+ )
+ for _, tcase = range listCase {
+ stream, err = libbytes.ParseHexDump(tdata.Input[tcase], true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ msg, err = UnpackMessage(stream)
+ if err != nil {
+ t.Fatal(tcase, err)
+ }
+
+ stream, err = json.MarshalIndent(&msg, ``, ` `)
+ if err != nil {
+ t.Fatal(err)
+ }
+ test.Assert(t, tcase, string(tdata.Output[tcase]), string(stream))
+
+ // Pack the unpacked message again to generate hexdump that
+ // should be equal with input.
+
+ stream, err = msg.Pack()
+ if err != nil {
+ t.Fatal(err)
+ }
+ bbuf.Reset()
+ libbytes.DumpPrettyTable(&bbuf, msg.Question.String(), stream)
+ tcase += `.hexdump`
+ test.Assert(t, tcase, string(tdata.Output[tcase]), bbuf.String())
+ }
+}
+
func TestUnpackMessage_SVCB(t *testing.T) {
var (
logp = `TestUnpackMessage_SVCB`
@@ -2108,7 +2161,6 @@ func TestUnpackMessage_SVCB(t *testing.T) {
msg *Message
)
for _, name = range listCase {
-
stream, err = libbytes.ParseHexDump(tdata.Input[name], true)
if err != nil {
t.Fatal(logp, err)
diff --git a/lib/dns/rdata_opt.go b/lib/dns/rdata_opt.go
index 329c9c77..fffba7ec 100644
--- a/lib/dns/rdata_opt.go
+++ b/lib/dns/rdata_opt.go
@@ -7,35 +7,33 @@ package dns
import (
"fmt"
"strings"
+
+ libbytes "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
)
// RDataOPT define format of RDATA for OPT.
//
// The extended RCODE and flags, which OPT stores in the RR Time to Live
-// (TTL) field, contains ExtRCode, Version
+// (TTL) field, contains ExtRCode, Version, and DNSSEC OK flag.
type RDataOPT struct {
- // Varies per OPTION-CODE. MUST be treated as a bit field.
- Data []byte
-
- // Assigned by the Expert Review process as defined by the DNSEXT
- // working group and the IESG.
- Code uint16
+ // ListVar list of pair of code-value inside the RDATA.
+ ListVar []RDataOPTVar
- // Size (in octets) of OPTION-DATA.
- Length uint16
-
- // Forms the upper 8 bits of extended 12-bit RCODE (together with the
- // 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
- // indicates that an unextended RCODE is in use (values 0 through 15).
+ // Forms the upper 8 bits of extended 12-bit RCODE (together with
+ // the 4 bits defined message header).
+ // Note that the value of 0 indicates that the RCODE in message
+ // header is in use (values 0 through 15).
ExtRCode byte
- // Indicates the implementation level of the setter. Full conformance
- // with this specification is indicated by version '0'. Requestors
- // are encouraged to set this to the lowest implemented level capable
- // of expressing a transaction, to minimise the responder and network
- // load of discovering the greatest common implementation level
- // between requestor and responder. A requestor's version numbering
- // strategy MAY ideally be a run-time configuration option.
+ // Indicates the implementation level of the setter.
+ // Full conformance with this specification is indicated by version
+ // '0'.
+ // Requestors are encouraged to set this to the lowest implemented
+ // level capable of expressing a transaction, to minimise the
+ // responder and network load of discovering the greatest common
+ // implementation level between requestor and responder.
+ // A requestor's version numbering strategy MAY ideally be a
+ // run-time configuration option.
Version byte
// DNSSEC OK bit as defined by [RFC3225].
@@ -46,9 +44,45 @@ type RDataOPT struct {
func (opt *RDataOPT) String() string {
var b strings.Builder
- fmt.Fprintf(&b, "{ExtRCode:%d Version:%d DO:%v Code:%d Length:%d Data:%s}",
- opt.ExtRCode, opt.Version, opt.DO, opt.Code, opt.Length,
- opt.Data)
+ fmt.Fprintf(&b, "{ExtRCode:%d Version:%d DO:%v}",
+ opt.ExtRCode, opt.Version, opt.DO)
return b.String()
}
+
+// pack the ListVar for RDATA.
+func (opt *RDataOPT) pack() (rdata []byte) {
+ var optvar RDataOPTVar
+ for _, optvar = range opt.ListVar {
+ rdata = libbytes.AppendUint16(rdata, optvar.Code)
+ rdata = libbytes.AppendUint16(rdata, uint16(len(optvar.Data)))
+ rdata = append(rdata, optvar.Data...)
+ }
+ return rdata
+}
+
+// unpack extended-rcode with flags from ext (RR TTL), and RDATA from raw
+// packet.
+func (opt *RDataOPT) unpack(rdlen int, packet []byte) (err error) {
+ var x int
+
+ for x < rdlen {
+ var optvar = RDataOPTVar{}
+
+ optvar.Code = libbytes.ReadUint16(packet, uint(x))
+ x += 2
+
+ var optlen = int(libbytes.ReadUint16(packet, uint(x)))
+ x += 2
+
+ if x+optlen > len(packet) {
+ return fmt.Errorf(`option-length is out of range (want=%d, got=%d)`, optlen, len(packet))
+ }
+
+ optvar.Data = append(optvar.Data, packet[x:x+optlen]...)
+ x += optlen
+
+ opt.ListVar = append(opt.ListVar, optvar)
+ }
+ return nil
+}
diff --git a/lib/dns/rdata_opt_var.go b/lib/dns/rdata_opt_var.go
new file mode 100644
index 00000000..c08ef7b2
--- /dev/null
+++ b/lib/dns/rdata_opt_var.go
@@ -0,0 +1,16 @@
+// Copyright 2024, Shulhan <ms@kilabit.info>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dns
+
+// RDataOPTVar contains the option in OPT RDATA.
+type RDataOPTVar struct {
+ // Varies per Code.
+ // MUST be treated as a bit field.
+ Data []byte
+
+ // Assigned by the Expert Review process as defined by the DNSEXT
+ // working group and the IESG.
+ Code uint16
+}
diff --git a/lib/dns/resource_record.go b/lib/dns/resource_record.go
index f32ab334..4f620222 100644
--- a/lib/dns/resource_record.go
+++ b/lib/dns/resource_record.go
@@ -5,7 +5,6 @@
package dns
import (
- "errors"
"fmt"
"log"
"net"
@@ -188,8 +187,6 @@ func (rr *ResourceRecord) unpack(packet []byte, startIdx uint) (x uint, err erro
var (
logp = "ResourceRecord.unpack"
lenPacket = uint(len(packet))
-
- lenXRdata uint
)
x = startIdx
@@ -209,7 +206,7 @@ func (rr *ResourceRecord) unpack(packet []byte, startIdx uint) (x uint, err erro
rr.rdlen = libbytes.ReadUint16(packet, x)
x += 2
- lenXRdata = x + uint(rr.rdlen)
+ var lenXRdata uint = x + uint(rr.rdlen)
if lenPacket < lenXRdata {
return x, fmt.Errorf("%s: %s %d: packet length %d smaller than index+rdata %d+%d (%d)",
logp, rr.Name, rr.Type, lenPacket, x, rr.rdlen, lenXRdata)
@@ -513,37 +510,26 @@ func (rr *ResourceRecord) unpackSRV(packet []byte, x uint) (err error) {
return
}
-func (rr *ResourceRecord) unpackOPT(packet []byte, x uint) error {
- var (
- rrOPT = &RDataOPT{}
-
- endIdx uint
- )
+func (rr *ResourceRecord) unpackOPT(packet []byte, x uint) (err error) {
+ var rrOPT = &RDataOPT{}
rr.Value = rrOPT
- // Unpack extended RCODE and flags from TTL.
rrOPT.ExtRCode = byte(rr.TTL >> 24)
rrOPT.Version = byte(rr.TTL >> 16)
if rr.TTL&maskOPTDO == maskOPTDO {
rrOPT.DO = true
}
-
if rr.rdlen == 0 {
return nil
}
- // Unpack the RDATA
- rrOPT.Code = libbytes.ReadUint16(packet, x)
- x += 2
- rrOPT.Length = libbytes.ReadUint16(packet, x)
- x += 2
- endIdx = x + uint(rr.rdlen)
- if int(endIdx) >= len(packet) {
- return errors.New("unpackOPT: data length is out of range")
+ err = rrOPT.unpack(int(rr.rdlen), packet[x:])
+ if err != nil {
+ return fmt.Errorf(`unpackOPT: %w`, err)
}
- rrOPT.Data = append(rrOPT.Data, packet[x:endIdx]...)
+
return nil
}
diff --git a/lib/dns/testdata/message/UnpackMessage_OPT_test.txt b/lib/dns/testdata/message/UnpackMessage_OPT_test.txt
new file mode 100644
index 00000000..244df80a
--- /dev/null
+++ b/lib/dns/testdata/message/UnpackMessage_OPT_test.txt
@@ -0,0 +1,171 @@
+>>> cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:00
+0000000 4d2d 8182 0001 0000 0000 0001 1c43 4756
+0000010 3548 6936 6843 6e73 4453 6631 6574 4b59
+0000020 515a 7655 7a64 7975 7305 6d75 6c74 6905
+0000030 7375 7262 6c03 6f72 6700 0001 0001 0000
+0000040 2904 d000 0000 0000 1900 0f00 1500 1674
+0000050 696d 6520 6c69 6d69 7420 6578 6365 6564
+0000060 6564
+
+<<< cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:00
+{
+ "Answer": null,
+ "Authority": null,
+ "Additional": [
+ {
+ "Value": {
+ "ListVar": [
+ {
+ "Data": "ABZ0aW1lIGxpbWl0IGV4Y2VlZGVk",
+ "Code": 15
+ }
+ ],
+ "ExtRCode": 0,
+ "Version": 0,
+ "DO": false
+ },
+ "Name": "",
+ "Type": 41,
+ "Class": 1232,
+ "TTL": 0
+ }
+ ],
+ "Question": {
+ "Name": "cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org",
+ "Type": 1,
+ "Class": 1
+ },
+ "Header": {
+ "ID": 19757,
+ "IsQuery": false,
+ "Op": 0,
+ "IsAA": false,
+ "IsTC": false,
+ "IsRD": true,
+ "IsRA": true,
+ "RCode": 2,
+ "QDCount": 1,
+ "ANCount": 0,
+ "NSCount": 0,
+ "ARCount": 1
+ }
+}
+
+<<< cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:00.hexdump
+{Name:cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org Type:A}
+ | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |
+ | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |
+0x00000000| 4d 2d 81 82 00 01 00 00 | M-...... | 77 45 129 130 0 1 0 0 |0
+0x00000008| 00 00 00 01 1c 63 67 76 | .....cgv | 0 0 0 1 28 99 103 118 |8
+0x00000010| 35 68 69 36 68 63 6e 73 | 5hi6hcns | 53 104 105 54 104 99 110 115 |16
+0x00000018| 64 73 66 31 65 74 6b 79 | dsf1etky | 100 115 102 49 101 116 107 121 |24
+0x00000020| 71 7a 76 75 7a 64 79 75 | qzvuzdyu | 113 122 118 117 122 100 121 117 |32
+0x00000028| 73 05 6d 75 6c 74 69 05 | s.multi. | 115 5 109 117 108 116 105 5 |40
+0x00000030| 73 75 72 62 6c 03 6f 72 | surbl.or | 115 117 114 98 108 3 111 114 |48
+0x00000038| 67 00 00 01 00 01 00 00 | g....... | 103 0 0 1 0 1 0 0 |56
+0x00000040| 29 04 d0 00 00 00 00 00 | )....... | 41 4 208 0 0 0 0 0 |64
+0x00000048| 19 00 0f 00 15 00 16 74 | .......t | 25 0 15 0 21 0 22 116 |72
+0x00000050| 69 6d 65 20 6c 69 6d 69 | ime.limi | 105 109 101 32 108 105 109 105 |80
+0x00000058| 74 20 65 78 63 65 65 64 | t.exceed | 116 32 101 120 99 101 101 100 |88
+0x00000060| 65 64 | ed | 101 100 |96
+
+>>> cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:01
+0000000 4d2d 8182 0001 0000
+0000008 0000 0001 1c43 4756
+0000010 3548 6936 6843 6e73
+0000018 4453 6631 6574 4b59
+0000020 515a 7655 7a64 7975
+0000028 7305 6d75 6c74 6905
+0000030 7375 7262 6c03 6f72
+0000038 6700 0001 0001 0000
+0000040 2904 d000 0000 0000
+0000048 6d00 0f00 1500 1674
+0000050 696d 6520 6c69 6d69
+0000058 7420 6578 6365 6564
+0000060 6564 000f 0050 0017
+0000068 3134 352e 3130 302e
+0000070 3138 382e 3232 3a35
+0000078 3320 7469 6d65 6420
+0000080 6f75 7420 666f 7220
+0000088 4347 5635 4869 3668
+0000090 436e 7344 5366 3165
+0000098 744b 5951 5a76 557a
+00000a0 6479 7573 2e6d 756c
+00000a8 7469 2e73 7572 626c
+00000b0 2e6f 7267 2041
+
+<<< cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:01
+{
+ "Answer": null,
+ "Authority": null,
+ "Additional": [
+ {
+ "Value": {
+ "ListVar": [
+ {
+ "Data": "ABZ0aW1lIGxpbWl0IGV4Y2VlZGVk",
+ "Code": 15
+ },
+ {
+ "Data": "ABcxNDUuMTAwLjE4OC4yMjo1MyB0aW1lZCBvdXQgZm9yIENHVjVIaTZoQ25zRFNmMWV0S1lRWnZVemR5dXMubXVsdGkuc3VyYmwub3JnIEE=",
+ "Code": 15
+ }
+ ],
+ "ExtRCode": 0,
+ "Version": 0,
+ "DO": false
+ },
+ "Name": "",
+ "Type": 41,
+ "Class": 1232,
+ "TTL": 0
+ }
+ ],
+ "Question": {
+ "Name": "cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org",
+ "Type": 1,
+ "Class": 1
+ },
+ "Header": {
+ "ID": 19757,
+ "IsQuery": false,
+ "Op": 0,
+ "IsAA": false,
+ "IsTC": false,
+ "IsRD": true,
+ "IsRA": true,
+ "RCode": 2,
+ "QDCount": 1,
+ "ANCount": 0,
+ "NSCount": 0,
+ "ARCount": 1
+ }
+}
+
+<<< cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org:01.hexdump
+{Name:cgv5hi6hcnsdsf1etkyqzvuzdyus.multi.surbl.org Type:A}
+ | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |
+ | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |
+0x00000000| 4d 2d 81 82 00 01 00 00 | M-...... | 77 45 129 130 0 1 0 0 |0
+0x00000008| 00 00 00 01 1c 63 67 76 | .....cgv | 0 0 0 1 28 99 103 118 |8
+0x00000010| 35 68 69 36 68 63 6e 73 | 5hi6hcns | 53 104 105 54 104 99 110 115 |16
+0x00000018| 64 73 66 31 65 74 6b 79 | dsf1etky | 100 115 102 49 101 116 107 121 |24
+0x00000020| 71 7a 76 75 7a 64 79 75 | qzvuzdyu | 113 122 118 117 122 100 121 117 |32
+0x00000028| 73 05 6d 75 6c 74 69 05 | s.multi. | 115 5 109 117 108 116 105 5 |40
+0x00000030| 73 75 72 62 6c 03 6f 72 | surbl.or | 115 117 114 98 108 3 111 114 |48
+0x00000038| 67 00 00 01 00 01 00 00 | g....... | 103 0 0 1 0 1 0 0 |56
+0x00000040| 29 04 d0 00 00 00 00 00 | )....... | 41 4 208 0 0 0 0 0 |64
+0x00000048| 6d 00 0f 00 15 00 16 74 | m......t | 109 0 15 0 21 0 22 116 |72
+0x00000050| 69 6d 65 20 6c 69 6d 69 | ime.limi | 105 109 101 32 108 105 109 105 |80
+0x00000058| 74 20 65 78 63 65 65 64 | t.exceed | 116 32 101 120 99 101 101 100 |88
+0x00000060| 65 64 00 0f 00 50 00 17 | ed...P.. | 101 100 0 15 0 80 0 23 |96
+0x00000068| 31 34 35 2e 31 30 30 2e | 145.100. | 49 52 53 46 49 48 48 46 |104
+0x00000070| 31 38 38 2e 32 32 3a 35 | 188.22:5 | 49 56 56 46 50 50 58 53 |112
+0x00000078| 33 20 74 69 6d 65 64 20 | 3.timed. | 51 32 116 105 109 101 100 32 |120
+0x00000080| 6f 75 74 20 66 6f 72 20 | out.for. | 111 117 116 32 102 111 114 32 |128
+0x00000088| 43 47 56 35 48 69 36 68 | CGV5Hi6h | 67 71 86 53 72 105 54 104 |136
+0x00000090| 43 6e 73 44 53 66 31 65 | CnsDSf1e | 67 110 115 68 83 102 49 101 |144
+0x00000098| 74 4b 59 51 5a 76 55 7a | tKYQZvUz | 116 75 89 81 90 118 85 122 |152
+0x000000a0| 64 79 75 73 2e 6d 75 6c | dyus.mul | 100 121 117 115 46 109 117 108 |160
+0x000000a8| 74 69 2e 73 75 72 62 6c | ti.surbl | 116 105 46 115 117 114 98 108 |168
+0x000000b0| 2e 6f 72 67 20 41 | .org.A | 46 111 114 103 32 65 |176