From 928b4797f5666ac8b3b1bcaf0e28d8d3bd45c4a2 Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Sun, 26 Jan 2025 23:24:44 +0200 Subject: [PATCH] implement ident.me WAN option provider --- main.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/main.go b/main.go index 42807db..5176da1 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/xml" "flag" "fmt" @@ -9,6 +10,7 @@ import ( "lux/node" "lux/proto" "lux/rpc" + "net" "net/netip" "os" "os/signal" @@ -383,6 +385,92 @@ func (wan *HostStaticWAN) Provide() (host.LuxOption, error) { return &host.LuxOptionWAN{Addr4: wan.addr4, Addr6: wan.addr6}, nil } +type HostIdentWAN struct{} + +func dialIdentMe(identIp netip.Addr) (netip.Addr, error) { + var network string + if identIp.Is6() { + network = "tcp6" + } else { + network = "tcp4" + } + + cl, err := net.DialTCP(network, nil, + net.TCPAddrFromAddrPort(netip.AddrPortFrom(identIp, 80))) + if err != nil { + return netip.Addr{}, err + } + defer cl.Close() + + // I dont want to bundle http package for embedded reasons, + // so I will just bang http headers directly + const req = "GET / HTTP/1.1\r\nHost: ident.me\r\nUser-Agent: LUX-v1\r\nAccept: */*\r\n\r\n" + _, err = cl.Write([]byte(req)) + if err != nil { + return netip.Addr{}, err + } + + res := make([]byte, 1024) + n, err := cl.Read(res) + if err != nil { + return netip.Addr{}, err + } + + // parse http headers to get response + sep := []byte{0x0D, 0x0A, 0x0D, 0x0A} + + ipStr := "" + for i := 0; i < n-4; i++ { + if bytes.Equal(res[i:i+4], sep) { + ipStr = string(res[i+4 : n]) + } + } + + // parse ip + if ipStr == "" { + return netip.Addr{}, fmt.Errorf("IP not found in ident.me response: %s", string(res)) + } + + // to prevent panics + parsedIp := net.ParseIP(ipStr) + if parsedIp == nil { + return netip.Addr{}, fmt.Errorf("failed to parse ident.me IP") + } + + return proto.LuxProtoIPToAddr(parsedIp), nil +} + +func (*HostIdentWAN) Provide() (host.LuxOption, error) { + wan := host.NewLuxOptionWAN() + // so first we gonna resolve ident.me and see if there is IPv6 + addrs, err := net.LookupHost("ident.me") + if err != nil { + return &wan, err + } + + for _, addr := range addrs { + ip := net.ParseIP(addr) + if ip.To4() == nil && wan.Addr6.IsUnspecified() { + // we gonna resolve IPv6 + addr, err := dialIdentMe(proto.LuxProtoIPToAddr(ip)) + if err == nil { + // we got IPv6 + wan.Addr6 = addr + } + } else if wan.Addr4.IsUnspecified() { + addr, err := dialIdentMe(proto.LuxProtoIPToAddr(ip)) + if err != nil { + // if no IPv4 its considered catastrophic error + return &wan, err + } else { + wan.Addr4 = addr + } + } + } + + return &wan, nil +} + func hostMain() { xmlBytes, err := os.ReadFile(configPath) if err != nil { @@ -444,6 +532,8 @@ func hostMain() { } host.AddOptionProvider(&provider) + } else if wan.Method == "identme" { + host.AddOptionProvider(&HostIdentWAN{}) } } }