mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-29 19:43:57 +01:00
d1703479ff
On Android, because of the hard SELinux policies can not list the interfaces of the ICE package. Without it can not generate a host type candidate. In this pull request, the list of interfaces comes via the Java interface.
441 lines
9.7 KiB
Go
441 lines
9.7 KiB
Go
package peer
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/magiconair/properties/assert"
|
|
"github.com/pion/ice/v2"
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
"github.com/netbirdio/netbird/iface"
|
|
sproto "github.com/netbirdio/netbird/signal/proto"
|
|
)
|
|
|
|
var connConf = ConnConfig{
|
|
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
|
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
|
StunTurn: []*ice.URL{},
|
|
InterfaceBlackList: nil,
|
|
Timeout: time.Second,
|
|
ProxyConfig: proxy.Config{},
|
|
LocalWgPort: 51820,
|
|
}
|
|
|
|
func TestNewConn_interfaceFilter(t *testing.T) {
|
|
ignore := []string{iface.WgInterfaceDefault, "tun0", "zt", "ZeroTier", "utun", "wg", "ts",
|
|
"Tailscale", "tailscale"}
|
|
|
|
filter := interfaceFilter(ignore)
|
|
|
|
for _, s := range ignore {
|
|
assert.Equal(t, filter(s), false)
|
|
}
|
|
|
|
}
|
|
|
|
func TestConn_GetKey(t *testing.T) {
|
|
conn, err := NewConn(connConf, nil, nil, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
got := conn.GetKey()
|
|
|
|
assert.Equal(t, got, connConf.Key, "they should be equal")
|
|
}
|
|
|
|
func TestConn_OnRemoteOffer(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"), nil, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
go func() {
|
|
<-conn.remoteOffersCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
accepted := conn.OnRemoteOffer(OfferAnswer{
|
|
IceCredentials: IceCredentials{
|
|
UFrag: "test",
|
|
Pwd: "test",
|
|
},
|
|
WgListenPort: 0,
|
|
Version: "",
|
|
})
|
|
if accepted {
|
|
wg.Done()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestConn_OnRemoteAnswer(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"), nil, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
go func() {
|
|
<-conn.remoteAnswerCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
accepted := conn.OnRemoteAnswer(OfferAnswer{
|
|
IceCredentials: IceCredentials{
|
|
UFrag: "test",
|
|
Pwd: "test",
|
|
},
|
|
WgListenPort: 0,
|
|
Version: "",
|
|
})
|
|
if accepted {
|
|
wg.Done()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
func TestConn_Status(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"), nil, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
tables := []struct {
|
|
name string
|
|
status ConnStatus
|
|
want ConnStatus
|
|
}{
|
|
{"StatusConnected", StatusConnected, StatusConnected},
|
|
{"StatusDisconnected", StatusDisconnected, StatusDisconnected},
|
|
{"StatusConnecting", StatusConnecting, StatusConnecting},
|
|
}
|
|
|
|
for _, table := range tables {
|
|
t.Run(table.name, func(t *testing.T) {
|
|
conn.status = table.status
|
|
|
|
got := conn.Status()
|
|
assert.Equal(t, got, table.want, "they should be equal")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConn_Close(t *testing.T) {
|
|
|
|
conn, err := NewConn(connConf, NewRecorder("https://mgm"), nil, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
go func() {
|
|
<-conn.closeCh
|
|
wg.Done()
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
err := conn.Close()
|
|
if err != nil {
|
|
continue
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
type mockICECandidate struct {
|
|
ice.Candidate
|
|
AddressFunc func() string
|
|
TypeFunc func() ice.CandidateType
|
|
}
|
|
|
|
// Address mocks and overwrite ice.Candidate Address method
|
|
func (m *mockICECandidate) Address() string {
|
|
if m.AddressFunc != nil {
|
|
return m.AddressFunc()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Type mocks and overwrite ice.Candidate Type method
|
|
func (m *mockICECandidate) Type() ice.CandidateType {
|
|
if m.TypeFunc != nil {
|
|
return m.TypeFunc()
|
|
}
|
|
return ice.CandidateTypeUnspecified
|
|
}
|
|
|
|
func TestConn_ShouldUseProxy(t *testing.T) {
|
|
publicHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "8.8.8.8"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
privateHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "10.0.0.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
srflxCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeServerReflexive
|
|
},
|
|
}
|
|
|
|
prflxCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypePeerReflexive
|
|
},
|
|
}
|
|
|
|
relayCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeRelay
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
candatePair *ice.CandidatePair
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "Use Proxy When Local Candidate Is Relay",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Remote Candidate Is Relay",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: relayCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Local Candidate Is Peer Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: prflxCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Use Proxy When Remote Candidate Is Peer Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: prflxCandidate,
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Local Candidate Is Public And Remote Is Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Remote Candidate Is Public And Local Is Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Local Candidate is Public And Remote Is Server Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: srflxCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Remote Candidate is Public And Local Is Server Reflexive",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: srflxCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Both Candidates Are Public",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Don't Use Proxy When Both Candidates Are Private",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: privateHostCandidate,
|
|
Remote: privateHostCandidate,
|
|
},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
result := shouldUseProxy(testCase.candatePair)
|
|
if result != testCase.expected {
|
|
t.Errorf("got a different result. Expected %t Got %t", testCase.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetProxyWithMessageExchange(t *testing.T) {
|
|
publicHostCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "8.8.8.8"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeHost
|
|
},
|
|
}
|
|
relayCandidate := &mockICECandidate{
|
|
AddressFunc: func() string {
|
|
return "1.1.1.1"
|
|
},
|
|
TypeFunc: func() ice.CandidateType {
|
|
return ice.CandidateTypeRelay
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
candatePair *ice.CandidatePair
|
|
inputDirectModeSupport bool
|
|
inputRemoteModeMessage bool
|
|
expected proxy.Type
|
|
}{
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Local Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: true,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Remote Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Wireguard Proxy When Remote Direct Mode Support Is False And Local Eval Is Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: relayCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: false,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeWireguard,
|
|
},
|
|
{
|
|
name: "Should Result In Using Direct When Remote Direct Mode Support Is False And Local Eval Is No Use Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: false,
|
|
inputRemoteModeMessage: false,
|
|
expected: proxy.TypeNoProxy,
|
|
},
|
|
{
|
|
name: "Should Result In Using Direct When Local And Remote Eval Is No Proxy",
|
|
candatePair: &ice.CandidatePair{
|
|
Local: publicHostCandidate,
|
|
Remote: publicHostCandidate,
|
|
},
|
|
inputDirectModeSupport: true,
|
|
inputRemoteModeMessage: true,
|
|
expected: proxy.TypeNoProxy,
|
|
},
|
|
}
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
g := errgroup.Group{}
|
|
conn, err := NewConn(connConf, nil, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conn.meta.protoSupport.DirectCheck = testCase.inputDirectModeSupport
|
|
conn.SetSendSignalMessage(func(message *sproto.Message) error {
|
|
return nil
|
|
})
|
|
|
|
g.Go(func() error {
|
|
return conn.OnModeMessage(ModeMessage{
|
|
Direct: testCase.inputRemoteModeMessage,
|
|
})
|
|
})
|
|
|
|
resultProxy := conn.getProxyWithMessageExchange(testCase.candatePair, 1000)
|
|
|
|
err = g.Wait()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if resultProxy.Type() != testCase.expected {
|
|
t.Errorf("result didn't match expected value: Expected: %s, Got: %s", testCase.expected, resultProxy.Type())
|
|
}
|
|
})
|
|
}
|
|
}
|