diff --git a/src/libvncclient/rfbclient.c b/src/libvncclient/rfbclient.c index 3d488ba9d..1dd641ab3 100644 --- a/src/libvncclient/rfbclient.c +++ b/src/libvncclient/rfbclient.c @@ -1798,6 +1798,43 @@ sendExtClientCutTextNotify(rfbClient *client) return ret; } +/** + * Due to bugs, many servers (including most versions of LibVNCServer) can't + * properly handle zlib streams created by compress() function of zlib library. + * + * Primary bug is that these servers don't expect inflate() to return Z_STREAM_END + * which is the case for (correct) streams created by compress(). + * + * So this function creates a compatible stream, for which inflate() returns Z_OK. + * This is how some clients create zlib streams, unintentionally avoiding the bug. + */ +static int +CompressClipData(Bytef *dest, uLongf *destLen, Bytef *source, uLong sourceLen) +{ + int ret; + z_stream *zs = (z_stream*)malloc(sizeof(z_stream)); + memset(zs, 0, sizeof(z_stream)); + + zs->zfree = Z_NULL; + zs->zalloc = Z_NULL; + zs->opaque = Z_NULL; + ret = deflateInit(zs, Z_DEFAULT_COMPRESSION); + if (ret == Z_OK) { + zs->avail_in = sourceLen; + zs->next_in = source; + zs->avail_out = *destLen; + zs->next_out = dest; + + do { + // Using Z_SYNC_FLUSH instead of Z_FINISH is the key here. + ret = deflate(zs, Z_SYNC_FLUSH); + } while (ret >= 0 && zs->avail_in > 0); + + *destLen = zs->total_out; + deflateEnd(zs); + } + return ret; +} /* * sendExtClientCutTextProvide @@ -1808,20 +1845,21 @@ static rfbBool sendExtClientCutTextProvide(rfbClient *client, char* data, int len) { rfbClientCutTextMsg cct = {0, }; + int sentLen = len + 1; /* Sent data is null terminated*/ const uint32_t be_flags = rfbClientSwap32IfLE(rfbExtendedClipboard_Provide | rfbExtendedClipboard_Text); /*text and provide*/ - const uint32_t be_size = rfbClientSwap32IfLE(len); - const size_t sz_to_compressed = sizeof(be_size) + len; /*size, data*/ - uLong csz = compressBound(sz_to_compressed + 1); /*tricky, some server need extar byte to flush data*/ + const uint32_t be_size = rfbClientSwap32IfLE(sentLen); + const size_t sz_to_compressed = sizeof(be_size) + sentLen; /*size, data*/ + uLong csz = compressBound(sz_to_compressed); - unsigned char *buf = malloc(sz_to_compressed + 1); /*tricky, some server need extra byte to flush data*/ + unsigned char *buf = malloc(sz_to_compressed); if (!buf) { rfbClientLog("sendExtClientCutTextProvide. alloc buf failed\n"); return FALSE; } memcpy(buf, &be_size, sizeof(be_size)); memcpy(buf + sizeof(be_size), data, len); - buf[sz_to_compressed] = 0; + buf[sz_to_compressed - 1] = 0; /* Null terminate sent data */ unsigned char *cbuf = malloc(sizeof(be_flags) + csz); /*flag, compressed*/ if (!cbuf) { @@ -1830,7 +1868,7 @@ sendExtClientCutTextProvide(rfbClient *client, char* data, int len) return FALSE; } memcpy(cbuf, &be_flags, sizeof(be_flags)); - if (compress(cbuf + sizeof(be_flags), &csz, buf, sz_to_compressed + 1) != Z_OK) { + if (CompressClipData(cbuf + sizeof(be_flags), &csz, buf, sz_to_compressed) != Z_OK) { rfbClientLog("sendExtClientCutTextProvide: compress cbuf failed\n"); free(buf); free(cbuf); diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index 3fed80e43..5a8b4e726 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -2179,7 +2179,8 @@ rfbProcessExtendedServerCutTextData(rfbClientPtr cl, uint32_t flags, const char } stream.avail_out = size; stream.next_out = (unsigned char *)buf; - if (inflate(&stream, Z_NO_FLUSH) != Z_OK) { + err = inflate(&stream, Z_NO_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { rfbLogPerror("rfbProcessExtendedServerCutTextData: zlib inflation error"); free(buf); inflateEnd(&stream);