Files
elmprodvpn/selective-vpn-api/app/transport_policy_targets.go

165 lines
4.5 KiB
Go

package app
import (
"strings"
"time"
)
const (
transportPolicyTargetAdGuardID = "adguardvpn"
transportPolicyTargetAdGuardIfaceID = transportDefaultIfaceID
transportPolicyTargetAdGuardTable = "agvpn"
)
var transportPolicyTargetAdGuardAllowedActions = []string{"start", "stop", "restart"}
func isTransportPolicyVirtualClientID(id string) bool {
return sanitizeID(id) == transportPolicyTargetAdGuardID
}
func isTransportPolicyVirtualClient(client TransportClient) bool {
if isTransportPolicyVirtualClientID(client.ID) {
return true
}
return strings.EqualFold(strings.TrimSpace(string(client.Kind)), transportPolicyTargetAdGuardID)
}
func resolveTransportPolicyVirtualClient(id string) (TransportClient, bool) {
if !isTransportPolicyVirtualClientID(id) {
return TransportClient{}, false
}
return buildTransportPolicyAdGuardTarget()
}
func transportPolicyPersistableClients(items []TransportClient) []TransportClient {
if len(items) == 0 {
return nil
}
out := make([]TransportClient, 0, len(items))
for _, it := range items {
if isTransportPolicyVirtualClient(it) {
continue
}
out = append(out, it)
}
return out
}
func transportPolicyClientsWithVirtualTargets(base []TransportClient) []TransportClient {
out := append([]TransportClient(nil), base...)
for i := range out {
out[i].ID = sanitizeID(out[i].ID)
}
if adg, ok := buildTransportPolicyAdGuardTarget(); ok {
if idx := findTransportClientIndex(out, adg.ID); idx >= 0 {
out[idx] = adg
} else {
out = append(out, adg)
}
}
return out
}
func buildTransportPolicyAdGuardTarget() (TransportClient, bool) {
now := time.Now().UTC()
stdout, _, _, err := runCommandTimeout(1500*time.Millisecond, "systemctl", "is-active", adgvpnUnit)
unitState := strings.TrimSpace(stdout)
if err != nil && unitState == "" {
return TransportClient{}, false
}
word, raw := parseAutoloopStatus(tailFile(autoloopLogPath, 120))
return buildTransportPolicyAdGuardTargetFromObservation(unitState, word, raw, now), true
}
func buildTransportPolicyAdGuardTargetFromObservation(unitState, statusWord, raw string, observedAt time.Time) TransportClient {
ts := observedAt.UTC().Format(time.RFC3339)
status := adGuardPolicyTargetStatus(unitState, statusWord)
iface := adGuardPolicyTargetIface(raw, status)
lastError := ""
if status == TransportClientDown || status == TransportClientDegraded {
msg := strings.TrimSpace(unitState)
if msg == "" {
msg = strings.TrimSpace(statusWord)
}
lastError = msg
}
return TransportClient{
ID: transportPolicyTargetAdGuardID,
Name: "AdGuard VPN",
Kind: TransportClientKind(transportPolicyTargetAdGuardID),
Enabled: true,
Status: status,
IfaceID: transportPolicyTargetAdGuardIfaceID,
Iface: iface,
RoutingTable: transportPolicyTargetAdGuardTable,
Capabilities: []string{"vpn", "systemd", "autoloop"},
Health: TransportClientHealth{
LastCheck: ts,
LastError: lastError,
},
Runtime: TransportClientRuntime{
Backend: "adguard",
AllowedActions: append([]string(nil), transportPolicyTargetAdGuardAllowedActions...),
LastAction: "observe",
LastActionAt: ts,
},
UpdatedAt: ts,
}
}
func adGuardPolicyTargetStatus(unitState, statusWord string) TransportClientStatus {
word := strings.ToUpper(strings.TrimSpace(statusWord))
switch word {
case "CONNECTED":
return TransportClientUp
case "RECONNECTING":
return TransportClientStarting
case "DISCONNECTED":
return TransportClientDown
case "ERROR":
return TransportClientDegraded
}
state := strings.ToLower(strings.TrimSpace(unitState))
switch state {
case "active":
return TransportClientUp
case "activating", "reloading":
return TransportClientStarting
case "failed", "inactive", "deactivating":
return TransportClientDown
default:
return TransportClientDown
}
}
func adGuardPolicyTargetIface(raw string, status TransportClientStatus) string {
v := strings.TrimSpace(raw)
if v != "" {
low := strings.ToLower(v)
needle := "running on "
if idx := strings.LastIndex(low, needle); idx >= 0 {
tail := strings.TrimSpace(v[idx+len(needle):])
if tail != "" {
iface := strings.Trim(tail, ".,;:()[]{}")
if parts := strings.Fields(iface); len(parts) > 0 {
if strings.TrimSpace(parts[0]) != "" {
return strings.TrimSpace(parts[0])
}
}
}
}
}
if iface, _ := resolveTrafficIface(loadTrafficModeState().PreferredIface); strings.TrimSpace(iface) != "" {
return strings.TrimSpace(iface)
}
if status == TransportClientUp || status == TransportClientStarting {
return "tun0"
}
return ""
}