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

111 lines
2.8 KiB
Go

package app
import (
"fmt"
"strings"
)
const transportBootstrapStateVersion = 1
var transportBootstrapStatePath = transportBootstrapPath
type transportBootstrapState struct {
Version int `json:"version"`
UpdatedAt string `json:"updated_at,omitempty"`
Clients map[string][]string `json:"clients,omitempty"`
}
type transportMainRoute struct {
Dev string
Via string
}
func transportMaybeSyncBootstrapBypass(client TransportClient, action string) (string, error) {
act := strings.ToLower(strings.TrimSpace(action))
switch act {
case "start", "restart", "stop":
default:
return "", nil
}
if !transportBootstrapBypassEnabled(client) {
return "", nil
}
if act == "stop" {
if err := transportBootstrapRemoveClientRoutes(client.ID); err != nil {
return "", err
}
return "bootstrap bypass routes removed", nil
}
return transportBootstrapSyncClientRoutes(client)
}
func transportBootstrapBypassEnabled(client TransportClient) bool {
if transportConfigHasKey(client.Config, "bootstrap_bypass") {
return transportConfigBool(client.Config, "bootstrap_bypass")
}
// Default: enable for SingBox only.
return client.Kind == TransportClientSingBox
}
func transportBootstrapBypassStrict(client TransportClient) bool {
return transportConfigBool(client.Config, "bootstrap_bypass_strict")
}
func transportBootstrapSyncClientRoutes(client TransportClient) (string, error) {
candidates := transportCollectBootstrapCandidates(client)
ips, resolveWarn := transportResolveBootstrapIPv4(candidates)
if len(ips) == 0 {
if err := transportBootstrapReplaceClientRoutes(client.ID, nil, transportMainRoute{}); err != nil {
return "", err
}
if resolveWarn != nil {
return "", resolveWarn
}
return "bootstrap bypass: no endpoints found", nil
}
mainRoute, err := transportDetectMainIPv4Route()
if err != nil {
return "", err
}
if err := transportBootstrapReplaceClientRoutes(client.ID, ips, mainRoute); err != nil {
return "", err
}
msg := fmt.Sprintf(
"bootstrap bypass synced: ips=%d via=%s dev=%s",
len(ips),
transportOr(mainRoute.Via, "link"),
mainRoute.Dev,
)
if resolveWarn != nil {
msg += "; " + resolveWarn.Error()
}
return msg, nil
}
func transportCommandError(cmd, stdout, stderr string, code int, err error) error {
msg := strings.TrimSpace(stderr)
if msg == "" {
msg = strings.TrimSpace(stdout)
}
if msg == "" && err != nil {
msg = strings.TrimSpace(err.Error())
}
if msg == "" {
msg = fmt.Sprintf("exit code %d", code)
}
if err != nil {
return fmt.Errorf("%s failed: %s", strings.TrimSpace(cmd), msg)
}
return fmt.Errorf("%s failed (code=%d): %s", strings.TrimSpace(cmd), code, msg)
}
func transportOr(v, fallback string) string {
if strings.TrimSpace(v) != "" {
return strings.TrimSpace(v)
}
return strings.TrimSpace(fallback)
}