Compare commits

...

3 Commits
master ... wg

Author SHA1 Message Date
293f10ac51
wg 2022-10-28 00:34:48 -07:00
359b3a6cc6
losing my mind 2022-08-01 04:05:50 -07:00
4a41defb69
yeet 2022-08-01 01:21:37 -07:00
13 changed files with 1329 additions and 53 deletions

1
.gitignore vendored

@ -1,3 +1,4 @@
.idea/
*.save
*.swp
mullf0x

110
api.go

@ -1,15 +1,18 @@
package mullsox
import jsoniter "github.com/json-iterator/go"
import (
"net/http"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json "github.com/pquerna/ffjson/ffjson"
"github.com/valyala/fasthttp"
)
const (
baseDomain = "mullvad.net"
baseEndpoint = "am.i." + baseDomain
ipv4Endpoint = `https://ipv4.` + baseEndpoint
ipv6Endpoint = `https://ipv6.` + baseEndpoint
servEndpoint = `https://api.` + baseDomain + `www/relays/all/`
baseDomain = "mullvad.net"
baseEndpoint = "am.i." + baseDomain
EndpointCheck4 = `https://ipv4.` + baseEndpoint
EndpointCheck6 = `https://ipv6.` + baseEndpoint
EndpointRelays = `https://api.` + baseDomain + `/www/relays/all/`
)
type MyIPDetails struct {
@ -33,22 +36,79 @@ type MyIPDetails struct {
}
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"`
Type string `json:"type"`
StatusMessages []interface{} `json:"status_messages"`
Pubkey string `json:"pubkey,omitempty"`
MultihopPort int `json:"multihop_port,omitempty"`
SocksName string `json:"socks_name,omitempty"`
SshFingerprintSha256 string `json:"ssh_fingerprint_sha256,omitempty"`
SshFingerprintMd5 string `json:"ssh_fingerprint_md5,omitempty"`
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"`
Type string `json:"type"`
Pubkey string `json:"pubkey,omitempty"`
MultihopPort int `json:"multihop_port,omitempty"`
SocksName string `json:"socks_name,omitempty"`
SSHFingerprintSHA256 string `json:"ssh_fingerprint_sha256,omitempty"`
SSHFingerprintMD5 string `json:"ssh_fingerprint_md5,omitempty"`
}
func (mvs MullvadServer) String() string {
return mvs.Hostname
}
type relays []MullvadServer
func GetMullvadServers() (*relays, error) {
var servers = new(relays)
req := fasthttp.AcquireRequest()
res := fasthttp.AcquireResponse()
defer func() {
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(res)
}()
req.Header.SetUserAgent("mulls0x/v0.0.1")
req.Header.SetContentType("application/json")
req.Header.SetMethod(http.MethodGet)
req.SetRequestURI(EndpointRelays)
if err := fasthttp.Do(req, res); err != nil {
return nil, err
}
if err := json.Unmarshal(res.Body(), servers); err != nil {
return nil, err
}
return servers, nil
}
func GetWireguardServers() ([]WireguardServer, error) {
srvs, err := GetMullvadServers()
if err != nil {
return nil, err
}
return srvs.getWireguards()
}
func (servers *relays) getWireguards() ([]WireguardServer, error) {
var wgs []WireguardServer
for _, srv := range *servers {
if srv.Type != "wireguard" {
continue
}
if srv.MultihopPort < 0 {
continue
}
pub, err := encodeBase64ToHex(srv.Pubkey)
if err != nil {
return nil, err
}
wgs = append(wgs, WireguardServer{
Parent: &srv,
WGPublicKey: pub,
In4: srv.Ipv4AddrIn,
In6: srv.Ipv6AddrIn,
})
}
return wgs, nil
}

