feat: add uTLS support (#94)

* feat: add uTLS

* feat: change `Utls.Imitate` to `UtlsImitate`

* feat: add desc and example config

* chore: refine code and docs

* docs(example.dae): adjust order

---------

Co-authored-by: mzz2017 <2017@duck.com>
This commit is contained in:
秋のかえで 2023-05-27 10:52:13 +08:00 committed by GitHub
parent eac04e991b
commit 80d3940483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 193 additions and 48 deletions

View File

@ -39,6 +39,8 @@ type GlobalOption struct {
CheckTolerance time.Duration
CheckDnsTcp bool
AllowInsecure bool
TlsImplementation string
UtlsImitate string
}
type InstanceOption struct {

View File

@ -48,11 +48,12 @@ func NewTrojan(option *dialer.GlobalOption, iOption dialer.InstanceOption, link
func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOption) (*dialer.Dialer, error) {
d := direct.FullconeDirect // Trojan Proxy supports full-cone.
u := url.URL{
Scheme: "tls",
Scheme: option.TlsImplementation,
Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
RawQuery: url.Values{
"sni": []string{s.Sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
"utlsImitate": []string{option.UtlsImitate},
}.Encode(),
}
var err error

View File

@ -110,11 +110,12 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
sni = s.Host
}
u := url.URL{
Scheme: "tls",
Scheme: option.TlsImplementation,
Host: net.JoinHostPort(s.Add, s.Port),
RawQuery: url.Values{
"sni": []string{sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
"utlsImitate": []string{option.UtlsImitate},
}.Encode(),
}
d, err = tls.NewTls(u.String(), d)

View File

@ -3,17 +3,22 @@ package tls
import (
"crypto/tls"
"fmt"
"github.com/mzz2017/softwind/netproxy"
"net/url"
"github.com/mzz2017/softwind/netproxy"
utls "github.com/refraction-networking/utls"
)
// Tls is a base Tls struct
type Tls struct {
dialer netproxy.Dialer
addr string
serverName string
skipVerify bool
tlsConfig *tls.Config
dialer netproxy.Dialer
addr string
serverName string
skipVerify bool
tlsImplentation string
utlsImitate string
tlsConfig *tls.Config
}
// NewTls returns a Tls infra.
@ -24,12 +29,14 @@ func NewTls(s string, d netproxy.Dialer) (*Tls, error) {
}
t := &Tls{
dialer: d,
addr: u.Host,
dialer: d,
addr: u.Host,
tlsImplentation: u.Scheme,
}
query := u.Query()
t.serverName = query.Get("sni")
t.utlsImitate = query.Get("utlsImitate")
// skipVerify
if query.Get("allowInsecure") == "true" || query.Get("allowInsecure") == "1" ||
@ -72,11 +79,35 @@ func (s *Tls) DialTcp(addr string) (conn netproxy.Conn, err error) {
return nil, fmt.Errorf("[Tls]: dial to %s: %w", s.addr, err)
}
tlsConn := tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
var tlsConn interface {
netproxy.Conn
Handshake() error
}
switch s.tlsImplentation {
case "tls":
tlsConn = tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
case "utls":
clientHelloID, err := nameToUtlsClientHelloID(s.utlsImitate)
if err != nil {
return nil, err
}
tlsConn = utls.UClient(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, uTLSConfigFromTLSConfig(s.tlsConfig), *clientHelloID)
default:
return nil, fmt.Errorf("unknown tls implementation: %v", s.tlsImplentation)
}
if err := tlsConn.Handshake(); err != nil {
return nil, err
}

View File

@ -0,0 +1,63 @@
package tls
import (
"crypto/tls"
"fmt"
utls "github.com/refraction-networking/utls"
)
func uTLSConfigFromTLSConfig(config *tls.Config) *utls.Config {
return &utls.Config{
ServerName: config.ServerName,
InsecureSkipVerify: config.InsecureSkipVerify,
}
}
var clientHelloIDMap = map[string]*utls.ClientHelloID{
"randomized": &utls.HelloRandomized,
"randomizedalpn": &utls.HelloRandomizedALPN,
"randomizednoalpn": &utls.HelloRandomizedNoALPN,
"firefox_auto": &utls.HelloFirefox_Auto,
"firefox_55": &utls.HelloFirefox_55,
"firefox_56": &utls.HelloFirefox_56,
"firefox_63": &utls.HelloFirefox_63,
"firefox_65": &utls.HelloFirefox_65,
"firefox_99": &utls.HelloFirefox_99,
"firefox_102": &utls.HelloFirefox_102,
"firefox_105": &utls.HelloFirefox_105,
"chrome_auto": &utls.HelloChrome_Auto,
"chrome_58": &utls.HelloChrome_58,
"chrome_62": &utls.HelloChrome_62,
"chrome_70": &utls.HelloChrome_70,
"chrome_72": &utls.HelloChrome_72,
"chrome_83": &utls.HelloChrome_83,
"chrome_87": &utls.HelloChrome_87,
"chrome_96": &utls.HelloChrome_96,
"chrome_100": &utls.HelloChrome_100,
"chrome_102": &utls.HelloChrome_102,
"ios_auto": &utls.HelloIOS_Auto,
"ios_11_1": &utls.HelloIOS_11_1,
"ios_12_1": &utls.HelloIOS_12_1,
"ios_13": &utls.HelloIOS_13,
"ios_14": &utls.HelloIOS_14,
"android_11_okhttp": &utls.HelloAndroid_11_OkHttp,
"edge_auto": &utls.HelloEdge_Auto,
"edge_85": &utls.HelloEdge_85,
"edge_106": &utls.HelloEdge_106,
"safari_auto": &utls.HelloSafari_Auto,
"safari_16_0": &utls.HelloSafari_16_0,
"360_auto": &utls.Hello360_Auto,
"360_7_5": &utls.Hello360_7_5,
"360_11_0": &utls.Hello360_11_0,
"qq_auto": &utls.HelloQQ_Auto,
"qq_11_1": &utls.HelloQQ_11_1,
}
func nameToUtlsClientHelloID(name string) (*utls.ClientHelloID, error) {
clientHelloID, ok := clientHelloIDMap[name]
if !ok {
return nil, fmt.Errorf("unknown uTLS Client Hello ID: %s", name)
}
return clientHelloID, nil
}

View File

@ -30,6 +30,12 @@ type Global struct {
DisableWaitingNetwork bool `mapstructure:"disable_waiting_network" default:"false"`
AutoConfigKernelParameter bool `mapstructure:"auto_config_kernel_parameter" default:"false"`
SniffingTimeout time.Duration `mapstructure:"sniffing_timeout" default:"100ms"`
TlsImplementation string `mapstructure:"tls_implementation" default:"tls"`
UtlsImitate string `mapstructure:"utls_imitate" default:"chrome_auto"`
}
type Utls struct {
Imitate string `mapstructure:"imitate"`
}
type FunctionOrString interface{}

View File

@ -53,6 +53,8 @@ var GlobalDesc = Desc{
"disable_waiting_network": "Disable waiting for network before pulling subscriptions.",
"auto_config_kernel_parameter": "Automatically configure Linux kernel parameters like ip_forward and send_redirects. Check out https://github.com/daeuniverse/dae/blob/main/docs/getting-started/kernel-parameters.md to see what will dae do.",
"sniffing_timeout": "Timeout to waiting for first data sending for sniffing. It is always 0 if dial_mode is ip. Set it higher is useful in high latency LAN network.",
"tls_implementation": "TLS implementation. \"tls\" is to use Go's crypto/tls. \"utls\" is to use uTLS, which can imitate browser's Client Hello.",
"utls_imitate": "The Client Hello ID for uTLS to imitate. This takes effect only if tls_implementation is utls. See more: https://github.com/daeuniverse/dae/blob/331fa23c16/component/outbound/transport/tls/utls.go#L17",
}
var DnsDesc = Desc{

View File

@ -233,6 +233,8 @@ func NewControlPlane(
CheckTolerance: global.CheckTolerance,
CheckDnsTcp: true,
AllowInsecure: global.AllowInsecure,
TlsImplementation: global.TlsImplementation,
UtlsImitate: global.UtlsImitate,
}
outbounds := []*outbound.DialerGroup{
outbound.NewDialerGroup(option, consts.OutboundDirect.String(),

View File

@ -1,4 +1,7 @@
global {
##### Software options.
###
# tproxy port to listen on. It is NOT a HTTP/SOCKS port, and is just used by eBPF program.
# In normal case, you do not need to use it.
tproxy_port: 12345
@ -6,7 +9,29 @@ global {
# Log level: error, warn, info, debug, trace.
log_level: info
# Node connectivity check.
# Disable waiting for network before pulling subscriptions.
disable_waiting_network: false
##### Interface and kernel options.
###
# The LAN interface to bind. Use it if you want to proxy LAN.
# Multiple interfaces split by ",".
#lan_interface: docker0
# The WAN interface to bind. Use it if you want to proxy localhost.
# Multiple interfaces split by ",". Use "auto" to auto detect.
wan_interface: auto
# Automatically configure Linux kernel parameters like ip_forward and send_redirects. Check out
# https://github.com/daeuniverse/dae/blob/main/docs/getting-started/kernel-parameters.md to see what will dae do.
auto_config_kernel_parameter: true
##### Node connectivity check.
###
# Host of URL should have both IPv4 and IPv6 if you have double stack in local.
# First is URL, others are IP addresses if given.
# Considering traffic consumption, it is recommended to choose a site with anycast IP and less response.
@ -29,16 +54,9 @@ global {
# Group will switch node only when new_latency <= old_latency - tolerance.
check_tolerance: 50ms
# The LAN interface to bind. Use it if you want to proxy LAN.
# Multiple interfaces split by ",".
#lan_interface: docker0
# The WAN interface to bind. Use it if you want to proxy localhost.
# Multiple interfaces split by ",". Use "auto" to auto detect.
wan_interface: auto
# Allow insecure TLS certificates. It is not recommended to turn it on unless you have to.
allow_insecure: false
##### Connecting options.
###
# Optional values of dial_mode are:
# 1. "ip". Dial proxy using the IP from DNS directly. This allows your ipv4, ipv6 to choose the optimal path
@ -57,16 +75,19 @@ global {
# domain based traffic split ability. It doesn't work for direct traffic and consumes more CPU resources.
dial_mode: domain
# Disable waiting for network before pulling subscriptions.
disable_waiting_network: false
# Automatically configure Linux kernel parameters like ip_forward and send_redirects. Check out
# https://github.com/daeuniverse/dae/blob/main/docs/getting-started/kernel-parameters.md to see what will dae do.
auto_config_kernel_parameter: true
# Allow insecure TLS certificates. It is not recommended to turn it on unless you have to.
allow_insecure: false
# Timeout to waiting for first data sending for sniffing. It is always 0 if dial_mode is ip. Set it higher is useful
# in high latency LAN network.
sniffing_timeout: 100ms
# TLS implementation. tls is to use Go's crypto/tls. utls is to use uTLS, which can imitate browser's Client Hello.
tls_implementation: tls
# The Client Hello ID for uTLS to imitate. This takes effect only if tls_implementation is utls.
# See more: https://github.com/daeuniverse/dae/blob/331fa23c16/component/outbound/transport/tls/utls.go#L17
utls_imitate: chrome_auto
}
# Subscriptions defined here will be resolved as nodes and merged as a part of the global node pool.

19
go.mod
View File

@ -19,13 +19,19 @@ require (
github.com/v2rayA/ahocorasick-domain v0.0.0-20230218160829-122a074c48c8
github.com/vishvananda/netlink v1.1.0
github.com/x-cray/logrus-prefixed-formatter v0.5.2
golang.org/x/crypto v0.5.0
golang.org/x/crypto v0.9.0
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
golang.org/x/net v0.5.0
golang.org/x/sys v0.4.0
golang.org/x/net v0.10.0
golang.org/x/sys v0.8.0
google.golang.org/protobuf v1.28.1
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/gaukas/godicttls v0.0.3 // indirect
github.com/klauspost/compress v1.15.15 // indirect
)
require (
github.com/bits-and-blooms/bitset v1.3.1 // indirect
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect
@ -44,13 +50,14 @@ require (
github.com/mzz2017/disk-bloom v1.0.1 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.26.0 // indirect
github.com/refraction-networking/utls v1.3.2
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c // indirect
golang.org/x/term v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37 // indirect
golang.org/x/term v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.52.0 // indirect
)

31
go.sum
View File

@ -1,5 +1,7 @@
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/bits-and-blooms/bitset v1.3.1 h1:y+qrlmq3XsWi+xZqSaueaE8ry8Y127iMxlMfqcK8p0g=
@ -29,6 +31,8 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=
github.com/gaukas/godicttls v0.0.3/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@ -55,6 +59,8 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -89,6 +95,8 @@ github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safchain/ethtool v0.0.0-20230116090318-67cc41908669 h1:2uB3lH1zhL5dQflRl7+qLNKdZhiLAg+McePwsnJcBw8=
@ -125,13 +133,13 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c h1:yrfrd1u7MWIwWIulet2TZPEkeNQhQ/GcPLdPXgiEEr0=
gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c/go.mod h1:3x6b94nWCP/a2XB/joOPMiGYUBvqbLfeY/BkHLeDs6s=
gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37 h1:ZrWBE3u/o9cHU2mySXf1687MaK09JOeZt1A+fHnCjmU=
gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37/go.mod h1:3x6b94nWCP/a2XB/joOPMiGYUBvqbLfeY/BkHLeDs6s=
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.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -140,8 +148,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -160,14 +168,15 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
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.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=