mirror of
https://github.com/fosrl/olm.git
synced 2026-02-08 05:56:41 +00:00
Add rebind endpoints for the shared socket
This commit is contained in:
34
api/api.go
34
api/api.go
@@ -78,6 +78,7 @@ type API struct {
|
|||||||
onMetadataChange func(MetadataChangeRequest) error
|
onMetadataChange func(MetadataChangeRequest) error
|
||||||
onDisconnect func() error
|
onDisconnect func() error
|
||||||
onExit func() error
|
onExit func() error
|
||||||
|
onRebind func() error
|
||||||
|
|
||||||
statusMu sync.RWMutex
|
statusMu sync.RWMutex
|
||||||
peerStatuses map[int]*PeerStatus
|
peerStatuses map[int]*PeerStatus
|
||||||
@@ -126,11 +127,13 @@ func (s *API) SetHandlers(
|
|||||||
onMetadataChange func(MetadataChangeRequest) error,
|
onMetadataChange func(MetadataChangeRequest) error,
|
||||||
onDisconnect func() error,
|
onDisconnect func() error,
|
||||||
onExit func() error,
|
onExit func() error,
|
||||||
|
onRebind func() error,
|
||||||
) {
|
) {
|
||||||
s.onConnect = onConnect
|
s.onConnect = onConnect
|
||||||
s.onSwitchOrg = onSwitchOrg
|
s.onSwitchOrg = onSwitchOrg
|
||||||
s.onDisconnect = onDisconnect
|
s.onDisconnect = onDisconnect
|
||||||
s.onExit = onExit
|
s.onExit = onExit
|
||||||
|
s.onRebind = onRebind
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the HTTP server
|
// Start starts the HTTP server
|
||||||
@@ -147,6 +150,7 @@ func (s *API) Start() error {
|
|||||||
mux.HandleFunc("/disconnect", s.handleDisconnect)
|
mux.HandleFunc("/disconnect", s.handleDisconnect)
|
||||||
mux.HandleFunc("/exit", s.handleExit)
|
mux.HandleFunc("/exit", s.handleExit)
|
||||||
mux.HandleFunc("/health", s.handleHealth)
|
mux.HandleFunc("/health", s.handleHealth)
|
||||||
|
mux.HandleFunc("/rebind", s.handleRebind)
|
||||||
|
|
||||||
s.server = &http.Server{
|
s.server = &http.Server{
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
@@ -560,3 +564,33 @@ func (s *API) GetStatus() StatusResponse {
|
|||||||
NetworkSettings: network.GetSettings(),
|
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
|
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
|
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 {
|
func (o *Olm) AddDevice(fd uint32) error {
|
||||||
if o.middleDev == nil {
|
if o.middleDev == nil {
|
||||||
return fmt.Errorf("middle device is not initialized")
|
return fmt.Errorf("middle device is not initialized")
|
||||||
|
|||||||
Reference in New Issue
Block a user