Files
elmprodvpn/selective-vpn-api/app/transport_bootstrap_bypass_resolve.go

138 lines
3.0 KiB
Go

package app
import (
"context"
"fmt"
"net"
"net/netip"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
func transportResolveBootstrapIPv4(candidates []string) ([]string, error) {
set := map[string]struct{}{}
var warns []string
for _, raw := range candidates {
host := transportNormalizeBootstrapTarget(raw)
if host == "" {
continue
}
if addr, err := netip.ParseAddr(host); err == nil {
if addr.Is4() {
set[addr.String()] = struct{}{}
}
continue
}
ctx, cancel := context.WithTimeout(context.Background(), 2500*time.Millisecond)
ipAddrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
cancel()
if err != nil {
warns = append(warns, host+": "+strings.TrimSpace(err.Error()))
continue
}
resolved := 0
for _, ipa := range ipAddrs {
v4 := ipa.IP.To4()
if v4 == nil {
continue
}
set[v4.String()] = struct{}{}
resolved++
}
if resolved == 0 {
warns = append(warns, host+": no IPv4 records")
}
}
ips := make([]string, 0, len(set))
for ip := range set {
ips = append(ips, ip)
}
sort.Strings(ips)
if len(warns) == 0 {
return ips, nil
}
if len(warns) > 3 {
warns = append(warns[:3], fmt.Sprintf("... and %d more", len(warns)-3))
}
return ips, fmt.Errorf("bootstrap resolve warnings: %s", strings.Join(warns, "; "))
}
func transportNormalizeBootstrapTarget(raw string) string {
s := strings.TrimSpace(raw)
if s == "" {
return ""
}
if strings.Contains(s, "://") {
if u, err := url.Parse(s); err == nil {
host := strings.TrimSpace(u.Host)
if host != "" {
s = host
} else if strings.TrimSpace(u.Opaque) != "" {
s = strings.TrimSpace(u.Opaque)
}
}
}
if at := strings.LastIndex(s, "@"); at >= 0 {
s = s[at+1:]
}
if idx := strings.IndexAny(s, "/?#"); idx >= 0 {
s = s[:idx]
}
s = strings.TrimSpace(s)
if s == "" {
return ""
}
if host, _, err := net.SplitHostPort(s); err == nil {
s = host
} else if strings.Count(s, ":") == 1 {
i := strings.LastIndex(s, ":")
if i > 0 {
if _, err := strconv.Atoi(strings.TrimSpace(s[i+1:])); err == nil {
s = s[:i]
}
}
}
s = strings.TrimSpace(strings.Trim(s, "[]"))
s = strings.TrimSuffix(s, ".")
if s == "" {
return ""
}
return strings.ToLower(s)
}
func transportDetectMainIPv4Route() (transportMainRoute, error) {
stdout, stderr, code, err := transportRunCommand(5*time.Second, "ip", "-4", "route", "show", "table", "main", "default")
if err != nil || code != 0 {
return transportMainRoute{}, transportCommandError("ip -4 route show table main default", stdout, stderr, code, err)
}
for _, line := range strings.Split(stdout, "\n") {
fields := strings.Fields(strings.TrimSpace(line))
if len(fields) == 0 || fields[0] != "default" {
continue
}
route := transportMainRoute{}
for i := 0; i < len(fields)-1; i++ {
switch fields[i] {
case "dev":
route.Dev = strings.TrimSpace(fields[i+1])
case "via":
route.Via = strings.TrimSpace(fields[i+1])
}
}
if route.Dev != "" {
return route, nil
}
}
return transportMainRoute{}, fmt.Errorf("main default route not found")
}