Skip to content

Commit 583eeea

Browse files
committed
Merge branch 'master' into multicastvnc
2 parents 291fd5f + 5277c0e commit 583eeea

6 files changed

Lines changed: 147 additions & 4 deletions

File tree

include/rfb/rfb.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,11 @@ typedef struct _rfbScreenInfo
390390
/* Timeout value for select() calls, mainly used for multithreaded servers. */
391391
int select_timeout_usec;
392392

393+
/** Set by rfbRequestListenRebind() to ask the listener thread to re-create
394+
* its listening sockets from the current listenInterface/listen6Interface/
395+
* port/ipv6port values on its next loop iteration. Cleared by the thread. */
396+
rfbBool rebindListenSockets;
397+
393398
/*
394399
multicast stuff
395400
*/
@@ -852,6 +857,15 @@ extern int rfbMaxClientWait;
852857

853858
extern void rfbInitSockets(rfbScreenInfoPtr rfbScreen);
854859
extern void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen);
860+
/** Close and re-create the TCP/TCP6 listening sockets from the screen's current
861+
* listenInterface/listen6Interface/port/ipv6port. Must run on the same thread
862+
* that select()s on those sockets; off-thread callers use rfbRequestListenRebind().
863+
* Returns TRUE if at least one family is now listening. */
864+
extern rfbBool rfbRebindListenSockets(rfbScreenInfoPtr rfbScreen);
865+
/** Thread-safe request to rebind the listening sockets: the actual swap is done
866+
* by the background listener thread on its next iteration. Caller must have set
867+
* the desired listenInterface/listen6Interface/port/ipv6port beforehand. */
868+
extern void rfbRequestListenRebind(rfbScreenInfoPtr rfbScreen);
855869
extern void rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen);
856870
extern void rfbCloseClient(rfbClientPtr cl);
857871
extern int rfbReadExact(rfbClientPtr cl, char *buf, int len);

src/libvncclient/tight.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
341341

342342
numRows = (bufferSize - zs->avail_out) / rowSize;
343343

344+
/* The decompressed stream is server-controlled and may yield more rows
345+
than the rectangle's declared height. filterFn() writes directly into
346+
client->frameBuffer, so clamp here before writing to avoid running past
347+
the framebuffer (heap out-of-bounds write). The post-loop
348+
"rowsProcessed != rh" check happens too late. */
349+
if (numRows > rh - rowsProcessed) {
350+
rfbClientLog("Tight: too many scan lines after decompression.\n");
351+
return FALSE;
352+
}
353+
344354
filterFn(client, rx, ry+rowsProcessed, numRows);
345355

346356
extraBytes = bufferSize - zs->avail_out - numRows * rowSize;

src/libvncserver/httpd.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ rfbHttpInitSockets(rfbScreenInfoPtr rfbScreen)
130130
INIT_MUTEX(cl.outputMutex);
131131
INIT_MUTEX(cl.refCountMutex);
132132
INIT_MUTEX(cl.sendMutex);
133+
cl.readFromSocket = rfbDefaultReadFromSocket;
134+
cl.peekAtSocket = rfbDefaultPeekAtSocket;
135+
cl.hasPendingOnSocket = rfbDefaultHasPendingOnSocket;
136+
cl.writeToSocket = rfbDefaultWriteToSocket;
133137
}
134138

135139
void rfbHttpShutdownSockets(rfbScreenInfoPtr rfbScreen) {
@@ -225,15 +229,20 @@ rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen)
225229
|| (rfbScreen->httpListen6Sock != RFB_INVALID_SOCKET && FD_ISSET(rfbScreen->httpListen6Sock, &fds))) {
226230
if (rfbScreen->httpSock != RFB_INVALID_SOCKET) rfbCloseSocket(rfbScreen->httpSock);
227231

232+
/*
233+
* Mirror the RFB listener in listenerRun(): on a failed accept() just bail and
234+
* keep the listening socket. While the bound interface's address is gone the
235+
* accept() fails (e.g. EINVAL), but the socket is not dead -- it resumes once
236+
* the address returns. Logging every failure here would flood the log for the
237+
* whole down period, so stay silent like the RFB path does.
238+
*/
228239
if(FD_ISSET(rfbScreen->httpListenSock, &fds)) {
229240
if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, (struct sockaddr *)&addr, &addrlen)) == RFB_INVALID_SOCKET) {
230-
rfbLogPerror("httpCheckFds: accept");
231241
return;
232242
}
233243
}
234244
else if(FD_ISSET(rfbScreen->httpListen6Sock, &fds)) {
235245
if ((rfbScreen->httpSock = accept(rfbScreen->httpListen6Sock, (struct sockaddr *)&addr, &addrlen)) == RFB_INVALID_SOCKET) {
236-
rfbLogPerror("httpCheckFds: accept");
237246
return;
238247
}
239248
}

src/libvncserver/main.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,12 @@ listenerRun(void *data)
736736
/* Reset the pipe */
737737
char buf;
738738
while (read(screen->pipe_notify_listener_thread[0], &buf, sizeof(buf)) == sizeof(buf));
739+
/* A rebind request is serviced here, on the listener thread, so the
740+
close()/socket() swap cannot race the select() above. */
741+
if (screen->rebindListenSockets) {
742+
screen->rebindListenSockets = FALSE;
743+
rfbRebindListenSockets(screen);
744+
}
739745
/* Go on with loop */
740746
continue;
741747
}
@@ -761,7 +767,28 @@ listenerRun(void *data)
761767

762768
#endif
763769

764-
void
770+
void
771+
rfbRequestListenRebind(rfbScreenInfoPtr screen)
772+
{
773+
/* Flag the request; the listener thread picks it up after being woken. */
774+
screen->rebindListenSockets = TRUE;
775+
776+
#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) && !defined(WIN32)
777+
if (screen->backgroundLoop && screen->pipe_notify_listener_thread[1] != -1) {
778+
const char buf = 'r';
779+
if (write(screen->pipe_notify_listener_thread[1], &buf, sizeof(buf)) < 0)
780+
rfbLogPerror("rfbRequestListenRebind: write to notify pipe");
781+
return;
782+
}
783+
#endif
784+
785+
/* No background listener thread: do the swap inline. Safe because the
786+
single-threaded event loop is not concurrently in select() here. */
787+
screen->rebindListenSockets = FALSE;
788+
rfbRebindListenSockets(screen);
789+
}
790+
791+
void
765792
rfbStartOnHoldClient(rfbClientPtr cl)
766793
{
767794
cl->onHold = FALSE;

src/libvncserver/rfbserver.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,12 @@ rfbClientConnectionGone(rfbClientPtr cl)
660660
if(cl->sock != RFB_INVALID_SOCKET)
661661
rfbCloseSocket(cl->sock);
662662

663+
/* Clean up file descriptor of an interrupted file transfer */
664+
if (cl->fileTransfer.fd != -1) {
665+
close(cl->fileTransfer.fd);
666+
cl->fileTransfer.fd = -1;
667+
}
668+
663669
if (cl->scaledScreen!=NULL)
664670
cl->scaledScreen->scaledScreenRefCount--;
665671

@@ -4921,7 +4927,7 @@ rfbSendExtDesktopSize(rfbClientPtr cl,
49214927
rfbBool
49224928
rfbSendUpdateBuf(rfbClientPtr cl)
49234929
{
4924-
if(cl->sock<0)
4930+
if(cl->sock<0 || cl->state == RFB_SHUTDOWN)
49254931
return FALSE;
49264932

49274933
if (rfbWriteExact(cl, cl->updateBuf, cl->ublen) < 0) {

src/libvncserver/sockets.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)