/* * Copyright (C) 2002 RealVNC Ltd. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ /* * rfbproto.c - functions to deal with client side of RFB protocol. */ #include #include #include #include "vncviewer.h" #include "rfb/vncauth.h" static Bool HandleRRE8(int rx, int ry, int rw, int rh); static Bool HandleRRE16(int rx, int ry, int rw, int rh); static Bool HandleRRE32(int rx, int ry, int rw, int rh); static Bool HandleCoRRE8(int rx, int ry, int rw, int rh); static Bool HandleCoRRE16(int rx, int ry, int rw, int rh); static Bool HandleCoRRE32(int rx, int ry, int rw, int rh); static Bool HandleHextile8(int rx, int ry, int rw, int rh); static Bool HandleHextile16(int rx, int ry, int rw, int rh); static Bool HandleHextile32(int rx, int ry, int rw, int rh); char *desktopName; rfbPixelFormat myFormat; Bool pendingFormatChange = False; /* * Macro to compare pixel formats. */ #define PF_EQ(x,y) \ ((x.bitsPerPixel == y.bitsPerPixel) && \ (x.depth == y.depth) && \ ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ (x.trueColour == y.trueColour) && \ (!x.trueColour || ((x.redMax == y.redMax) && \ (x.greenMax == y.greenMax) && \ (x.blueMax == y.blueMax) && \ (x.redShift == y.redShift) && \ (x.greenShift == y.greenShift) && \ (x.blueShift == y.blueShift)))) int currentEncoding = rfbEncodingZRLE; Bool pendingEncodingChange = False; int supportedEncodings[] = { rfbEncodingZRLE, rfbEncodingHextile, rfbEncodingCoRRE, rfbEncodingRRE, rfbEncodingRaw }; #define NUM_SUPPORTED_ENCODINGS (sizeof(supportedEncodings)/sizeof(int)) rfbServerInitMsg si; char *serverCutText = NULL; Bool newServerCutText = False; int endianTest = 1; /* note that the CoRRE encoding uses this buffer and assumes it is big enough to hold 255 * 255 * 32 bits -> 260100 bytes. 640*480 = 307200 bytes */ /* also hextile assumes it is big enough to hold 16 * 16 * 32 bits */ #define BUFFER_SIZE (640*480) static char buffer[BUFFER_SIZE]; /* * InitialiseRFBConnection. */ Bool InitialiseRFBConnection() { rfbProtocolVersionMsg pv; int major,minor; CARD32 authScheme, reasonLen, authResult; char *reason; CARD8 challenge[CHALLENGESIZE]; char *passwd; int i; rfbClientInitMsg ci; if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) return False; pv[sz_rfbProtocolVersionMsg] = 0; if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { fprintf(stderr,"Not a valid VNC server\n"); return False; } fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n", major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion); major = rfbProtocolMajorVersion; minor = rfbProtocolMinorVersion; sprintf(pv,rfbProtocolVersionFormat,major,minor); if (!WriteToRFBServer(pv, sz_rfbProtocolVersionMsg)) return False; if (!ReadFromRFBServer((char *)&authScheme, 4)) return False; authScheme = Swap32IfLE(authScheme); switch (authScheme) { case rfbConnFailed: if (!ReadFromRFBServer((char *)&reasonLen, 4)) return False; reasonLen = Swap32IfLE(reasonLen); reason = malloc(reasonLen); if (!ReadFromRFBServer(reason, reasonLen)) return False; fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason); return False; case rfbNoAuth: fprintf(stderr,"No authentication needed\n"); break; case rfbVncAuth: if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) return False; if (appData.passwordFile) { passwd = vncDecryptPasswdFromFile(appData.passwordFile); if (!passwd) { fprintf(stderr,"Cannot read valid password from file \"%s\"\n", appData.passwordFile); return False; } } else if (appData.passwordDialog) { passwd = DoPasswordDialog(); } else { passwd = getpass("Password: "); } if ((!passwd) || (strlen(passwd) == 0)) { fprintf(stderr,"Reading password failed\n"); return False; } if (strlen(passwd) > 8) { passwd[8] = '\0'; } vncEncryptBytes(challenge, passwd); /* Lose the password from memory */ for (i = strlen(passwd); i >= 0; i--) { passwd[i] = '\0'; } if (!WriteToRFBServer((char *)challenge, CHALLENGESIZE)) return False; if (!ReadFromRFBServer((char *)&authResult, 4)) return False; authResult = Swap32IfLE(authResult); switch (authResult) { case rfbVncAuthOK: fprintf(stderr,"VNC authentication succeeded\n"); break; case rfbVncAuthFailed: fprintf(stderr,"VNC authentication failed\n"); return False; case rfbVncAuthTooMany: fprintf(stderr,"VNC authentication failed - too many tries\n"); return False; default: fprintf(stderr,"Unknown VNC authentication result: %d\n", (int)authResult); return False; } break; default: fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n", (int)authScheme); return False; } ci.shared = (appData.shareDesktop ? 1 : 0); if (!WriteToRFBServer((char *)&ci, sz_rfbClientInitMsg)) return False; if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return False; si.framebufferWidth = Swap16IfLE(si.framebufferWidth); si.framebufferHeight = Swap16IfLE(si.framebufferHeight); si.format.redMax = Swap16IfLE(si.format.redMax); si.format.greenMax = Swap16IfLE(si.format.greenMax); si.format.blueMax = Swap16IfLE(si.format.blueMax); si.nameLength = Swap32IfLE(si.nameLength); desktopName = malloc(si.nameLength + 1); if (!ReadFromRFBServer(desktopName, si.nameLength)) return False; desktopName[si.nameLength] = 0; fprintf(stderr,"Desktop name \"%s\"\n",desktopName); fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n", rfbProtocolMajorVersion, rfbProtocolMinorVersion); fprintf(stderr,"VNC server default format:\n"); PrintPixelFormat(&si.format); return True; } Bool SendFramebufferUpdateRequest(int x, int y, int w, int h, Bool incremental) { rfbFramebufferUpdateRequestMsg fur; fur.type = rfbFramebufferUpdateRequest; fur.incremental = incremental ? 1 : 0; fur.x = Swap16IfLE(x); fur.y = Swap16IfLE(y); fur.w = Swap16IfLE(w); fur.h = Swap16IfLE(h); if (!WriteToRFBServer((char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) return False; return True; } Bool SendSetPixelFormat() { rfbSetPixelFormatMsg spf; spf.type = rfbSetPixelFormat; spf.format = myFormat; spf.format.redMax = Swap16IfLE(spf.format.redMax); spf.format.greenMax = Swap16IfLE(spf.format.greenMax); spf.format.blueMax = Swap16IfLE(spf.format.blueMax); return WriteToRFBServer((char *)&spf, sz_rfbSetPixelFormatMsg); } Bool SendSetEncodings() { char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf; CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]); int len = 0; int i; se->type = rfbSetEncodings; se->nEncodings = 0; if (appData.encodingsString) { char *encStr = appData.encodingsString; int encStrLen; do { char *nextEncStr = strchr(encStr, ' '); if (nextEncStr) { encStrLen = nextEncStr - encStr; nextEncStr++; } else { encStrLen = strlen(encStr); } if (strncasecmp(encStr,"raw",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); } else if (strncasecmp(encStr,"corre",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); } else if (strncasecmp(encStr,"rre",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE); } else if (strncasecmp(encStr,"zrle",encStrLen) == 0) { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZRLE); } else { fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr); } encStr = nextEncStr; } while (encStr && se->nEncodings < MAX_ENCODINGS); } else { encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); encs[se->nEncodings++] = Swap32IfLE(currentEncoding); for (i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) { if (supportedEncodings[i] != currentEncoding) encs[se->nEncodings++] = Swap32IfLE(supportedEncodings[i]); } } len = sz_rfbSetEncodingsMsg + se->nEncodings * 4; se->nEncodings = Swap16IfLE(se->nEncodings); return WriteToRFBServer(buf, len); } /* * SendPointerEvent. */ Bool SendPointerEvent(int x, int y, int buttonMask) { rfbPointerEventMsg pe; pe.type = rfbPointerEvent; pe.buttonMask = buttonMask; if (x < 0) x = 0; if (y < 0) y = 0; pe.x = Swap16IfLE(x); pe.y = Swap16IfLE(y); return WriteToRFBServer((char *)&pe, sz_rfbPointerEventMsg); } /* * SendKeyEvent. */ Bool SendKeyEvent(CARD32 key, Bool down) { rfbKeyEventMsg ke; ke.type = rfbKeyEvent; ke.down = down ? 1 : 0; ke.key = Swap32IfLE(key); return WriteToRFBServer((char *)&ke, sz_rfbKeyEventMsg); } /* * SendClientCutText. */ Bool SendClientCutText(char *str, int len) { rfbClientCutTextMsg cct; if (serverCutText) free(serverCutText); serverCutText = NULL; cct.type = rfbClientCutText; cct.length = Swap32IfLE(len); return (WriteToRFBServer((char *)&cct, sz_rfbClientCutTextMsg) && WriteToRFBServer(str, len)); } void AutoSelectFormatAndEncodings() { /* Above 16Mbps (timing for at least a second), same machine, switch to raw Above 10Mbps, switch to hextile Below 4Mbps, switch to ZRLE Above 1Mbps, switch from 8bit */ int kbitsPerSecond = KbitsPerSecond(); if (!appData.encodingsString) { if (kbitsPerSecond > 16000 && sameMachine && TimeWaitedIn100us() >= 10000) { if (currentEncoding != rfbEncodingRaw) { fprintf(stderr,"Throughput %d kbit/s - changing to Raw\n", kbitsPerSecond); currentEncoding = rfbEncodingRaw; pendingEncodingChange = True; } } else if (kbitsPerSecond > 10000) { if (currentEncoding != rfbEncodingHextile) { fprintf(stderr,"Throughput %d kbit/s - changing to Hextile\n", kbitsPerSecond); currentEncoding = rfbEncodingHextile; pendingEncodingChange = True; } } else if (kbitsPerSecond < 4000) { if (currentEncoding != rfbEncodingZRLE) { fprintf(stderr,"Throughput %d kbit/s - changing to ZRLE\n", kbitsPerSecond); currentEncoding = rfbEncodingZRLE; pendingEncodingChange = True; } } } if (kbitsPerSecond > 1000) { if (appData.useBGR233 && vis->class == TrueColor) { /* only makes sense if using a TrueColor visual */ fprintf(stderr,"Throughput %d kbit/s - changing from 8bit\n", kbitsPerSecond); appData.useBGR233 = False; pendingFormatChange = True; } } } int updateNeededX1, updateNeededY1, updateNeededX2, updateNeededY2; Bool updateNeeded = False; Bool updateDefinitelyExpected = False; void UpdateNeeded(int x, int y, int w, int h) { if (!updateNeeded) { updateNeededX1 = x; updateNeededY1 = y; updateNeededX2 = x+w; updateNeededY2 = y+h; updateNeeded = True; } else { if (x < updateNeededX1) updateNeededX1 = x; if (y < updateNeededY1) updateNeededY1 = y; if (x+w > updateNeededX2) updateNeededX2 = x+w; if (y+h > updateNeededY2) updateNeededY2 = y+h; } } Bool CheckUpdateNeeded() { if (updateNeeded && !updateDefinitelyExpected) { if (!SendFramebufferUpdateRequest(updateNeededX1, updateNeededY1, updateNeededX2-updateNeededX1, updateNeededY2-updateNeededY1, False)) return False; updateDefinitelyExpected = True; updateNeeded = False; } return True; } Bool RequestNewUpdate() { updateDefinitelyExpected = False; if (pendingEncodingChange) { pendingEncodingChange = False; if (!SendSetEncodings()) return False; } if (pendingFormatChange) { rfbPixelFormat oldFormat = myFormat; pendingFormatChange = False; SetMyFormatFromVisual(); if (!PF_EQ(oldFormat, myFormat)) { if (!SendSetPixelFormat()) return False; UpdateNeeded(0, 0, si.framebufferWidth, si.framebufferHeight); } } if (updateNeeded) { if (!CheckUpdateNeeded()) return False; } else { if (!SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, si.framebufferHeight, True)) return False; } return True; } /* * HandleRFBServerMessage. */ Bool HandleRFBServerMessage() { rfbServerToClientMsg msg; if (!ReadFromRFBServer((char *)&msg, 1)) return False; switch (msg.type) { case rfbSetColourMapEntries: { int i; CARD16 rgb[3]; XColor xc; if (!ReadFromRFBServer(((char *)&msg) + 1, sz_rfbSetColourMapEntriesMsg - 1)) return False; msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); msg.scme.nColours = Swap16IfLE(msg.scme.nColours); for (i = 0; i < msg.scme.nColours; i++) { if (!ReadFromRFBServer((char *)rgb, 6)) return False; xc.pixel = msg.scme.firstColour + i; xc.red = Swap16IfLE(rgb[0]); xc.green = Swap16IfLE(rgb[1]); xc.blue = Swap16IfLE(rgb[2]); xc.flags = DoRed|DoGreen|DoBlue; XStoreColor(dpy, cmap, &xc); } break; } case rfbFramebufferUpdate: { rfbFramebufferUpdateRectHeader rect; int linesToRead; int bytesPerLine; int i; if (appData.autoDetect) StartTiming(); if (!ReadFromRFBServer(((char *)&msg.fu) + 1, sz_rfbFramebufferUpdateMsg - 1)) return False; msg.fu.nRects = Swap16IfLE(msg.fu.nRects); for (i = 0; i < msg.fu.nRects; i++) { if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader)) return False; rect.r.x = Swap16IfLE(rect.r.x); rect.r.y = Swap16IfLE(rect.r.y); rect.r.w = Swap16IfLE(rect.r.w); rect.r.h = Swap16IfLE(rect.r.h); rect.encoding = Swap32IfLE(rect.encoding); if ((rect.r.x + rect.r.w > si.framebufferWidth) || (rect.r.y + rect.r.h > si.framebufferHeight)) { fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n", rect.r.w, rect.r.h, rect.r.x, rect.r.y); return False; } if ((rect.r.h * rect.r.w) == 0) { fprintf(stderr,"Zero size rect - ignoring\n"); continue; } switch (rect.encoding) { case rfbEncodingRaw: bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8; linesToRead = BUFFER_SIZE / bytesPerLine; while (rect.r.h > 0) { if (linesToRead > rect.r.h) linesToRead = rect.r.h; if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead)) return False; CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w, linesToRead); rect.r.h -= linesToRead; rect.r.y += linesToRead; } break; case rfbEncodingCopyRect: { rfbCopyRect cr; if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect)) return False; cr.srcX = Swap16IfLE(cr.srcX); cr.srcY = Swap16IfLE(cr.srcY); CopyRectangularArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h, rect.r.x, rect.r.y); break; } case rfbEncodingRRE: { switch (myFormat.bitsPerPixel) { case 8: if (!HandleRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 16: if (!HandleRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 32: if (!HandleRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; } break; } case rfbEncodingCoRRE: { switch (myFormat.bitsPerPixel) { case 8: if (!HandleCoRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 16: if (!HandleCoRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 32: if (!HandleCoRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; } break; } case rfbEncodingHextile: { switch (myFormat.bitsPerPixel) { case 8: if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 16: if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; case 32: if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; } break; } case rfbEncodingZRLE: if (!zrleDecode(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) return False; break; default: fprintf(stderr,"Unknown rect encoding %d\n", (int)rect.encoding); return False; } } if (appData.autoDetect) { StopTiming(); AutoSelectFormatAndEncodings(); } #ifdef MITSHM /* if using shared memory PutImage, make sure that the X server has updated its framebuffer before we reuse the shared memory. This is mainly to avoid copyrect using invalid screen contents - not sure if we'd need it otherwise. */ if (appData.useShm) XSync(dpy, False); #endif if (!RequestNewUpdate()) return False; break; } case rfbBell: XBell(dpy,100); break; case rfbServerCutText: { if (!ReadFromRFBServer(((char *)&msg) + 1, sz_rfbServerCutTextMsg - 1)) return False; msg.sct.length = Swap32IfLE(msg.sct.length); if (serverCutText) free(serverCutText); serverCutText = malloc(msg.sct.length+1); if (!ReadFromRFBServer(serverCutText, msg.sct.length)) return False; serverCutText[msg.sct.length] = 0; newServerCutText = True; break; } default: fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); return False; } return True; } #define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++) #define GET_PIXEL16(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ ((CARD8*)&(pix))[1] = *(ptr)++) #define GET_PIXEL32(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ ((CARD8*)&(pix))[1] = *(ptr)++, \ ((CARD8*)&(pix))[2] = *(ptr)++, \ ((CARD8*)&(pix))[3] = *(ptr)++) /* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also expands its arguments if they are macros */ #define CONCAT2(a,b) a##b #define CONCAT2E(a,b) CONCAT2(a,b) #define BPP 8 #include "rre.c" #include "corre.c" #include "hextile.c" #undef BPP #define BPP 16 #include "rre.c" #include "corre.c" #include "hextile.c" #undef BPP #define BPP 32 #include "rre.c" #include "corre.c" #include "hextile.c" #undef BPP /* * PrintPixelFormat. */ void PrintPixelFormat(format) rfbPixelFormat *format; { if (format->bitsPerPixel == 1) { fprintf(stderr," Single bit per pixel.\n"); fprintf(stderr, " %s significant bit in each byte is leftmost on the screen.\n", (format->bigEndian ? "Most" : "Least")); } else { fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel); if (format->bitsPerPixel != 8) { fprintf(stderr," %s significant byte first in each pixel.\n", (format->bigEndian ? "Most" : "Least")); } if (format->trueColour) { fprintf(stderr," True colour: max red %d green %d blue %d", format->redMax, format->greenMax, format->blueMax); fprintf(stderr,", shift red %d green %d blue %d\n", format->redShift, format->greenShift, format->blueShift); } else { fprintf(stderr," Colour map (not true colour).\n"); } } }