@@ -460,6 +460,83 @@ void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen)
460460
461461}
462462
463+ /*
464+ * rfbRebindListenSockets closes the current TCP/TCP6 listening sockets and
465+ * re-creates them from the screen's current listenInterface/listen6Interface
466+ * and port/ipv6port. This is used to follow an interface whose IP address
467+ * changed (e.g. after the bound network interface went down and back up).
468+ *
469+ * It must be called on the thread that select()s on the listening sockets so
470+ * that the close()/socket() swap cannot race a concurrent select(): in the
471+ * threaded (background loop) case that is the listener thread, which invokes
472+ * this from listenerRun() in response to rfbRequestListenRebind(). Already-
473+ * connected clients keep their own sockets and are unaffected. The HTTP
474+ * listeners are bound to the same interface address, so they are re-created too.
475+ */
476+ rfbBool
477+ rfbRebindListenSockets (rfbScreenInfoPtr rfbScreen )
478+ {
479+ in_addr_t iface = rfbScreen -> listenInterface ;
480+
481+ /* This deliberately only handles the plain TCP/TCP6 listeners; inetd and
482+ autoPort setups have no stable address to rebind to. */
483+ if (rfbScreen -> inetdSock != RFB_INVALID_SOCKET || rfbScreen -> autoPort )
484+ return FALSE;
485+
486+ /* tear down the existing listeners; rfbCloseSocket() also resets the fd to
487+ RFB_INVALID_SOCKET, the outer if only skips the FD_CLR when there is none */
488+ if (rfbScreen -> listenSock != RFB_INVALID_SOCKET ) {
489+ FD_CLR (rfbScreen -> listenSock , & rfbScreen -> allFds );
490+ rfbCloseSocket (rfbScreen -> listenSock );
491+ }
492+ #ifdef LIBVNCSERVER_IPv6
493+ if (rfbScreen -> listen6Sock != RFB_INVALID_SOCKET ) {
494+ FD_CLR (rfbScreen -> listen6Sock , & rfbScreen -> allFds );
495+ rfbCloseSocket (rfbScreen -> listen6Sock );
496+ }
497+ #endif
498+
499+ /* re-create from the current interface addresses */
500+ if (rfbScreen -> port > 0 ) {
501+ if ((rfbScreen -> listenSock = rfbListenOnTCPPort (rfbScreen -> port , iface )) == RFB_INVALID_SOCKET ) {
502+ rfbLogPerror ("rfbRebindListenSockets: ListenOnTCPPort" );
503+ } else {
504+ rfbLog ("rfbRebindListenSockets: listening for VNC connections on TCP port %d\n" , rfbScreen -> port );
505+ FD_SET (rfbScreen -> listenSock , & rfbScreen -> allFds );
506+ rfbScreen -> maxFd = rfbMax ((int )rfbScreen -> listenSock , rfbScreen -> maxFd );
507+ }
508+ }
509+
510+ #ifdef LIBVNCSERVER_IPv6
511+ if (rfbScreen -> ipv6port > 0 ) {
512+ if ((rfbScreen -> listen6Sock = rfbListenOnTCP6Port (rfbScreen -> ipv6port , rfbScreen -> listen6Interface )) == RFB_INVALID_SOCKET ) {
513+ /* rfbListenOnTCP6Port has its own detailed error printout */
514+ } else {
515+ rfbLog ("rfbRebindListenSockets: listening for VNC connections on TCP6 port %d\n" , rfbScreen -> ipv6port );
516+ FD_SET (rfbScreen -> listen6Sock , & rfbScreen -> allFds );
517+ rfbScreen -> maxFd = rfbMax ((int )rfbScreen -> listen6Sock , rfbScreen -> maxFd );
518+ }
519+ }
520+ #endif
521+
522+ /* The HTTP listeners are bound to the same interface address and go stale the
523+ same way, so re-create them too. rfbHttpCheckFds() runs on this same listener
524+ thread, so closing/reopening here cannot race it. Only relevant with an httpDir
525+ configured; reset httpInitDone so rfbHttpInitSockets() actually runs again. This
526+ is best-effort and does not affect the RFB-based return value below. */
527+ if (rfbScreen -> httpDir ) {
528+ rfbHttpShutdownSockets (rfbScreen );
529+ rfbScreen -> httpInitDone = FALSE;
530+ rfbHttpInitSockets (rfbScreen );
531+ }
532+
533+ return (rfbScreen -> listenSock != RFB_INVALID_SOCKET
534+ #ifdef LIBVNCSERVER_IPv6
535+ || rfbScreen -> listen6Sock != RFB_INVALID_SOCKET
536+ #endif
537+ ) ? TRUE : FALSE;
538+ }
539+
463540/*
464541 * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB
465542 * socket(s). If there is input to process, the appropriate function in the
0 commit comments