mirror of
https://github.com/fosrl/newt.git
synced 2026-07-11 10:24:53 -05:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20907685f3 | ||
|
|
689acff6f8 | ||
|
|
0019d0dc51 |
@@ -27,17 +27,17 @@ type ExitNode struct {
|
||||
|
||||
// Manager handles UDP hole punching operations
|
||||
type Manager struct {
|
||||
mu sync.Mutex
|
||||
running bool
|
||||
stopChan chan struct{}
|
||||
sharedBind *bind.SharedBind
|
||||
ID string
|
||||
token string
|
||||
publicKey string
|
||||
clientType string
|
||||
exitNodes map[string]ExitNode // key is endpoint
|
||||
updateChan chan struct{} // signals the goroutine to refresh exit nodes
|
||||
publicDNS []string
|
||||
mu sync.Mutex
|
||||
running bool
|
||||
stopChan chan struct{}
|
||||
sharedBind *bind.SharedBind
|
||||
ID string
|
||||
token string
|
||||
publicKey string
|
||||
clientType string
|
||||
exitNodes map[string]ExitNode // key is endpoint
|
||||
updateChan chan struct{} // signals the goroutine to refresh exit nodes
|
||||
publicDNS []string
|
||||
|
||||
sendHolepunchInterval time.Duration
|
||||
sendHolepunchIntervalMin time.Duration
|
||||
@@ -56,7 +56,7 @@ func NewManager(sharedBind *bind.SharedBind, ID string, clientType string, publi
|
||||
ID: ID,
|
||||
clientType: clientType,
|
||||
publicKey: publicKey,
|
||||
publicDNS: publicDNS,
|
||||
publicDNS: publicDNS,
|
||||
exitNodes: make(map[string]ExitNode),
|
||||
sendHolepunchInterval: defaultSendHolepunchIntervalMin,
|
||||
sendHolepunchIntervalMin: defaultSendHolepunchIntervalMin,
|
||||
@@ -73,6 +73,14 @@ func (m *Manager) SetToken(token string) {
|
||||
m.token = token
|
||||
}
|
||||
|
||||
// SetPublicDNS replaces the list of DNS servers used to resolve exit-node
|
||||
// endpoints. The servers must be in "host:port" format (e.g. "8.8.8.8:53").
|
||||
func (m *Manager) SetPublicDNS(servers []string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.publicDNS = servers
|
||||
}
|
||||
|
||||
// IsRunning returns whether hole punching is currently active
|
||||
func (m *Manager) IsRunning() bool {
|
||||
m.mu.Lock()
|
||||
|
||||
@@ -43,17 +43,17 @@ func DefaultTestOptions() TestConnectionOptions {
|
||||
|
||||
// cachedAddr holds a cached resolved UDP address
|
||||
type cachedAddr struct {
|
||||
addr *net.UDPAddr
|
||||
addr *net.UDPAddr
|
||||
resolvedAt time.Time
|
||||
}
|
||||
|
||||
// HolepunchTester monitors holepunch connectivity using magic packets
|
||||
type HolepunchTester struct {
|
||||
sharedBind *bind.SharedBind
|
||||
publicDNS []string
|
||||
mu sync.RWMutex
|
||||
running bool
|
||||
stopChan chan struct{}
|
||||
sharedBind *bind.SharedBind
|
||||
publicDNS []string
|
||||
mu sync.RWMutex
|
||||
running bool
|
||||
stopChan chan struct{}
|
||||
|
||||
// Pending requests waiting for responses (key: echo data as string)
|
||||
pendingRequests sync.Map // map[string]*pendingRequest
|
||||
@@ -88,12 +88,24 @@ type pendingRequest struct {
|
||||
func NewHolepunchTester(sharedBind *bind.SharedBind, publicDNS []string) *HolepunchTester {
|
||||
return &HolepunchTester{
|
||||
sharedBind: sharedBind,
|
||||
publicDNS: publicDNS,
|
||||
publicDNS: publicDNS,
|
||||
addrCache: make(map[string]*cachedAddr),
|
||||
addrCacheTTL: 5 * time.Minute, // Cache addresses for 5 minutes
|
||||
}
|
||||
}
|
||||
|
||||
// SetPublicDNS replaces the list of DNS servers used to resolve peer endpoints.
|
||||
// The servers must be in "host:port" format (e.g. "8.8.8.8:53").
|
||||
func (t *HolepunchTester) SetPublicDNS(servers []string) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.publicDNS = servers
|
||||
// Invalidate the address cache so endpoints are re-resolved with the new DNS.
|
||||
t.addrCacheMu.Lock()
|
||||
t.addrCache = make(map[string]*cachedAddr)
|
||||
t.addrCacheMu.Unlock()
|
||||
}
|
||||
|
||||
// SetCallback sets the callback for connection status changes
|
||||
func (t *HolepunchTester) SetCallback(callback HolepunchStatusCallback) {
|
||||
t.mu.Lock()
|
||||
|
||||
4
main.go
4
main.go
@@ -691,6 +691,10 @@ func runNewtMain(ctx context.Context) {
|
||||
Platform: newtPlatform,
|
||||
TLSConfig: tlsCfg,
|
||||
}); err != nil {
|
||||
if errors.Is(err, updates.ErrAutoUpdateUnsupportedInOfficialContainer) {
|
||||
logger.Debug("checkAndSelfUpdate: auto-update skipped: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Error("Auto-update check failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -50,6 +51,10 @@ type versionResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ErrAutoUpdateUnsupportedInOfficialContainer indicates auto-update is not
|
||||
// available when running inside official Fossorial container images.
|
||||
var ErrAutoUpdateUnsupportedInOfficialContainer = errors.New("auto-update unsupported in official Fossorial container images")
|
||||
|
||||
// isOfficialContainer returns true when the process is running inside an
|
||||
// official Fossorial-built container image. The image sets
|
||||
// NEWT_SYSTEM_SUBSTRATE="CONTAINER" at build time; users running newt in their own
|
||||
@@ -113,7 +118,7 @@ func CheckAndSelfUpdate(cfg SelfUpdateConfig) error {
|
||||
|
||||
if isOfficialContainer() {
|
||||
logger.Debug("checkAndSelfUpdate: running inside official container, skipping auto-update")
|
||||
return fmt.Errorf("auto-update is not supported in official Fossorial container images; pull a new image tag instead")
|
||||
return fmt.Errorf("%w; pull a new image tag instead", ErrAutoUpdateUnsupportedInOfficialContainer)
|
||||
}
|
||||
|
||||
if cfg.CurrentVersion == "version_replaceme" {
|
||||
|
||||
@@ -9,11 +9,19 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// GitHubRelease represents the GitHub API response for a release
|
||||
type GitHubRelease struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Name string `json:"name"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
// VersionsAPIEntry represents one component's version payload.
|
||||
type VersionsAPIEntry struct {
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
ReleaseNotes string `json:"releaseNotes"`
|
||||
}
|
||||
|
||||
// VersionsAPIResponse represents the versions API response.
|
||||
type VersionsAPIResponse struct {
|
||||
Data map[string]VersionsAPIEntry `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Error bool `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// Version represents a semantic version
|
||||
@@ -75,14 +83,15 @@ func (v Version) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
}
|
||||
|
||||
// CheckForUpdate checks GitHub for a newer version and prints an update banner if found
|
||||
// CheckForUpdate checks the Fossorial versions API for a newer version and prints an update banner if found.
|
||||
func CheckForUpdate(owner, repo, currentVersion string) error {
|
||||
if currentVersion == "version_replaceme" {
|
||||
return nil
|
||||
}
|
||||
_ = owner
|
||||
|
||||
// GitHub API URL for latest release
|
||||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
|
||||
// Versions API URL
|
||||
url := "https://api.fossorial.io/api/v1/versions"
|
||||
|
||||
// Create HTTP client with timeout
|
||||
client := &http.Client{
|
||||
@@ -97,13 +106,18 @@ func CheckForUpdate(owner, repo, currentVersion string) error {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("GitHub API returned status: %d", resp.StatusCode)
|
||||
return fmt.Errorf("versions API returned status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Parse the JSON response
|
||||
var release GitHubRelease
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return fmt.Errorf("failed to parse release info: %w", err)
|
||||
// Parse the JSON response.
|
||||
var versionsResp VersionsAPIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&versionsResp); err != nil {
|
||||
return fmt.Errorf("failed to parse versions response: %w", err)
|
||||
}
|
||||
|
||||
componentVersion, ok := versionsResp.Data[repo]
|
||||
if !ok {
|
||||
return fmt.Errorf("component %q not found in versions API response", repo)
|
||||
}
|
||||
|
||||
// Parse current and latest versions
|
||||
@@ -112,14 +126,18 @@ func CheckForUpdate(owner, repo, currentVersion string) error {
|
||||
return fmt.Errorf("invalid current version: %w", err)
|
||||
}
|
||||
|
||||
latestVer, err := parseVersion(release.TagName)
|
||||
latestVer, err := parseVersion(componentVersion.LatestVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid latest version: %w", err)
|
||||
}
|
||||
|
||||
// Check if update is available
|
||||
if currentVer.isNewer(latestVer) {
|
||||
printUpdateBanner(currentVer.String(), latestVer.String(), "curl -fsSL https://static.pangolin.net/get-newt.sh | bash")
|
||||
releaseNotes := componentVersion.ReleaseNotes
|
||||
if releaseNotes == "" {
|
||||
releaseNotes = "curl -fsSL https://static.pangolin.net/get-newt.sh | bash"
|
||||
}
|
||||
printUpdateBanner(currentVer.String(), latestVer.String(), releaseNotes)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user