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.
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
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, ""
|
|
}
|