mirror of
https://github.com/fosrl/olm.git
synced 2026-02-07 21:46:40 +00:00
Add rebind endpoints for the shared socket
Former-commit-id: 6fd0984b13
This commit is contained in:
34
api/api.go
34
api/api.go
@@ -78,6 +78,7 @@ type API struct {
|
||||
onMetadataChange func(MetadataChangeRequest) error
|
||||
onDisconnect func() error
|
||||
onExit func() error
|
||||
onRebind func() error
|
||||
|
||||
statusMu sync.RWMutex
|
||||
peerStatuses map[int]*PeerStatus
|
||||
@@ -126,11 +127,13 @@ func (s *API) SetHandlers(
|
||||
onMetadataChange func(MetadataChangeRequest) error,
|
||||
onDisconnect func() error,
|
||||
onExit func() error,
|
||||
onRebind func() error,
|
||||
) {
|
||||
s.onConnect = onConnect
|
||||
s.onSwitchOrg = onSwitchOrg
|
||||
s.onDisconnect = onDisconnect
|
||||
s.onExit = onExit
|
||||
s.onRebind = onRebind
|
||||
}
|
||||
|
||||
// Start starts the HTTP server
|
||||
@@ -147,6 +150,7 @@ func (s *API) Start() error {
|
||||
mux.HandleFunc("/disconnect", s.handleDisconnect)
|
||||
mux.HandleFunc("/exit", s.handleExit)
|
||||
mux.HandleFunc("/health", s.handleHealth)
|
||||
mux.HandleFunc("/rebind", s.handleRebind)
|
||||
|
||||
s.server = &http.Server{
|
||||
Handler: mux,
|
||||
@@ -560,3 +564,33 @@ func (s *API) GetStatus() StatusResponse {
|
||||
NetworkSettings: network.GetSettings(),
|
||||
}
|
||||
}
|
||||
|
||||
// handleRebind handles the /rebind endpoint
|
||||
// This triggers a socket rebind, which is necessary when network connectivity changes
|
||||
// (e.g., WiFi to cellular transition on macOS/iOS) and the old socket becomes stale.
|
||||
func (s *API) handleRebind(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Received rebind request via API")
|
||||
|
||||
// Call the rebind handler if set
|
||||
if s.onRebind != nil {
|
||||
if err := s.onRebind(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Rebind failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "Rebind handler not configured", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
// Return a success response
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "socket rebound successfully",
|
||||
})
|
||||
}
|
||||
|
||||
71
olm/olm.go
71
olm/olm.go
@@ -273,6 +273,11 @@ func (o *Olm) registerAPICallbacks() {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// onRebind
|
||||
func() error {
|
||||
logger.Info("Processing rebind request via API")
|
||||
return o.RebindSocket()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -783,6 +788,72 @@ func (o *Olm) SetPowerMode(mode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RebindSocket recreates the UDP socket when network connectivity changes.
|
||||
// This is necessary on macOS/iOS when transitioning between WiFi and cellular,
|
||||
// as the old socket becomes stale and can no longer route packets.
|
||||
// Call this method when detecting a network path change.
|
||||
func (o *Olm) RebindSocket() error {
|
||||
if o.sharedBind == nil {
|
||||
return fmt.Errorf("shared bind is not initialized")
|
||||
}
|
||||
|
||||
// Get the current port so we can try to reuse it
|
||||
currentPort := o.sharedBind.GetPort()
|
||||
|
||||
logger.Info("Rebinding UDP socket (current port: %d)", currentPort)
|
||||
|
||||
// Create a new UDP socket
|
||||
var newConn *net.UDPConn
|
||||
var newPort uint16
|
||||
var err error
|
||||
|
||||
// First try to bind to the same port
|
||||
localAddr := &net.UDPAddr{
|
||||
Port: int(currentPort),
|
||||
IP: net.IPv4zero,
|
||||
}
|
||||
|
||||
newConn, err = net.ListenUDP("udp4", localAddr)
|
||||
if err != nil {
|
||||
// If we can't reuse the port, find a new one
|
||||
logger.Warn("Could not rebind to port %d, finding new port: %v", currentPort, err)
|
||||
newPort, err = util.FindAvailableUDPPort(49152, 65535)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find available UDP port: %w", err)
|
||||
}
|
||||
|
||||
localAddr = &net.UDPAddr{
|
||||
Port: int(newPort),
|
||||
IP: net.IPv4zero,
|
||||
}
|
||||
|
||||
// Use udp4 explicitly to avoid IPv6 dual-stack issues
|
||||
newConn, err = net.ListenUDP("udp4", localAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create new UDP socket: %w", err)
|
||||
}
|
||||
} else {
|
||||
newPort = currentPort
|
||||
}
|
||||
|
||||
// Rebind the shared bind with the new connection
|
||||
if err := o.sharedBind.Rebind(newConn); err != nil {
|
||||
newConn.Close()
|
||||
return fmt.Errorf("failed to rebind shared bind: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Successfully rebound UDP socket on port %d", newPort)
|
||||
|
||||
// Trigger a hole punch to re-establish NAT mappings with the new socket
|
||||
if o.holePunchManager != nil {
|
||||
o.holePunchManager.TriggerHolePunch()
|
||||
o.holePunchManager.ResetServerHolepunchInterval()
|
||||
logger.Info("Triggered hole punch after socket rebind")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Olm) AddDevice(fd uint32) error {
|
||||
if o.middleDev == nil {
|
||||
return fmt.Errorf("middle device is not initialized")
|
||||
|
||||
Reference in New Issue
Block a user