90 lines
1.9 KiB
Go
90 lines
1.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
PingMethodICMP = "icmp"
|
|
PingMethodTCP = "tcp"
|
|
|
|
defaultPingCount = "1"
|
|
defaultPingWaitSec = "2"
|
|
defaultTCPTimeout = 3 * time.Second
|
|
)
|
|
|
|
// PingResult is the outcome of a reachability probe.
|
|
type PingResult struct {
|
|
OK bool
|
|
Method string
|
|
Error error
|
|
}
|
|
|
|
type pingCommandFunc func(hostIP string) error
|
|
type tcpDialFunc func(address string, timeout time.Duration) error
|
|
|
|
var (
|
|
runICMPPing = defaultRunICMPPing
|
|
dialTCP = defaultDialTCP
|
|
)
|
|
|
|
// PingHost probes host reachability: ICMP first, then TCP :22 when ICMP is
|
|
// unavailable or the host does not respond to ICMP.
|
|
func PingHost(hostIP string) PingResult {
|
|
hostIP = strings.TrimSpace(hostIP)
|
|
if hostIP == "" {
|
|
return PingResult{OK: false, Error: fmt.Errorf("host IP is required")}
|
|
}
|
|
|
|
icmpErr := runICMPPing(hostIP)
|
|
if icmpErr == nil {
|
|
return PingResult{OK: true, Method: PingMethodICMP}
|
|
}
|
|
|
|
address := net.JoinHostPort(hostIP, defaultSSHPort)
|
|
tcpErr := dialTCP(address, defaultTCPTimeout)
|
|
if tcpErr == nil {
|
|
return PingResult{OK: true, Method: PingMethodTCP}
|
|
}
|
|
|
|
return PingResult{
|
|
OK: false,
|
|
Method: PingMethodTCP,
|
|
Error: fmt.Errorf(
|
|
"icmp: %v; tcp %s: %w",
|
|
icmpErr,
|
|
address,
|
|
tcpErr,
|
|
),
|
|
}
|
|
}
|
|
|
|
func defaultRunICMPPing(hostIP string) error {
|
|
pingPath, err := exec.LookPath("ping")
|
|
if err != nil {
|
|
return fmt.Errorf("ping unavailable: %w", err)
|
|
}
|
|
|
|
cmd := exec.Command(pingPath, "-c", defaultPingCount, "-W", defaultPingWaitSec, hostIP)
|
|
output, runErr := cmd.CombinedOutput()
|
|
if runErr == nil {
|
|
return nil
|
|
}
|
|
|
|
combined := strings.TrimSpace(string(output) + " " + runErr.Error())
|
|
return fmt.Errorf("%s", strings.TrimSpace(combined))
|
|
}
|
|
|
|
func defaultDialTCP(address string, timeout time.Duration) error {
|
|
conn, err := net.DialTimeout("tcp", address, timeout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = conn.Close()
|
|
return nil
|
|
}
|