111 lines
2.8 KiB
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)
|
|
}
|