diff --git a/dhcpv6/prettyprint_test.go b/dhcpv6/bigmessage_test.go similarity index 85% rename from dhcpv6/prettyprint_test.go rename to dhcpv6/bigmessage_test.go index def212c9..8e5a333f 100644 --- a/dhcpv6/prettyprint_test.go +++ b/dhcpv6/bigmessage_test.go @@ -9,7 +9,7 @@ import ( "github.com/insomniacslk/dhcp/iana" ) -func TestPrint(t *testing.T) { +func makeBigMessage() (*Message, *RelayMessage) { m4, _ := dhcpv4.NewDiscovery(net.HardwareAddr{0x1, 0x2, 0xde, 0xad, 0xbe, 0xef}) m, _ := NewSolicit(net.HardwareAddr{0x1, 0x2, 0xde, 0xad, 0xbe, 0xef}, WithRapidCommit) @@ -80,10 +80,8 @@ func TestPrint(t *testing.T) { WithOption(&OptVendorClass{EnterpriseNumber: 300, Data: [][]byte{[]byte("foo"), []byte("bar")}}), WithOption(vendorOpts), ) - t.Log(adv.String()) - t.Log(adv.Summary()) - relayfw := RelayMessage{ + relayfw := &RelayMessage{ MessageType: MessageTypeRelayForward, } relayfw.Options.Add(OptRelayMessage(adv)) @@ -91,6 +89,29 @@ func TestPrint(t *testing.T) { EnterpriseNumber: 0x123, RemoteID: []byte{0x1, 0x2}, }) - t.Log(relayfw.String()) - t.Log(relayfw.Summary()) + return adv, relayfw +} + +func TestPrint(t *testing.T) { + m, r := makeBigMessage() + t.Log(m.String()) + t.Log(m.Summary()) + + t.Log(r.String()) + t.Log(r.Summary()) +} + +func BenchmarkToBytes(b *testing.B) { + _, r := makeBigMessage() + for i := 0; i < b.N; i++ { + _ = r.ToBytes() + } +} + +func BenchmarkFromBytes(b *testing.B) { + _, r := makeBigMessage() + buf := r.ToBytes() + for i := 0; i < b.N; i++ { + _, _ = FromBytes(buf) + } } diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go index 45025ab8..0138d0d5 100644 --- a/dhcpv6/dhcpv6.go +++ b/dhcpv6/dhcpv6.go @@ -10,9 +10,9 @@ import ( ) type DHCPv6 interface { + Serializable + Type() MessageType - ToBytes() []byte - String() string Summary() string LongString(indent int) string IsRelay() bool @@ -35,50 +35,20 @@ type Modifier func(d DHCPv6) // MessageFromBytes parses a DHCPv6 message from a byte stream. func MessageFromBytes(data []byte) (*Message, error) { - buf := uio.NewBigEndianBuffer(data) - messageType := MessageType(buf.Read8()) - - if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { - return nil, fmt.Errorf("wrong message type") - } - - d := &Message{ - MessageType: messageType, - } - buf.ReadBytes(d.TransactionID[:]) - if buf.Error() != nil { - return nil, fmt.Errorf("failed to parse DHCPv6 header: %w", buf.Error()) - } - if err := d.Options.FromBytes(buf.Data()); err != nil { + var m Message + if err := m.FromBytes(data); err != nil { return nil, err } - return d, nil + return &m, nil } // RelayMessageFromBytes parses a relay message from a byte stream. func RelayMessageFromBytes(data []byte) (*RelayMessage, error) { - buf := uio.NewBigEndianBuffer(data) - messageType := MessageType(buf.Read8()) - - if messageType != MessageTypeRelayForward && messageType != MessageTypeRelayReply { - return nil, fmt.Errorf("wrong message type") - } - - d := &RelayMessage{ - MessageType: messageType, - HopCount: buf.Read8(), - } - d.LinkAddr = net.IP(buf.CopyN(net.IPv6len)) - d.PeerAddr = net.IP(buf.CopyN(net.IPv6len)) - - if buf.Error() != nil { - return nil, fmt.Errorf("Error parsing RelayMessage header: %v", buf.Error()) - } - // TODO: fail if no OptRelayMessage is present. - if err := d.Options.FromBytes(buf.Data()); err != nil { + var r RelayMessage + if err := r.FromBytes(data); err != nil { return nil, err } - return d, nil + return &r, nil } // FromBytes reads a DHCPv6 message from a byte stream. @@ -90,10 +60,18 @@ func FromBytes(data []byte) (DHCPv6, error) { } if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { - return RelayMessageFromBytes(data) - } else { - return MessageFromBytes(data) + r, err := RelayMessageFromBytes(data) + if err != nil { + return nil, err + } + return r, nil + } + + m, err := MessageFromBytes(data) + if err != nil { + return nil, err } + return m, nil } // NewMessage creates a new DHCPv6 message with default options diff --git a/dhcpv6/dhcpv6_test.go b/dhcpv6/dhcpv6_test.go index 210f19d4..bef18052 100644 --- a/dhcpv6/dhcpv6_test.go +++ b/dhcpv6/dhcpv6_test.go @@ -1,9 +1,11 @@ package dhcpv6 import ( + "bytes" "encoding/binary" "errors" "net" + "reflect" "strconv" "testing" @@ -11,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/u-root/uio/rand" + "github.com/u-root/uio/uio" ) func randomReadMock(value []byte, n int, err error) func([]byte) (int, error) { @@ -130,32 +133,109 @@ func TestToBytes(t *testing.T) { } func TestFromAndToBytes(t *testing.T) { - expected := [][]byte{ - {01, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00}, - []byte("0000\x00\x01\x00\x0e\x00\x01000000000000"), - } - t.Parallel() - for i, packet := range expected { - t.Run(strconv.Itoa(i), func(t *testing.T) { - d, err := FromBytes(packet) - require.NoError(t, err) - toBytes := d.ToBytes() - require.Equal(t, packet, toBytes) - }) - } -} - -func TestFromBytesInvalid(t *testing.T) { - expected := [][]byte{ - {}, - {30}, - {12}, - } t.Parallel() - for i, packet := range expected { + for i, tt := range []struct { + buf []byte + want DHCPv6 + err error + }{ + { + buf: []byte{0x01, 0xab, 0xcd, 0xef}, + want: &Message{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{0xab, 0xcd, 0xef}, + Options: MessageOptions{Options: Options{}}, + }, + }, + { + buf: []byte{0x01, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00}, + want: &Message{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{0xab, 0xcd, 0xef}, + Options: MessageOptions{Options: Options{ + &OptionGeneric{OptionCode: 0}, + }}, + }, + }, + { + buf: []byte{ + 0, 0, 0, 0, + 0, 1, // ClientID + 0, 14, // Length + 0, 1, // DUID_LLT + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }, + want: &Message{ + MessageType: MessageTypeNone, + TransactionID: [3]byte{0, 0, 0}, + Options: MessageOptions{Options: Options{ + OptClientID(&DUIDLLT{HWType: 0, Time: 0, LinkLayerAddr: []byte{0, 0, 0, 0, 0, 0}}), + }}, + }, + }, + { + buf: nil, + err: uio.ErrBufferTooShort, + }, + { + buf: []byte{30}, // Message + err: uio.ErrBufferTooShort, + }, + { + buf: []byte{12}, // RelayMessage + err: uio.ErrBufferTooShort, + }, + { + buf: []byte{ + 0x0c, + 0, // hopcount + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // LinkAddr + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // PeerAddr + 0x00, 0x00, 0x00, 0x00, + }, + want: &RelayMessage{ + MessageType: MessageTypeRelayForward, + LinkAddr: net.ParseIP("::"), + PeerAddr: net.ParseIP("::"), + Options: RelayOptions{Options: Options{ + &OptionGeneric{OptionCode: 0}, + }}, + }, + }, + { + // Message, option missing length. + buf: []byte{0x01, 0xab, 0xcd, 0xef, 0x00, 0x00}, + err: uio.ErrUnreadBytes, + }, + { + // RelayMessage, option missing length. + buf: []byte{ + 0x0c, + 0, // hopcount + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // LinkAddr + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // PeerAddr + 0x00, 0x00, + }, + err: uio.ErrUnreadBytes, + }, + } { t.Run(strconv.Itoa(i), func(t *testing.T) { - _, err := FromBytes(packet) - require.Error(t, err) + got, err := FromBytes(tt.buf) + if !errors.Is(err, tt.err) { + t.Errorf("FromBytes = %v, want %v", err, tt.err) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FromBytes = %#v, want %#v", got, tt.want) + } + + if tt.want != nil { + gotb := tt.want.ToBytes() + if !bytes.Equal(gotb, tt.buf) { + t.Errorf("ToBytes = %v, want %v", gotb, tt.buf) + } + } }) } } diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index 9bb83bb8..7e22b967 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -361,6 +361,26 @@ type Message struct { Options MessageOptions } +// FromBytes parses a DHCPv6 message from a byte stream. +func (m *Message) FromBytes(data []byte) error { + buf := uio.NewBigEndianBuffer(data) + m.Unmarshal(buf) + return buf.FinError() +} + +// Unmarshal parses a DHCPv6 message from buf. +func (m *Message) Unmarshal(buf *uio.Lexer) { + messageType := MessageType(buf.Read8()) + + if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { + buf.SetError(fmt.Errorf("wrong message type")) + } + + m.MessageType = messageType + buf.ReadBytes(m.TransactionID[:]) + m.Options.Unmarshal(buf) +} + var randomRead = rand.Read // GenerateTransactionID generates a random 3-byte transaction ID. diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go index 5a29c876..45b6f17d 100644 --- a/dhcpv6/dhcpv6relay.go +++ b/dhcpv6/dhcpv6relay.go @@ -79,6 +79,30 @@ type RelayMessage struct { Options RelayOptions } +// FromBytes parses a relay message from a byte stream. +func (r *RelayMessage) FromBytes(data []byte) error { + buf := uio.NewBigEndianBuffer(data) + r.Unmarshal(buf) + return buf.FinError() +} + +// Unmarshal parses a relay message from a buf. +func (r *RelayMessage) Unmarshal(buf *uio.Lexer) { + messageType := MessageType(buf.Read8()) + + if messageType != MessageTypeRelayForward && messageType != MessageTypeRelayReply { + buf.SetError(fmt.Errorf("wrong message type")) + } + + r.MessageType = messageType + r.HopCount = buf.Read8() + r.LinkAddr = net.IP(buf.CopyN(net.IPv6len)) + r.PeerAddr = net.IP(buf.CopyN(net.IPv6len)) + + // TODO: fail if no OptRelayMessage is present. + r.Options.Unmarshal(buf) +} + func write16(b *uio.Lexer, ip net.IP) { if ip == nil || ip.To16() == nil { var zeros [net.IPv6len]byte diff --git a/dhcpv6/duid.go b/dhcpv6/duid.go index 0470f9f9..e05e2b07 100644 --- a/dhcpv6/duid.go +++ b/dhcpv6/duid.go @@ -11,10 +11,8 @@ import ( // DUID is the interface that all DUIDs adhere to. type DUID interface { - fmt.Stringer + Serializable - ToBytes() []byte - FromBytes(p []byte) error DUIDType() DUIDType Equal(d DUID) bool } diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go index bc562545..b82c8322 100644 --- a/dhcpv6/option_iaaddress.go +++ b/dhcpv6/option_iaaddress.go @@ -80,9 +80,6 @@ func (op *OptIAAddress) FromBytes(data []byte) error { t2.Unmarshal(buf) op.PreferredLifetime = t1.Duration op.ValidLifetime = t2.Duration - - if err := op.Options.FromBytes(buf.ReadAll()); err != nil { - return err - } + op.Options.Unmarshal(buf) return buf.FinError() } diff --git a/dhcpv6/option_iaaddress_test.go b/dhcpv6/option_iaaddress_test.go index d77d3ff1..05409138 100644 --- a/dhcpv6/option_iaaddress_test.go +++ b/dhcpv6/option_iaaddress_test.go @@ -90,9 +90,9 @@ func TestIAAddressParseAndGetter(t *testing.T) { }, }, { - buf: []byte{0, 3, 0, 1, 0}, + buf: []byte{0, 5, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ diff --git a/dhcpv6/option_iapd.go b/dhcpv6/option_iapd.go index fbf40b4f..65e41fbc 100644 --- a/dhcpv6/option_iapd.go +++ b/dhcpv6/option_iapd.go @@ -93,9 +93,6 @@ func (op *OptIAPD) FromBytes(data []byte) error { t2.Unmarshal(buf) op.T1 = t1.Duration op.T2 = t2.Duration - - if err := op.Options.FromBytes(buf.ReadAll()); err != nil { - return err - } + op.Options.Unmarshal(buf) return buf.FinError() } diff --git a/dhcpv6/option_iapd_test.go b/dhcpv6/option_iapd_test.go index 37a835a1..da72f0ca 100644 --- a/dhcpv6/option_iapd_test.go +++ b/dhcpv6/option_iapd_test.go @@ -111,7 +111,7 @@ func TestIAPDParseAndGetter(t *testing.T) { { buf: []byte{0, 25, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go index f7d3e761..be9a6a64 100644 --- a/dhcpv6/option_iaprefix.go +++ b/dhcpv6/option_iaprefix.go @@ -96,8 +96,6 @@ func (op *OptIAPrefix) FromBytes(data []byte) error { IP: ip, } } - if err := op.Options.FromBytes(buf.ReadAll()); err != nil { - return err - } + op.Options.Unmarshal(buf) return buf.FinError() } diff --git a/dhcpv6/option_iaprefix_test.go b/dhcpv6/option_iaprefix_test.go index de1960ce..d59bb019 100644 --- a/dhcpv6/option_iaprefix_test.go +++ b/dhcpv6/option_iaprefix_test.go @@ -96,9 +96,9 @@ func TestIAPrefixParseAndGetter(t *testing.T) { }, }, { - buf: []byte{0, 3, 0, 1, 0}, + buf: []byte{0, 26, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go index 27349319..c750f87c 100644 --- a/dhcpv6/option_nontemporaryaddress.go +++ b/dhcpv6/option_nontemporaryaddress.go @@ -113,9 +113,6 @@ func (op *OptIANA) FromBytes(data []byte) error { t2.Unmarshal(buf) op.T1 = t1.Duration op.T2 = t2.Duration - - if err := op.Options.FromBytes(buf.ReadAll()); err != nil { - return err - } + op.Options.Unmarshal(buf) return buf.FinError() } diff --git a/dhcpv6/option_nontemporaryaddress_test.go b/dhcpv6/option_nontemporaryaddress_test.go index ed9cebd3..d4647717 100644 --- a/dhcpv6/option_nontemporaryaddress_test.go +++ b/dhcpv6/option_nontemporaryaddress_test.go @@ -97,7 +97,7 @@ func TestIANAParseAndGetter(t *testing.T) { { buf: []byte{0, 3, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ diff --git a/dhcpv6/option_temporaryaddress.go b/dhcpv6/option_temporaryaddress.go index cc102a4a..b62e458c 100644 --- a/dhcpv6/option_temporaryaddress.go +++ b/dhcpv6/option_temporaryaddress.go @@ -43,9 +43,6 @@ func (op *OptIATA) LongString(indentSpace int) string { func (op *OptIATA) FromBytes(data []byte) error { buf := uio.NewBigEndianBuffer(data) buf.ReadBytes(op.IaId[:]) - - if err := op.Options.FromBytes(buf.ReadAll()); err != nil { - return err - } + op.Options.Unmarshal(buf) return buf.FinError() } diff --git a/dhcpv6/option_temporaryaddress_test.go b/dhcpv6/option_temporaryaddress_test.go index 693ffa6c..673172ac 100644 --- a/dhcpv6/option_temporaryaddress_test.go +++ b/dhcpv6/option_temporaryaddress_test.go @@ -85,7 +85,7 @@ func TestIATAParseAndGetter(t *testing.T) { { buf: []byte{0, 4, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ @@ -94,7 +94,7 @@ func TestIATAParseAndGetter(t *testing.T) { 1, 0, 0, // IAID too short }, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{ diff --git a/dhcpv6/option_vendor_opts.go b/dhcpv6/option_vendor_opts.go index 8412fd9c..09ec481d 100644 --- a/dhcpv6/option_vendor_opts.go +++ b/dhcpv6/option_vendor_opts.go @@ -43,9 +43,7 @@ func (op *OptVendorOpts) LongString(indent int) string { func (op *OptVendorOpts) FromBytes(data []byte) error { buf := uio.NewBigEndianBuffer(data) op.EnterpriseNumber = buf.Read32() - if err := op.VendorOpts.FromBytesWithParser(buf.ReadAll(), vendParseOption); err != nil { - return err - } + op.VendorOpts.UnmarshalWithParser(buf, vendParseOption) return buf.FinError() } diff --git a/dhcpv6/option_vendor_opts_test.go b/dhcpv6/option_vendor_opts_test.go index 569b45ef..0b8bf36e 100644 --- a/dhcpv6/option_vendor_opts_test.go +++ b/dhcpv6/option_vendor_opts_test.go @@ -54,7 +54,7 @@ func TestVendorOptsParseAndGetter(t *testing.T) { { buf: []byte{0, 17, 0, 1, 0}, want: nil, - err: uio.ErrUnreadBytes, + err: uio.ErrBufferTooShort, }, { buf: []byte{0, 17, 0}, diff --git a/dhcpv6/options.go b/dhcpv6/options.go index 552abd05..f9a8a8fd 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -9,10 +9,9 @@ import ( // Option is an interface that all DHCPv6 options adhere to. type Option interface { + Serializable + Code() OptionCode - ToBytes() []byte - String() string - FromBytes([]byte) error } type OptionGeneric struct { @@ -35,6 +34,11 @@ func (og *OptionGeneric) String() string { return fmt.Sprintf("%s: %v", og.OptionCode, og.OptionData) } +// Unmarshal copies all data from buf into OptionData. +func (og *OptionGeneric) Unmarshal(buf *uio.Lexer) { + og.OptionData = buf.ReadAll() +} + // FromBytes resets OptionData to p. func (og *OptionGeneric) FromBytes(p []byte) error { og.OptionData = append([]byte(nil), p...) @@ -218,21 +222,34 @@ func (o *Options) FromBytes(data []byte) error { return o.FromBytesWithParser(data, ParseOption) } +// Unmarshal reads data into o and returns an error if the options are not a +// valid serialized representation of DHCPv6 options per RFC 3315. +func (o *Options) Unmarshal(buf *uio.Lexer) { + o.UnmarshalWithParser(buf, ParseOption) +} + // OptionParser is a function signature for option parsing type OptionParser func(code OptionCode, data []byte) (Option, error) // FromBytesWithParser parses Options from byte sequences using the parsing // function that is passed in as a paremeter func (o *Options) FromBytesWithParser(data []byte, parser OptionParser) error { + buf := uio.NewBigEndianBuffer(data) + o.UnmarshalWithParser(buf, parser) + return buf.FinError() +} + +// UnmarshalWithParser parses Options from byte sequences using the parsing +// function that is passed in as a paremeter +func (o *Options) UnmarshalWithParser(buf *uio.Lexer, parser OptionParser) { if *o == nil { *o = make(Options, 0, 10) } - if len(data) == 0 { + if len(buf.Data()) == 0 { // no options, no party - return nil + return } - buf := uio.NewBigEndianBuffer(data) for buf.Has(4) { code := OptionCode(buf.Read16()) length := int(buf.Read16()) @@ -243,9 +260,9 @@ func (o *Options) FromBytesWithParser(data []byte, parser OptionParser) error { opt, err := parser(code, optData) if err != nil { - return err + buf.SetError(err) + return } *o = append(*o, opt) } - return buf.FinError() } diff --git a/dhcpv6/types.go b/dhcpv6/types.go index be5d9254..95e575cf 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -4,6 +4,14 @@ import ( "fmt" ) +// Serializable is any type that can be encoded to or decoded from DHCPv6 messages. +type Serializable interface { + fmt.Stringer + + ToBytes() []byte + FromBytes([]byte) error +} + // TransactionID is a DHCPv6 Transaction ID defined by RFC 3315, Section 6. type TransactionID [3]byte diff --git a/go.mod b/go.mod index 90b005ac..773d84c9 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mdlayher/netlink v1.1.1 github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 github.com/stretchr/testify v1.6.1 - github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 + github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/sys v0.5.0 ) diff --git a/go.sum b/go.sum index 43f0b87c..18b4643d 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c h1:PHoGTnweZP+KIg/8Zc6+ github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg= +github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=