Add proxy dialer interface

Enable user to implement the golang.org/x/net/proxy
dialer interface in order to have a customized dialer.
The customized dialer could be one that connect through
a corporate HTTP/HTTPS proxy.

Resolves #284
This commit is contained in:
Assad Obaid
2020-10-09 12:09:21 -07:00
committed by Sean DuBois
parent 9f7fab666f
commit 64a2cf0c47
6 changed files with 96 additions and 3 deletions
+1
View File
@@ -59,6 +59,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
* [JooYoung Lim](https://github.com/DevRockstarZ)
* [Kory Miller](https://github.com/korymiller1489)
* [ZHENK](https://github.com/scorpionknifes)
* [Assad Obaid](https://github.com/assadobaid)
### License
MIT License - see [LICENSE](LICENSE) for full text
+4
View File
@@ -15,6 +15,7 @@ import (
"github.com/pion/stun"
"github.com/pion/transport/packetio"
"github.com/pion/transport/vnet"
"golang.org/x/net/proxy"
)
type bindingRequest struct {
@@ -126,6 +127,8 @@ type Agent struct {
interfaceFilter func(string) bool
insecureSkipVerify bool
proxyDialer proxy.Dialer
}
type task struct {
@@ -292,6 +295,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
loggerFactory: loggerFactory,
log: log,
net: config.Net,
proxyDialer: config.ProxyDialer,
mDNSMode: mDNSMode,
mDNSName: mDNSName,
+5
View File
@@ -5,6 +5,7 @@ import (
"github.com/pion/logging"
"github.com/pion/transport/vnet"
"golang.org/x/net/proxy"
)
const (
@@ -143,6 +144,10 @@ type AgentConfig struct {
// Currently only passive candidates are supported. This functionality is
// experimental and the API might change in the future.
TCPMux TCPMux
// Proxy Dialer is a dialer that should be implemented by the user based on golang.org/x/net/proxy
// dial interface in order to support corporate proxies
ProxyDialer proxy.Dialer
}
// initWithDefaults populates an agent and falls back to defaults if fields are unset
+1 -2
View File
@@ -19,8 +19,7 @@ import (
"github.com/stretchr/testify/require"
)
type mockPacketConn struct {
}
type mockPacketConn struct{}
func (m *mockPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return 0, nil, nil }
func (m *mockPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return 0, nil }
+12
View File
@@ -369,6 +369,18 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //noli
RelAddr = locConn.LocalAddr().(*net.UDPAddr).IP.String()
RelPort = locConn.LocalAddr().(*net.UDPAddr).Port
case a.proxyDialer != nil && url.Proto == ProtoTypeTCP &&
(url.Scheme == SchemeTypeTURN || url.Scheme == SchemeTypeTURNS):
conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), TURNServerAddr)
if connectErr != nil {
a.log.Warnf("Failed to Dial TCP Addr %s via proxy dialer: %v\n", TURNServerAddr, connectErr)
return
}
RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String()
RelPort = conn.LocalAddr().(*net.TCPAddr).Port
locConn = turn.NewSTUNConn(conn)
case url.Proto == ProtoTypeTCP && url.Scheme == SchemeTypeTURN:
tcpAddr, connectErr := net.ResolveTCPAddr(NetworkTypeTCP4.String(), TURNServerAddr)
if connectErr != nil {
+73 -1
View File
@@ -5,7 +5,9 @@ package ice
import (
"context"
"crypto/tls"
"io"
"net"
"net/url"
"reflect"
"sort"
"strconv"
@@ -19,6 +21,7 @@ import (
"github.com/pion/turn/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/proxy"
)
func TestListenUDP(t *testing.T) {
@@ -197,7 +200,7 @@ func TestTURNConcurrency(t *testing.T) {
Username: "username",
Password: "password",
Proto: protocol,
Port: serverPort + 1,
Port: serverPort + 1 + i,
})
}
urls = append(urls, &URL{
@@ -412,3 +415,72 @@ func TestCloseConnLog(t *testing.T) {
assert.NoError(t, a.Close())
}
type mockProxy struct {
proxyWasDialed func()
}
type mockConn struct{}
func (m *mockConn) Read(b []byte) (n int, err error) { return 0, io.EOF }
func (m *mockConn) Write(b []byte) (int, error) { return 0, io.EOF }
func (m *mockConn) Close() error { return io.EOF }
func (m *mockConn) LocalAddr() net.Addr { return &net.TCPAddr{} }
func (m *mockConn) RemoteAddr() net.Addr { return &net.TCPAddr{} }
func (m *mockConn) SetDeadline(t time.Time) error { return io.EOF }
func (m *mockConn) SetReadDeadline(t time.Time) error { return io.EOF }
func (m *mockConn) SetWriteDeadline(t time.Time) error { return io.EOF }
func (m *mockProxy) Dial(network, addr string) (net.Conn, error) {
m.proxyWasDialed()
return &mockConn{}, nil
}
func TestTURNProxyDialer(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
lim := test.TimeOut(time.Second * 30)
defer lim.Stop()
proxyWasDialed, proxyWasDialedFunc := context.WithCancel(context.Background())
proxy.RegisterDialerType("tcp", func(*url.URL, proxy.Dialer) (proxy.Dialer, error) {
return &mockProxy{proxyWasDialedFunc}, nil
})
tcpProxyURI, err := url.Parse("tcp://fakeproxy:3128")
assert.NoError(t, err)
proxyDialer, err := proxy.FromURL(tcpProxyURI, proxy.Direct)
assert.NoError(t, err)
a, err := NewAgent(&AgentConfig{
CandidateTypes: []CandidateType{CandidateTypeRelay},
NetworkTypes: supportedNetworkTypes(),
Urls: []*URL{
{
Scheme: SchemeTypeTURN,
Host: "127.0.0.1",
Username: "username",
Password: "password",
Proto: ProtoTypeTCP,
Port: 5000,
},
},
ProxyDialer: proxyDialer,
})
assert.NoError(t, err)
candidateGatherFinish, candidateGatherFinishFunc := context.WithCancel(context.Background())
assert.NoError(t, a.OnCandidate(func(c Candidate) {
if c == nil {
candidateGatherFinishFunc()
}
}))
assert.NoError(t, a.GatherCandidates())
<-candidateGatherFinish.Done()
<-proxyWasDialed.Done()
assert.NoError(t, a.Close())
}