Add outbound proxy configuration propagation

This commit is contained in:
Shelikhoo 2023-10-19 11:40:57 +01:00
parent f43da1d2d2
commit 5df7a06eee
No known key found for this signature in database
GPG key ID: C4D5E79D22B25316
7 changed files with 139 additions and 37 deletions

View file

@ -16,8 +16,10 @@ package nat
import (
"errors"
"fmt"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
"log"
"net"
"net/url"
"time"
"github.com/pion/stun"
@ -31,23 +33,28 @@ const (
NATUnrestricted = "unrestricted"
)
// This function checks the NAT mapping and filtering
// Deprecated: Use CheckIfRestrictedNATWithProxy Instead.
func CheckIfRestrictedNAT(server string) (bool, error) {
return CheckIfRestrictedNATWithProxy(server, nil)
}
// CheckIfRestrictedNATWithProxy checks the NAT mapping and filtering
// behaviour and returns true if the NAT is restrictive
// (address-dependent mapping and/or port-dependent filtering)
// and false if the NAT is unrestrictive (meaning it
// will work with most other NATs),
func CheckIfRestrictedNAT(server string) (bool, error) {
return isRestrictedMapping(server)
func CheckIfRestrictedNATWithProxy(server string, proxy *url.URL) (bool, error) {
return isRestrictedMapping(server, proxy)
}
// Performs two tests from RFC 5780 to determine whether the mapping type
// of the client's NAT is address-independent or address-dependent
// Returns true if the mapping is address-dependent and false otherwise
func isRestrictedMapping(addrStr string) (bool, error) {
func isRestrictedMapping(addrStr string, proxy *url.URL) (bool, error) {
var xorAddr1 stun.XORMappedAddress
var xorAddr2 stun.XORMappedAddress
mapTestConn, err := connect(addrStr)
mapTestConn, err := connect(addrStr, proxy)
if err != nil {
return false, fmt.Errorf("Error creating STUN connection: %w", err)
}
@ -98,10 +105,10 @@ func isRestrictedMapping(addrStr string) (bool, error) {
// Note: This function is no longer used because a client's NAT type is
// determined only by their mapping type, but the functionality might
// be useful in the future and remains here.
func isRestrictedFiltering(addrStr string) (bool, error) {
func isRestrictedFiltering(addrStr string, proxy *url.URL) (bool, error) {
var xorAddr stun.XORMappedAddress
mapTestConn, err := connect(addrStr)
mapTestConn, err := connect(addrStr, proxy)
if err != nil {
log.Printf("Error creating STUN connection: %s", err.Error())
return false, err
@ -142,23 +149,41 @@ func isRestrictedFiltering(addrStr string) (bool, error) {
}
// Given an address string, returns a StunServerConn
func connect(addrStr string) (*StunServerConn, error) {
func connect(addrStr string, proxyAddr *url.URL) (*StunServerConn, error) {
// Creating a "connection" to STUN server.
addr, err := net.ResolveUDPAddr("udp4", addrStr)
var conn net.PacketConn
ResolveUDPAddr := net.ResolveUDPAddr
if proxyAddr != nil {
socksClient := proxy.NewSocks5UDPClient(proxyAddr)
ResolveUDPAddr = socksClient.ResolveUDPAddr
}
addr, err := ResolveUDPAddr("udp4", addrStr)
if err != nil {
log.Printf("Error resolving address: %s\n", err.Error())
return nil, err
}
c, err := net.ListenUDP("udp4", nil)
if err != nil {
return nil, err
if proxyAddr == nil {
c, err := net.ListenUDP("udp4", nil)
if err != nil {
return nil, err
}
conn = c
} else {
socksClient := proxy.NewSocks5UDPClient(proxyAddr)
c, err := socksClient.ListenPacket("udp", nil)
if err != nil {
return nil, err
}
conn = c
}
mChan := listen(c)
mChan := listen(conn)
return &StunServerConn{
conn: c,
conn: conn,
PrimaryAddr: addr,
messageChan: mChan,
}, nil
@ -203,13 +228,13 @@ func (c *StunServerConn) AddOtherAddr(addrStr string) error {
}
// taken from https://github.com/pion/stun/blob/master/cmd/stun-traversal/main.go
func listen(conn *net.UDPConn) chan *stun.Message {
func listen(conn net.PacketConn) chan *stun.Message {
messages := make(chan *stun.Message)
go func() {
for {
buf := make([]byte, 1024)
n, _, err := conn.ReadFromUDP(buf)
n, _, err := conn.ReadFrom(buf)
if err != nil {
close(messages)
return

View file

@ -261,6 +261,10 @@ type transportWrapper struct {
sc *SocksClient
}
func (t *transportWrapper) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) {
return t.sc.ListenPacket(network, nil)
}
func (t *transportWrapper) ListenPacket(network string, address string) (net.PacketConn, error) {
return t.sc.ListenPacket(network, nil)
}

View file

@ -5,8 +5,10 @@ import (
"crypto/tls"
"errors"
"fmt"
"golang.org/x/net/proxy"
"net"
"net/http"
"net/url"
"sync"
"time"
@ -14,7 +16,13 @@ import (
"golang.org/x/net/http2"
)
// NewUTLSHTTPRoundTripper creates an instance of RoundTripper that dial to remote HTTPS endpoint with
// Deprecated: use NewUTLSHTTPRoundTripperWithProxy instead
func NewUTLSHTTPRoundTripper(clientHelloID utls.ClientHelloID, uTlsConfig *utls.Config,
backdropTransport http.RoundTripper, removeSNI bool) http.RoundTripper {
return NewUTLSHTTPRoundTripperWithProxy(clientHelloID, uTlsConfig, backdropTransport, removeSNI, nil)
}
// NewUTLSHTTPRoundTripperWithProxy creates an instance of RoundTripper that dial to remote HTTPS endpoint with
// an alternative version of TLS implementation that attempts to imitate browsers' fingerprint.
// clientHelloID is the clientHello that uTLS attempts to imitate
// uTlsConfig is the TLS Configuration template
@ -22,8 +30,8 @@ import (
// removeSNI indicates not to send Server Name Indication Extension
// returns a RoundTripper: its behaviour is documented at
// https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/76#note_2777161
func NewUTLSHTTPRoundTripper(clientHelloID utls.ClientHelloID, uTlsConfig *utls.Config,
backdropTransport http.RoundTripper, removeSNI bool) http.RoundTripper {
func NewUTLSHTTPRoundTripperWithProxy(clientHelloID utls.ClientHelloID, uTlsConfig *utls.Config,
backdropTransport http.RoundTripper, removeSNI bool, proxy *url.URL) http.RoundTripper {
rtImpl := &uTLSHTTPRoundTripperImpl{
clientHelloID: clientHelloID,
config: uTlsConfig,
@ -31,6 +39,7 @@ func NewUTLSHTTPRoundTripper(clientHelloID utls.ClientHelloID, uTlsConfig *utls.
backdropTransport: backdropTransport,
pendingConn: map[pendingConnKey]*unclaimedConnection{},
removeSNI: removeSNI,
proxyAddr: proxy,
}
rtImpl.init()
return rtImpl
@ -51,6 +60,7 @@ type uTLSHTTPRoundTripperImpl struct {
pendingConn map[pendingConnKey]*unclaimedConnection
removeSNI bool
proxyAddr *url.URL
}
type pendingConnKey struct {
@ -174,7 +184,19 @@ func (r *uTLSHTTPRoundTripperImpl) dialTLS(ctx context.Context, addr string) (*u
}
config.ServerName = host
dialer := &net.Dialer{}
systemDialer := &net.Dialer{}
var dialer proxy.ContextDialer
dialer = systemDialer
if r.proxyAddr != nil {
proxyDialer, err := proxy.FromURL(r.proxyAddr, systemDialer)
if err != nil {
return nil, err
}
dialer = proxyDialer.(proxy.ContextDialer)
}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err