Compare commits

..

2 Commits

Author SHA1 Message Date
dependabot[bot]
41102fbdcb chore(nix): fix hash for updated go dependencies 2026-03-17 09:45:53 +00:00
dependabot[bot]
4cbd7ea2bd chore(deps): bump the prod-patch-updates group across 1 directory with 2 updates
Bumps the prod-patch-updates group with 2 updates in the / directory: [github.com/gaissmai/bart](https://github.com/gaissmai/bart) and [google.golang.org/grpc](https://github.com/grpc/grpc-go).


Updates `github.com/gaissmai/bart` from 0.26.0 to 0.26.1
- [Release notes](https://github.com/gaissmai/bart/releases)
- [Commits](https://github.com/gaissmai/bart/compare/v0.26.0...v0.26.1)

Updates `google.golang.org/grpc` from 1.79.1 to 1.79.2
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.1...v1.79.2)

---
updated-dependencies:
- dependency-name: github.com/gaissmai/bart
  dependency-version: 0.26.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-17 09:44:22 +00:00
8 changed files with 32 additions and 136 deletions

View File

@@ -2,8 +2,6 @@ package clients
import ( import (
"context" "context"
"crypto/rand"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
@@ -36,7 +34,6 @@ type WgConfig struct {
IpAddress string `json:"ipAddress"` IpAddress string `json:"ipAddress"`
Peers []Peer `json:"peers"` Peers []Peer `json:"peers"`
Targets []Target `json:"targets"` Targets []Target `json:"targets"`
ChainId string `json:"chainId"`
} }
type Target struct { type Target struct {
@@ -85,8 +82,7 @@ type WireGuardService struct {
host string host string
serverPubKey string serverPubKey string
token string token string
stopGetConfig func() stopGetConfig func()
pendingConfigChainId string
// Netstack fields // Netstack fields
tun tun.Device tun tun.Device
tnet *netstack2.Net tnet *netstack2.Net
@@ -111,13 +107,6 @@ type WireGuardService struct {
wgTesterServer *wgtester.Server wgTesterServer *wgtester.Server
} }
// generateChainId generates a random chain ID for deduplicating round-trip messages.
func generateChainId() string {
b := make([]byte, 8)
_, _ = rand.Read(b)
return hex.EncodeToString(b)
}
func NewWireGuardService(interfaceName string, port uint16, mtu int, host string, newtId string, wsClient *websocket.Client, dns string, useNativeInterface bool) (*WireGuardService, error) { func NewWireGuardService(interfaceName string, port uint16, mtu int, host string, newtId string, wsClient *websocket.Client, dns string, useNativeInterface bool) (*WireGuardService, error) {
key, err := wgtypes.GeneratePrivateKey() key, err := wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
@@ -452,12 +441,9 @@ func (s *WireGuardService) LoadRemoteConfig() error {
s.stopGetConfig() s.stopGetConfig()
s.stopGetConfig = nil s.stopGetConfig = nil
} }
chainId := generateChainId()
s.pendingConfigChainId = chainId
s.stopGetConfig = s.client.SendMessageInterval("newt/wg/get-config", map[string]interface{}{ s.stopGetConfig = s.client.SendMessageInterval("newt/wg/get-config", map[string]interface{}{
"publicKey": s.key.PublicKey().String(), "publicKey": s.key.PublicKey().String(),
"port": s.Port, "port": s.Port,
"chainId": chainId,
}, 2*time.Second) }, 2*time.Second)
logger.Debug("Requesting WireGuard configuration from remote server") logger.Debug("Requesting WireGuard configuration from remote server")
@@ -482,17 +468,6 @@ func (s *WireGuardService) handleConfig(msg websocket.WSMessage) {
logger.Info("Error unmarshaling target data: %v", err) logger.Info("Error unmarshaling target data: %v", err)
return return
} }
// Deduplicate using chainId: discard responses that don't match the
// pending request, or that we have already processed.
if config.ChainId != "" {
if config.ChainId != s.pendingConfigChainId {
logger.Debug("Discarding duplicate/stale newt/wg/get-config response (chainId=%s, expected=%s)", config.ChainId, s.pendingConfigChainId)
return
}
s.pendingConfigChainId = "" // consume further duplicates are rejected
}
s.config = config s.config = config
if s.stopGetConfig != nil { if s.stopGetConfig != nil {

View File

@@ -285,18 +285,11 @@ func startPingCheck(tnet *netstack.Net, serverIP string, client *websocket.Clien
if tunnelID != "" { if tunnelID != "" {
telemetry.IncReconnect(context.Background(), tunnelID, "client", telemetry.ReasonTimeout) telemetry.IncReconnect(context.Background(), tunnelID, "client", telemetry.ReasonTimeout)
} }
pingChainId := generateChainId() stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{}, 3*time.Second)
pendingPingChainId = pingChainId
stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{
"chainId": pingChainId,
}, 3*time.Second)
// Send registration message to the server for backward compatibility // Send registration message to the server for backward compatibility
bcChainId := generateChainId()
pendingRegisterChainId = bcChainId
err := client.SendMessage("newt/wg/register", map[string]interface{}{ err := client.SendMessage("newt/wg/register", map[string]interface{}{
"publicKey": publicKey.String(), "publicKey": publicKey.String(),
"backwardsCompatible": true, "backwardsCompatible": true,
"chainId": bcChainId,
}) })
if err != nil { if err != nil {
logger.Error("Failed to send registration message: %v", err) logger.Error("Failed to send registration message: %v", err)

View File

@@ -35,7 +35,7 @@
inherit version; inherit version;
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
vendorHash = "sha256-kmQM8Yy5TuOiNpMpUme/2gfE+vrhUK+0AphN+p71wGs="; vendorHash = "sha256-u7iQCKF8Jh1o0OQoPK4jSmO5pKMl9yT5Sj4GD2UuTU8=";
nativeInstallCheckInputs = [ pkgs.versionCheckHook ]; nativeInstallCheckInputs = [ pkgs.versionCheckHook ];

4
go.mod
View File

@@ -4,7 +4,7 @@ go 1.25.0
require ( require (
github.com/docker/docker v28.5.2+incompatible github.com/docker/docker v28.5.2+incompatible
github.com/gaissmai/bart v0.26.0 github.com/gaissmai/bart v0.26.1
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_golang v1.23.2
github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netlink v1.3.1
@@ -24,7 +24,7 @@ require (
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
golang.zx2c4.com/wireguard/windows v0.5.3 golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.79.1 google.golang.org/grpc v1.79.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c
software.sslmate.com/src/go-pkcs12 v0.7.0 software.sslmate.com/src/go-pkcs12 v0.7.0

8
go.sum
View File

@@ -26,8 +26,8 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gaissmai/bart v0.26.0 h1:xOZ57E9hJLBiQaSyeZa9wgWhGuzfGACgqp4BE77OkO0= github.com/gaissmai/bart v0.26.1 h1:+w4rnLGNlA2GDVn382Tfe3jOsK5vOr5n4KmigJ9lbTo=
github.com/gaissmai/bart v0.26.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= github.com/gaissmai/bart v0.26.1/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -159,8 +159,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -5,9 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -367,12 +365,11 @@ func (m *Monitor) performHealthCheck(target *Target) {
target.LastCheck = time.Now() target.LastCheck = time.Now()
target.LastError = "" target.LastError = ""
// Build URL (use net.JoinHostPort to properly handle IPv6 addresses with ports) // Build URL
host := target.Config.Hostname url := fmt.Sprintf("%s://%s", target.Config.Scheme, target.Config.Hostname)
if target.Config.Port > 0 { if target.Config.Port > 0 {
host = net.JoinHostPort(target.Config.Hostname, strconv.Itoa(target.Config.Port)) url = fmt.Sprintf("%s:%d", url, target.Config.Port)
} }
url := fmt.Sprintf("%s://%s", target.Config.Scheme, host)
if target.Config.Path != "" { if target.Config.Path != "" {
if !strings.HasPrefix(target.Config.Path, "/") { if !strings.HasPrefix(target.Config.Path, "/") {
url += "/" url += "/"

74
main.go
View File

@@ -3,16 +3,13 @@ package main
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/rand"
"crypto/tls" "crypto/tls"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"net/http/pprof"
"net/netip" "net/netip"
"os" "os"
"os/signal" "os/signal"
@@ -48,7 +45,6 @@ type WgData struct {
TunnelIP string `json:"tunnelIP"` TunnelIP string `json:"tunnelIP"`
Targets TargetsByType `json:"targets"` Targets TargetsByType `json:"targets"`
HealthCheckTargets []healthcheck.Config `json:"healthCheckTargets"` HealthCheckTargets []healthcheck.Config `json:"healthCheckTargets"`
ChainId string `json:"chainId"`
} }
type TargetsByType struct { type TargetsByType struct {
@@ -62,7 +58,6 @@ type TargetData struct {
type ExitNodeData struct { type ExitNodeData struct {
ExitNodes []ExitNode `json:"exitNodes"` ExitNodes []ExitNode `json:"exitNodes"`
ChainId string `json:"chainId"`
} }
// ExitNode represents an exit node with an ID, endpoint, and weight. // ExitNode represents an exit node with an ID, endpoint, and weight.
@@ -132,8 +127,6 @@ var (
publicKey wgtypes.Key publicKey wgtypes.Key
pingStopChan chan struct{} pingStopChan chan struct{}
stopFunc func() stopFunc func()
pendingRegisterChainId string
pendingPingChainId string
healthFile string healthFile string
useNativeInterface bool useNativeInterface bool
authorizedKeysFile string authorizedKeysFile string
@@ -154,7 +147,6 @@ var (
adminAddr string adminAddr string
region string region string
metricsAsyncBytes bool metricsAsyncBytes bool
pprofEnabled bool
blueprintFile string blueprintFile string
noCloud bool noCloud bool
@@ -167,13 +159,6 @@ var (
tlsPrivateKey string tlsPrivateKey string
) )
// generateChainId generates a random chain ID for deduplicating round-trip messages.
func generateChainId() string {
b := make([]byte, 8)
_, _ = rand.Read(b)
return hex.EncodeToString(b)
}
func main() { func main() {
// Check for subcommands first (only principals exits early) // Check for subcommands first (only principals exits early)
if len(os.Args) > 1 { if len(os.Args) > 1 {
@@ -240,7 +225,6 @@ func runNewtMain(ctx context.Context) {
adminAddrEnv := os.Getenv("NEWT_ADMIN_ADDR") adminAddrEnv := os.Getenv("NEWT_ADMIN_ADDR")
regionEnv := os.Getenv("NEWT_REGION") regionEnv := os.Getenv("NEWT_REGION")
asyncBytesEnv := os.Getenv("NEWT_METRICS_ASYNC_BYTES") asyncBytesEnv := os.Getenv("NEWT_METRICS_ASYNC_BYTES")
pprofEnabledEnv := os.Getenv("NEWT_PPROF_ENABLED")
disableClientsEnv := os.Getenv("DISABLE_CLIENTS") disableClientsEnv := os.Getenv("DISABLE_CLIENTS")
disableClients = disableClientsEnv == "true" disableClients = disableClientsEnv == "true"
@@ -406,14 +390,6 @@ func runNewtMain(ctx context.Context) {
metricsAsyncBytes = v metricsAsyncBytes = v
} }
} }
// pprof debug endpoint toggle
if pprofEnabledEnv == "" {
flag.BoolVar(&pprofEnabled, "pprof", false, "Enable pprof debug endpoints on admin server")
} else {
if v, err := strconv.ParseBool(pprofEnabledEnv); err == nil {
pprofEnabled = v
}
}
// Optional region flag (resource attribute) // Optional region flag (resource attribute)
if regionEnv == "" { if regionEnv == "" {
flag.StringVar(&region, "region", "", "Optional region resource attribute (also NEWT_REGION)") flag.StringVar(&region, "region", "", "Optional region resource attribute (also NEWT_REGION)")
@@ -509,14 +485,6 @@ func runNewtMain(ctx context.Context) {
if tel.PrometheusHandler != nil { if tel.PrometheusHandler != nil {
mux.Handle("/metrics", tel.PrometheusHandler) mux.Handle("/metrics", tel.PrometheusHandler)
} }
if pprofEnabled {
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
logger.Info("pprof debugging enabled on %s/debug/pprof/", tcfg.AdminAddr)
}
admin := &http.Server{ admin := &http.Server{
Addr: tcfg.AdminAddr, Addr: tcfg.AdminAddr,
Handler: otelhttp.NewHandler(mux, "newt-admin"), Handler: otelhttp.NewHandler(mux, "newt-admin"),
@@ -719,24 +687,6 @@ func runNewtMain(ctx context.Context) {
defer func() { defer func() {
telemetry.IncSiteRegistration(ctx, regResult) telemetry.IncSiteRegistration(ctx, regResult)
}() }()
// Deduplicate using chainId: if the server echoes back a chainId we have
// already consumed (or one that doesn't match our current pending request),
// throw the message away to avoid setting up the tunnel twice.
var chainData struct {
ChainId string `json:"chainId"`
}
if jsonBytes, err := json.Marshal(msg.Data); err == nil {
_ = json.Unmarshal(jsonBytes, &chainData)
}
if chainData.ChainId != "" {
if chainData.ChainId != pendingRegisterChainId {
logger.Debug("Discarding duplicate/stale newt/wg/connect (chainId=%s, expected=%s)", chainData.ChainId, pendingRegisterChainId)
return
}
pendingRegisterChainId = "" // consume further duplicates with this id are rejected
}
if stopFunc != nil { if stopFunc != nil {
stopFunc() // stop the ws from sending more requests stopFunc() // stop the ws from sending more requests
stopFunc = nil // reset stopFunc to nil to avoid double stopping stopFunc = nil // reset stopFunc to nil to avoid double stopping
@@ -921,11 +871,8 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
} }
// Request exit nodes from the server // Request exit nodes from the server
pingChainId := generateChainId()
pendingPingChainId = pingChainId
stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{ stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{
"noCloud": noCloud, "noCloud": noCloud,
"chainId": pingChainId,
}, 3*time.Second) }, 3*time.Second)
logger.Info("Tunnel destroyed, ready for reconnection") logger.Info("Tunnel destroyed, ready for reconnection")
@@ -954,7 +901,6 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
client.RegisterHandler("newt/ping/exitNodes", func(msg websocket.WSMessage) { client.RegisterHandler("newt/ping/exitNodes", func(msg websocket.WSMessage) {
logger.Debug("Received ping message") logger.Debug("Received ping message")
if stopFunc != nil { if stopFunc != nil {
stopFunc() // stop the ws from sending more requests stopFunc() // stop the ws from sending more requests
stopFunc = nil // reset stopFunc to nil to avoid double stopping stopFunc = nil // reset stopFunc to nil to avoid double stopping
@@ -974,14 +920,6 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
} }
exitNodes := exitNodeData.ExitNodes exitNodes := exitNodeData.ExitNodes
if exitNodeData.ChainId != "" {
if exitNodeData.ChainId != pendingPingChainId {
logger.Debug("Discarding duplicate/stale newt/ping/exitNodes (chainId=%s, expected=%s)", exitNodeData.ChainId, pendingPingChainId)
return
}
pendingPingChainId = "" // consume further duplicates with this id are rejected
}
if len(exitNodes) == 0 { if len(exitNodes) == 0 {
logger.Info("No exit nodes provided") logger.Info("No exit nodes provided")
return return
@@ -1014,13 +952,10 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
}, },
} }
chainId := generateChainId()
pendingRegisterChainId = chainId
stopFunc = client.SendMessageInterval(topicWGRegister, map[string]interface{}{ stopFunc = client.SendMessageInterval(topicWGRegister, map[string]interface{}{
"publicKey": publicKey.String(), "publicKey": publicKey.String(),
"pingResults": pingResults, "pingResults": pingResults,
"newtVersion": newtVersion, "newtVersion": newtVersion,
"chainId": chainId,
}, 2*time.Second) }, 2*time.Second)
return return
@@ -1120,13 +1055,10 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
} }
// Send the ping results to the cloud for selection // Send the ping results to the cloud for selection
chainId := generateChainId()
pendingRegisterChainId = chainId
stopFunc = client.SendMessageInterval(topicWGRegister, map[string]interface{}{ stopFunc = client.SendMessageInterval(topicWGRegister, map[string]interface{}{
"publicKey": publicKey.String(), "publicKey": publicKey.String(),
"pingResults": pingResults, "pingResults": pingResults,
"newtVersion": newtVersion, "newtVersion": newtVersion,
"chainId": chainId,
}, 2*time.Second) }, 2*time.Second)
logger.Debug("Sent exit node ping results to cloud for selection: pingResults=%+v", pingResults) logger.Debug("Sent exit node ping results to cloud for selection: pingResults=%+v", pingResults)
@@ -1776,11 +1708,8 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
stopFunc() stopFunc()
} }
// request from the server the list of nodes to ping // request from the server the list of nodes to ping
pingChainId := generateChainId()
pendingPingChainId = pingChainId
stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{ stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{
"noCloud": noCloud, "noCloud": noCloud,
"chainId": pingChainId,
}, 3*time.Second) }, 3*time.Second)
logger.Debug("Requesting exit nodes from server") logger.Debug("Requesting exit nodes from server")
@@ -1792,13 +1721,10 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
} }
// Send registration message to the server for backward compatibility // Send registration message to the server for backward compatibility
bcChainId := generateChainId()
pendingRegisterChainId = bcChainId
err := client.SendMessage(topicWGRegister, map[string]interface{}{ err := client.SendMessage(topicWGRegister, map[string]interface{}{
"publicKey": publicKey.String(), "publicKey": publicKey.String(),
"newtVersion": newtVersion, "newtVersion": newtVersion,
"backwardsCompatible": true, "backwardsCompatible": true,
"chainId": bcChainId,
}) })
sendBlueprint(client) sendBlueprint(client)

View File

@@ -21,10 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
) )
const ( const errUnsupportedProtoFmt = "unsupported protocol: %s"
errUnsupportedProtoFmt = "unsupported protocol: %s"
maxUDPPacketSize = 65507
)
// Target represents a proxy target with its address and port // Target represents a proxy target with its address and port
type Target struct { type Target struct {
@@ -108,9 +105,13 @@ func classifyProxyError(err error) string {
if errors.Is(err, net.ErrClosed) { if errors.Is(err, net.ErrClosed) {
return "closed" return "closed"
} }
var ne net.Error if ne, ok := err.(net.Error); ok {
if errors.As(err, &ne) && ne.Timeout() { if ne.Timeout() {
return "timeout" return "timeout"
}
if ne.Temporary() {
return "temporary"
}
} }
msg := strings.ToLower(err.Error()) msg := strings.ToLower(err.Error())
switch { switch {
@@ -436,6 +437,14 @@ func (pm *ProxyManager) Stop() error {
pm.udpConns = append(pm.udpConns[:i], pm.udpConns[i+1:]...) pm.udpConns = append(pm.udpConns[:i], pm.udpConns[i+1:]...)
} }
// // Clear the target maps
// for k := range pm.tcpTargets {
// delete(pm.tcpTargets, k)
// }
// for k := range pm.udpTargets {
// delete(pm.udpTargets, k)
// }
// Give active connections a chance to close gracefully // Give active connections a chance to close gracefully
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -489,7 +498,7 @@ func (pm *ProxyManager) handleTCPProxy(listener net.Listener, targetAddr string)
if !pm.running { if !pm.running {
return return
} }
if errors.Is(err, net.ErrClosed) { if ne, ok := err.(net.Error); ok && !ne.Temporary() {
logger.Info("TCP listener closed, stopping proxy handler for %v", listener.Addr()) logger.Info("TCP listener closed, stopping proxy handler for %v", listener.Addr())
return return
} }
@@ -555,7 +564,7 @@ func (pm *ProxyManager) handleTCPProxy(listener net.Listener, targetAddr string)
} }
func (pm *ProxyManager) handleUDPProxy(conn *gonet.UDPConn, targetAddr string) { func (pm *ProxyManager) handleUDPProxy(conn *gonet.UDPConn, targetAddr string) {
buffer := make([]byte, maxUDPPacketSize) // Max UDP packet size buffer := make([]byte, 65507) // Max UDP packet size
clientConns := make(map[string]*net.UDPConn) clientConns := make(map[string]*net.UDPConn)
var clientsMutex sync.RWMutex var clientsMutex sync.RWMutex
@@ -574,7 +583,7 @@ func (pm *ProxyManager) handleUDPProxy(conn *gonet.UDPConn, targetAddr string) {
} }
// Check for connection closed conditions // Check for connection closed conditions
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) { if err == io.EOF || strings.Contains(err.Error(), "use of closed network connection") {
logger.Info("UDP connection closed, stopping proxy handler") logger.Info("UDP connection closed, stopping proxy handler")
// Clean up existing client connections // Clean up existing client connections
@@ -653,14 +662,10 @@ func (pm *ProxyManager) handleUDPProxy(conn *gonet.UDPConn, targetAddr string) {
telemetry.IncProxyConnectionEvent(context.Background(), tunnelID, "udp", telemetry.ProxyConnectionClosed) telemetry.IncProxyConnectionEvent(context.Background(), tunnelID, "udp", telemetry.ProxyConnectionClosed)
}() }()
buffer := make([]byte, maxUDPPacketSize) buffer := make([]byte, 65507)
for { for {
n, _, err := targetConn.ReadFromUDP(buffer) n, _, err := targetConn.ReadFromUDP(buffer)
if err != nil { if err != nil {
// Connection closed is normal during cleanup
if errors.Is(err, net.ErrClosed) || errors.Is(err, io.EOF) {
return // defer will handle cleanup, result stays "success"
}
logger.Error("Error reading from target: %v", err) logger.Error("Error reading from target: %v", err)
result = "failure" result = "failure"
return // defer will handle cleanup return // defer will handle cleanup