@@ -336,6 +336,83 @@ void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen)
336336#endif
337337}
338338
339+ /*
340+ * rfbRebindListenSockets closes the current TCP/TCP6 listening sockets and
341+ * re-creates them from the screen's current listenInterface/listen6Interface
342+ * and port/ipv6port. This is used to follow an interface whose IP address
343+ * changed (e.g. after the bound network interface went down and back up).
344+ *
345+ * It must be called on the thread that select()s on the listening sockets so
346+ * that the close()/socket() swap cannot race a concurrent select(): in the
347+ * threaded (background loop) case that is the listener thread, which invokes
348+ * this from listenerRun() in response to rfbRequestListenRebind(). Already-
349+ * connected clients keep their own sockets and are unaffected. The HTTP
350+ * listeners are bound to the same interface address, so they are re-created too.
351+ */
352+ rfbBool
353+ rfbRebindListenSockets (rfbScreenInfoPtr rfbScreen )
354+ {
355+ in_addr_t iface = rfbScreen -> listenInterface ;
356+
357+ /* This deliberately only handles the plain TCP/TCP6 listeners; inetd and
358+ autoPort setups have no stable address to rebind to. */
359+ if (rfbScreen -> inetdSock != RFB_INVALID_SOCKET || rfbScreen -> autoPort )
360+ return FALSE;
361+
362+ /* tear down the existing listeners; rfbCloseSocket() also resets the fd to
363+ RFB_INVALID_SOCKET, the outer if only skips the FD_CLR when there is none */
364+ if (rfbScreen -> listenSock != RFB_INVALID_SOCKET ) {
365+ FD_CLR (rfbScreen -> listenSock , & rfbScreen -> allFds );
366+ rfbCloseSocket (rfbScreen -> listenSock );
367+ }
368+ #ifdef LIBVNCSERVER_IPv6
369+ if (rfbScreen -> listen6Sock != RFB_INVALID_SOCKET ) {
370+ FD_CLR (rfbScreen -> listen6Sock , & rfbScreen -> allFds );
371+ rfbCloseSocket (rfbScreen -> listen6Sock );
372+ }
373+ #endif
374+
375+ /* re-create from the current interface addresses */
376+ if (rfbScreen -> port > 0 ) {
377+ if ((rfbScreen -> listenSock = rfbListenOnTCPPort (rfbScreen -> port , iface )) == RFB_INVALID_SOCKET ) {
378+ rfbLogPerror ("rfbRebindListenSockets: ListenOnTCPPort" );
379+ } else {
380+ rfbLog ("rfbRebindListenSockets: listening for VNC connections on TCP port %d\n" , rfbScreen -> port );
381+ FD_SET (rfbScreen -> listenSock , & rfbScreen -> allFds );
382+ rfbScreen -> maxFd = rfbMax ((int )rfbScreen -> listenSock , rfbScreen -> maxFd );
383+ }
384+ }
385+
386+ #ifdef LIBVNCSERVER_IPv6
387+ if (rfbScreen -> ipv6port > 0 ) {
388+ if ((rfbScreen -> listen6Sock = rfbListenOnTCP6Port (rfbScreen -> ipv6port , rfbScreen -> listen6Interface )) == RFB_INVALID_SOCKET ) {
389+ /* rfbListenOnTCP6Port has its own detailed error printout */
390+ } else {
391+ rfbLog ("rfbRebindListenSockets: listening for VNC connections on TCP6 port %d\n" , rfbScreen -> ipv6port );
392+ FD_SET (rfbScreen -> listen6Sock , & rfbScreen -> allFds );
393+ rfbScreen -> maxFd = rfbMax ((int )rfbScreen -> listen6Sock , rfbScreen -> maxFd );
394+ }
395+ }
396+ #endif
397+
398+ /* The HTTP listeners are bound to the same interface address and go stale the
399+ same way, so re-create them too. rfbHttpCheckFds() runs on this same listener
400+ thread, so closing/reopening here cannot race it. Only relevant with an httpDir
401+ configured; reset httpInitDone so rfbHttpInitSockets() actually runs again. This
402+ is best-effort and does not affect the RFB-based return value below. */
403+ if (rfbScreen -> httpDir ) {
404+ rfbHttpShutdownSockets (rfbScreen );
405+ rfbScreen -> httpInitDone = FALSE;
406+ rfbHttpInitSockets (rfbScreen );
407+ }
408+
409+ return (rfbScreen -> listenSock != RFB_INVALID_SOCKET
410+ #ifdef LIBVNCSERVER_IPv6
411+ || rfbScreen -> listen6Sock != RFB_INVALID_SOCKET
412+ #endif
413+ ) ? TRUE : FALSE;
414+ }
415+
339416/*
340417 * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB
341418 * socket(s). If there is input to process, the appropriate function in the
0 commit comments