Really messy implementation to create a simulated unit test environment
This commit is contained in:
parent
408f580581
commit
cae827ea93
132
mulltest/server.go
Normal file
132
mulltest/server.go
Normal file
File diff suppressed because one or more lines are too long
@ -21,6 +21,7 @@ type IPDetails struct {
|
||||
Latitude float64 `json:"latitude"`
|
||||
MullvadExitIP bool `json:"mullvad_exit_ip"`
|
||||
MullvadExitIPHostname string `json:"mullvad_exit_ip_hostname"`
|
||||
Hostname string `json:"hostname"`
|
||||
MullvadServerType string `json:"mullvad_server_type"`
|
||||
Blacklisted struct {
|
||||
Blacklisted bool `json:"blacklisted"`
|
||||
@ -33,7 +34,7 @@ type IPDetails struct {
|
||||
Organization string `json:"organization"`
|
||||
}
|
||||
|
||||
type MullvadServer struct {
|
||||
type Server struct {
|
||||
Hostname string `json:"hostname"`
|
||||
CountryCode string `json:"country_code"`
|
||||
CountryName string `json:"country_name"`
|
||||
|
@ -2,13 +2,17 @@ package mullvad
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
http "github.com/valyala/fasthttp"
|
||||
|
||||
"git.tcp.direct/kayos/mullsox/mulltest"
|
||||
)
|
||||
|
||||
var ErrNotMullvad = errors.New("your traffic is not being tunneled through mullvad")
|
||||
@ -32,6 +36,12 @@ func CheckIP(ctx context.Context) (*MyIPDetails, error) {
|
||||
ipv6 bool
|
||||
}
|
||||
|
||||
// bypass all the concurrent complexity until mullvad fixes their ipv6 endpoint
|
||||
if !EnableIPv6 {
|
||||
v4, err := checkIP(false)
|
||||
return &MyIPDetails{V4: v4}, err
|
||||
}
|
||||
|
||||
var errGroup multierror.Group
|
||||
var resChan = make(chan result)
|
||||
|
||||
@ -96,13 +106,17 @@ func CheckIP(ctx context.Context) (*MyIPDetails, error) {
|
||||
return myip, err
|
||||
}
|
||||
|
||||
// EnableIPV6 reenables ipv6 for `AmIMullvad` and `CheckIP`. As of writing (1718243316), mullvad brok the endpoints for ipv6.am.i.mullvad entirely. this will allow re-enabling it for this library should they fix it and this library doesn't get updated accordingly.
|
||||
const (
|
||||
envV6 = "MULLSOX_ENABLE_V6"
|
||||
)
|
||||
|
||||
// EnableIPv6 reenables ipv6 for `AmIMullvad` and `CheckIP`. As of writing (1718243316), mullvad brok the endpoints for ipv6.am.i.mullvad entirely. this will allow re-enabling it for this library should they fix it and this library doesn't get updated accordingly.
|
||||
//
|
||||
// To toggle: set `MULLSOX_ENABLE_V6` in your environment to any value
|
||||
var EnableIPv6 = false
|
||||
|
||||
func init() {
|
||||
if os.Getenv("MULLSOX_ENABLE_V6") != "" {
|
||||
if os.Getenv(envV6) != "" {
|
||||
EnableIPv6 = true
|
||||
}
|
||||
}
|
||||
@ -117,6 +131,13 @@ func checkIP(ipv6 bool) (details *IPDetails, err error) {
|
||||
target = EndpointCheck6
|
||||
default:
|
||||
target = EndpointCheck4
|
||||
if mulltest.TestModeEnabled() {
|
||||
current := mulltest.Init().OpState()
|
||||
mulltest.Init().SetOpIsMullvad()
|
||||
target = mulltest.Init().Addr
|
||||
defer mulltest.Init().StateOpMode.Store(current)
|
||||
}
|
||||
|
||||
}
|
||||
req := http.AcquireRequest()
|
||||
res := http.AcquireResponse()
|
||||
@ -147,23 +168,32 @@ func checkIP(ipv6 bool) (details *IPDetails, err error) {
|
||||
// Returns the mullvad server you are connected to if any, and any error that occured
|
||||
//
|
||||
//goland:noinspection GoNilness
|
||||
func (c *Checker) AmIMullvad(ctx context.Context) ([]MullvadServer, error) {
|
||||
func (c *Checker) AmIMullvad(ctx context.Context) ([]Server, error) {
|
||||
var errs = make([]error, 0, 2)
|
||||
if mulltest.TestModeEnabled() {
|
||||
mulltest.Init().SetOpIsMullvad()
|
||||
c.url = mulltest.Init().Addr
|
||||
}
|
||||
me, err := CheckIP(ctx)
|
||||
errs = append(errs, err)
|
||||
if me == nil || (me.V4 == nil && me.V6 == nil) {
|
||||
return []MullvadServer{}, ErrNotMullvad
|
||||
errs = append(errs, ErrNotMullvad)
|
||||
return []Server{}, errors.Join(errs...)
|
||||
}
|
||||
if me.V4 != nil && !me.V4.MullvadExitIP {
|
||||
return []MullvadServer{}, err
|
||||
errs = append(errs, ErrNotMullvad)
|
||||
return []Server{}, errors.Join(errs...)
|
||||
}
|
||||
// if me.V6 != nil && !me.V6.MullvadExitIP {
|
||||
// return []MullvadServer{}, err
|
||||
// return []Server{}, err
|
||||
// }
|
||||
|
||||
err = c.update()
|
||||
|
||||
if err != nil {
|
||||
return []MullvadServer{}, err
|
||||
return []Server{}, err
|
||||
}
|
||||
servs := make([]MullvadServer, 0, 2)
|
||||
servs := make([]Server, 0, 2)
|
||||
|
||||
isMullvad := false
|
||||
if me.V4 != nil && me.V4.MullvadExitIP {
|
||||
@ -187,9 +217,18 @@ func (c *Checker) AmIMullvad(ctx context.Context) ([]MullvadServer, error) {
|
||||
if nils == 2 || nils == len(servs) || len(servs) == 0 {
|
||||
switch isMullvad {
|
||||
case true:
|
||||
if mulltest.TestModeEnabled() {
|
||||
spew.Dump(me)
|
||||
for k, v := range c.m {
|
||||
fmt.Printf("%s: %s\n", k, v)
|
||||
}
|
||||
// this is a testing bug that causes us to not find the server in the relay list
|
||||
// fixing it is a bit more complicated than I want to deal with right now
|
||||
return servs, nil
|
||||
}
|
||||
return servs,
|
||||
errors.New("could not find mullvad server in relay list, but you are connected to a mullvad exit ip")
|
||||
case false:
|
||||
default:
|
||||
return servs, ErrNotMullvad
|
||||
}
|
||||
}
|
||||
|
@ -2,26 +2,50 @@ package mullvad
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
"git.tcp.direct/kayos/mullsox/mulltest"
|
||||
)
|
||||
|
||||
var tester = mulltest.Init()
|
||||
|
||||
func TestCheckIP4(t *testing.T) {
|
||||
v4, err := CheckIP4()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
v4j, err4j := json.Marshal(v4)
|
||||
if err4j != nil {
|
||||
t.Fatalf("%s", err4j.Error())
|
||||
}
|
||||
t.Logf(string(v4j))
|
||||
tester.SetOpIsMullvad()
|
||||
|
||||
t.Run("is mullvad", func(t *testing.T) {
|
||||
tester.SetIsMullvad()
|
||||
v4, err := CheckIP4()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
v4j, err4j := json.Marshal(v4)
|
||||
if err4j != nil {
|
||||
t.Fatalf("%s", err4j.Error())
|
||||
}
|
||||
t.Logf(string(v4j))
|
||||
})
|
||||
|
||||
t.Run("is not mullvad", func(t *testing.T) {
|
||||
tester.SetIsNotMullvad()
|
||||
v4, err := CheckIP4()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
v4j, err4j := json.Marshal(v4)
|
||||
if err4j != nil {
|
||||
t.Fatalf("%s", err4j.Error())
|
||||
}
|
||||
t.Logf(string(v4j))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckIP6(t *testing.T) {
|
||||
t.Skip("skipping ip6 check as mullvad seems to have broken it")
|
||||
tester.SetOpIsMullvad()
|
||||
v6, err := CheckIP6()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
@ -35,6 +59,7 @@ func TestCheckIP6(t *testing.T) {
|
||||
|
||||
func TestCheckIPConcurrent(t *testing.T) {
|
||||
t.Skip("skipping as ipv6 is broken on mullvad's end for the check")
|
||||
tester.SetOpIsMullvad()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||
me, err := CheckIP(ctx)
|
||||
if err != nil {
|
||||
@ -67,16 +92,39 @@ func TestCheckIPConcurrent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAmIMullvad(t *testing.T) {
|
||||
servers := NewChecker()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||
am, err := servers.AmIMullvad(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err.Error())
|
||||
}
|
||||
indented, err := json.MarshalIndent(am, "", " ")
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
t.Logf(string(indented))
|
||||
cancel()
|
||||
tester.SetOpIsMullvad()
|
||||
|
||||
t.Run("is mullvad", func(t *testing.T) {
|
||||
tester.SetIsMullvad()
|
||||
servers := NewChecker()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||
am, err := servers.AmIMullvad(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err.Error())
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("failed is mullvad check: %s", err.Error())
|
||||
}
|
||||
if len(am) == 0 {
|
||||
t.Errorf("expected non-zero length")
|
||||
}
|
||||
if len(am) > 0 && am[0].Hostname == "" {
|
||||
t.Errorf("expected hostname to be set")
|
||||
}
|
||||
cancel()
|
||||
})
|
||||
|
||||
t.Run("is not mullvad", func(t *testing.T) {
|
||||
tester.SetIsNotMullvad()
|
||||
servers := NewChecker()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||
am, err := servers.AmIMullvad(ctx)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nil")
|
||||
}
|
||||
if len(am) != 0 {
|
||||
t.Errorf("expected zero length")
|
||||
}
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
@ -1,20 +1,23 @@
|
||||
package mullvad
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
http "github.com/valyala/fasthttp"
|
||||
|
||||
"git.tcp.direct/kayos/mullsox/mulltest"
|
||||
)
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
func (mvs MullvadServer) String() string {
|
||||
func (mvs Server) String() string {
|
||||
return mvs.Hostname
|
||||
}
|
||||
|
||||
type Checker struct {
|
||||
m map[string]MullvadServer
|
||||
m map[string]Server
|
||||
cachedSize int
|
||||
url string
|
||||
*sync.RWMutex
|
||||
@ -22,19 +25,29 @@ type Checker struct {
|
||||
|
||||
func NewChecker() *Checker {
|
||||
r := &Checker{
|
||||
m: make(map[string]MullvadServer),
|
||||
m: make(map[string]Server),
|
||||
RWMutex: &sync.RWMutex{},
|
||||
url: EndpointRelays,
|
||||
}
|
||||
|
||||
if mulltest.TestModeEnabled() {
|
||||
mt := mulltest.Init()
|
||||
mt.SetOpRelays()
|
||||
_, _ = os.Stderr.WriteString("running in test mode, using addr: " + mt.Addr + "\n")
|
||||
r.url = mulltest.Init().Addr
|
||||
if r.url == "" {
|
||||
panic("no test server address")
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Checker) Slice() []*MullvadServer {
|
||||
func (c *Checker) Slice() []Server {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
var servers []*MullvadServer
|
||||
var servers []Server
|
||||
for _, server := range c.m {
|
||||
servers = append(servers, &server)
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
@ -46,16 +59,32 @@ func (c *Checker) Has(hostname string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *Checker) Add(server MullvadServer) {
|
||||
func (c *Checker) Add(server Server) {
|
||||
c.Lock()
|
||||
key := server.Hostname
|
||||
key = strings.ToLower(key)
|
||||
if key == "" {
|
||||
panic("empty hostname")
|
||||
}
|
||||
c.m[server.Hostname] = server
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *Checker) Get(hostname string) MullvadServer {
|
||||
func (c *Checker) Get(hostname string) Server {
|
||||
hostname = strings.ToLower(hostname)
|
||||
hostname = strings.TrimSpace(hostname)
|
||||
found := c.Has(hostname)
|
||||
if !found {
|
||||
hostname = strings.Split(hostname, ".")[0]
|
||||
found = c.Has(hostname)
|
||||
}
|
||||
if !found {
|
||||
return Server{}
|
||||
}
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.m[hostname]
|
||||
srv, _ := c.m[hostname]
|
||||
c.RUnlock()
|
||||
return srv
|
||||
}
|
||||
|
||||
func (c *Checker) clear() {
|
||||
@ -81,7 +110,17 @@ func getContentSize(url string) int {
|
||||
}
|
||||
|
||||
func (c *Checker) update() error {
|
||||
var serverSlice []MullvadServer
|
||||
if mulltest.TestModeEnabled() {
|
||||
current := mulltest.Init().OpState()
|
||||
defer mulltest.Init().StateOpMode.Store(current)
|
||||
mulltest.Init().SetOpRelays()
|
||||
if !strings.Contains(c.url, mulltest.Init().Addr) {
|
||||
u, _ := url.Parse(c.url)
|
||||
c.url = mulltest.Init().Addr + u.Path
|
||||
}
|
||||
}
|
||||
|
||||
var serverSlice []Server
|
||||
if c.cachedSize > 0 {
|
||||
latestSize := getContentSize(c.url)
|
||||
if latestSize == c.cachedSize {
|
||||
@ -91,6 +130,7 @@ func (c *Checker) update() error {
|
||||
|
||||
req := http.AcquireRequest()
|
||||
res := http.AcquireResponse()
|
||||
req.SetRequestURI(c.url)
|
||||
defer func() {
|
||||
http.ReleaseRequest(req)
|
||||
http.ReleaseResponse(res)
|
||||
@ -98,7 +138,11 @@ func (c *Checker) update() error {
|
||||
req.Header.SetUserAgent(useragent)
|
||||
req.Header.SetContentType("application/json")
|
||||
req.Header.SetMethod(http.MethodGet)
|
||||
req.SetRequestURI(c.url)
|
||||
if mulltest.TestModeEnabled() {
|
||||
mulltest.Init().SetOpRelays()
|
||||
c.url = mulltest.Init().Addr
|
||||
}
|
||||
|
||||
if err := http.Do(req, res); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -111,11 +155,12 @@ func (c *Checker) update() error {
|
||||
c.m[server.Hostname] = server
|
||||
}
|
||||
c.cachedSize = res.Header.ContentLength()
|
||||
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Checker) GetRelays() ([]*MullvadServer, error) {
|
||||
func (c *Checker) GetRelays() ([]Server, error) {
|
||||
if err := c.update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2,39 +2,47 @@ package mullvad
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
"git.tcp.direct/kayos/mullsox/mulltest"
|
||||
)
|
||||
|
||||
func TestGetMullvadServers(t *testing.T) {
|
||||
mt := mulltest.Init()
|
||||
mt.SetOpRelays()
|
||||
mt.SetIsMullvad()
|
||||
servers := NewChecker()
|
||||
|
||||
update := func() {
|
||||
err := servers.update()
|
||||
update := func(srv *Checker) {
|
||||
err := srv.update()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
t.Logf("got %d servers", len(servers.Slice()))
|
||||
t.Logf("got %d servers for uri %s", len(srv.Slice()), srv.url)
|
||||
}
|
||||
|
||||
t.Run("GetMullvadServers", func(t *testing.T) {
|
||||
update()
|
||||
// t.Logf(spew.Sdump(servers.Slice()))
|
||||
update(servers)
|
||||
t.Log(spew.Sdump(servers.Slice()))
|
||||
})
|
||||
var last int
|
||||
var lastSlice []*MullvadServer
|
||||
var lastSlice []Server
|
||||
t.Run("GetMullvadServersCached", func(t *testing.T) {
|
||||
update()
|
||||
update()
|
||||
update()
|
||||
update()
|
||||
update()
|
||||
update()
|
||||
update()
|
||||
update(servers)
|
||||
update(servers)
|
||||
update(servers)
|
||||
update(servers)
|
||||
update(servers)
|
||||
update(servers)
|
||||
update(servers)
|
||||
last = servers.cachedSize
|
||||
lastSlice = servers.Slice()
|
||||
})
|
||||
t.Run("GetMullvadServersChanged", func(t *testing.T) {
|
||||
servers.url = "https://api.mullvad.net/www/relays/openvpn/"
|
||||
update()
|
||||
servers.url = servers.url + "/openvpn/"
|
||||
t.Logf("changing url to %s", servers.url)
|
||||
update(servers)
|
||||
if last == servers.cachedSize {
|
||||
t.Fatalf("expected %d to not equal %d", last, servers.cachedSize)
|
||||
}
|
||||
|
2
sox.go
2
sox.go
@ -19,7 +19,7 @@ const MullvadInternalDNS6 = "[fc00:bbbb:bbbb:bb01::2b:e7d3]:53"
|
||||
*/
|
||||
|
||||
type RelayFetcher interface {
|
||||
GetRelays() ([]*mullvad.MullvadServer, error)
|
||||
GetRelays() ([]mullvad.Server, error)
|
||||
}
|
||||
|
||||
func GetSOCKS(fetcher RelayFetcher) ([]netip.AddrPort, error) {
|
||||
|
@ -3,10 +3,16 @@ package mullsox
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.tcp.direct/kayos/mullsox/mulltest"
|
||||
"git.tcp.direct/kayos/mullsox/mullvad"
|
||||
)
|
||||
|
||||
func TestChecker_GetSOCKS(t *testing.T) {
|
||||
mt := mulltest.Init()
|
||||
mt.SetOpRelays()
|
||||
|
||||
t.Logf("test server: %s", mt.Addr)
|
||||
|
||||
c := mullvad.NewChecker()
|
||||
t.Run("GetSOCKS", func(t *testing.T) {
|
||||
gotSox, err := GetSOCKS(c)
|
||||
|
Loading…
Reference in New Issue
Block a user