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

109 lines
2.3 KiB
Go

package app
import (
"encoding/json"
"net/netip"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
func loadTransportBootstrapState() transportBootstrapState {
st := transportBootstrapState{Version: transportBootstrapStateVersion}
data, err := os.ReadFile(transportBootstrapStatePath)
if err != nil {
return st
}
if err := json.Unmarshal(data, &st); err != nil {
return transportBootstrapState{Version: transportBootstrapStateVersion}
}
return normalizeTransportBootstrapState(st)
}
func saveTransportBootstrapState(st transportBootstrapState) error {
st = normalizeTransportBootstrapState(st)
st.Version = transportBootstrapStateVersion
st.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
data, err := json.MarshalIndent(st, "", " ")
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(transportBootstrapStatePath), 0o755); err != nil {
return err
}
tmp := transportBootstrapStatePath + ".tmp"
if err := os.WriteFile(tmp, data, 0o644); err != nil {
return err
}
return os.Rename(tmp, transportBootstrapStatePath)
}
func normalizeTransportBootstrapState(st transportBootstrapState) transportBootstrapState {
st.Version = transportBootstrapStateVersion
if len(st.Clients) == 0 {
st.Clients = nil
return st
}
out := make(map[string][]string, len(st.Clients))
for rawID, rawIPs := range st.Clients {
id := sanitizeID(rawID)
if id == "" {
continue
}
ips := normalizeBootstrapIPv4List(rawIPs)
if len(ips) == 0 {
continue
}
out[id] = ips
}
if len(out) == 0 {
st.Clients = nil
return st
}
st.Clients = out
return st
}
func normalizeBootstrapIPv4List(in []string) []string {
seen := map[string]struct{}{}
out := make([]string, 0, len(in))
for _, raw := range in {
ip := strings.TrimSpace(raw)
addr, err := netip.ParseAddr(ip)
if err != nil || !addr.Is4() {
continue
}
key := addr.String()
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, key)
}
sort.Strings(out)
return out
}
func dedupeStrings(in []string) []string {
if len(in) == 0 {
return nil
}
seen := map[string]struct{}{}
out := make([]string, 0, len(in))
for _, raw := range in {
v := strings.TrimSpace(raw)
if v == "" {
continue
}
if _, ok := seen[v]; ok {
continue
}
seen[v] = struct{}{}
out = append(out, v)
}
return out
}