138 lines
3.0 KiB
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")
|
|
}
|