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

@ -7,6 +7,7 @@ import (
"crypto/tls"
"errors"
"fmt"
"net/url"
"log"
"net/http"
@ -51,12 +52,15 @@ type BrokerChannel struct {
// We make a copy of DefaultTransport because we want the default Dial
// and TLSHandshakeTimeout settings. But we want to disable the default
// ProxyFromEnvironment setting.
func createBrokerTransport() http.RoundTripper {
func createBrokerTransport(proxy *url.URL) http.RoundTripper {
tlsConfig := &tls.Config{
RootCAs: certs.GetRootCAs(),
}
transport := &http.Transport{TLSClientConfig: tlsConfig}
transport.Proxy = nil
if proxy != nil {
transport.Proxy = http.ProxyURL(proxy)
}
transport.ResponseHeaderTimeout = 15 * time.Second
return transport
}
@ -68,7 +72,7 @@ func newBrokerChannelFromConfig(config ClientConfig) (*BrokerChannel, error) {
log.Printf("Domain fronting using a randomly selected domain from: %v", config.FrontDomains)
}
brokerTransport := createBrokerTransport()
brokerTransport := createBrokerTransport(config.CommunicationProxy)
if config.UTLSClientID != "" {
utlsClientHelloID, err := utlsutil.NameToUTLSID(config.UTLSClientID)
@ -78,7 +82,8 @@ func newBrokerChannelFromConfig(config ClientConfig) (*BrokerChannel, error) {
utlsConfig := &utls.Config{
RootCAs: certs.GetRootCAs(),
}
brokerTransport = utlsutil.NewUTLSHTTPRoundTripper(utlsClientHelloID, utlsConfig, brokerTransport, config.UTLSRemoveSNI)
brokerTransport = utlsutil.NewUTLSHTTPRoundTripperWithProxy(utlsClientHelloID, utlsConfig, brokerTransport,
config.UTLSRemoveSNI, config.CommunicationProxy)
}
var rendezvous RendezvousMethod
@ -168,14 +173,22 @@ type WebRTCDialer struct {
max int
eventLogger event.SnowflakeEventReceiver
proxy *url.URL
}
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
func NewWebRTCDialer(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int) *WebRTCDialer {
return NewWebRTCDialerWithEvents(broker, iceServers, max, nil)
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, nil, nil)
}
// NewWebRTCDialerWithEvents constructs a new WebRTCDialer.
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
func NewWebRTCDialerWithEvents(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int, eventLogger event.SnowflakeEventReceiver) *WebRTCDialer {
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventLogger, nil)
}
// NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer.
func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int,
eventLogger event.SnowflakeEventReceiver, proxy *url.URL) *WebRTCDialer {
config := webrtc.Configuration{
ICEServers: iceServers,
}
@ -186,6 +199,7 @@ func NewWebRTCDialerWithEvents(broker *BrokerChannel, iceServers []webrtc.ICESer
max: max,
eventLogger: eventLogger,
proxy: proxy,
}
}
@ -193,7 +207,7 @@ func NewWebRTCDialerWithEvents(broker *BrokerChannel, iceServers []webrtc.ICESer
func (w WebRTCDialer) Catch() (*WebRTCPeer, error) {
// TODO: [#25591] Fetch ICE server information from Broker.
// TODO: [#25596] Consider TURN servers here too.
return NewWebRTCPeerWithEvents(w.webrtcConfig, w.BrokerChannel, w.eventLogger)
return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy)
}
// GetMax returns the maximum number of snowflakes to collect.

View file

@ -110,6 +110,8 @@ type ClientConfig struct {
// BridgeFingerprint is the fingerprint of the bridge that the client will eventually
// connect to, as specified in the Bridge line of the torrc.
BridgeFingerprint string
// CommunicationProxy is the proxy address for network communication
CommunicationProxy *url.URL
}
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple
@ -147,14 +149,14 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) {
if err != nil {
return nil, err
}
go updateNATType(iceServers, broker)
go updateNATType(iceServers, broker, config.CommunicationProxy)
max := 1
if config.Max > max {
max = config.Max
}
eventsLogger := event.NewSnowflakeEventDispatcher()
transport := &Transport{dialer: NewWebRTCDialerWithEvents(broker, iceServers, max, eventsLogger), eventDispatcher: eventsLogger}
transport := &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy), eventDispatcher: eventsLogger}
return transport, nil
}
@ -247,13 +249,13 @@ func (conn *SnowflakeConn) Close() error {
// loop through all provided STUN servers until we exhaust the list or find
// one that is compatible with RFC 5780
func updateNATType(servers []webrtc.ICEServer, broker *BrokerChannel) {
func updateNATType(servers []webrtc.ICEServer, broker *BrokerChannel, proxy *url.URL) {
var restrictedNAT bool
var err error
for _, server := range servers {
addr := strings.TrimPrefix(server.URLs[0], "stun:")
restrictedNAT, err = nat.CheckIfRestrictedNAT(addr)
restrictedNAT, err = nat.CheckIfRestrictedNATWithProxy(addr, proxy)
if err != nil {
log.Printf("Warning: NAT checking failed for server at %s: %s", addr, err)

View file

@ -5,8 +5,11 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/pion/transport/v2"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
"io"
"log"
"net/url"
"strings"
"sync"
"time"
@ -38,20 +41,28 @@ type WebRTCPeer struct {
bytesLogger bytesLogger
eventsLogger event.SnowflakeEventReceiver
proxy *url.URL
}
// Deprecated: Use NewWebRTCPeerWithEventsAndProxy Instead.
func NewWebRTCPeer(config *webrtc.Configuration,
broker *BrokerChannel) (*WebRTCPeer, error) {
return NewWebRTCPeerWithEvents(config, broker, nil)
return NewWebRTCPeerWithEventsAndProxy(config, broker, nil, nil)
}
// NewWebRTCPeerWithEvents constructs a WebRTC PeerConnection to a snowflake proxy.
// Deprecated: Use NewWebRTCPeerWithEventsAndProxy Instead.
func NewWebRTCPeerWithEvents(config *webrtc.Configuration,
broker *BrokerChannel, eventsLogger event.SnowflakeEventReceiver) (*WebRTCPeer, error) {
return NewWebRTCPeerWithEventsAndProxy(config, broker, eventsLogger, nil)
}
// NewWebRTCPeerWithEventsAndProxy constructs a WebRTC PeerConnection to a snowflake proxy.
//
// The creation of the peer handles the signaling to the Snowflake broker, including
// the exchange of SDP information, the creation of a PeerConnection, and the establishment
// of a DataChannel to the Snowflake proxy.
func NewWebRTCPeerWithEvents(config *webrtc.Configuration,
broker *BrokerChannel, eventsLogger event.SnowflakeEventReceiver) (*WebRTCPeer, error) {
func NewWebRTCPeerWithEventsAndProxy(config *webrtc.Configuration,
broker *BrokerChannel, eventsLogger event.SnowflakeEventReceiver, proxy *url.URL) (*WebRTCPeer, error) {
if eventsLogger == nil {
eventsLogger = event.NewSnowflakeEventDispatcher()
}
@ -73,6 +84,7 @@ func NewWebRTCPeerWithEvents(config *webrtc.Configuration,
connection.recvPipe, connection.writePipe = io.Pipe()
connection.eventsLogger = eventsLogger
connection.proxy = proxy
err := connection.connect(config, broker)
if err != nil {
@ -199,7 +211,17 @@ func (c *WebRTCPeer) preparePeerConnection(config *webrtc.Configuration) error {
// to get snowflake working in shadow (where the AF_NETLINK family is not implemented).
// These two lines of code functionally revert a new change in pion by silently ignoring
// when net.Interfaces() fails, rather than throwing an error
vnet, _ := stdnet.NewNet()
var vnet transport.Net
vnet, _ = stdnet.NewNet()
if c.proxy != nil {
if err = proxy.CheckProxyProtocolSupport(c.proxy); err != nil {
return err
}
socksClient := proxy.NewSocks5UDPClient(c.proxy)
vnet = proxy.NewTransportWrapper(&socksClient, vnet)
}
s.SetNet(vnet)
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
c.pc, err = api.NewPeerConnection(*config)

View file

@ -4,6 +4,7 @@ package main
import (
"flag"
"fmt"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
"io"
"io/ioutil"
"log"
@ -231,8 +232,20 @@ func main() {
log.Fatal(err)
}
if ptInfo.ProxyURL != nil {
pt.ProxyError("proxy is not supported")
os.Exit(1)
if err := proxy.CheckProxyProtocolSupport(ptInfo.ProxyURL); err != nil {
pt.ProxyError("proxy is not supported:" + err.Error())
os.Exit(1)
} else {
config.CommunicationProxy = ptInfo.ProxyURL
client := proxy.NewSocks5UDPClient(config.CommunicationProxy)
conn, err := client.ListenPacket("udp", nil)
if err != nil {
pt.ProxyError("proxy test failure:" + err.Error())
os.Exit(1)
}
conn.Close()
pt.ProxyDone()
}
}
listeners := make([]net.Listener, 0)
shutdown := make(chan struct{})