From d3939b3d6568cf4262c0391486a5203873b68bfc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 15 Jul 2020 11:42:43 +0100 Subject: [PATCH] Support bare Yggdrasil sessions with encrypted QUIC --- cmd/dendrite-demo-yggdrasil/main.go | 26 ------------ cmd/dendrite-demo-yggdrasil/yggconn/client.go | 1 + cmd/dendrite-demo-yggdrasil/yggconn/node.go | 41 ++++++------------ .../yggconn/session.go | 42 +++++++++++++++---- go.mod | 3 +- go.sum | 27 +----------- 6 files changed, 50 insertions(+), 90 deletions(-) diff --git a/cmd/dendrite-demo-yggdrasil/main.go b/cmd/dendrite-demo-yggdrasil/main.go index 65747756..55ae590c 100644 --- a/cmd/dendrite-demo-yggdrasil/main.go +++ b/cmd/dendrite-demo-yggdrasil/main.go @@ -32,7 +32,6 @@ import ( "github.com/matrix-org/dendrite/eduserver" "github.com/matrix-org/dendrite/eduserver/cache" "github.com/matrix-org/dendrite/federationsender" - "github.com/matrix-org/dendrite/federationsender/api" "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/internal/httputil" @@ -40,7 +39,6 @@ import ( "github.com/matrix-org/dendrite/roomserver" "github.com/matrix-org/dendrite/userapi" "github.com/matrix-org/gomatrixserverlib" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/sirupsen/logrus" ) @@ -154,30 +152,6 @@ func main() { base.UseHTTPAPIs, ) - ygg.NotifySessionNew(func(boxPubKey crypto.BoxPubKey) { - req := &api.PerformServersAliveRequest{ - Servers: []gomatrixserverlib.ServerName{ - gomatrixserverlib.ServerName(boxPubKey.String()), - }, - } - res := &api.PerformServersAliveResponse{} - if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil { - logrus.WithError(err).Warn("Failed to notify server alive due to new session") - } - }) - - ygg.NotifyLinkNew(func(boxPubKey crypto.BoxPubKey, linkType, remote string) { - req := &api.PerformServersAliveRequest{ - Servers: []gomatrixserverlib.ServerName{ - gomatrixserverlib.ServerName(boxPubKey.String()), - }, - } - res := &api.PerformServersAliveResponse{} - if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil { - logrus.WithError(err).Warn("Failed to notify server alive due to new link") - } - }) - // Build both ends of a HTTP multiplex. httpServer := &http.Server{ Addr: ":0", diff --git a/cmd/dendrite-demo-yggdrasil/yggconn/client.go b/cmd/dendrite-demo-yggdrasil/yggconn/client.go index 399993e3..02b93e0d 100644 --- a/cmd/dendrite-demo-yggdrasil/yggconn/client.go +++ b/cmd/dendrite-demo-yggdrasil/yggconn/client.go @@ -65,6 +65,7 @@ func (n *Node) CreateFederationClient( ResponseHeaderTimeout: 15 * time.Second, IdleConnTimeout: 60 * time.Second, DialContext: n.yggdialerctx, + TLSClientConfig: n.tlsConfig, }, }, ) diff --git a/cmd/dendrite-demo-yggdrasil/yggconn/node.go b/cmd/dendrite-demo-yggdrasil/yggconn/node.go index 89fb69b5..e281d8b2 100644 --- a/cmd/dendrite-demo-yggdrasil/yggconn/node.go +++ b/cmd/dendrite-demo-yggdrasil/yggconn/node.go @@ -33,9 +33,7 @@ import ( "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/convert" "github.com/matrix-org/gomatrixserverlib" - yggdrasiladmin "github.com/yggdrasil-network/yggdrasil-go/src/admin" yggdrasilconfig "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" yggdrasilmulticast "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" @@ -46,10 +44,8 @@ type Node struct { core *yggdrasil.Core config *yggdrasilconfig.NodeConfig state *yggdrasilconfig.NodeState - admin *yggdrasiladmin.AdminSocket multicast *yggdrasilmulticast.Multicast log *gologme.Logger - packetConn *yggdrasil.PacketConn listener quic.Listener tlsConfig *tls.Config quicConfig *quic.Config @@ -85,12 +81,11 @@ func Setup(instanceName, storageDirectory string) (*Node, error) { n := &Node{ core: &yggdrasil.Core{}, config: yggdrasilconfig.GenerateConfig(), - admin: &yggdrasiladmin.AdminSocket{}, multicast: &yggdrasilmulticast.Multicast{}, log: gologme.New(os.Stdout, "YGG ", log.Flags()), incoming: make(chan QUICStream), } - n.core.SetBuildInfo(n) + //n.core.SetBuildInfo(n) yggfile := fmt.Sprintf("%s/%s-yggdrasil.conf", storageDirectory, instanceName) if _, err := os.Stat(yggfile); !os.IsNotExist(err) { @@ -131,20 +126,22 @@ func Setup(instanceName, storageDirectory string) (*Node, error) { panic(err) } - n.packetConn = n.core.PacketConn() n.tlsConfig = n.generateTLSConfig() n.quicConfig = &quic.Config{ MaxIncomingStreams: 0, MaxIncomingUniStreams: 0, KeepAlive: true, - MaxIdleTimeout: time.Second * 60, - HandshakeTimeout: time.Second * 15, + MaxIdleTimeout: time.Minute * 30, + HandshakeTimeout: time.Second * 30, } n.log.Println("Public curve25519:", n.core.EncryptionPublicKey()) n.log.Println("Public ed25519:", n.core.SigningPublicKey()) - go n.listenFromYgg() + go func() { + time.Sleep(time.Second) + n.listenFromYgg() + }() return n, nil } @@ -190,9 +187,11 @@ func (n *Node) PeerCount() int { func (n *Node) KnownNodes() []gomatrixserverlib.ServerName { nodemap := map[string]struct{}{} - for _, peer := range n.core.GetSwitchPeers() { - nodemap[hex.EncodeToString(peer.SigningKey[:])] = struct{}{} - } + /* + for _, peer := range n.core.GetSwitchPeers() { + nodemap[hex.EncodeToString(peer.SigningKey[:])] = struct{}{} + } + */ n.sessions.Range(func(_, v interface{}) bool { session, ok := v.(quic.Session) if !ok { @@ -263,19 +262,3 @@ func (n *Node) SetStaticPeer(uri string) error { } return nil } - -func (n *Node) NotifyLinkNew(f func(boxPubKey crypto.BoxPubKey, linkType, remote string)) { - n.core.NotifyLinkNew(f) -} - -func (n *Node) NotifyLinkGone(f func(boxPubKey crypto.BoxPubKey, linkType, remote string)) { - n.core.NotifyLinkGone(f) -} - -func (n *Node) NotifySessionNew(f func(boxPubKey crypto.BoxPubKey)) { - n.core.NotifySessionNew(f) -} - -func (n *Node) NotifySessionGone(f func(boxPubKey crypto.BoxPubKey)) { - n.core.NotifySessionGone(f) -} diff --git a/cmd/dendrite-demo-yggdrasil/yggconn/session.go b/cmd/dendrite-demo-yggdrasil/yggconn/session.go index 01cec813..0b537584 100644 --- a/cmd/dendrite-demo-yggdrasil/yggconn/session.go +++ b/cmd/dendrite-demo-yggdrasil/yggconn/session.go @@ -24,6 +24,7 @@ import ( "encoding/hex" "encoding/pem" "errors" + "fmt" "math/big" "net" "time" @@ -35,7 +36,7 @@ import ( func (n *Node) listenFromYgg() { var err error n.listener, err = quic.Listen( - n.packetConn, // yggdrasil.PacketConn + n.core, // yggdrasil.PacketConn n.tlsConfig, // TLS config n.quicConfig, // QUIC config ) @@ -44,18 +45,25 @@ func (n *Node) listenFromYgg() { } for { + n.log.Infoln("Waiting to accept QUIC sessions") session, err := n.listener.Accept(context.TODO()) if err != nil { n.log.Println("n.listener.Accept:", err) return } - go n.listenFromQUIC(session) + if len(session.ConnectionState().PeerCertificates) != 1 { + session.CloseWithError(0, "expected a peer certificate") + return + } + address := session.ConnectionState().PeerCertificates[0].Subject.CommonName + n.log.Infoln("Accepted connection from", address) + go n.listenFromQUIC(session, address) } } -func (n *Node) listenFromQUIC(session quic.Session) { - n.sessions.Store(session.RemoteAddr().String(), session) - defer n.sessions.Delete(session.RemoteAddr()) +func (n *Node) listenFromQUIC(session quic.Session, address string) { + n.sessions.Store(address, session) + defer n.sessions.Delete(address) for { st, err := session.AcceptStream(context.TODO()) if err != nil { @@ -100,10 +108,23 @@ func (n *Node) DialContext(ctx context.Context, network, address string) (net.Co } var pubKey crypto.BoxPubKey copy(pubKey[:], dest) + nodeID := crypto.GetNodeID(&pubKey) + nodeMask := &crypto.NodeID{} + for i := range nodeMask { + nodeMask[i] = 0xFF + } + + fmt.Println("Resolving coords") + coords, err := n.core.Resolve(nodeID, nodeMask) + if err != nil { + return nil, fmt.Errorf("n.core.Resolve: %w", err) + } + fmt.Println("Found coords:", coords) + fmt.Println("Dialling") session, err = quic.Dial( - n.packetConn, // yggdrasil.PacketConn - &pubKey, // dial address + n.core, // yggdrasil.PacketConn + coords, // dial address address, // dial SNI n.tlsConfig, // TLS config n.quicConfig, // QUIC config @@ -112,7 +133,8 @@ func (n *Node) DialContext(ctx context.Context, network, address string) (net.Co n.log.Println("n.dialer.DialContext:", err) return nil, err } - go n.listenFromQUIC(session) + fmt.Println("Dial OK") + go n.listenFromQUIC(session, address) } st, err := session.OpenStream() if err != nil { @@ -150,5 +172,9 @@ func (n *Node) generateTLSConfig() *tls.Config { Certificates: []tls.Certificate{tlsCert}, NextProtos: []string{"quic-matrix-ygg"}, InsecureSkipVerify: true, + ClientAuth: tls.RequireAnyClientCert, + GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { + return &tlsCert, nil + }, } } diff --git a/go.mod b/go.mod index 5c896a37..ea93cd86 100644 --- a/go.mod +++ b/go.mod @@ -36,10 +36,9 @@ require ( github.com/uber-go/atomic v1.3.0 // indirect github.com/uber/jaeger-client-go v2.15.0+incompatible github.com/uber/jaeger-lib v1.5.0 - github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708124809-79077e271c6d + github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200715104113-1046b00c3be3 go.uber.org/atomic v1.4.0 golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 - golang.org/x/mobile v0.0.0-20200629153529-33b80540585f // indirect gopkg.in/h2non/bimg.v1 v1.0.18 gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 1fd01ce9..eaec0805 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOv github.com/Arceliar/phony v0.0.0-20191006174943-d0c68492aca0 h1:p3puK8Sl2xK+2FnnIvY/C0N1aqJo2kbEsdAzU+Tnv48= github.com/Arceliar/phony v0.0.0-20191006174943-d0c68492aca0/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Shopify/sarama v1.26.1 h1:3jnfWKD7gVwbB1KSy/lE0szA9duPuSFLViK0o/d3DgA= github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= @@ -422,8 +421,6 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20200522092705-bc8506ccbcf3 h1:Yb+Wlf github.com/matrix-org/go-sqlite3-js v0.0.0-20200522092705-bc8506ccbcf3/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26 h1:Hr3zjRsq2bhrnp3Ky1qgx/fzCtCALOoGYylh2tpS9K4= github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0= -github.com/matrix-org/gomatrixserverlib v0.0.0-20200708143827-8bfb7222929b h1:mHFK2pcy+fhettE42aoq+JTcj3ysqFqkQLlUpC33Fwg= -github.com/matrix-org/gomatrixserverlib v0.0.0-20200708143827-8bfb7222929b/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU= github.com/matrix-org/gomatrixserverlib v0.0.0-20200708152912-d034ccb75e2d h1:v3MGEwCLlFjzZcYJu+aI3kUoNxQyCM3DUurjJFlaI04= github.com/matrix-org/gomatrixserverlib v0.0.0-20200708152912-d034ccb75e2d/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU= github.com/matrix-org/naffka v0.0.0-20200422140631-181f1ee7401f h1:pRz4VTiRCO4zPlEMc3ESdUOcW4PXHH4Kj+YDz1XyE+Y= @@ -655,14 +652,8 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855/go.mod h1:xQdsh08Io6nV4WRnOVTe6gI8/2iTvfLDQ0CYa5aMt+I= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200707124509-16343a00055c h1:tK1FySfDA5xVT5sAK/3XjUCE9LEoandmVnrg4Hj0fXk= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200707124509-16343a00055c/go.mod h1:d+Nz6SPeG6kmeSPFL0cvfWfgwEql75fUnZiAONgvyBE= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708120322-c6245869aeac h1:TMehvDn6TmWemBMxBFQoF/rQ513OmytfDY6sLPz3M64= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708120322-c6245869aeac/go.mod h1:d+Nz6SPeG6kmeSPFL0cvfWfgwEql75fUnZiAONgvyBE= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708123331-4e0b0e723459 h1:B7wypB+eA3xdB9bH/rFNywP3vg0MuIoMb3SojWApt+4= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708123331-4e0b0e723459/go.mod h1:d+Nz6SPeG6kmeSPFL0cvfWfgwEql75fUnZiAONgvyBE= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708124809-79077e271c6d h1:ly327dysc3r7lfG+AKJWPSAQmGf4h++fk+Y2dD8nDV4= -github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200708124809-79077e271c6d/go.mod h1:d+Nz6SPeG6kmeSPFL0cvfWfgwEql75fUnZiAONgvyBE= +github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200715104113-1046b00c3be3 h1:teLoIJgPHysREs8P6GlcS/PgaU9W9+GQndikFCQ1lY0= +github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20200715104113-1046b00c3be3/go.mod h1:d+Nz6SPeG6kmeSPFL0cvfWfgwEql75fUnZiAONgvyBE= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= @@ -688,7 +679,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -701,19 +691,10 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20200629153529-33b80540585f h1:9MxnlCHwn6IfUTinHBBzcBhmrX4OXfRmi954tWGKq+M= -golang.org/x/mobile v0.0.0-20200629153529-33b80540585f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -800,11 +781,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=