cmd/tailscale: fix "tailscale ip $self-host-hostname"

And in the process, fix the related confusing error messages from
pinging your own IP or hostname.

Fixes #2803

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-09-07 11:57:23 -07:00
parent 4917a96aec
commit 7bfd4f521d
6 changed files with 59 additions and 22 deletions

View File

@@ -91,7 +91,7 @@ func runCp(ctx context.Context, args []string) error {
} else if hadBrackets && (err != nil || !ip.Is6()) {
return errors.New("unexpected brackets around target")
}
ip, err := tailscaleIPFromArg(ctx, target)
ip, _, err := tailscaleIPFromArg(ctx, target)
if err != nil {
return err
}

View File

@@ -57,7 +57,7 @@ func runIP(ctx context.Context, args []string) error {
}
ips := st.TailscaleIPs
if of != "" {
ip, err := tailscaleIPFromArg(ctx, of)
ip, _, err := tailscaleIPFromArg(ctx, of)
if err != nil {
return err
}
@@ -101,5 +101,12 @@ func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus,
}
}
}
if ps := st.Self; ps != nil {
for _, pip := range ps.TailscaleIPs {
if ip == pip {
return ps, true
}
}
}
return nil, false
}

View File

@@ -84,10 +84,14 @@ func runPing(ctx context.Context, args []string) error {
go func() { pumpErr <- pump(ctx, bc, c) }()
hostOrIP := args[0]
ip, err := tailscaleIPFromArg(ctx, hostOrIP)
ip, self, err := tailscaleIPFromArg(ctx, hostOrIP)
if err != nil {
return err
}
if self {
fmt.Printf("%v is local Tailscale IP\n", ip)
return nil
}
if pingArgs.verbose && ip != hostOrIP {
log.Printf("lookup %q => %q", hostOrIP, ip)
@@ -107,6 +111,10 @@ func runPing(ctx context.Context, args []string) error {
case pr := <-prc:
timer.Stop()
if pr.Err != "" {
if pr.IsLocalIP {
fmt.Println(pr.Err)
return nil
}
return errors.New(pr.Err)
}
latency := time.Duration(pr.LatencySeconds * float64(time.Second)).Round(time.Millisecond)
@@ -147,33 +155,39 @@ func runPing(ctx context.Context, args []string) error {
}
}
func tailscaleIPFromArg(ctx context.Context, hostOrIP string) (ip string, err error) {
func tailscaleIPFromArg(ctx context.Context, hostOrIP string) (ip string, self bool, err error) {
// If the argument is an IP address, use it directly without any resolution.
if net.ParseIP(hostOrIP) != nil {
return hostOrIP, nil
return hostOrIP, false, nil
}
// Otherwise, try to resolve it first from the network peer list.
st, err := tailscale.Status(ctx)
if err != nil {
return "", err
return "", false, err
}
match := func(ps *ipnstate.PeerStatus) bool {
return strings.EqualFold(hostOrIP, dnsOrQuoteHostname(st, ps)) || hostOrIP == ps.DNSName
}
for _, ps := range st.Peer {
if hostOrIP == dnsOrQuoteHostname(st, ps) || hostOrIP == ps.DNSName {
if match(ps) {
if len(ps.TailscaleIPs) == 0 {
return "", errors.New("node found but lacks an IP")
return "", false, errors.New("node found but lacks an IP")
}
return ps.TailscaleIPs[0].String(), nil
return ps.TailscaleIPs[0].String(), false, nil
}
}
if match(st.Self) && len(st.Self.TailscaleIPs) > 0 {
return st.Self.TailscaleIPs[0].String(), true, nil
}
// Finally, use DNS.
var res net.Resolver
if addrs, err := res.LookupHost(ctx, hostOrIP); err != nil {
return "", fmt.Errorf("error looking up IP of %q: %v", hostOrIP, err)
return "", false, fmt.Errorf("error looking up IP of %q: %v", hostOrIP, err)
} else if len(addrs) == 0 {
return "", fmt.Errorf("no IPs found for %q", hostOrIP)
return "", false, fmt.Errorf("no IPs found for %q", hostOrIP)
} else {
return addrs[0], nil
return addrs[0], false, nil
}
}