chore: wasm further work

This commit is contained in:
braginini 2021-11-14 18:08:33 +01:00
parent 9587b9a930
commit 6457c48281
5 changed files with 340 additions and 8 deletions

View File

@ -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() {

34
browser/conn/cond.go Normal file
View File

@ -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
}

217
browser/conn/conn.go Normal file
View File

@ -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()
}
}

3
go.mod
View File

@ -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

28
go.sum
View File

@ -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=