diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go new file mode 100644 index 00000000..fe26285e --- /dev/null +++ b/adapter/outbound/wireguard.go @@ -0,0 +1,325 @@ +package outbound + +import ( + "context" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "net" + "net/netip" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/resolver" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/sing" + + wireguard "github.com/MetaCubeX/sing-wireguard" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/debug" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/wireguard-go/device" +) + +type WireGuard struct { + *Base + bind *wireguard.ClientBind + device *device.Device + tunDevice wireguard.Device + dialer *wgDialer + startOnce sync.Once +} + +type WireGuardOption struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Ip string `proxy:"ip"` + Ipv6 string `proxy:"ipv6,omitempty"` + PrivateKey string `proxy:"private-key"` + PublicKey string `proxy:"public-key"` + PreSharedKey string `proxy:"pre-shared-key,omitempty"` + Reserved []int `proxy:"reserved,omitempty"` + Workers int `proxy:"workers,omitempty"` + MTU int `proxy:"mtu,omitempty"` + UDP bool `proxy:"udp,omitempty"` +} + +type wgDialer struct { + options []dialer.Option +} + +func (d *wgDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return dialer.DialContext(ctx, network, destination.String(), d.options...) +} + +func (d *wgDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return dialer.ListenPacket(ctx, "udp", "", d.options...) +} + +func NewWireGuard(option WireGuardOption) (*WireGuard, error) { + outbound := &WireGuard{ + Base: &Base{ + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.WireGuard, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + dialer: &wgDialer{}, + } + runtime.SetFinalizer(outbound, closeWireGuard) + + var reserved [3]uint8 + if len(option.Reserved) > 0 { + if len(option.Reserved) != 3 { + return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved)) + } + reserved[0] = uint8(option.Reserved[0]) + reserved[1] = uint8(option.Reserved[1]) + reserved[2] = uint8(option.Reserved[2]) + } + peerAddr := M.ParseSocksaddr(option.Server) + peerAddr.Port = uint16(option.Port) + outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved) + localPrefixes := make([]netip.Prefix, 0, 2) + if len(option.Ip) == 0 { + return nil, E.New("missing local address") + } + if !strings.Contains(option.Ip, "/") { + option.Ip = option.Ip + "/32" + } + if prefix, err := netip.ParsePrefix(option.Ip); err == nil { + localPrefixes = append(localPrefixes, prefix) + } else { + return nil, E.Cause(err, "ip address parse error") + } + if len(option.Ipv6) > 0 { + if !strings.Contains(option.Ipv6, "/") { + option.Ipv6 = option.Ipv6 + "/128" + } + if prefix, err := netip.ParsePrefix(option.Ipv6); err == nil { + localPrefixes = append(localPrefixes, prefix) + } else { + return nil, E.Cause(err, "ipv6 address parse error") + } + } + var privateKey, peerPublicKey, preSharedKey string + { + bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey) + if err != nil { + return nil, E.Cause(err, "decode private key") + } + privateKey = hex.EncodeToString(bytes) + } + { + bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode peer public key") + } + peerPublicKey = hex.EncodeToString(bytes) + } + if option.PreSharedKey != "" { + bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) + if err != nil { + return nil, E.Cause(err, "decode pre shared key") + } + preSharedKey = hex.EncodeToString(bytes) + } + ipcConf := "private_key=" + privateKey + ipcConf += "\npublic_key=" + peerPublicKey + ipcConf += "\nendpoint=" + peerAddr.String() + if preSharedKey != "" { + ipcConf += "\npreshared_key=" + preSharedKey + } + var has4, has6 bool + for _, address := range localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" + } + mtu := option.MTU + if mtu == 0 { + mtu = 1408 + } + var err error + outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) + if err != nil { + return nil, E.Cause(err, "create WireGuard device") + } + outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{ + Verbosef: func(format string, args ...interface{}) { + sing.Logger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) + }, + Errorf: func(format string, args ...interface{}) { + sing.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...)) + }, + }, option.Workers) + if debug.Enabled { + sing.Logger.Trace("created wireguard ipc conf: \n", ipcConf) + } + err = outbound.device.IpcSet(ipcConf) + if err != nil { + return nil, E.Cause(err, "setup wireguard") + } + //err = outbound.tunDevice.Start() + return outbound, nil +} + +func closeWireGuard(w *WireGuard) { + if w.device != nil { + w.device.Close() + } + _ = common.Close(w.tunDevice) +} + +func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + w.dialer.options = opts + var conn net.Conn + w.startOnce.Do(func() { + err = w.tunDevice.Start() + }) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + addrs, err := resolver.ResolveAllIP(metadata.Host) + if err != nil { + return nil, err + } + conn, err = N.DialSerial(ctx, w.tunDevice, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()), addrs) + } else { + conn, err = w.tunDevice.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + } + if err != nil { + return nil, err + } + return NewConn(&wgConn{conn, w}, w), nil +} + +func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + w.dialer.options = opts + var pc net.PacketConn + w.startOnce.Do(func() { + err = w.tunDevice.Start() + }) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + pc, err = w.tunDevice.ListenPacket(ctx, M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + if err != nil { + return nil, err + } + return newPacketConn(&wgPacketConn{pc, w}, w), nil +} + +type wgConn struct { + conn net.Conn + wg *WireGuard +} + +func (c *wgConn) Read(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.wg) + return c.conn.Read(b) +} + +func (c *wgConn) Write(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.wg) + return c.conn.Write(b) +} + +func (c *wgConn) Close() error { + defer runtime.KeepAlive(c.wg) + return c.conn.Close() +} + +func (c *wgConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(c.wg) + return c.conn.LocalAddr() +} + +func (c *wgConn) RemoteAddr() net.Addr { + defer runtime.KeepAlive(c.wg) + return c.conn.RemoteAddr() +} + +func (c *wgConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetDeadline(t) +} + +func (c *wgConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetReadDeadline(t) +} + +func (c *wgConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetWriteDeadline(t) +} + +type wgPacketConn struct { + pc net.PacketConn + wg *WireGuard +} + +func (pc *wgPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + defer runtime.KeepAlive(pc.wg) + return pc.pc.ReadFrom(p) +} + +func (pc *wgPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + defer runtime.KeepAlive(pc.wg) + return pc.pc.WriteTo(p, addr) +} + +func (pc *wgPacketConn) Close() error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.Close() +} + +func (pc *wgPacketConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(pc.wg) + return pc.pc.LocalAddr() +} + +func (pc *wgPacketConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetDeadline(t) +} + +func (pc *wgPacketConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetReadDeadline(t) +} + +func (pc *wgPacketConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetWriteDeadline(t) +} diff --git a/adapter/parser.go b/adapter/parser.go index b68e81ab..5d145998 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -88,6 +88,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy, err = outbound.NewHysteria(*hyOption) + case "wireguard": + hyOption := &outbound.WireGuardOption{} + err = decoder.Decode(mapping, hyOption) + if err != nil { + break + } + proxy, err = outbound.NewWireGuard(*hyOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/constant/adapters.go b/constant/adapters.go index 7fc31667..71613487 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -31,6 +31,7 @@ const ( Vless Trojan Hysteria + WireGuard ) const ( @@ -165,6 +166,8 @@ func (at AdapterType) String() string { return "Trojan" case Hysteria: return "Hysteria" + case WireGuard: + return "WireGuard" case Relay: return "Relay" diff --git a/go.mod b/go.mod index e8ef2549..9a8994f1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Dreamacro/clash go 1.19 require ( + github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 github.com/database64128/tfo-go v1.1.2 @@ -20,21 +21,22 @@ require ( github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 + github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f + github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be + golang.org/x/crypto v0.1.0 golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 - golang.org/x/net v0.0.0-20221004154528-8021a29435af + golang.org/x/net v0.1.0 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 - golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 + golang.org/x/sys v0.1.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 @@ -64,7 +66,7 @@ require ( github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect diff --git a/go.sum b/go.sum index 86f8a3e5..48c5cecb 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1 github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= +github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a h1:PG0LSxUoj+FFuTEJh6/phbhvLLiu31VhzcxyiBP+Fgc= +github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a/go.mod h1:8Gonxyo+mozu+jCshM+1krRnkNRW5RkrPPQSAJ8758s= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -103,12 +105,14 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq1GxCj+H8NvYESaex90iX72gadw= -github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= +github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= +github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -138,8 +142,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -159,8 +163,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= @@ -190,14 +194,14 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU= -golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=