cmd/tailscale: make "tailscale ping" also resolve names without DNS
This lets "tailscale ping $NAME" work even if MagicDNS is off, letting you
ping a name that shows up in "tailscale status".
More user friendly.
@@ -64,11 +64,50 @@ func runPing(ctx context.Context, args []string) error {
c, bc, ctx, cancel := connect(ctx)
defer cancel()
- if len(args) != 1 {
+ if len(args) != 1 || args[0] == "" {
return errors.New("usage: ping <hostname-or-IP>")
}
- hostOrIP := args[0]
var ip string
+ prc := make(chan *ipnstate.PingResult, 1)
+ stc := make(chan *ipnstate.Status, 1)
+ bc.SetNotifyCallback(func(n ipn.Notify) {
+ if n.ErrMessage != nil {
+ log.Fatal(*n.ErrMessage)
+ }
+ if pr := n.PingResult; pr != nil && pr.IP == ip {
+ prc <- pr
+ }
+ if n.Status != nil {
+ stc <- n.Status
+ }
+ })
+ go pump(ctx, bc, c)
+
+ hostOrIP := args[0]
+
+ // If the argument is an IP address, use it directly without any resolution.
+ if net.ParseIP(hostOrIP) != nil {
+ ip = hostOrIP
+ }
+
+ // Otherwise, try to resolve it first from the network peer list.
+ if ip == "" {
+ bc.RequestStatus()
+ select {
+ case st := <-stc:
+ for _, ps := range st.Peer {
+ if hostOrIP == dnsOrQuoteHostname(st, ps) || hostOrIP == ps.DNSName {
+ ip = ps.TailAddr
+ break
+ }
+ }
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+
+ // Finally, use DNS.
+ if ip == "" {
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)
@@ -77,21 +116,11 @@ func runPing(ctx context.Context, args []string) error {
} else {
ip = addrs[0]
}
+ }
if pingArgs.verbose && ip != hostOrIP {
log.Printf("lookup %q => %q", hostOrIP, ip)
}
- ch := make(chan *ipnstate.PingResult, 1)
- bc.SetNotifyCallback(func(n ipn.Notify) {
- if n.ErrMessage != nil {
- log.Fatal(*n.ErrMessage)
- }
- if pr := n.PingResult; pr != nil && pr.IP == ip {
- ch <- pr
- }
- })
- go pump(ctx, bc, c)
-
n := 0
anyPong := false
for {
@@ -101,7 +130,7 @@ func runPing(ctx context.Context, args []string) error {
select {
case <-timer.C:
fmt.Printf("timeout waiting for ping reply\n")
- case pr := <-ch:
+ case pr := <-prc:
timer.Stop()
if pr.Err != "" {
return errors.New(pr.Err)
