UltraVNC icon indicating copy to clipboard operation
UltraVNC copied to clipboard

odd responses to FramebufferUpdateRequest

Open mbey-mw opened this issue 2 years ago • 7 comments

The following behaviour was observed when communicating with an UltraVNC server 1.3.8.1 x64 on a Windows 10 Pro.

I'm developing a very slim and specialized VNC client, which does not process video data. Still I want to receive DesktopSize changes. As a result the client requests framebuffer updates with an empty area, which should result in only pseudo-encoded rectangles (see rfbproto 7.4.3). Most tested VNC servers do not send framebuffer data, one only a vew very small rectangles (64x64) - not perfect but ok.

Sending a FramebufferUpdateRequest with incremental set to zero (false) to UltraVNC server results in a full framebuffer update ignoring the requested area. It makes no difference if an empty area, a small rectangle (0, 0, 10, 10) or only half the framebuffer is requested. Because my VNC client is very slim it only supports raw encoding und will receive megabytes of not requested framebuffer data. It would be great to have this somehow corrected.

The more critical behaviour results from sending a FramebufferUpdateRequest with incremental set to one (true) and an empty area (0, 0, 0, 0). The UltraVNC server just closes the connection. In Wireshark I can see a packet flagged FIN and ACK as a reply to the FramebufferUpdateRequest (hex 03 01 00 00 00 00 00 00 00 00). Expected behaviour would be a framebuffer update containing only pseudo-encoded rectangles.

mbey-mw avatar Sep 13 '22 09:09 mbey-mw

This is indeed how it's implemented. if (update_rgn.is_empty()) return false; //disconnect the viewer non incremental is a full update

You can change the behaviour if you change the code like this.

if (!fur.incremental) { if (m_use_ExtDesktopSize && m_firstExtDesktop) { m_NewSWUpdateWaiting = true; } -update.tl.x = (m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale; -update.tl.y = (m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale; -update.br.x = update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale; -update.br.y = update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale; +update.tl.x = (Swap16IfLE(fur.x) + monitor_Offsetx) * m_nScale; +update.tl.y = (Swap16IfLE(fur.y) + monitor_Offsety) * m_nScale; +update.br.x = update.tl.x + Swap16IfLE(fur.w) * m_nScale; +update.br.y = update.tl.y + Swap16IfLE(fur.h) * m_nScale; +// Verify max size, scaled changed on server while not pushed to viewer +if (update.tl.x < (int)((m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale)) + update.tl.x = (m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale; +if (update.tl.y < (int)((m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale)) + update.tl.y = (m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale; +if (update.br.x > (int)(update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale)) + update.br.x = update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale; +if (update.br.y > (int)(update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale)) + update.br.y = update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale; update_rgn = update; if (update_rgn.is_empty()) -return false; +return TRUE; m_update_tracker.add_changed(update_rgn); m_encodemgr.m_buffer->m_desktop->UpdateFullScreen(); } else { if (m_firstExtDesktopIncremental) { //The first full was used for the extDesktopSize, we send it now update.tl.x = (m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale; update.tl.y = (m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale; update.br.x = update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale; update.br.y = update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale; update_rgn = update; if (update_rgn.is_empty()) -return false; +return TRUE; m_update_tracker.add_changed(update_rgn); m_encodemgr.m_buffer->m_desktop->UpdateFullScreen(); m_firstExtDesktopIncremental = false; } else { update.tl.x = (Swap16IfLE(fur.x) + monitor_Offsetx) * m_nScale; update.tl.y = (Swap16IfLE(fur.y) + monitor_Offsety) * m_nScale; update.br.x = update.tl.x + Swap16IfLE(fur.w) * m_nScale; update.br.y = update.tl.y + Swap16IfLE(fur.h) * m_nScale; // Verify max size, scaled changed on server while not pushed to viewer if (update.tl.x < (int)((m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale)) update.tl.x = (m_ScaledScreen.tl.x + monitor_Offsetx) * m_nScale; if (update.tl.y < (int)((m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale)) update.tl.y = (m_ScaledScreen.tl.y + monitor_Offsety) * m_nScale; if (update.br.x > (int)(update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale)) update.br.x = update.tl.x + (m_ScaledScreen.br.x - m_ScaledScreen.tl.x) * m_nScale; if (update.br.y > (int)(update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale)) update.br.y = update.tl.y + (m_ScaledScreen.br.y - m_ScaledScreen.tl.y) * m_nScale; update_rgn = update; if (update_rgn.is_empty()) -return false; +return TRUE; } } #ifdef _DEBUG

RudiDeVos avatar Sep 15 '22 18:09 RudiDeVos

@Neustradamus: I have seen it, but have not tried it yet. At the moment, the slim VNC client supports a switch to disable the use of pseudo encodings. This works in the known application scenarios, but not in all theoretically possible configurations. Priority is currently low due to this, would still consider it a bug in the implementation of the RFB protocol.

mbey-mw avatar Jan 04 '23 09:01 mbey-mw

@mbey-mw: Have you progressed on this ticket?

Neustradamus avatar Jun 26 '23 18:06 Neustradamus

@mbey-mw: It is possible to have some news from you?

Neustradamus avatar Sep 13 '23 19:09 Neustradamus

@mbey-mw: Have you progressed on this ticket?

Neustradamus avatar May 06 '24 21:05 Neustradamus

@mbey-mw: It is possible to have a comeback from you?

Thanks in advance.

Neustradamus avatar Jun 01 '24 15:06 Neustradamus