147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
egressutilpkg "selective-vpn-api/app/egressutil"
|
|
)
|
|
|
|
func egressProbeExternalIPInNetns(client TransportClient, ns string) (string, error) {
|
|
endpoints := egressLimitEndpointsForNetns(egressIPEndpoints())
|
|
ip, errs := egressutilpkg.ProbeFirstSuccess(endpoints, func(rawURL string) (string, error) {
|
|
return egressProbeURLInNetns(client, ns, rawURL, egressIdentityProbeTimeout)
|
|
})
|
|
if strings.TrimSpace(ip) != "" {
|
|
return ip, nil
|
|
}
|
|
if len(errs) == 0 {
|
|
return "", fmt.Errorf("egress probe endpoints are not configured")
|
|
}
|
|
return "", fmt.Errorf("%s", egressJoinErrorsCompact(errs))
|
|
}
|
|
|
|
func egressProbeExternalIPInNetnsViaProxy(client TransportClient, ns, proxyURL string) (string, error) {
|
|
proxy := strings.TrimSpace(proxyURL)
|
|
if proxy == "" {
|
|
return "", fmt.Errorf("proxy url is empty")
|
|
}
|
|
endpoints := egressLimitEndpointsForNetns(egressIPEndpoints())
|
|
ip, errs := egressutilpkg.ProbeFirstSuccess(endpoints, func(rawURL string) (string, error) {
|
|
return egressProbeURLInNetnsViaProxy(client, ns, rawURL, proxy, egressIdentityProbeTimeout)
|
|
})
|
|
if strings.TrimSpace(ip) != "" {
|
|
return ip, nil
|
|
}
|
|
if len(errs) == 0 {
|
|
return "", fmt.Errorf("egress probe endpoints are not configured")
|
|
}
|
|
return "", fmt.Errorf("%s", egressJoinErrorsCompact(errs))
|
|
}
|
|
|
|
func egressProbeURLViaInterface(rawURL, iface string, timeout time.Duration) (string, error) {
|
|
curl := egressutilpkg.ResolveCurlPath()
|
|
sec := egressutilpkg.TimeoutSec(timeout)
|
|
if curl != "" {
|
|
args := []string{
|
|
"-4",
|
|
"-fsSL",
|
|
"--max-time", strconv.Itoa(sec),
|
|
"--connect-timeout", "2",
|
|
"--interface", iface,
|
|
rawURL,
|
|
}
|
|
stdout, stderr, code, err := runCommandTimeout(timeout+time.Second, curl, args...)
|
|
if err != nil || code != 0 {
|
|
return "", transportCommandError(shellJoinArgs(append([]string{curl}, args...)), stdout, stderr, code, err)
|
|
}
|
|
return egressutilpkg.ParseIPFromBody(stdout)
|
|
}
|
|
|
|
wget := egressutilpkg.ResolveWgetPath()
|
|
if wget == "" {
|
|
return "", fmt.Errorf("curl/wget are not available for interface-bound egress probe")
|
|
}
|
|
bindAddr := egressInterfaceBindAddress(iface)
|
|
if bindAddr == "" {
|
|
return "", fmt.Errorf("cannot resolve IPv4 address for interface %q", iface)
|
|
}
|
|
args := []string{
|
|
"-4",
|
|
"-q",
|
|
"-T", strconv.Itoa(sec),
|
|
"-O", "-",
|
|
"--bind-address", bindAddr,
|
|
rawURL,
|
|
}
|
|
stdout, stderr, code, err := runCommandTimeout(timeout+time.Second, wget, args...)
|
|
if err != nil || code != 0 {
|
|
return "", transportCommandError(shellJoinArgs(append([]string{wget}, args...)), stdout, stderr, code, err)
|
|
}
|
|
return egressutilpkg.ParseIPFromBody(stdout)
|
|
}
|
|
|
|
func egressProbeURLInNetns(client TransportClient, ns, rawURL string, timeout time.Duration) (string, error) {
|
|
sec := egressutilpkg.TimeoutSec(timeout)
|
|
resolveHost, resolvePort, resolveIP := egressutilpkg.ResolvedHostForURL(rawURL)
|
|
|
|
curlBin := egressutilpkg.ResolveCurlPath()
|
|
if curlBin == "" {
|
|
return "", fmt.Errorf("curl is not available for netns probe")
|
|
}
|
|
curlArgs := []string{
|
|
"-4",
|
|
"-fsSL",
|
|
"--max-time", strconv.Itoa(sec),
|
|
"--connect-timeout", "2",
|
|
}
|
|
if resolveHost != "" && resolveIP != "" && resolvePort > 0 {
|
|
curlArgs = append(curlArgs, "--resolve", fmt.Sprintf("%s:%d:%s", resolveHost, resolvePort, resolveIP))
|
|
}
|
|
curlArgs = append(curlArgs, rawURL)
|
|
curlCmd := append([]string{curlBin}, curlArgs...)
|
|
name, args, err := transportNetnsExecCommand(client, ns, curlCmd...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
stdout, stderr, code, runErr := runCommandTimeout(timeout+time.Second, name, args...)
|
|
if runErr != nil || code != 0 {
|
|
return "", transportCommandError(shellJoinArgs(append([]string{name}, args...)), stdout, stderr, code, runErr)
|
|
}
|
|
return egressutilpkg.ParseIPFromBody(stdout)
|
|
}
|
|
|
|
func egressProbeURLInNetnsViaProxy(
|
|
client TransportClient,
|
|
ns string,
|
|
rawURL string,
|
|
proxyURL string,
|
|
timeout time.Duration,
|
|
) (string, error) {
|
|
curlBin := egressutilpkg.ResolveCurlPath()
|
|
if curlBin == "" {
|
|
return "", fmt.Errorf("curl is not available for proxy probe")
|
|
}
|
|
sec := egressutilpkg.TimeoutSec(timeout)
|
|
args := []string{
|
|
"-4",
|
|
"-fsSL",
|
|
"--max-time", strconv.Itoa(sec),
|
|
"--connect-timeout", "3",
|
|
"--proxy", strings.TrimSpace(proxyURL),
|
|
rawURL,
|
|
}
|
|
cmd := append([]string{curlBin}, args...)
|
|
name, netnsArgs, err := transportNetnsExecCommand(client, ns, cmd...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
stdout, stderr, code, runErr := runCommandTimeout(timeout+time.Second, name, netnsArgs...)
|
|
if runErr != nil || code != 0 {
|
|
return "", transportCommandError(shellJoinArgs(append([]string{name}, netnsArgs...)), stdout, stderr, code, runErr)
|
|
}
|
|
return egressutilpkg.ParseIPFromBody(stdout)
|
|
}
|