From e5dcd4753e09be69a9378d312c9402732d0a63a3 Mon Sep 17 00:00:00 2001 From: Mikhail Bragin Date: Wed, 16 Feb 2022 20:00:21 +0100 Subject: [PATCH] single socket ice (#232) Enables single socket for HOST and SRFLX candidates by utilizing pion.ice UDPMux --- client/internal/engine.go | 86 ++++++++++++++++++++++++++++------ client/internal/engine_test.go | 4 +- client/internal/peer/conn.go | 20 +++++--- go.mod | 12 +++-- go.sum | 25 +++++----- 5 files changed, 107 insertions(+), 40 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 9b92df5ad..f68e01d02 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/rand" + "net" "strings" "sync" "time" @@ -22,10 +23,13 @@ import ( ) // PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer. -// E.g. this peer will wait PeerConnectionTimeoutMax for the remote peer to respond, if not successful then it will retry the connection attempt. +// E.g. this peer will wait PeerConnectionTimeoutMax for the remote peer to respond, +// if not successful then it will retry the connection attempt. // Todo pass timeout at EnginConfig -const PeerConnectionTimeoutMax = 45000 //ms -const PeerConnectionTimeoutMin = 30000 //ms +const ( + PeerConnectionTimeoutMax = 45000 // ms + PeerConnectionTimeoutMin = 30000 // ms +) const WgPort = 51820 @@ -33,14 +37,23 @@ const WgPort = 51820 type EngineConfig struct { WgPort int WgIfaceName string + // WgAddr is a Wireguard local address (Wiretrustee Network IP) WgAddr string + // WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine) WgPrivateKey wgtypes.Key + // IFaceBlackList is a list of network interfaces to ignore when discovering connection candidates (ICE related) IFaceBlackList map[string]struct{} PreSharedKey *wgtypes.Key + + // UDPMuxPort default value 0 - the system will pick an available port + UDPMuxPort int + + // UDPMuxSrflxPort default value 0 - the system will pick an available port + UDPMuxSrflxPort int } // Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers. @@ -67,6 +80,11 @@ type Engine struct { wgInterface iface.WGIface + udpMux ice.UDPMux + udpMuxSrflx ice.UniversalUDPMux + udpMuxConn *net.UDPConn + udpMuxConnSrflx *net.UDPConn + // networkSerial is the latest Serial (state ID) of the network sent by the Management service networkSerial uint64 } @@ -78,7 +96,10 @@ type Peer struct { } // NewEngine creates a new Connection Engine -func NewEngine(signalClient signal.Client, mgmClient mgm.Client, config *EngineConfig, cancel context.CancelFunc, ctx context.Context) *Engine { +func NewEngine( + signalClient signal.Client, mgmClient mgm.Client, config *EngineConfig, + cancel context.CancelFunc, ctx context.Context, +) *Engine { return &Engine{ signal: signalClient, mgmClient: mgmClient, @@ -111,6 +132,30 @@ func (e *Engine) Stop() error { } } + if e.udpMux != nil { + if err := e.udpMux.Close(); err != nil { + log.Debugf("close udp mux: %v", err) + } + } + + if e.udpMuxSrflx != nil { + if err := e.udpMuxSrflx.Close(); err != nil { + log.Debugf("close server reflexive udp mux: %v", err) + } + } + + if e.udpMuxConn != nil { + if err := e.udpMuxConn.Close(); err != nil { + log.Debugf("close udp mux connection: %v", err) + } + } + + if e.udpMuxConnSrflx != nil { + if err := e.udpMuxConnSrflx.Close(); err != nil { + log.Debugf("close server reflexive udp mux connection: %v", err) + } + } + log.Infof("stopped Wiretrustee Engine") return nil @@ -134,6 +179,21 @@ func (e *Engine) Start() error { return err } + e.udpMuxConn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: e.config.UDPMuxPort}) + if err != nil { + log.Errorf("failed listening on UDP port %d: [%s]", e.config.UDPMuxPort, err.Error()) + return err + } + + e.udpMuxConnSrflx, err = net.ListenUDP("udp4", &net.UDPAddr{Port: e.config.UDPMuxSrflxPort}) + if err != nil { + log.Errorf("failed listening on UDP port %d: [%s]", e.config.UDPMuxSrflxPort, err.Error()) + return err + } + + e.udpMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{UDPConn: e.udpMuxConn}) + e.udpMuxSrflx = ice.NewUniversalUDPMuxDefault(ice.UniversalUDPMuxParams{UDPConn: e.udpMuxConnSrflx}) + err = e.wgInterface.Create() if err != nil { log.Errorf("failed creating tunnel interface %s: [%s]", wgIfaceName, err.Error()) @@ -154,7 +214,6 @@ func (e *Engine) Start() error { // removePeers finds and removes peers that do not exist anymore in the network map received from the Management Service func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error { - currentPeers := make([]string, 0, len(e.peerConns)) for p := range e.peerConns { currentPeers = append(currentPeers, p) @@ -209,7 +268,6 @@ func (e *Engine) removePeer(peerKey string) error { // GetPeerConnectionStatus returns a connection Status or nil if peer connection wasn't found func (e *Engine) GetPeerConnectionStatus(peerKey string) peer.ConnStatus { - conn, exists := e.peerConns[peerKey] if exists && conn != nil { return conn.Status() @@ -217,6 +275,7 @@ func (e *Engine) GetPeerConnectionStatus(peerKey string) peer.ConnStatus { return -1 } + func (e *Engine) GetPeers() []string { e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() @@ -254,7 +313,7 @@ func signalCandidate(candidate ice.Candidate, myKey wgtypes.Key, remoteKey wgtyp }) if err != nil { log.Errorf("failed signaling candidate to the remote peer %s %s", remoteKey.String(), err) - //todo ?? + // todo ?? return err } @@ -262,7 +321,6 @@ func signalCandidate(candidate ice.Candidate, myKey wgtypes.Key, remoteKey wgtyp } func signalAuth(uFrag string, pwd string, myKey wgtypes.Key, remoteKey wgtypes.Key, s signal.Client, isAnswer bool) error { - var t sProto.Body_Type if isAnswer { t = sProto.Body_ANSWER @@ -272,7 +330,8 @@ func signalAuth(uFrag string, pwd string, myKey wgtypes.Key, remoteKey wgtypes.K msg, err := signal.MarshalCredential(myKey, remoteKey, &signal.Credential{ UFrag: uFrag, - Pwd: pwd}, t) + Pwd: pwd, + }, t) if err != nil { return err } @@ -299,7 +358,7 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { return err } - //todo update signal + // todo update signal } if update.GetNetworkMap() != nil { @@ -311,7 +370,6 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { } return nil - } // receiveManagementEvents connects to the Management Service event stream to receive updates from the management service @@ -371,7 +429,6 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error { } func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { - serial := networkMap.GetSerial() if e.networkSerial > serial { log.Debugf("received outdated NetworkMap with serial %d, ignoring", serial) @@ -455,7 +512,6 @@ func (e Engine) peerExists(peerKey string) bool { } func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) { - var stunTurn []*ice.URL stunTurn = append(stunTurn, e.STUNs...) stunTurn = append(stunTurn, e.TURNs...) @@ -481,6 +537,8 @@ func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, er StunTurn: stunTurn, InterfaceBlackList: interfaceBlacklist, Timeout: timeout, + UDPMux: e.udpMux, + UDPMuxSrflx: e.udpMuxSrflx, ProxyConfig: proxyConfig, } @@ -515,11 +573,9 @@ func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, er // receiveSignalEvents connects to the Signal Service event stream to negotiate connection with remote peers func (e *Engine) receiveSignalEvents() { - go func() { // connect to a stream of messages coming from the signal server err := e.signal.Receive(func(msg *sProto.Message) error { - e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 8a9d154b1..3605f9cec 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -256,6 +256,7 @@ func TestEngine_Sync(t *testing.T) { select { case <-timeout: t.Fatalf("timeout while waiting for test to finish") + return default: } @@ -397,11 +398,12 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin ifaceName = fmt.Sprintf("wt%d", i) } + wgPort := 33100 + i conf := &EngineConfig{ WgIfaceName: ifaceName, WgAddr: resp.PeerConfig.Address, WgPrivateKey: key, - WgPort: 33100 + i, + WgPort: wgPort, } return NewEngine(signalClient, mgmtClient, conf, cancel, ctx), nil diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index f9324af3e..1b1dcb8ac 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -2,12 +2,13 @@ package peer import ( "context" - "github.com/pion/ice/v2" - log "github.com/sirupsen/logrus" - "github.com/wiretrustee/wiretrustee/client/internal/proxy" "net" "sync" "time" + + "github.com/pion/ice/v2" + log "github.com/sirupsen/logrus" + "github.com/wiretrustee/wiretrustee/client/internal/proxy" ) // ConnConfig is a peer Connection configuration @@ -28,6 +29,9 @@ type ConnConfig struct { Timeout time.Duration ProxyConfig proxy.Config + + UDPMux ice.UDPMux + UDPMuxSrflx ice.UniversalUDPMux } // IceCredentials ICE protocol credentials struct @@ -104,6 +108,8 @@ func (conn *Conn) reCreateAgent() error { CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}, FailedTimeout: &failedTimeout, InterfaceFilter: interfaceFilter(conn.config.InterfaceBlackList), + UDPMux: conn.config.UDPMux, + UDPMuxSrflx: conn.config.UDPMuxSrflx, }) if err != nil { return err @@ -174,7 +180,7 @@ func (conn *Conn) Open() error { log.Debugf("received connection confirmation from peer %s", conn.config.Key) - //at this point we received offer/answer and we are ready to gather candidates + // at this point we received offer/answer and we are ready to gather candidates conn.mu.Lock() conn.status = StatusConnecting conn.ctx, conn.notifyDisconnected = context.WithCancel(context.Background()) @@ -287,7 +293,7 @@ func (conn *Conn) SetSignalCandidate(handler func(candidate ice.Candidate) error // and then signals them to the remote peer func (conn *Conn) onICECandidate(candidate ice.Candidate) { if candidate != nil { - //log.Debugf("discovered local candidate %s", candidate.String()) + // log.Debugf("discovered local candidate %s", candidate.String()) go func() { err := conn.signalCandidate(candidate) if err != nil { @@ -386,7 +392,7 @@ func (conn *Conn) OnRemoteOffer(remoteAuth IceCredentials) bool { return true default: log.Debugf("OnRemoteOffer skipping message from peer %s on status %s because is not ready", conn.config.Key, conn.status.String()) - //connection might not be ready yet to receive so we ignore the message + // connection might not be ready yet to receive so we ignore the message return false } } @@ -400,7 +406,7 @@ func (conn *Conn) OnRemoteAnswer(remoteAuth IceCredentials) bool { case conn.remoteAnswerCh <- remoteAuth: return true default: - //connection might not be ready yet to receive so we ignore the message + // connection might not be ready yet to receive so we ignore the message log.Debugf("OnRemoteAnswer skipping message from peer %s on status %s because is not ready", conn.config.Key, conn.status.String()) return false } diff --git a/go.mod b/go.mod index 393b7dc58..fa5e0daa1 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/spf13/cobra v1.3.0 github.com/spf13/pflag v1.0.5 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 + golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de @@ -44,18 +44,18 @@ require ( github.com/mdlayher/netlink v1.4.2 // indirect github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/pion/dtls/v2 v2.0.12 // indirect + github.com/pion/dtls/v2 v2.1.2 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.5 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/stun v0.3.5 // indirect - github.com/pion/transport v0.12.3 // indirect - github.com/pion/turn/v2 v2.0.5 // indirect + github.com/pion/transport v0.13.0 // indirect + github.com/pion/turn/v2 v2.0.7 // indirect github.com/pion/udp v0.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9 // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect @@ -67,3 +67,5 @@ require ( gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect honnef.co/go/tools v0.2.2 // indirect ) + +replace github.com/pion/ice/v2 => github.com/wiretrustee/ice/v2 v2.1.21-0.20220216144753-138db20d36ad diff --git a/go.sum b/go.sum index 4e17ca4e2..05dd1a827 100644 --- a/go.sum +++ b/go.sum @@ -356,10 +356,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pion/dtls/v2 v2.0.12 h1:QMSvNht7FM/XDXij3Ic90SCbl5yL7kppeI4ghfF4in8= -github.com/pion/dtls/v2 v2.0.12/go.mod h1:5Pe3QJI0Ajsx+uCfxREeewGFlKYBzLrXe9ku7Y0oRXM= -github.com/pion/ice/v2 v2.1.17 h1:z7aBWgs85AEeRgtj0bHnCrShzaGnZ/RS4pMoRmbYxtY= -github.com/pion/ice/v2 v2.1.17/go.mod h1:M0MJ/tBR3IyDcaJv49hAiHEzaVBqWCV/MuWqIffBsrw= +github.com/pion/dtls/v2 v2.1.2 h1:22Q1Jk9L++Yo7BIf9130MonNPfPVb+YgdYLeyQotuAA= +github.com/pion/dtls/v2 v2.1.2/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= @@ -368,12 +366,11 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= -github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= -github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw= -github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= -github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA= -github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw= +github.com/pion/transport v0.13.0 h1:KWTA5ZrQogizzYwPEciGtHPLwpAjE91FgXnyu+Hv2uY= +github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= +github.com/pion/turn/v2 v2.0.7 h1:SZhc00WDovK6czaN1RSiHqbwANtIO6wfZQsU0m0KNE8= +github.com/pion/turn/v2 v2.0.7/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -436,6 +433,8 @@ github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/wiretrustee/ice/v2 v2.1.21-0.20220216144753-138db20d36ad h1:S61dy7FWFITWs/WHk2JJvJd600rWyT8Qsm9ct9nUpOQ= +github.com/wiretrustee/ice/v2 v2.1.21-0.20220216144753-138db20d36ad/go.mod h1:XT1Nrb4OxbVFPffbQMbq4PaeEkpRLVzdphh3fjrw7DY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -469,10 +468,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -570,8 +570,8 @@ golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9 h1:kmreh1vGI63l2FxOAYS3Yv6ATsi7lSTuwNSVbGfJV9I= -golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -694,6 +694,7 @@ golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=