Skip to content

Commit 5743301

Browse files
committed
libvncclient: handle half-open TCP connections
When a connection is not reset properly at the TCP level (e.g. sudden power loss or process crash) the TCP connection becomes half-open and read() always returns -1 with errno = EAGAIN while select() always returns 0. This leads to an infinite loop and can be fixed by closing the connection after a certain number of retries (based on a timeout) has been exceeded.
1 parent 323aa5e commit 5743301

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

Diff for: libvncclient/sockets.c

+16-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ rfbBool errorMessageOnReadFailure = TRUE;
6262
rfbBool
6363
ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
6464
{
65+
const int USECS_WAIT_PER_RETRY = 100000;
66+
int retries = 0;
6567
#undef DEBUG_READ_EXACT
6668
#ifdef DEBUG_READ_EXACT
6769
char* oout=out;
@@ -151,10 +153,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
151153
if (i <= 0) {
152154
if (i < 0) {
153155
if (errno == EWOULDBLOCK || errno == EAGAIN) {
156+
if (client->readTimeout > 0 &&
157+
++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
158+
{
159+
rfbClientLog("Connection timed out\n");
160+
return FALSE;
161+
}
154162
/* TODO:
155163
ProcessXtEvents();
156164
*/
157-
WaitForMessage(client, 100000);
165+
WaitForMessage(client, USECS_WAIT_PER_RETRY);
158166
i = 0;
159167
} else {
160168
rfbClientErr("read (%d: %s)\n",errno,strerror(errno));
@@ -194,10 +202,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
194202
errno=WSAGetLastError();
195203
#endif
196204
if (errno == EWOULDBLOCK || errno == EAGAIN) {
205+
if (client->readTimeout > 0 &&
206+
++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
207+
{
208+
rfbClientLog("Connection timed out\n");
209+
return FALSE;
210+
}
197211
/* TODO:
198212
ProcessXtEvents();
199213
*/
200-
WaitForMessage(client, 100000);
214+
WaitForMessage(client, USECS_WAIT_PER_RETRY);
201215
i = 0;
202216
} else {
203217
rfbClientErr("read (%s)\n",strerror(errno));

Diff for: libvncclient/vncviewer.c

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
272272
client->destPort = 5900;
273273

274274
client->connectTimeout = DEFAULT_CONNECT_TIMEOUT;
275+
client->readTimeout = DEFAULT_READ_TIMEOUT;
275276

276277
client->CurrentKeyboardLedState = 0;
277278
client->HandleKeyboardLedState = (HandleKeyboardLedStateProc)DummyPoint;

Diff for: rfb/rfbclient.h

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#define SERVER_PORT_OFFSET 5900
8686

8787
#define DEFAULT_CONNECT_TIMEOUT 60
88+
#define DEFAULT_READ_TIMEOUT 0
8889

8990
#define DEFAULT_SSH_CMD "/usr/bin/ssh"
9091
#define DEFAULT_TUNNEL_CMD \
@@ -454,6 +455,9 @@ typedef struct _rfbClient {
454455
#endif
455456
/* timeout in seconds for select() after connect() */
456457
unsigned int connectTimeout;
458+
/* timeout in seconds when reading from half-open connections in
459+
* ReadFromRFBServer() - keep at 0 to disable timeout detection and handling */
460+
unsigned int readTimeout;
457461
} rfbClient;
458462

459463
/* cursor.c */

0 commit comments

Comments
 (0)