mirror of
https://github.com/fosrl/newt.git
synced 2026-03-08 19:56:47 +00:00
Merge branch 'dev' into hp-multi-client
This commit is contained in:
@@ -53,7 +53,23 @@ func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
|
|||||||
if level < l.level {
|
if level < l.level {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
timestamp := time.Now().Format("2006/01/02 15:04:05")
|
|
||||||
|
// Get timezone from environment variable or use local timezone
|
||||||
|
timezone := os.Getenv("LOGGER_TIMEZONE")
|
||||||
|
var location *time.Location
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if timezone != "" {
|
||||||
|
location, err = time.LoadLocation(timezone)
|
||||||
|
if err != nil {
|
||||||
|
// If invalid timezone, fall back to local
|
||||||
|
location = time.Local
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
location = time.Local
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := time.Now().In(location).Format("2006/01/02 15:04:05")
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
l.logger.Printf("%s: %s %s", level.String(), timestamp, message)
|
l.logger.Printf("%s: %s %s", level.String(), timestamp, message)
|
||||||
}
|
}
|
||||||
|
|||||||
161
main.go
161
main.go
@@ -117,7 +117,12 @@ func ping(tnet *netstack.Net, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startPingCheck(tnet *netstack.Net, serverIP string, stopChan chan struct{}) {
|
func startPingCheck(tnet *netstack.Net, serverIP string, stopChan chan struct{}) {
|
||||||
ticker := time.NewTicker(10 * time.Second)
|
initialInterval := 10 * time.Second
|
||||||
|
maxInterval := 60 * time.Second
|
||||||
|
currentInterval := initialInterval
|
||||||
|
consecutiveFailures := 0
|
||||||
|
|
||||||
|
ticker := time.NewTicker(currentInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -126,8 +131,34 @@ func startPingCheck(tnet *netstack.Net, serverIP string, stopChan chan struct{})
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
err := ping(tnet, serverIP)
|
err := ping(tnet, serverIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("Periodic ping failed: %v", err)
|
consecutiveFailures++
|
||||||
|
logger.Warn("Periodic ping failed (%d consecutive failures): %v",
|
||||||
|
consecutiveFailures, err)
|
||||||
logger.Warn("HINT: Do you have UDP port 51820 (or the port in config.yml) open on your Pangolin server?")
|
logger.Warn("HINT: Do you have UDP port 51820 (or the port in config.yml) open on your Pangolin server?")
|
||||||
|
|
||||||
|
// Increase interval if we have consistent failures, with a maximum cap
|
||||||
|
if consecutiveFailures >= 3 && currentInterval < maxInterval {
|
||||||
|
// Increase by 50% each time, up to the maximum
|
||||||
|
currentInterval = time.Duration(float64(currentInterval) * 1.5)
|
||||||
|
if currentInterval > maxInterval {
|
||||||
|
currentInterval = maxInterval
|
||||||
|
}
|
||||||
|
ticker.Reset(currentInterval)
|
||||||
|
logger.Info("Increased ping check interval to %v due to consecutive failures",
|
||||||
|
currentInterval)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On success, if we've backed off, gradually return to normal interval
|
||||||
|
if currentInterval > initialInterval {
|
||||||
|
currentInterval = time.Duration(float64(currentInterval) * 0.8)
|
||||||
|
if currentInterval < initialInterval {
|
||||||
|
currentInterval = initialInterval
|
||||||
|
}
|
||||||
|
ticker.Reset(currentInterval)
|
||||||
|
logger.Info("Decreased ping check interval to %v after successful ping",
|
||||||
|
currentInterval)
|
||||||
|
}
|
||||||
|
consecutiveFailures = 0
|
||||||
}
|
}
|
||||||
case <-stopChan:
|
case <-stopChan:
|
||||||
logger.Info("Stopping ping check")
|
logger.Info("Stopping ping check")
|
||||||
@@ -137,34 +168,97 @@ func startPingCheck(tnet *netstack.Net, serverIP string, stopChan chan struct{})
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to track connection status and trigger reconnection as needed
|
||||||
|
func monitorConnectionStatus(tnet *netstack.Net, serverIP string, client *websocket.Client) {
|
||||||
|
const checkInterval = 30 * time.Second
|
||||||
|
connectionLost := false
|
||||||
|
ticker := time.NewTicker(checkInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
// Try a ping to see if connection is alive
|
||||||
|
err := ping(tnet, serverIP)
|
||||||
|
|
||||||
|
if err != nil && !connectionLost {
|
||||||
|
// We just lost connection
|
||||||
|
connectionLost = true
|
||||||
|
logger.Warn("Connection to server lost. Continuous reconnection attempts will be made.")
|
||||||
|
|
||||||
|
// Notify the user they might need to check their network
|
||||||
|
logger.Warn("Please check your internet connection and ensure the Pangolin server is online.")
|
||||||
|
logger.Warn("Newt will continue reconnection attempts automatically when connectivity is restored.")
|
||||||
|
} else if err == nil && connectionLost {
|
||||||
|
// Connection has been restored
|
||||||
|
connectionLost = false
|
||||||
|
logger.Info("Connection to server restored!")
|
||||||
|
|
||||||
|
// Tell the server we're back
|
||||||
|
err := client.SendMessage("newt/wg/register", map[string]interface{}{
|
||||||
|
"publicKey": fmt.Sprintf("%s", privateKey.PublicKey()),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to send registration message after reconnection: %v", err)
|
||||||
|
} else {
|
||||||
|
logger.Info("Successfully re-registered with server after reconnection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func pingWithRetry(tnet *netstack.Net, dst string) error {
|
func pingWithRetry(tnet *netstack.Net, dst string) error {
|
||||||
const (
|
const (
|
||||||
maxAttempts = 5
|
initialMaxAttempts = 15
|
||||||
retryDelay = 2 * time.Second
|
initialRetryDelay = 2 * time.Second
|
||||||
|
maxRetryDelay = 60 * time.Second // Cap the maximum delay
|
||||||
)
|
)
|
||||||
|
|
||||||
var lastErr error
|
attempt := 1
|
||||||
for attempt := 1; attempt <= maxAttempts; attempt++ {
|
retryDelay := initialRetryDelay
|
||||||
logger.Info("Ping attempt %d of %d", attempt, maxAttempts)
|
|
||||||
|
|
||||||
if err := ping(tnet, dst); err != nil {
|
|
||||||
lastErr = err
|
|
||||||
logger.Warn("Ping attempt %d failed: %v", attempt, err)
|
|
||||||
|
|
||||||
if attempt < maxAttempts {
|
|
||||||
time.Sleep(retryDelay)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return fmt.Errorf("all ping attempts failed after %d tries, last error: %w",
|
|
||||||
maxAttempts, lastErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// First try with the initial parameters
|
||||||
|
logger.Info("Ping attempt %d", attempt)
|
||||||
|
if err := ping(tnet, dst); err == nil {
|
||||||
// Successful ping
|
// Successful ping
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
logger.Warn("Ping attempt %d failed: %v", attempt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This shouldn't be reached due to the return in the loop, but added for completeness
|
// Start a goroutine that will attempt pings indefinitely with increasing delays
|
||||||
return fmt.Errorf("unexpected error: all ping attempts failed")
|
go func() {
|
||||||
|
attempt = 2 // Continue from attempt 2
|
||||||
|
|
||||||
|
for {
|
||||||
|
logger.Info("Ping attempt %d", attempt)
|
||||||
|
|
||||||
|
if err := ping(tnet, dst); err != nil {
|
||||||
|
logger.Warn("Ping attempt %d failed: %v", attempt, err)
|
||||||
|
|
||||||
|
// Increase delay after certain thresholds but cap it
|
||||||
|
if attempt%5 == 0 && retryDelay < maxRetryDelay {
|
||||||
|
retryDelay = time.Duration(float64(retryDelay) * 1.5)
|
||||||
|
if retryDelay > maxRetryDelay {
|
||||||
|
retryDelay = maxRetryDelay
|
||||||
|
}
|
||||||
|
logger.Info("Increasing ping retry delay to %v", retryDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(retryDelay)
|
||||||
|
attempt++
|
||||||
|
} else {
|
||||||
|
// Successful ping
|
||||||
|
logger.Info("Ping succeeded after %d attempts", attempt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Return an error for the first batch of attempts (to maintain compatibility with existing code)
|
||||||
|
return fmt.Errorf("initial ping attempts failed, continuing in background")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLogLevel(level string) logger.LogLevel {
|
func parseLogLevel(level string) logger.LogLevel {
|
||||||
@@ -393,13 +487,8 @@ func main() {
|
|||||||
|
|
||||||
if connected {
|
if connected {
|
||||||
logger.Info("Already connected! But I will send a ping anyway...")
|
logger.Info("Already connected! But I will send a ping anyway...")
|
||||||
// ping(tnet, wgData.ServerIP)
|
// Even if pingWithRetry returns an error, it will continue trying in the background
|
||||||
err = pingWithRetry(tnet, wgData.ServerIP)
|
_ = pingWithRetry(tnet, wgData.ServerIP) // Ignoring initial error as pings will continue
|
||||||
if err != nil {
|
|
||||||
// Handle complete failure after all retries
|
|
||||||
logger.Warn("Failed to ping %s: %v", wgData.ServerIP, err)
|
|
||||||
logger.Warn("HINT: Do you have UDP port 51820 (or the port in config.yml) open on your Pangolin server?")
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,18 +547,18 @@ persistent_keepalive_interval=5`, fixKey(fmt.Sprintf("%s", privateKey)), fixKey(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("WireGuard device created. Lets ping the server now...")
|
logger.Info("WireGuard device created. Lets ping the server now...")
|
||||||
// Ping to bring the tunnel up on the server side quickly
|
|
||||||
// ping(tnet, wgData.ServerIP)
|
|
||||||
err = pingWithRetry(tnet, wgData.ServerIP)
|
|
||||||
if err != nil {
|
|
||||||
// Handle complete failure after all retries
|
|
||||||
logger.Error("Failed to ping %s: %v", wgData.ServerIP, err)
|
|
||||||
fmt.Sprintf("%s", privateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Even if pingWithRetry returns an error, it will continue trying in the background
|
||||||
|
_ = pingWithRetry(tnet, wgData.ServerIP)
|
||||||
|
|
||||||
|
// Always mark as connected and start the proxy manager regardless of initial ping result
|
||||||
|
// as the pings will continue in the background
|
||||||
if !connected {
|
if !connected {
|
||||||
logger.Info("Starting ping check")
|
logger.Info("Starting ping check")
|
||||||
startPingCheck(tnet, wgData.ServerIP, pingStopChan)
|
startPingCheck(tnet, wgData.ServerIP, pingStopChan)
|
||||||
|
|
||||||
|
// Start connection monitoring in a separate goroutine
|
||||||
|
go monitorConnectionStatus(tnet, wgData.ServerIP, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create proxy manager
|
// Create proxy manager
|
||||||
|
|||||||
Reference in New Issue
Block a user