feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser, HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
This commit is contained in:
93
agent/client/connectivity_probe.go
Normal file
93
agent/client/connectivity_probe.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ConnectivityProbeReport is diagnostic-only reachability (not a C2 channel).
|
||||
type ConnectivityProbeReport struct {
|
||||
C2Host string `json:"c2_host"`
|
||||
C2DNSOK bool `json:"c2_dns_ok"`
|
||||
C2DNSAddrs []string `json:"c2_dns_addrs,omitempty"`
|
||||
C2TCPOK bool `json:"c2_tcp_ok"`
|
||||
C2TCPError string `json:"c2_tcp_error,omitempty"`
|
||||
PoolHost string `json:"pool_host,omitempty"`
|
||||
PoolDNSOK bool `json:"pool_dns_ok,omitempty"`
|
||||
PoolDNSAddrs []string `json:"pool_dns_addrs,omitempty"`
|
||||
PoolTCPOK bool `json:"pool_tcp_ok,omitempty"`
|
||||
PoolTCPError string `json:"pool_tcp_error,omitempty"`
|
||||
}
|
||||
|
||||
func runConnectivityProbe(serverURL, poolHost string, poolPort int) ConnectivityProbeReport {
|
||||
report := ConnectivityProbeReport{}
|
||||
host, port, err := hostPortFromServerURL(serverURL)
|
||||
if err != nil {
|
||||
report.C2Host = serverURL
|
||||
report.C2TCPError = err.Error()
|
||||
return report
|
||||
}
|
||||
report.C2Host = net.JoinHostPort(host, port)
|
||||
report.C2DNSOK, report.C2DNSAddrs = probeDNSResolve(host)
|
||||
report.C2TCPOK, report.C2TCPError = probeTCPConnect(host, port)
|
||||
|
||||
if poolHost != "" {
|
||||
pport := poolPort
|
||||
if pport <= 0 {
|
||||
pport = 3333
|
||||
}
|
||||
pportStr := fmt.Sprintf("%d", pport)
|
||||
report.PoolHost = net.JoinHostPort(poolHost, pportStr)
|
||||
report.PoolDNSOK, report.PoolDNSAddrs = probeDNSResolve(poolHost)
|
||||
report.PoolTCPOK, report.PoolTCPError = probeTCPConnect(poolHost, pportStr)
|
||||
}
|
||||
return report
|
||||
}
|
||||
|
||||
func hostPortFromServerURL(serverURL string) (host, port string, err error) {
|
||||
raw := strings.TrimSpace(serverURL)
|
||||
if raw == "" {
|
||||
return "", "", fmt.Errorf("empty server URL")
|
||||
}
|
||||
if !strings.Contains(raw, "://") {
|
||||
raw = "http://" + raw
|
||||
}
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
host = u.Hostname()
|
||||
port = u.Port()
|
||||
if port == "" {
|
||||
if u.Scheme == "https" {
|
||||
port = "443"
|
||||
} else {
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
return "", "", fmt.Errorf("no host in server URL")
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
func probeDNSResolve(host string) (bool, []string) {
|
||||
addrs, err := net.LookupHost(host)
|
||||
if err != nil || len(addrs) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return true, addrs
|
||||
}
|
||||
|
||||
func probeTCPConnect(host, port string) (bool, string) {
|
||||
addr := net.JoinHostPort(host, port)
|
||||
conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
_ = conn.Close()
|
||||
return true, ""
|
||||
}
|
||||
Reference in New Issue
Block a user