diff --git a/browser/conn/bind_webrtc.go b/browser/conn/bind_webrtc.go index 085b36ad1..939b9b646 100644 --- a/browser/conn/bind_webrtc.go +++ b/browser/conn/bind_webrtc.go @@ -1,14 +1,63 @@ package conn -import "net" -import "golang.zx2c4.com/wireguard/conn" +import ( + "github.com/pion/webrtc/v3" + "golang.zx2c4.com/wireguard/conn" + "net" + "sync" +) + +func (*WebRTCBind) makeReceive(dcConn *DataChannelConn) conn.ReceiveFunc { + return func(buff []byte) (int, conn.Endpoint, error) { + n, err := dcConn.Read(buff) + if err != nil { + return 0, nil, err + } + addr := dcConn.RemoteAddr().(*DataChannelAddr) + return n, (*WebRTCEndpoint)(addr), err + } +} // WebRTCBind is an implementation of Wireguard Bind interface backed by WebRTC data channel type WebRTCBind struct { + id string + pc *webrtc.PeerConnection + conn *DataChannelConn + mu sync.Mutex } -func (*WebRTCBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) { - return nil, 0, nil +func NewWebRTCBind(id string, pc *webrtc.PeerConnection) conn.Bind { + return &WebRTCBind{ + id: id, + pc: pc, + conn: nil, + mu: sync.Mutex{}, + } +} + +func (bind *WebRTCBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) { + bind.mu.Lock() + defer bind.mu.Unlock() + + //todo whole webrtc logic from the beginning + //todo create peer connection, offer/answer, wait until connected + + dc, err := bind.pc.CreateDataChannel(bind.id, nil) + if err != nil { + return nil, 0, nil + } + + dcConn, err := WrapDataChannel(dc) + if err != nil { + dc.Close() + return nil, 0, err + } + + bind.conn = dcConn + + fns = append(fns, bind.makeReceive(bind.conn)) + + return fns, 38676, nil } func (*WebRTCBind) Close() error { @@ -19,7 +68,11 @@ func (*WebRTCBind) SetMark(mark uint32) error { return nil } -func (*WebRTCBind) Send(b []byte, ep conn.Endpoint) error { +func (bind *WebRTCBind) Send(b []byte, ep conn.Endpoint) error { + _, err := bind.conn.Write(b) + if err != nil { + return err + } return nil } @@ -28,8 +81,7 @@ func (*WebRTCBind) ParseEndpoint(s string) (conn.Endpoint, error) { } // WebRTCEndpoint is an implementation of Wireguard's Endpoint interface backed by WebRTC -type WebRTCEndpoint struct { -} +type WebRTCEndpoint DataChannelAddr func (*WebRTCEndpoint) ClearSrc() { diff --git a/browser/conn/cond.go b/browser/conn/cond.go new file mode 100644 index 000000000..6eedbaae0 --- /dev/null +++ b/browser/conn/cond.go @@ -0,0 +1,34 @@ +package conn + +// credits to https://github.com/rtctunnel/rtctunnel + +import "sync" + +// A Cond is a condition variable like sync.Cond, but using a channel so we can use select. +type Cond struct { + once sync.Once + C chan struct{} +} + +// NewCond creates a new condition variable. +func NewCond() *Cond { + return &Cond{C: make(chan struct{})} +} + +// Do runs f if the condition hasn't been signaled yet. Afterwards it will be signaled. +func (c *Cond) Do(f func()) { + c.once.Do(func() { + f() + close(c.C) + }) +} + +// Signal closes the condition variable channel. +func (c *Cond) Signal() { + c.Do(func() {}) +} + +// Wait waits for the condition variable channel to close. +func (c *Cond) Wait() { + <-c.C +} diff --git a/browser/conn/conn.go b/browser/conn/conn.go new file mode 100644 index 000000000..84250be8d --- /dev/null +++ b/browser/conn/conn.go @@ -0,0 +1,217 @@ +package conn + +// credits to https://github.com/rtctunnel/rtctunnel + +import ( + "context" + "errors" + "github.com/pion/webrtc/v3" + "io" + "net" + "time" +) + +var ErrClosedByPeer = errors.New("closed by peer") + +type DataChannelAddr struct{} + +func (addr DataChannelAddr) Network() string { + return "webrtc" +} + +func (addr DataChannelAddr) String() string { + return "webrtc://datachannel" +} + +// A DataChannelConn implements the net.Conn interface over a webrtc data channel +type DataChannelConn struct { + dc *webrtc.DataChannel + rr ContextReadCloser + rw ContextWriteCloser + + openCond *Cond + closeCond *Cond + closeErr error +} + +// WrapDataChannel wraps an rtc data channel and implements the net.Conn +// interface +func WrapDataChannel(rtcDataChannel *webrtc.DataChannel) (*DataChannelConn, error) { + rr, rw := io.Pipe() + + conn := &DataChannelConn{ + dc: rtcDataChannel, + rr: ContextReadCloser{Context: context.Background(), ReadCloser: rr}, + rw: ContextWriteCloser{Context: context.Background(), WriteCloser: rw}, + + openCond: NewCond(), + closeCond: NewCond(), + } + conn.dc.OnClose(func() { + _ = conn.closeWithError(ErrClosedByPeer) + }) + conn.dc.OnOpen(func() { + // for reasons I don't understand, when opened the data channel is not immediately available for use + time.Sleep(50 * time.Millisecond) + conn.openCond.Signal() + }) + conn.dc.OnMessage(func(msg webrtc.DataChannelMessage) { + if rw != nil { + _, err := rw.Write(msg.Data) + if err != nil { + _ = conn.closeWithError(err) + rw = nil + } + } + }) + + select { + case <-conn.closeCond.C: + err := conn.closeErr + if err == nil { + err = errors.New("datachannel closed for unknown reasons") + } + return nil, err + case <-conn.openCond.C: + } + + return conn, nil +} + +func (dc *DataChannelConn) Read(b []byte) (n int, err error) { + return dc.rr.Read(b) +} + +func (dc *DataChannelConn) Write(b []byte) (n int, err error) { + err = dc.dc.Send(b) + if err != nil { + return 0, err + } + return len(b), nil +} + +func (dc *DataChannelConn) Close() error { + return dc.closeWithError(nil) +} + +func (dc *DataChannelConn) LocalAddr() net.Addr { + return DataChannelAddr{} +} + +func (dc *DataChannelConn) RemoteAddr() net.Addr { + return DataChannelAddr{} +} + +func (dc *DataChannelConn) SetDeadline(t time.Time) error { + var err error + if e := dc.SetReadDeadline(t); e != nil { + err = e + } + if e := dc.SetWriteDeadline(t); e != nil { + err = e + } + return err +} + +func (dc *DataChannelConn) SetReadDeadline(t time.Time) error { + return dc.rr.SetReadDeadline(t) +} + +func (dc *DataChannelConn) SetWriteDeadline(t time.Time) error { + return dc.rw.SetWriteDeadline(t) +} + +func (dc *DataChannelConn) closeWithError(err error) error { + dc.closeCond.Do(func() { + e := dc.rr.Close() + if err == nil { + err = e + } + e = dc.rw.Close() + if err == nil { + err = e + } + e = dc.dc.Close() + if err == nil { + err = e + } + dc.closeErr = err + }) + return err +} + +type ContextReadCloser struct { + context.Context + io.ReadCloser + cancel func() +} + +func (cr ContextReadCloser) Close() error { + err := cr.ReadCloser.Close() + if cr.cancel != nil { + cr.cancel() + cr.cancel = nil + } + return err +} + +func (cr ContextReadCloser) SetReadDeadline(t time.Time) error { + if cr.cancel != nil { + cr.cancel() + cr.cancel = nil + } + cr.Context, cr.cancel = context.WithDeadline(context.Background(), t) + return nil +} + +func (cr ContextReadCloser) Read(p []byte) (n int, err error) { + done := make(chan struct{}) + go func() { + n, err = cr.ReadCloser.Read(p) + close(done) + }() + select { + case <-done: + return n, err + case <-cr.Context.Done(): + return 0, cr.Context.Err() + } +} + +type ContextWriteCloser struct { + context.Context + io.WriteCloser + cancel func() +} + +func (cw ContextWriteCloser) Close() error { + err := cw.WriteCloser.Close() + if cw.cancel != nil { + cw.cancel() + cw.cancel = nil + } + return err +} + +func (cw ContextWriteCloser) SetWriteDeadline(t time.Time) error { + if cw.cancel != nil { + cw.cancel() + cw.cancel = nil + } + cw.Context, cw.cancel = context.WithDeadline(context.Background(), t) + return nil +} + +func (cw ContextWriteCloser) Write(p []byte) (n int, err error) { + done := make(chan struct{}) + go func() { + n, err = cw.WriteCloser.Write(p) + close(done) + }() + select { + case <-done: + return n, err + case <-cw.Context.Done(): + return 0, cw.Context.Err() + } +} diff --git a/go.mod b/go.mod index fae2d544c..8e843885f 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,13 @@ require ( github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 github.com/pion/ice/v2 v2.1.13 + github.com/pion/webrtc/v3 v3.1.7 github.com/rs/cors v1.8.0 github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.3 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 - golang.org/x/sys v0.0.0-20211020174200-9d6173849985 + golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 golang.zx2c4.com/wireguard v0.0.0-20211026125340-e42c6c4bc2d0 golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211026125340-e42c6c4bc2d0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c diff --git a/go.sum b/go.sum index f28eb5908..06d338c35 100644 --- a/go.sum +++ b/go.sum @@ -515,6 +515,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= @@ -524,6 +525,7 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -544,18 +546,37 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pion/datachannel v1.5.0 h1:Jy6xWr9hysxet69qP23ibiJ6M0P30ZRnndHU+N6cpkY= +github.com/pion/datachannel v1.5.0/go.mod h1:TVbgWP+PVM9TlwL1IkG3JqXXfjGxLvsu9QUeFdpTegI= github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8= github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= +github.com/pion/dtls/v2 v2.0.10 h1:wgys7gPR1NMbWjmjJ3CW7lkUGaun8djgH8nahpNLnxI= +github.com/pion/dtls/v2 v2.0.10/go.mod h1:00OxfeCRWHShcqT9jx8pKKmBWuTt0NCZoVPCaC4VKvU= github.com/pion/ice/v2 v2.1.7 h1:FjgDfUNrVYTxQabJrkBX6ld12tvYbgzHenqPh3PJF6E= github.com/pion/ice/v2 v2.1.7/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= github.com/pion/ice/v2 v2.1.13 h1:/YNYcIw56LT/whwuzkTnrprcRnapj2ZNqUsR0W8elmo= github.com/pion/ice/v2 v2.1.13/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= +github.com/pion/interceptor v0.1.0 h1:SlXKaDlEvSl7cr4j8fJykzVz4UdH+7UDtcvx+u01wLU= +github.com/pion/interceptor v0.1.0/go.mod h1:j5NIl3tJJPB3u8+Z2Xz8MZs/VV6rc+If9mXEKNuFmEM= 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= github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= 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/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtcp v1.2.8 h1:Cys8X6r0xxU65ESTmXkqr8eU1Q1Wx+lNkoZCUH4zD7E= +github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA= +github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY= +github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8= +github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk= +github.com/pion/srtp/v2 v2.0.5 h1:ks3wcTvIUE/GHndO3FAvROQ9opy0uLELpwHJaQ1yqhQ= +github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A= 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= @@ -566,6 +587,8 @@ 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/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/webrtc/v3 v3.1.7 h1:BXkjdVmj+HYiou8eBX6F3Y8xdRkQejg8px5JudTOv0M= +github.com/pion/webrtc/v3 v3.1.7/go.mod h1:SQxttydYlKo2rCQjHzkUCJFAfEO/Oh2efMWbKYXLk98= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -610,6 +633,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -799,6 +823,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -810,6 +835,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -924,6 +950,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211020174200-9d6173849985 h1:LOlKVhfDyahgmqa97awczplwkjzNaELFg3zRIJ13RYo= golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=