@ -6,6 +6,8 @@ import (
"io"
"net/http"
"time"
json "github.com/pquerna/ffjson/ffjson"
)
func CheckIP4(ctx context.Context, h *http.Client) (details *MyIPDetails, err error) {
@ -77,14 +79,13 @@ func CheckIP(ctx context.Context, h *http.Client) (v4details *MyIPDetails, v6det
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"
target = EndpointCheck6 + "/json"
default:
target = ipv4Endpoint + "/json"
target = EndpointCheck4 + "/json"
}
req, _ := http.NewRequestWithContext(ctx, "GET", target, nil)
resp, err = h.Do(req)
@ -95,10 +96,11 @@ func checkIP(ctx context.Context, h *http.Client, ipv6 bool) (details *MyIPDetai
err = fmt.Errorf("bad status code from %s : %s", target, resp.Status)
return
}
cytes, err = io.ReadAll(resp.Body)
rbytes, err := io.ReadAll(resp.Body)
println(string(rbytes))
if err != nil {
return
return nil, err
}
err = json.Unmarshal(cytes, &details)
json.Unmarshal(rbytes, &details)
return
}

@ -5,6 +5,8 @@ import (
"net/http"
"testing"
"time"
"github.com/bytedance/sonic"
)
func TestCheckIP4(t *testing.T) {
@ -13,7 +15,7 @@ func TestCheckIP4(t *testing.T) {
if err != nil {
t.Fatalf("%s", err.Error())
}
v4j, err4j := json.Marshal(v4)
v4j, err4j := sonic.Marshal(v4)
if err4j != nil {
t.Fatalf("%s", err4j.Error())
}
@ -26,7 +28,7 @@ func TestCheckIP6(t *testing.T) {
if err != nil {
t.Fatalf("%s", err.Error())
}
v6j, err6j := json.Marshal(v6)
v6j, err6j := sonic.Marshal(v6)
if err6j != nil {
t.Fatalf("%s", err6j.Error())
}
@ -41,11 +43,11 @@ func TestCheckIPConcurrent(t *testing.T) {
t.Fatalf("%s", err.Error())
}
}
v4j, err4j := json.Marshal(v4)
v4j, err4j := sonic.Marshal(v4)
if err4j != nil {
t.Fatalf("%s", err4j.Error())
}
v6j, err6j := json.Marshal(v6)
v6j, err6j := sonic.Marshal(v6)
if err6j != nil {
t.Fatalf("%s", err6j.Error())
}

37
cmd/mullf0x/main.go Normal file

@ -0,0 +1,37 @@
package main
import (
"context"
"net/http"
"os"
"git.tcp.direct/kayos/mullsox"
)
func main() {
current, err := mullsox.CheckIP4(context.Background(), http.DefaultClient)
if err != nil {
println(err.Error())
os.Exit(1)
}
println("control group: " + current.IP)
mvu, err := mullsox.NewMullvadUser(
"default",
os.Args[1],
os.Args[2],
os.Args[3],
)
srvs, err := mullsox.GetWireguardServers()
if err != nil {
println(err.Error())
os.Exit(1)
}
for _, srv := range srvs {
ip, err := mvu.GetIPv4Out(srv)
if err != nil {
println(err.Error())
continue
}
println(srv.String() + ": " + ip)
}
}

9
go.mod

@ -1,10 +1,3 @@
module git.tcp.direct/kayos/mullsox
go 1.18
require github.com/json-iterator/go v1.1.12
require (
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
)
go 1.19

1020
go.sum

File diff suppressed because it is too large Load Diff

@ -1,8 +0,0 @@
package mullsox
import "context"
func GetRelays(ctx context.Context) (ret chan MullvadServer) {
ret = make(chan MullvadServer)
}

25
relays_test.go Normal file

@ -0,0 +1,25 @@
package mullsox
import (
"encoding/json"
"strings"
"testing"
)
func TestGetRelays(t *testing.T) {
srvs, err := GetMullvadServers()
if err != nil {
t.Fatalf("%s", err.Error())
}
// prettyPrint(srvs)
t.Run("GetWireguardServers", func(t *testing.T) {
wgs, err := srvs.getWireguards()
if err != nil {
t.Fatalf("%s", err.Error())
}
for _, wg := range wgs {
pp, _ := json.MarshalIndent(wg, "", "\t")
println(strings.ReplaceAll(string(pp), `"`, ""))
}
})
}

25
user.go Normal file

@ -0,0 +1,25 @@
package mullsox
type MullvadUser struct {
ID string `json:"id"`
Account int `json:"account"`
WGPrivateKey string `json:"private_key"`
WGIPv4 string `json:"ipv4"`
WGDNS string `json:"dns"`
WireguardPorts map[int]*WireguardServer `json:"wireguard_ports"`
}
func NewMullvadUser(id, privateKey, mvip, dns string) (*MullvadUser, error) {
k, err := encodeBase64ToHex(privateKey)
if err != nil {
return nil, err
}
mvu := &MullvadUser{
ID: id,
WGIPv4: mvip,
WGDNS: dns,
WGPrivateKey: k,
// Account: account,
}
return mvu, nil
}

72
userspace.go Normal file

@ -0,0 +1,72 @@
package mullsox
import (
"context"
"fmt"
"log"
"net/http"
"net/netip"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
)
func (mvu *MullvadUser) GetIPv4Out(server WireguardServer) (string, error) {
var dnsipas []netip.Addr //nolint:prealloc
for _, dnsip := range []string{mvu.WGDNS} {
dnsipas = append(dnsipas, netip.MustParseAddr(dnsip))
}
var localipas []netip.Addr //nolint:prealloc
for _, localip := range []string{mvu.WGIPv4} {
localipas = append(localipas, netip.MustParseAddr(localip))
}
tun, tnet, err := netstack.CreateNetTUN(
// VPN Adapter IPs
localipas,
// DNS
dnsipas,
// MTU
1420,
)
if err != nil {
log.Panic(err)
}
// fwmark := binary.BigEndian.Uint32([]byte("[redacted]"))
dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, ""))
err = dev.IpcSet(fmt.Sprintf(`private_key=%s
fwmark=%d
public_key=%s
endpoint=%s:%d
allowed_ip=0.0.0.0/0`,
mvu.WGPrivateKey, fwmark, server.WGPublicKey, server.In4, server.Leapfrog))
if err != nil {
return "", err
}
err = dev.Up()
if err != nil {
return "", err
}
client := &http.Client{
Transport: &http.Transport{
DialContext: tnet.DialContext,
/*func(ctx context.Context, network, addr string) (net.Conn, error) {
addr = strings.ReplaceAll(
addr, "ipv4.am.i.mullvad.net", "193.138.218.116")
ctx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*20))
println("\x1b[32mDialing: ", addr, "\x1b[0m")
return tnet.DialContext(ctx, network, addr)
},*/
/* TLSClientConfig: &tls.Config{
ServerName: "ipv4.am.i.mullvad.net",
InsecureSkipVerify: false,
},
*/},
}
details, err := CheckIP4(context.Background(), client)
if err != nil {
return "", err
}
return details.IP, nil
}

