Add proxy protocol support

This commit is contained in:
世界 2022-08-23 21:07:35 +08:00
parent aa8cdaee22
commit 1413c5022a
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
15 changed files with 260 additions and 71 deletions

View file

@ -0,0 +1,50 @@
package proxyproto
import (
"context"
"net"
"net/netip"
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/pires/go-proxyproto"
)
var _ N.Dialer = (*Dialer)(nil)
type Dialer struct {
N.Dialer
}
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch N.NetworkName(network) {
case N.NetworkTCP:
conn, err := d.Dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
var source M.Socksaddr
metadata := adapter.ContextFrom(ctx)
if metadata != nil {
source = metadata.Source
}
if !source.IsValid() {
source = M.SocksaddrFromNet(conn.LocalAddr())
}
if destination.Addr.Is6() {
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
}
h := proxyproto.HeaderProxyFromAddrs(1, source.TCPAddr(), destination.TCPAddr())
_, err = h.WriteTo(conn)
if err != nil {
conn.Close()
return nil, E.Cause(err, "write proxy protocol header")
}
return conn, nil
default:
return d.Dialer.DialContext(ctx, network, destination)
}
}

View file

@ -0,0 +1,40 @@
package proxyproto
import (
std_bufio "bufio"
"net"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
"github.com/pires/go-proxyproto"
)
type Listener struct {
net.Listener
}
func (l *Listener) Accept() (net.Conn, error) {
conn, err := l.Listener.Accept()
if err != nil {
return nil, err
}
bufReader := std_bufio.NewReader(conn)
header, err := proxyproto.Read(bufReader)
if err != nil {
return nil, err
}
if bufReader.Buffered() > 0 {
cache := buf.NewSize(bufReader.Buffered())
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
if err != nil {
return nil, err
}
conn = bufio.NewCachedConn(conn, cache)
}
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr),
}}, nil
}

1
go.mod
View file

