initial commit
This commit is contained in:
commit
81e509ba33
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module git.tcp.direct/kayos/mullsox
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
|
||||||
|
|
||||||
|
require (
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
|
||||||
|
)
|
29
go.sum
Normal file
29
go.sum
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw=
|
||||||
|
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8=
|
141
mullvad.go
Normal file
141
mullvad.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const AimHost = "am.i.mullvad.net"
|
||||||
|
const Ipv4Endpoint = `https://ipv4.` + AimHost
|
||||||
|
const Ipv6Endpoint = `https://ipv6.` + AimHost
|
||||||
|
|
||||||
|
func CheckIP4(ctx context.Context, h *http.Client) (details *MyIPDetails, err error) {
|
||||||
|
return checkIP(ctx, h, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIP6(ctx context.Context, h *http.Client) (details *MyIPDetails, err error) {
|
||||||
|
return checkIP(ctx, h, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIP(ctx context.Context, h *http.Client) (v4details *MyIPDetails, v6details *MyIPDetails, errs []error) {
|
||||||
|
type result struct {
|
||||||
|
details *MyIPDetails
|
||||||
|
ipv6 bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
resChan = make(chan result)
|
||||||
|
finished = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
check := func(resChan chan result, ipv6 bool) {
|
||||||
|
var r = result{ipv6: ipv6}
|
||||||
|
r.details, r.err = checkIP(ctx, h, r.ipv6)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
r.err = ctx.Err()
|
||||||
|
resChan <- r
|
||||||
|
case resChan <- r:
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go check(resChan, false)
|
||||||
|
go check(resChan, true)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if finished == 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
errs = append(errs, ctx.Err())
|
||||||
|
return
|
||||||
|
case res := <-resChan:
|
||||||
|
switch {
|
||||||
|
case res.err != nil:
|
||||||
|
prefix := "(v4)"
|
||||||
|
if res.ipv6 {
|
||||||
|
prefix = "(v6)"
|
||||||
|
}
|
||||||
|
errs = append(errs, fmt.Errorf("%s %s", prefix, res.err.Error()))
|
||||||
|
case res.ipv6:
|
||||||
|
v6details = res.details
|
||||||
|
case !res.ipv6:
|
||||||
|
v4details = res.details
|
||||||
|
}
|
||||||
|
finished++
|
||||||
|
default:
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIP(ctx context.Context, h *http.Client, ipv6 bool) (details *MyIPDetails, err error) {
|
||||||
|
var (
|
||||||
|
resp *http.Response
|
||||||
|
cytes []byte
|
||||||
|
target string
|
||||||
|
)
|
||||||
|
switch ipv6 {
|
||||||
|
case true:
|
||||||
|
target = Ipv6Endpoint + "/json"
|
||||||
|
default:
|
||||||
|
target = Ipv4Endpoint + "/json"
|
||||||
|
}
|
||||||
|
req, _ := http.NewRequestWithContext(ctx, "GET", target, nil)
|
||||||
|
resp, err = h.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
err = fmt.Errorf("bad status code from %s : %s", target, resp.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cytes, err = io.ReadAll(resp.Body)
|
||||||
|
err = json.Unmarshal(cytes, &details)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyIPDetails struct {
|
||||||
|
Ip string `json:"ip"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
MullvadExitIp bool `json:"mullvad_exit_ip"`
|
||||||
|
MullvadExitIpHostname string `json:"mullvad_exit_ip_hostname"`
|
||||||
|
MullvadServerType string `json:"mullvad_server_type"`
|
||||||
|
Blacklisted struct {
|
||||||
|
Blacklisted bool `json:"blacklisted"`
|
||||||
|
Results []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
Blacklisted bool `json:"blacklisted"`
|
||||||
|
} `json:"results"`
|
||||||
|
} `json:"blacklisted"`
|
||||||
|
Organization string `json:"organization"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MullvadServer struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
CountryCode string `json:"country_code"`
|
||||||
|
CountryName string `json:"country_name"`
|
||||||
|
CityCode string `json:"city_code"`
|
||||||
|
CityName string `json:"city_name"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Owned bool `json:"owned"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
Ipv4AddrIn string `json:"ipv4_addr_in"`
|
||||||
|
Ipv6AddrIn string `json:"ipv6_addr_in"`
|
||||||
|
NetworkPortSpeed int `json:"network_port_speed"`
|
||||||
|
Pubkey string `json:"pubkey"`
|
||||||
|
MultihopPort int `json:"multihop_port"`
|
||||||
|
SocksName string `json:"socks_name"`
|
||||||
|
}
|
55
mullvad_test.go
Normal file
55
mullvad_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckIP4(t *testing.T) {
|
||||||
|
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||||
|
v4, err := CheckIP4(ctx, http.DefaultClient)
|
||||||
|
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) {
|
||||||
|
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||||
|
v6, err := CheckIP6(ctx, http.DefaultClient)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
v6j, err6j := json.Marshal(v6)
|
||||||
|
if err6j != nil {
|
||||||
|
t.Fatalf("%s", err6j.Error())
|
||||||
|
}
|
||||||
|
t.Logf(string(v6j))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckIPConcurrent(t *testing.T) {
|
||||||
|
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
|
||||||
|
v4, v6, errs := CheckIP(ctx, http.DefaultClient)
|
||||||
|
for _, err := range errs {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v4j, err4j := json.Marshal(v4)
|
||||||
|
if err4j != nil {
|
||||||
|
t.Fatalf("%s", err4j.Error())
|
||||||
|
}
|
||||||
|
v6j, err6j := json.Marshal(v6)
|
||||||
|
if err6j != nil {
|
||||||
|
t.Fatalf("%s", err6j.Error())
|
||||||
|
}
|
||||||
|
t.Logf(string(v4j))
|
||||||
|
t.Logf(string(v6j))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user