mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
136 lines
4.3 KiB
Go
136 lines
4.3 KiB
Go
// Package util provides helper functions for SSH tunnel instructions and network-related tasks.
|
|
// This includes detecting the appropriate IP address and printing commands
|
|
// to help users connect to the local server from a remote machine.
|
|
package util
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var ipServices = []string{
|
|
"https://api.ipify.org",
|
|
"https://ifconfig.me/ip",
|
|
"https://icanhazip.com",
|
|
"https://ipinfo.io/ip",
|
|
}
|
|
|
|
// getPublicIP attempts to retrieve the public IP address from a list of external services.
|
|
// It iterates through the ipServices and returns the first successful response.
|
|
//
|
|
// Returns:
|
|
// - string: The public IP address as a string
|
|
// - error: An error if all services fail, nil otherwise
|
|
func getPublicIP() (string, error) {
|
|
for _, service := range ipServices {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
req, err := http.NewRequestWithContext(ctx, "GET", service, nil)
|
|
if err != nil {
|
|
log.Debugf("Failed to create request to %s: %v", service, err)
|
|
continue
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
log.Debugf("Failed to get public IP from %s: %v", service, err)
|
|
continue
|
|
}
|
|
defer func() {
|
|
if closeErr := resp.Body.Close(); closeErr != nil {
|
|
log.Warnf("Failed to close response body from %s: %v", service, closeErr)
|
|
}
|
|
}()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
log.Debugf("bad status code from %s: %d", service, resp.StatusCode)
|
|
continue
|
|
}
|
|
|
|
ip, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Debugf("Failed to read response body from %s: %v", service, err)
|
|
continue
|
|
}
|
|
return strings.TrimSpace(string(ip)), nil
|
|
}
|
|
return "", fmt.Errorf("all IP services failed")
|
|
}
|
|
|
|
// getOutboundIP retrieves the preferred outbound IP address of this machine.
|
|
// It uses a UDP connection to a public DNS server to determine the local IP
|
|
// address that would be used for outbound traffic.
|
|
//
|
|
// Returns:
|
|
// - string: The outbound IP address as a string
|
|
// - error: An error if the IP address cannot be determined, nil otherwise
|
|
func getOutboundIP() (string, error) {
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer func() {
|
|
if closeErr := conn.Close(); closeErr != nil {
|
|
log.Warnf("Failed to close UDP connection: %v", closeErr)
|
|
}
|
|
}()
|
|
|
|
localAddr, ok := conn.LocalAddr().(*net.UDPAddr)
|
|
if !ok {
|
|
return "", fmt.Errorf("could not assert UDP address type")
|
|
}
|
|
|
|
return localAddr.IP.String(), nil
|
|
}
|
|
|
|
// GetIPAddress attempts to find the best-available IP address.
|
|
// It first tries to get the public IP address, and if that fails,
|
|
// it falls back to getting the local outbound IP address.
|
|
//
|
|
// Returns:
|
|
// - string: The determined IP address (preferring public IPv4)
|
|
func GetIPAddress() string {
|
|
publicIP, err := getPublicIP()
|
|
if err == nil {
|
|
log.Debugf("Public IP detected: %s", publicIP)
|
|
return publicIP
|
|
}
|
|
log.Warnf("Failed to get public IP, falling back to outbound IP: %v", err)
|
|
outboundIP, err := getOutboundIP()
|
|
if err == nil {
|
|
log.Debugf("Outbound IP detected: %s", outboundIP)
|
|
return outboundIP
|
|
}
|
|
log.Errorf("Failed to get any IP address: %v", err)
|
|
return "127.0.0.1" // Fallback
|
|
}
|
|
|
|
// PrintSSHTunnelInstructions detects the IP address and prints SSH tunnel instructions
|
|
// for the user to connect to the local OAuth callback server from a remote machine.
|
|
//
|
|
// Parameters:
|
|
// - port: The local port number for the SSH tunnel
|
|
func PrintSSHTunnelInstructions(port int) {
|
|
ipAddress := GetIPAddress()
|
|
border := "================================================================================"
|
|
log.Infof("To authenticate from a remote machine, an SSH tunnel may be required.")
|
|
fmt.Println(border)
|
|
fmt.Println(" Run one of the following commands on your local machine (NOT the server):")
|
|
fmt.Println()
|
|
fmt.Printf(" # Standard SSH command (assumes SSH port 22):\n")
|
|
fmt.Printf(" ssh -L %d:127.0.0.1:%d root@%s -p 22\n", port, port, ipAddress)
|
|
fmt.Println()
|
|
fmt.Printf(" # If using an SSH key (assumes SSH port 22):\n")
|
|
fmt.Printf(" ssh -i <path_to_your_key> -L %d:127.0.0.1:%d root@%s -p 22\n", port, port, ipAddress)
|
|
fmt.Println()
|
|
fmt.Println(" NOTE: If your server's SSH port is not 22, please modify the '-p 22' part accordingly.")
|
|
fmt.Println(border)
|
|
}
|