35
util.go Normal file

@ -0,0 +1,35 @@
package mullsox
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"strings"
)
func encodeBase64ToHex(key string) (string, error) {
decoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", errors.New("invalid base64 string: " + key)
}
if len(decoded) != 32 {
return "", errors.New("key should be 32 bytes: " + key)
}
return hex.EncodeToString(decoded), nil
}
const padding = "+-+-+-+-+-+-+-+-+"
var arrowLeft = padding[:len(padding)-2] + "> "
var arrowRight = " <" + padding[:len(padding)-2]
func prettyPrint(srvs []MullvadServer) { //goland:noinspection ALL
for _, srv := range srvs {
border := padding + strings.Repeat("-", len(srv.String())) + padding
println(border + "\n" + arrowLeft + srv.String() + arrowRight + "\n" + border)
pp, _ := json.MarshalIndent(srv, "", "\t")
println(strings.ReplaceAll(string(pp), `"`, ""))
println("\n+" + strings.Repeat("-+", (len(border)/2)-1) + "\n")
}
}

16
wg_server.go Normal file

@ -0,0 +1,16 @@
package mullsox
type WireguardServer struct {
Parent *MullvadServer
WGPublicKey string
Leapfrog int
In4 string
In6 string
// Out addresses should be immutable
Out4 *string
Out6 *string
}
func (wgs WireguardServer) String() string {
return wgs.Parent.Hostname
}