@ -16,6 +16,7 @@ require (
github.com/hashicorp/yamux v0.1.1
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.6.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/netlink v0.0.0-20220820041223-3cd8365d17ac
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb

2
go.sum
View file

@ -121,6 +121,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View file

@ -9,6 +9,7 @@ import (
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/proxyproto"
"github.com/sagernet/sing-box/common/settings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
@ -45,7 +46,7 @@ type myInboundAdapter struct {
// internal
tcpListener *net.TCPListener
tcpListener net.Listener
udpConn *net.UDPConn
udpAddr M.Socksaddr
packetAccess sync.RWMutex
@ -101,10 +102,10 @@ func (a *myInboundAdapter) Start() error {
return nil
}
func (a *myInboundAdapter) ListenTCP() (*net.TCPListener, error) {
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
var err error
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
var tcpListener *net.TCPListener
var tcpListener net.Listener
if !a.listenOptions.TCPFastOpen {
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
} else {
@ -113,11 +114,15 @@ func (a *myInboundAdapter) ListenTCP() (*net.TCPListener, error) {
if err == nil {
a.logger.Info("tcp server started at ", tcpListener.Addr())
}
if a.listenOptions.ProxyProtocol {
a.logger.Debug("proxy protocol enabled")
tcpListener = &proxyproto.Listener{Listener: tcpListener}
}
a.tcpListener = tcpListener
return tcpListener, err
}
func (a *myInboundAdapter) ListenUDP() (*net.UDPConn, error) {
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
if err != nil {
@ -135,7 +140,7 @@ func (a *myInboundAdapter) Close() error {
err = a.clearSystemProxy()
}
return E.Errors(err, common.Close(
common.PtrOrNil(a.tcpListener),
a.tcpListener,
common.PtrOrNil(a.udpConn),
))
}
@ -168,7 +173,7 @@ func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.Packe
func (a *myInboundAdapter) loopTCPIn() {
tcpListener := a.tcpListener
for {
conn, err := tcpListener.AcceptTCP()
conn, err := tcpListener.Accept()
if err != nil {
return
}
@ -183,8 +188,15 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = N.NetworkTCP
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
if !metadata.Source.IsValid() {
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
}
if !metadata.Destination.IsValid() {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
}
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr())
}
return metadata
}

View file

@ -5,8 +5,6 @@ package inbound
import (
"bytes"
"context"
"net"
"net/netip"
"sync"
"github.com/sagernet/quic-go"
@ -26,23 +24,18 @@ import (
var _ adapter.Inbound = (*Hysteria)(nil)
type Hysteria struct {
ctx context.Context
router adapter.Router
logger log.ContextLogger
tag string
listenOptions option.ListenOptions
quicConfig *quic.Config
tlsConfig *TLSConfig
authKey []byte
xplusKey []byte
sendBPS uint64
recvBPS uint64
udpListener net.PacketConn
listener quic.Listener
udpAccess sync.RWMutex
udpSessionId uint32
udpSessions map[uint32]chan *hysteria.UDPMessage
udpDefragger hysteria.Defragger
myInboundAdapter
quicConfig *quic.Config
tlsConfig *TLSConfig
authKey []byte
xplusKey []byte
sendBPS uint64
recvBPS uint64
listener quic.Listener
udpAccess sync.RWMutex
udpSessionId uint32
udpSessions map[uint32]chan *hysteria.UDPMessage
udpDefragger hysteria.Defragger
}
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (*Hysteria, error) {
@ -101,17 +94,21 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
return nil, E.New("invalid down speed")
}
inbound := &Hysteria{
ctx: ctx,
router: router,
logger: logger,
tag: tag,
quicConfig: quicConfig,
listenOptions: options.ListenOptions,
authKey: auth,
xplusKey: xplus,
sendBPS: up,
recvBPS: down,
udpSessions: make(map[uint32]chan *hysteria.UDPMessage),
myInboundAdapter: myInboundAdapter{
protocol: C.TypeHysteria,
network: []string{N.NetworkUDP},
ctx: ctx,
router: router,
logger: logger,
tag: tag,
listenOptions: options.ListenOptions,
},
quicConfig: quicConfig,
authKey: auth,
xplusKey: xplus,
sendBPS: up,
recvBPS: down,
udpSessions: make(map[uint32]chan *hysteria.UDPMessage),
}
if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired
@ -127,19 +124,8 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
return inbound, nil
}
func (h *Hysteria) Type() string {
return C.TypeHysteria
}
func (h *Hysteria) Tag() string {
return h.tag
}
func (h *Hysteria) Start() error {
listenAddr := M.SocksaddrFrom(netip.Addr(h.listenOptions.Listen), h.listenOptions.ListenPort)
var packetConn net.PacketConn
var err error
packetConn, err = net.ListenUDP(M.NetworkFromNetAddr("udp", listenAddr.Addr), listenAddr.UDPAddr())
packetConn, err := h.myInboundAdapter.ListenUDP()
if err != nil {
return err
}
@ -147,7 +133,6 @@ func (h *Hysteria) Start() error {
packetConn = hysteria.NewXPlusPacketConn(packetConn, h.xplusKey)
packetConn = &hysteria.PacketConnWrapper{PacketConn: packetConn}
}
h.udpListener = packetConn
err = h.tlsConfig.Start()
if err != nil {
return err
@ -316,7 +301,7 @@ func (h *Hysteria) Close() error {
h.udpSessions = make(map[uint32]chan *hysteria.UDPMessage)
h.udpAccess.Unlock()
return common.Close(
h.udpListener,
&h.myInboundAdapter,
h.listener,
common.PtrOrNil(h.tlsConfig),
)

View file

@ -11,4 +11,5 @@ type DirectOutboundOptions struct {
OutboundDialerOptions
OverrideAddress string `json:"override_address,omitempty"`
OverridePort uint16 `json:"override_port,omitempty"`
ProxyProtocol uint8 `json:"proxy_protocol,omitempty"`
}

View file

@ -106,9 +106,10 @@ type InboundOptions struct {
}
type ListenOptions struct {
Listen ListenAddress `json:"listen"`
ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
Listen ListenAddress `json:"listen"`
ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
InboundOptions
}

View file

@ -27,7 +27,7 @@ type SocksOutboundOptions struct {
type HTTPOutboundOptions struct {
OutboundDialerOptions
ServerOptions
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
TLS *OutboundTLSOptions `json:"tls,omitempty"`
}

View file

@ -16,7 +16,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
}
switch options.Type {
case C.TypeDirect:
return NewDirect(router, logger, options.Tag, options.DirectOptions), nil
return NewDirect(router, logger, options.Tag, options.DirectOptions)
case C.TypeBlock:
return NewBlock(logger, options.Tag), nil
case C.TypeDNS:

View file

@ -3,14 +3,18 @@ package outbound
import (
"context"
"net"
"net/netip"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/pires/go-proxyproto"
)
var _ adapter.Outbound = (*Direct)(nil)
@ -20,9 +24,10 @@ type Direct struct {
dialer N.Dialer
overrideOption int
overrideDestination M.Socksaddr
proxyProto uint8
}
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) *Direct {
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
outbound := &Direct{
myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeDirect,
@ -31,7 +36,11 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
logger: logger,
tag: tag,
},
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
dialer: dialer.NewOutbound(router, options.OutboundDialerOptions),
proxyProto: options.ProxyProtocol,
}
if options.ProxyProtocol > 2 {
return nil, E.New("invalid proxy protocol option: ", options.ProxyProtocol)
}
if options.OverrideAddress != "" && options.OverridePort != 0 {
outbound.overrideOption = 1
@ -43,11 +52,12 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
outbound.overrideOption = 3
outbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
}
return outbound
return outbound, nil
}
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx)
originDestination := metadata.Destination
metadata.Outbound = h.tag
metadata.Destination = destination
switch h.overrideOption {
@ -60,13 +70,33 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
case 3:
destination.Port = h.overrideDestination.Port
}
network = N.NetworkName(network)
switch network {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
return h.dialer.DialContext(ctx, network, destination)
conn, err := h.dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
if h.proxyProto > 0 {
source := metadata.Source
if !source.IsValid() {
source = M.SocksaddrFromNet(conn.LocalAddr())
}
if originDestination.Addr.Is6() {
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
}
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
_, err = header.WriteTo(conn)
if err != nil {
conn.Close()
return nil, E.Cause(err, "write proxy protocol header")
}
}
return conn, nil
}
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

View file

@ -24,7 +24,7 @@ type HTTP struct {
}
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
detour, err := dialer.NewTLS(dialer.NewOutbound(router, options.OutboundDialerOptions), options.Server, common.PtrValueOrDefault(options.TLSOptions))
detour, err := dialer.NewTLS(dialer.NewOutbound(router, options.OutboundDialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

64
test/direct_test.go Normal file
View file

@ -0,0 +1,64 @@
package main
import (
"net/netip"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
func TestProxyProtocol(t *testing.T) {
startInstance(t, option.Options{
Log: &option.LogOptions{
Level: "error",
},
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeDirect,
DirectOptions: option.DirectInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
ProxyProtocol: true,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeDirect,
Tag: "trojan-out",
DirectOptions: option.DirectOutboundOptions{
OverrideAddress: "127.0.0.1",
OverridePort: serverPort,
ProxyProtocol: 2,
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "trojan-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

View file

@ -10,11 +10,11 @@ require (
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.2.0+incompatible
github.com/sagernet/sing v0.0.0-20220822075357-8b9965b73533
github.com/sagernet/sing v0.0.0-20220823075935-c333192241ec
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220812174116-3211cb980234
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
)
require (
@ -51,6 +51,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect

View file

@ -141,6 +141,8 @@ github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrB
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -160,8 +162,8 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220822075357-8b9965b73533 h1:oOOlmOE6QAGtkYeNyboTm/RzPO8g9mCybg0xveWxvnI=
github.com/sagernet/sing v0.0.0-20220822075357-8b9965b73533/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing v0.0.0-20220823075935-c333192241ec h1:71B48luR/x6uGug+8VN1oUwGuBNgpb7lgb0q9FjgsVw=
github.com/sagernet/sing v0.0.0-20220823075935-c333192241ec/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
@ -241,8 +243,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=