|
|
@ -64,34 +64,63 @@ func runPing(ctx context.Context, args []string) error {
|
|
|
|
c, bc, ctx, cancel := connect(ctx)
|
|
|
|
c, bc, ctx, cancel := connect(ctx)
|
|
|
|
defer cancel()
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
if len(args) != 1 {
|
|
|
|
if len(args) != 1 || args[0] == "" {
|
|
|
|
return errors.New("usage: ping <hostname-or-IP>")
|
|
|
|
return errors.New("usage: ping <hostname-or-IP>")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hostOrIP := args[0]
|
|
|
|
|
|
|
|
var ip string
|
|
|
|
var ip string
|
|
|
|
var res net.Resolver
|
|
|
|
prc := make(chan *ipnstate.PingResult, 1)
|
|
|
|
if addrs, err := res.LookupHost(ctx, hostOrIP); err != nil {
|
|
|
|
stc := make(chan *ipnstate.Status, 1)
|
|
|
|
return 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)
|
|
|
|
|
|
|
|
} 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) {
|
|
|
|
bc.SetNotifyCallback(func(n ipn.Notify) {
|
|
|
|
if n.ErrMessage != nil {
|
|
|
|
if n.ErrMessage != nil {
|
|
|
|
log.Fatal(*n.ErrMessage)
|
|
|
|
log.Fatal(*n.ErrMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pr := n.PingResult; pr != nil && pr.IP == ip {
|
|
|
|
if pr := n.PingResult; pr != nil && pr.IP == ip {
|
|
|
|
ch <- pr
|
|
|
|
prc <- pr
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.Status != nil {
|
|
|
|
|
|
|
|
stc <- n.Status
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
go pump(ctx, bc, c)
|
|
|
|
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)
|
|
|
|
|
|
|
|
} else if len(addrs) == 0 {
|
|
|
|
|
|
|
|
return fmt.Errorf("no IPs found for %q", hostOrIP)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
ip = addrs[0]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if pingArgs.verbose && ip != hostOrIP {
|
|
|
|
|
|
|
|
log.Printf("lookup %q => %q", hostOrIP, ip)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
n := 0
|
|
|
|
n := 0
|
|
|
|
anyPong := false
|
|
|
|
anyPong := false
|
|
|
|
for {
|
|
|
|
for {
|
|
|
@ -101,7 +130,7 @@ func runPing(ctx context.Context, args []string) error {
|
|
|
|
select {
|
|
|
|
select {
|
|
|
|
case <-timer.C:
|
|
|
|
case <-timer.C:
|
|
|
|
fmt.Printf("timeout waiting for ping reply\n")
|
|
|
|
fmt.Printf("timeout waiting for ping reply\n")
|
|
|
|
case pr := <-ch:
|
|
|
|
case pr := <-prc:
|
|
|
|
timer.Stop()
|
|
|
|
timer.Stop()
|
|
|
|
if pr.Err != "" {
|
|
|
|
if pr.Err != "" {
|
|
|
|
return errors.New(pr.Err)
|
|
|
|
return errors.New(pr.Err)
|
|
|
|