/* * RFB ("VNC") client. * * Usage: $0 [options] host[/port] * * The only unusual option here is -vnc, which specifies the host (and * optionally port) unambiguously. The port defaults to 5900 if not * specified. * * This requires a TrueColor or PseudoColor visual, at least 8 bits * deep. Unless overridden with -visual, it uses the default visual * if it's suitable; otherwise, it prefers TrueColor over PseudoColor * and, within a given class, greater depth over lesser. * * This sets a 3x3 pointer cursor with a single foreground pixel * centred in a background field, on the theory that the server will * probably be rendering a more ordinary pointer cursor, but we don't * want a completely invisible pointer cursor lest a network lossage * make it appear to lock up entirely. * * Currently handles nothing but raw encoding; we really should add * others. At least one other client lists CopyRect, Hextile, CoRRE, * and RRE (in that order of preference, more-preferred first). * (CoRRE appears to be just RRE with 8-bit subrect coordinates.) * * Also doesn't handle cut text in either direction. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "verbose.h" #include "rfbproto.h" extern const char *__progname; static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *Title: XVNC\n\ *IconName: XVNC\n\ *UpdateUsec: 5000000\n\ "; typedef enum { CP1 = 1, CP2, CP3H, CP3L, CP4 } CPIXELTYPE; typedef struct s_m S_M; typedef struct pixenc PIXENC; typedef struct listener LISTENER; struct listener { LISTENER *link; char *arg; char *str; int fd; int id; } ; struct pixenc { const char *name; unsigned long int value; int namelen; } ; struct s_m { int s; unsigned long int m; } ; static char *vncserver; static char *displayname; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *visualstr; static char *title; static char *iconname; static char *uustr; static char *rfblogfile; static char *klogfile; static int dosync = 0; static int dolisten = 0; static const char *resname = "xvnc"; static const char *resclass = "XVNC"; static char *xrmstr = 0; static int argc; static char **argv; Display *disp; static Screen *scr; static int scrno; static Window rootwin; static int width; static int height; static int depth; static Visual *visual; static XVisualInfo *visinfo; static int defvis; static int tcvis; static Colormap wincmap; static Colormap bordercmap; static int defcmap; static int (*old_err)(Display *, XErrorEvent *); static int (*old_ioerr)(Display *); static XColor fgcolour; static XColor bgcolour; static XColor bdcolour; static Window topwin; static int topw; static int toph; static int borderwidth; static Window mainwin; static Pixmap shadow; static GC wingc; static Cursor cursor; unsigned int verbose; static int xrid; static int xfid; static int vncfd; static int vncid; static struct addrinfo *vncai0; static struct addrinfo *vncai; static char vnchost[NI_MAXHOST]; static char vncserv[NI_MAXSERV]; static AIO_OQ vncoq; static unsigned char *vncib = 0; static unsigned char *vncib0; static int vnciba = 0; static int vncibl; static int (*vnc_step)(void); static int bpp; static int bpcp; static CPIXELTYPE cpt; static int fbw; static int fbh; static XImage *img; static char *imgdata; static int nrect; static unsigned int rectx; static unsigned int recty; static unsigned int rectw; static unsigned int recth; static int rectul; static int aty0; static int atx; static int aty; static int rectcount; static unsigned int tilebg; static unsigned int tilefg; static unsigned long int trle_palette[128]; static int trle_palsiz; static int tilex; static int tiley; static int tilew; static int tileh; static int vpfd; static int vpid; static PIXENC pixencs[] = { { "CopyRect", RFB_ENCODING_COPY_RECT }, { "TRLE", RFB_ENCODING_TRLE }, //{ "ZRLE", RFB_ENCODING_ZRLE }, { "Hextile", RFB_ENCODING_HEXTILE }, { "CoRRE", RFB_ENCODING_CORRE }, { "RRE", RFB_ENCODING_RRE }, { "Raw", RFB_ENCODING_RAW }, { 0 } }; static unsigned long int *enc = 0; static int n_enc = -1; static struct timeval update_tv; static LISTENER *listeners; static FILE *rfblog; static FILE *klog; static int pending_update; static int updid; static unsigned int kmods; static void *dequal(const volatile void *s) { return((((const volatile char *)s)-(const volatile char *)0)+(char *)0); } static unsigned short int get2be(const unsigned char *d) { return( (d[0] * 0x0100) + d[1] ); } static unsigned long int get3be(const unsigned char *d) { return( (d[0] * 0x010000UL) + (d[1] * 0x000100UL) + d[2] ); } static unsigned long int get4be(const unsigned char *d) { return( (d[0] * 0x01000000UL) + (d[1] * 0x00010000UL) + (d[2] * 0x00000100UL) + d[3] ); } static void put2be(unsigned char *d, unsigned short int v) { d[0] = (v >> 8) & 0xff; d[1] = v & 0xff; } static void put4be(unsigned char *d, unsigned long int v) { d[0] = (v >> 24) & 0xff; d[1] = (v >> 16) & 0xff; d[2] = (v >> 8) & 0xff; d[3] = v & 0xff; } static unsigned long int getpixel(unsigned char *d) { switch (bpp) { case 1: return(*d); break; case 2: return(get2be(d)); break; case 4: return(get4be(d)); break; } abort(); } static unsigned long int getcpixel(unsigned char *d) { switch (cpt) { case CP1: return(*d); break; case CP2: return(get2be(d)); break; case CP3H: return(get3be(d)<<8); break; case CP3L: return(get3be(d)); break; case CP4: return(get4be(d)); break; } abort(); } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = (char **) malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i ml) ml = l; } for (i=0;verbosities[i].tag;i++) { printf("%-*s %s\n",ml,verbosities[i].tag,verbosities[i].help); } return(1); } for (i=0;verbosities[i].tag;i++) { if (!strcmp(s,verbosities[i].tag)) { verbose |= 1U << i; printf("Verbose now %#x =",verbose); for (i=0;verbosities[i].tag;i++) { if (verbose & (1U << i)) printf(" %s",verbosities[i].tag); } printf("\n"); return(0); } } fprintf(stderr,"%s: unrecognized verbosity `%s'\n",__progname,s); fprintf(stderr,"%s: use `-v list' for a list\n",__progname); return(1); } static void default_encs(void) { int i; if (n_enc >= 0) return; for (i=0;pixencs[i].name;i++) ; n_enc = i; enc = malloc(sizeof(n_enc*sizeof(*enc))); for (i=0;pixencs[i].name;i++) enc[i] = i; if (i != n_enc) abort(); } static int set_encoding(const char *s) { int i; int j; char *comma; int l; char op; int errs; errs = 0; default_encs(); if (pixencs[0].namelen == 0) for (i=0;pixencs[i].name;i++) pixencs[i].namelen = strlen(pixencs[i].name); op = '='; if (*s == '-') { op = '-'; s ++; } else if (*s == '+') { op = '-'; s ++; } if (op == '=') { n_enc = 0; op = '+'; } while (1) { comma = index(s,','); l = comma ? comma - s : strlen(s); do <"found"> { for (i=0;pixencs[i].name;i++) { if ((pixencs[i].namelen == l) && !strncasecmp(s,pixencs[i].name,l)) { switch (op) { case '+': do <"found"> { for (j=n_enc-1;j>=0;j--) if (enc[j] == i) break <"found">; enc[n_enc++] = i; } while (0); break; case '-': for (j=n_enc-1;j>=0;j--) { if (enc[j] == i) { n_enc --; if (j != n_enc) bcopy(enc+j+1,enc+j,(n_enc-j)*sizeof(*enc)); break; } } break; } break <"found">; } } fprintf(stderr,"%s: unrecognized encoding name `%.*s'\n",__progname,l,s); errs = 1; } while (0); if (comma) s = comma+1; else return(errs); } } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { if (! vncserver) { vncserver = *av; } else { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; } continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); foreground = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); background = av[skip]; continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bc")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-resname")) { WANTARG(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); resclass = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&xrmstr,"\n", av[skip], (char *)0); continue; } if (!strcmp(*av,"-title")) { WANTARG(); title = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-sync")) { dosync = 1; continue; } if (!strcmp(*av,"-listen")) { dolisten = 1; continue; } if (!strcmp(*av,"-vnc")) { WANTARG(); vncserver = av[skip]; continue; } if (!strcmp(*av,"-enc")) { WANTARG(); errs |= set_encoding(av[skip]); continue; } if (!strcmp(*av,"-updus")) { WANTARG(); uustr = av[skip]; continue; } if (!strcmp(*av,"-rfblog")) { WANTARG(); rfblogfile = av[skip]; continue; } if (!strcmp(*av,"-klog")) { WANTARG(); klogfile = av[skip]; continue; } if (!strcmp(*av,"-v")) { WANTARG(); errs |= setverbose(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (! vncserver) { fprintf(stderr,"%s: need a vnc server address\n",__progname); errs = 1; } if (n_enc == 0) { fprintf(stderr,"%s: all pixel encodings disabled!\n",__progname); errs = 1; } if (errs) exit(1); } static Display *open_display(const char *disp) { Display *rv; rv = XOpenDisplay(disp); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(disp)); exit(1); } return(rv); } static int handler_err(Display *d, XErrorEvent *ee) { return((*old_err)(d,ee)); } static int handler_ioerr(Display *d) { return((*old_ioerr)(d)); } static void setup_errhand(void) { old_err = XSetErrorHandler(handler_err); old_ioerr = XSetIOErrorHandler(handler_ioerr); } /* * We can use any PseudoColor or TrueColor visual of 8bpp or deeper We * prefer TrueColor, with deeper preferred, then PseudoColor, with * deeper preferred - but, if the default visual is acceptable, it is * preferable to anything else. And visuals deeper than 32 bits are * never acceptable under any circumstances (can they happen?). * * If b is nil, this returns true if a is acceptable at all, false * otherwise; if b is non-nil, this returns true if a is preferred to * b, false if b is preferred or if there is no clear preference * between them. (In particular, if a is not acceptable, it returns * false regardless of b.) */ static int preferred_visual(XVisualInfo *a, XVisualInfo *b) { int acceptable_visual(XVisualInfo *v) { if (v->depth > 32) return(0); switch (v->class) { case StaticGray: case GrayScale: case StaticColor: case DirectColor: return(0); break; case PseudoColor: case TrueColor: if (v->depth < 8) return(0); break; } return(1); } if (! acceptable_visual(a)) return(0); if (!b || !acceptable_visual(b)) return(1); if (a->visual == visual) return(1); if (b->visual == visual) return(0); if (a->class != b->class) return(a->class==TrueColor); if (a->depth > b->depth) return(1); return(0); } static void setup_visual(void) { XVisualInfo template; unsigned long int template_mask; unsigned long int id; XVisualInfo *xvi; int nvi; char *ep; int besti; XVisualInfo *bestv; int i; visual = XDefaultVisualOfScreen(scr); template.screen = scrno; template_mask = VisualScreenMask; if (visualstr) { template_mask |= VisualClassMask; if (!strcasecmp(visualstr,"staticgray")) template.class = StaticGray; else if (!strcasecmp(visualstr,"grayscale")) template.class = GrayScale; else if (!strcasecmp(visualstr,"staticcolor")) template.class = StaticColor; else if (!strcasecmp(visualstr,"pseudocolor")) template.class = PseudoColor; else if (!strcasecmp(visualstr,"directcolor")) template.class = DirectColor; else if (!strcasecmp(visualstr,"truecolor")) template.class = TrueColor; else { id = strtoul(visualstr,&ep,0); if (*ep) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; template_mask = (template_mask & ~VisualClassMask) | VisualIDMask; } } xvi = XGetVisualInfo(disp,template_mask,&template,&nvi); if (xvi == 0) { if (visualstr) { fprintf(stderr,"%s: %s: no matching visual found\n",__progname,visualstr); } else { fprintf(stderr,"%s: no visuals on this screen?!\n",__progname); } exit(1); } if (nvi == 0) { fprintf(stderr,"%s: XGetVisualInfo returned non-nil but zero count?!\n",__progname); exit(1); } besti = -1; bestv = 0; for (i=nvi-1;i>=0;i--) { if (preferred_visual(&xvi[i],bestv)) { besti = i; bestv = &xvi[i]; } } defvis = (bestv->visual == visual); tcvis = (bestv->class == TrueColor); visual = bestv->visual; depth = bestv->depth; visinfo = bestv; } static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 5) str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #endif if (xrmstr) { db2 = XrmGetStringDatabase(xrmstr); XrmMergeDatabases(db2,&db); } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; int nl; int cl; static int buflen = -1; static int rnlen = 0; static int rclen = 0; static char *nbuf; static char *cbuf; nl = strlen(name); cl = strlen(class); if ((nl+1+rnlen >= buflen) || (cl+1+rclen >= buflen)) { if (buflen < 0) { rnlen = strlen(resname); rclen = strlen(resclass); buflen = 10; nbuf = malloc(buflen); cbuf = malloc(buflen); } if (buflen <= nl+1+rnlen) buflen = nl+1+rnlen + 1; if (buflen <= cl+1+rclen) buflen = cl+1+rclen + 1; nbuf = realloc(nbuf,buflen); cbuf = realloc(cbuf,buflen); } sprintf(nbuf,"%s.%s",resname,name); sprintf(cbuf,"%s.%s",resclass,class); if (XrmGetResource(db,nbuf,cbuf,&type,&value) == False) return(0); return(value.addr); } static void setup_colour(char *str, XColor *col) { if (XParseColor(disp,bordercmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } while (1) { if (XAllocColor(disp,bordercmap,col)) break; if (! defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for colour `%s'\n",__progname,str); exit(1); } bordercmap = XCopyColormapAndFree(disp,bordercmap); defcmap = 0; } } static void setup_colours(void) { if (defvis) { if (tcvis) { wincmap = XDefaultColormapOfScreen(scr); } else { wincmap = XCreateColormap(disp,rootwin,visual,AllocAll); } } else { if (tcvis) { wincmap = XCreateColormap(disp,rootwin,visual,AllocNone); } else { wincmap = XCreateColormap(disp,rootwin,visual,AllocAll); } } defcmap = defvis; if (defvis) { bordercmap = XDefaultColormapOfScreen(scr); } else if (tcvis) { bordercmap = wincmap; } else { bordercmap = XCreateColormap(disp,rootwin,visual,AllocNone); } setup_colour(foreground,&fgcolour); setup_colour(background,&bgcolour); setup_colour(bordercstr,&bdcolour); } static void setup_numbers(void) { unsigned long int us; char *ep; if (borderwstr) borderwidth = atoi(borderwstr); us = strtoul(uustr,&ep,0); if (*ep || (ep == uustr)) { fprintf(stderr,"%s: %s: invalid microsecond count\n",__progname,uustr); exit(1); } if (us > 60000000) { fprintf(stderr,"%s: warning: very slow updates\n",__progname); } update_tv.tv_sec = us / 1000000; update_tv.tv_usec = us % 1000000; } static void setup_cursor(void) { Pixmap pm; GC gc; pm = XCreatePixmap(disp,rootwin,3,3,1); gc = XCreateGC(disp,pm,0,0); XSetForeground(disp,gc,0); XDrawPoint(disp,pm,gc,0,0); XDrawPoint(disp,pm,gc,0,1); XDrawPoint(disp,pm,gc,0,2); XDrawPoint(disp,pm,gc,1,0); XDrawPoint(disp,pm,gc,1,2); XDrawPoint(disp,pm,gc,2,0); XDrawPoint(disp,pm,gc,2,1); XDrawPoint(disp,pm,gc,2,2); XSetForeground(disp,gc,1); XDrawPoint(disp,pm,gc,1,1); cursor = XCreatePixmapCursor(disp,pm,None,&fgcolour,&bgcolour,1,1); XFreeGC(disp,gc); XFreePixmap(disp,pm); } static void setup_windows(void) { int x; int y; int w; int h; unsigned long int attrmask; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; topw = 100; toph = 100; w = topw + (2 * borderwidth); h = toph + (2 * borderwidth); x = (width - w) / 2; y = (height - h) / 2; attrmask = 0; attr.background_pixel = bdcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolour.pixel; attrmask |= CWBorderPixel; attr.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; attr.cursor = cursor; attrmask |= CWCursor; w -= 2 * borderwidth; h -= 2 * borderwidth; topwin = XCreateWindow(disp,rootwin,0,0,w,h,borderwidth,depth,InputOutput,visual,attrmask,&attr); wn_prop.value = (unsigned char *) title; wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) iconname; in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); normal_hints.flags = PSize | PMinSize | PResizeInc; normal_hints.width = w; normal_hints.height = h; normal_hints.min_width = 1; normal_hints.min_height = 1; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = True; class_hints.res_name = dequal(resname); class_hints.res_class = dequal(resclass); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); attrmask = 0; attr.background_pixmap = None; attrmask |= CWBackPixmap; attr.event_mask = ExposureMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; attrmask |= CWEventMask; mainwin = XCreateWindow(disp,topwin,0,0,topw,toph,0,depth,InputOutput,CopyFromParent,attrmask,&attr); wingc = XCreateGC(disp,mainwin,0,0); } static void send_update_request(int x, int y, int w, int h, int incr) { unsigned char framebuffer_update_request[10]; framebuffer_update_request[0] = RFBC_FRAMEBUFFER_UPDATE_REQUEST; framebuffer_update_request[1] = incr ? 1 : 0; put2be(&framebuffer_update_request[2],x); put2be(&framebuffer_update_request[4],y); put2be(&framebuffer_update_request[6],w); put2be(&framebuffer_update_request[8],h); aio_oq_queue_copy(&vncoq,&framebuffer_update_request[0],10); } static void do_expose(Window win, int x, int y, int w, int h, int count) { if (win == mainwin) { if (VERB(XEVENTS)) printf("mainwin expose: %dx%d+%d+%d count=%d\n",w,h,x,y,count); XCopyArea(disp,shadow,mainwin,wingc,x,y,w,h,x,y); } } static void resize(Window win, int neww, int newh) { if (win == topwin) { if ((neww != topw) || (newh != toph)) { if (VERB(XEVENTS)) printf("resize to %dx%d\n",neww,newh); topw = neww; toph = newh; } } } static void keyevent(Window w, int bs, unsigned int kc, int down) { unsigned char key_event[8]; KeySym ks; if (w != topwin) return; if (VERB(XEVENTS)) printf("Key event: state=%#x kc=%u down=%d\n",bs,kc,down); ks = XKeycodeToKeysym(disp,kc,(bs&ShiftMask)?1:0); switch (ks) { case XK_Shift_L: case XK_Shift_R: case XK_Control_L: case XK_Control_R: if (VERB(XEVENTS)) printf("...eliding\n"); break; } if ((bs ^ kmods) & (ShiftMask | ControlMask)) { if ((bs ^ kmods) & ShiftMask) { key_event[0] = RFBC_KEY_EVENT; key_event[1] = (bs & ShiftMask) ? 1 : 0; key_event[2] = 0; key_event[3] = 0; put4be(&key_event[4],XK_Shift_L); aio_oq_queue_copy(&vncoq,&key_event[0],8); if (VERB(XEVENTS)) printf("Synthesizing KEY_EVENT, down=%d ks=%08x\n",(bs&ShiftMask)?1:0,(unsigned int)XK_Shift_L); } if ((bs ^ kmods) & ControlMask) { key_event[0] = RFBC_KEY_EVENT; key_event[1] = (bs & ControlMask) ? 1 : 0; key_event[2] = 0; key_event[3] = 0; put4be(&key_event[4],XK_Control_L); aio_oq_queue_copy(&vncoq,&key_event[0],8); if (VERB(XEVENTS)) printf("Synthesizing KEY_EVENT, down=%d ks=%08x\n",(bs&ControlMask)?1:0,(unsigned int)XK_Control_L); } kmods = bs & (ShiftMask | ControlMask); } key_event[0] = RFBC_KEY_EVENT; key_event[1] = down ? 1 : 0; key_event[2] = 0; key_event[3] = 0; put4be(&key_event[4],ks); aio_oq_queue_copy(&vncoq,&key_event[0],8); if (klog) { const char *kss; kss = XKeysymToString(ks); if (kss) { fprintf(klog,"Keystroke: %08x (%s) %s\n",(unsigned int)ks,kss,down?"down":"up"); } else { fprintf(klog,"Keystroke: %08x (unknown) %s\n",(unsigned int)ks,down?"down":"up"); } } } static void send_pointer_event(unsigned int bs, int x, int y) { unsigned char pointer_event[6]; pointer_event[0] = RFBC_POINTER_EVENT; pointer_event[1] = ((bs & Button1Mask) ? 1 : 0) | ((bs & Button2Mask) ? 2 : 0) | ((bs & Button3Mask) ? 4 : 0); if (x >= fbw) x = fbw-1; else if (x < 0) x = 0; if (y >= fbh) y = fbh-1; else if (y < 0) y = 0; put2be(&pointer_event[2],x); put2be(&pointer_event[4],y); aio_oq_queue_copy(&vncoq,&pointer_event[0],6); } static void buttonevent(Window w, int x, int y, int bs, int b, int down) { unsigned int bb; if (w != mainwin) return; if (VERB(XEVENTS)) printf("Button event: x=%d y=%d state=%#x b=%d down=%d",x,y,bs,b,down); switch (b) { case Button1: bb = Button1Mask; break; case Button2: bb = Button2Mask; break; case Button3: bb = Button3Mask; break; default: return; break; } if (down) bs |= bb; else bs &= ~bb; if (VERB(XEVENTS)) printf(" (updated state %#x)\n",bs); send_pointer_event(bs,x,y); } static void motionevent(Window w, int x, int y, int bs) { if (w != mainwin) return; if (VERB(XEVENTS)) printf("Motion event: x=%d y=%d state=%#x\n",x,y,bs); send_pointer_event(bs,x,y); } static void enterleave(Window w, int x, int y, int enter) { if (w != mainwin) return; if (VERB(XEVENTS)) printf("%s event: x=%d y=%d\n",enter?"Enter":"Leave",x,y); } static void handle_event(XEvent *e) { switch (e->type) { case Expose: do_expose(e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; case ConfigureNotify: resize(e->xconfigure.window,e->xconfigure.width,e->xconfigure.height); break; case KeyPress: keyevent(e->xkey.window,e->xkey.state,e->xkey.keycode,1); break; case KeyRelease: keyevent(e->xkey.window,e->xkey.state,e->xkey.keycode,0); break; case ButtonPress: buttonevent(e->xbutton.window,e->xbutton.x,e->xbutton.y,e->xbutton.state,e->xbutton.button,1); break; case ButtonRelease: buttonevent(e->xbutton.window,e->xbutton.x,e->xbutton.y,e->xbutton.state,e->xbutton.button,0); break; case MotionNotify: motionevent(e->xmotion.window,e->xmotion.x,e->xmotion.y,e->xmotion.state); break; case EnterNotify: enterleave(e->xcrossing.window,e->xcrossing.x,e->xcrossing.y,1); break; case LeaveNotify: enterleave(e->xcrossing.window,e->xcrossing.x,e->xcrossing.y,0); break; } } static int x_events(void) { int any; any = 0; while (XEventsQueued(disp,QueuedAfterFlush)) { XEvent e; XNextEvent(disp,&e); handle_event(&e); any = 1; } return(any); } static void rd_x(void *arg __attribute__((__unused__))) { x_events(); } static int flush_x(void *arg __attribute__((__unused__))) { return(x_events()?AIO_BLOCK_LOOP:AIO_BLOCK_NIL); } static void setup_aio(void) { xrid = aio_add_poll(XConnectionNumber(disp),&aio_rwtest_always,&aio_rwtest_never,&rd_x,0,0); xfid = aio_add_block(&flush_x,0); } static char *blk_to_str(const void *data, int len) { char *s; s = malloc(len+1); bcopy(data,s,len); s[len] = '\0'; return(s); } static int vnc_counted_msg_fail(const char *why) { unsigned long int len; if (vncibl < 4) return(0); len = get4be(&vncib[0]); if (len > 65536) { fprintf(stderr,"%s: %s/%s: %s, reason ludicrously long (%lu)\n",__progname,&vnchost[0],&vncserv[0],why,len); exit(1); } if (vncibl < len+4) return(0); fprintf(stderr,"%s: %s/%s: %s: %.*s\n",__progname,&vnchost[0],&vncserv[0],why,(int)len,vncib+4); exit(1); } static int vnc_badvers(void) { return(vnc_counted_msg_fail("server rejected version")); } static int vnc_sec_fail(void) { return(vnc_counted_msg_fail("security exchange failed")); } static void dump_pixel_format(const unsigned char *pf) { printf("",pf[13],pf[14],pf[15]); } static S_M find_s_m(unsigned long int m) { S_M r; r.s = 0; if (! (m & 0x0000ffffUL)) { m >>= 16; r.s += 16; } if (! (m & 0x000000ffUL)) { m >>= 8; r.s += 8; } if (! (m & 0x0000000fUL)) { m >>= 4; r.s += 4; } if (! (m & 0x00000003UL)) { m >>= 2; r.s += 2; } if (! (m & 0x00000001UL)) { m >>= 1; r.s ++; } if (m & ~0xffffUL) abort(); r.m = m; return(r); } static void resize_fb(int neww, int newh) { char *newidata; XImage *newi; Pixmap newsh; int minw; int minh; int i; int j; newidata = malloc(neww*newh*bpp); newi = XCreateImage(disp,visual,depth,ZPixmap,0,newidata,neww,newh,bpp*8,neww*bpp); newsh = XCreatePixmap(disp,rootwin,neww,newh,depth); XResizeWindow(disp,topwin,neww,newh); XResizeWindow(disp,mainwin,neww,newh); minw = (neww < fbw) ? neww : fbw; minh = (newh < fbh) ? newh : fbh; XCopyArea(disp,shadow,newsh,wingc,0,0,minw,minh,0,0); for (j=minh-1;j>=0;j--) for (i=minw-1;i>=0;i--) XPutPixel(newi,i,j,XGetPixel(img,i,j)); XDestroyImage(img); imgdata = newidata; img = newi; fbw = neww; fbh = newh; XFreePixmap(disp,shadow); shadow = newsh; if (neww > minw) send_update_request(minw,0,neww-minw,minh,0); if (newh > minh) send_update_request(0,minh,neww,newh-minh,0); } static void rfb_data(const char *tag, const void *data, int len) { int o; int w; if (!rfblog || (len < 1)) return; if (len <= 0x10) w = 1; else if (len <= 0x100) w = 2; else if (len <= 0x1000) w = 3; else if (len <= 0x10000) w = 4; else if (len <= 0x100000) w = 5; else if (len <= 0x1000000) w = 6; else if (len <= 0x10000000) w = 7; else w = 8; for (o=0;o= rectw) { atx = 0; aty += tilesize; if (aty >= recth) { return(1); } } return(0); } static int tile_inc(void) { tilex ++; if (tilex >= tilew) { tilex = 0; tiley ++; if (tiley >= tileh) { return(1); } } return(0); } static void end_rect(void) { nrect --; if (nrect == 0) vnc_step = &vnc_running; else vnc_step = &vnc_framebuffer_update_recthdr; } static void clipped_put_pixel(int x, int y, unsigned long int pix) { if ((x < 0) || (y < 0) || (x >= fbw) || (y >= fbh)) return; XPutPixel(img,x,y,pix); } static int rect_raw_pixels(void) { int npix; int i; int o; int done; if (vncibl < bpp) return(0); npix = vncibl / bpp; o = 0; for (i=npix;i>0;i--) { clipped_put_pixel(rectx+atx,recty+aty,getpixel(&vncib[o])); o += bpp; if (aty0 < 0) aty0 = aty; done = rect_inc(1); if (atx == 0) { if ((aty >= aty0+rectul) || done) { XPutImage(disp,shadow,wingc,img,rectx,recty+aty0,rectx,recty+aty0,rectw,aty-aty0); XCopyArea(disp,shadow,mainwin,wingc,rectx,recty+aty0,rectw,aty-aty0,rectx,recty+aty0); aty0 = -1; } } if (done) { end_rect(); break; } } return(log_consumed(o,"raw pixels")); } static int do_rect_raw(void) { if (VERB(RFB) || VERB(RAW)) printf(" rect (%d): %ux%u+%u+%u encoding=Raw\n",nrect,rectw,recth,rectx,recty); rectul = (1024 + rectw - 1) / rectw; if (rectul < 1) rectul = 1; aty0 = -1; atx = 0; aty = 0; vnc_step = &rect_raw_pixels; return(log_consumed(12,"Raw rect (%d) %ux%u at %u,%u",nrect,rectw,recth,rectx,recty)); } static int do_rect_copy_rect(void) { unsigned int fx; unsigned int fy; if (vncibl < 16) return(0); fx = get2be(&vncib[12]); fy = get2be(&vncib[14]); if (VERB(RFB) || VERB(COPYRECT)) printf(" rect (%d): %ux%u+%u+%u encoding=CopyRect from %u,%u\n",nrect,rectw,recth,rectx,recty,fx,fy); XCopyArea(disp,shadow,shadow,wingc,fx,fy,rectw,recth,rectx,recty); XCopyArea(disp,shadow,mainwin,wingc,rectx,recty,rectw,recth,rectx,recty); end_rect(); return(log_consumed(16,"CopyRect rect (%d) %ux%u at %u,%u from %u,%u",nrect,rectw,recth,rectx,recty,fx,fy)); } static void rect_rre_done(void) { XCopyArea(disp,shadow,mainwin,wingc,rectx,recty,rectw,recth,rectx,recty); end_rect(); } static int rect_rre_subrect(void) { unsigned long int pix; unsigned int x; unsigned int y; unsigned int w; unsigned int h; if (vncibl < bpp+8) return(0); pix = getpixel(&vncib[0]); x = get2be(&vncib[bpp]); y = get2be(&vncib[bpp+2]); w = get2be(&vncib[bpp+4]); h = get2be(&vncib[bpp+6]); XSetForeground(disp,wingc,pix); XFillRectangle(disp,shadow,wingc,rectx+x,recty+y,w,h); rectcount --; if (rectcount < 1) { vnc_step = &vnc_framebuffer_update_recthdr; rect_rre_done(); } return(log_consumed(bpp+8,"RRE subrectx %ux%u at %u,%u (%u,%u) pixel %lx",w,h,x,y,rectx+x,recty+y,pix)); } static int do_rect_rre(void) { unsigned long int v; if (vncibl < 16+bpp) return(0); v = get4be(&vncib[12]); if (v > 65536) { fprintf(stderr,"%s: %s/%s: RRE-encoding with ridiculously many subrects (%lu)\n",__progname,&vnchost[0],&vncserv[0],v); exit(1); } rectcount = v; if (VERB(RFB) || VERB(RRE)) printf(" rect (%d): %ux%u+%u+%u encoding=RRE nsubrect=%d\n",nrect,rectw,recth,rectx,recty,rectcount); XSetForeground(disp,wingc,getpixel(&vncib[16])); XFillRectangle(disp,shadow,wingc,rectx,recty,rectw,recth); if (rectcount > 0) vnc_step = &rect_rre_subrect; else rect_rre_done(); return(log_consumed(16+bpp,"RRE rect (%d) %ux%u at %u,%u nsubrect=%d",nrect,rectw,recth,rectx,recty,rectcount)); } static int rect_corre_subrect(void) { unsigned long int pix; unsigned int x; unsigned int y; unsigned int w; unsigned int h; if (vncibl < bpp+8) return(0); pix = getpixel(&vncib[0]); x = vncib[bpp]; y = vncib[bpp+1]; w = vncib[bpp+2]; h = vncib[bpp+3]; XSetForeground(disp,wingc,pix); XFillRectangle(disp,shadow,wingc,rectx+x,recty+y,w,h); rectcount --; if (rectcount < 1) { vnc_step = &vnc_framebuffer_update_recthdr; rect_rre_done(); } return(log_consumed(bpp+4,"CoRRE subrect %ux%u at %u,%u (%u,%u) pixel %lx",w,h,x,y,rectx+x,recty+y,pix)); } static int do_rect_corre(void) { unsigned long int v; if (vncibl < 16+bpp) return(0); v = get4be(&vncib[12]); if (v > 65536) { fprintf(stderr,"%s: %s/%s: CoRRE-encoding with ridiculously many subrects (%lu)\n",__progname,&vnchost[0],&vncserv[0],v); exit(1); } rectcount = v; if (VERB(RFB) || VERB(CORRE)) printf(" rect (%d): %ux%u+%u+%u encoding=CoRRE nsubrect=%d\n",nrect,rectw,recth,rectx,recty,rectcount); XSetForeground(disp,wingc,getpixel(&vncib[16])); XFillRectangle(disp,shadow,wingc,rectx,recty,rectw,recth); if (rectcount > 0) vnc_step = &rect_corre_subrect; else rect_rre_done(); return(log_consumed(16+bpp,"CoRRE rect (%d) %ux%u at %u,%u rectcount=%d",nrect,rectw,recth,rectx,recty,rectcount)); } static int rect_hextile_tile(void) { unsigned char type; int i; int j; int k; unsigned char xy; unsigned char wh; int tx; int ty; int tw; int th; int o; int done; if (vncibl < 1) return(0); tilew = rectw - atx; if (tilew > 16) tilew = 16; tileh = recth - aty; if (tileh > 16) tileh = 16; if (aty0 < 0) aty0 = aty; type = vncib[0]; o = 1; if (type & RFB_HEXTILE_RAW) { if (vncibl < 1+(tilew*tileh*bpp)) return(0); for (j=0;j=0;j--) for (i=tilew-1;i>=0;i--) { clipped_put_pixel(rectx+atx+i,recty+aty+j,tilebg); } for (k=rectcount;k>0;k--) { if (type & RFB_HEXTILE_COLOURED) { tilefg = getpixel(&vncib[o]); o += bpp; } xy = vncib[o++]; tx = xy >> 4; ty = xy & 15; wh = vncib[o++]; tw = wh >> 4; th = wh & 15; if ((tx+tw >= tilew) || (ty+th >= tileh)) { fprintf(stderr,"%s: %s/%s: hextile subrectangle overflows tile\n",__progname,&vnchost[0],&vncserv[0]); exit(1); } for (j=th;j>=0;j--) for (i=tw;i>=0;i--) clipped_put_pixel(rectx+atx+tx+i,recty+aty+ty+j,tilefg); } XPutImage(disp,shadow,wingc,img,rectx+atx,recty+aty,rectx+atx,recty+aty,tilew,tileh); } } done = rect_inc(16); if (atx == 0) { if ((aty >= aty0+rectul) || done) { if (aty > recth) aty = recth; XCopyArea(disp,shadow,mainwin,wingc,rectx,recty+aty0,rectw,aty-aty0,rectx,recty+aty0); aty0 = -1; } } if (done) end_rect(); return(log_consumed(o,"Hextile tile %dx%d at %d,%d",tilew,tileh,rectx+atx,recty+aty)); } static int do_rect_hextile(void) { if (VERB(RFB) || VERB(HEXTILE)) printf(" rect (%d): %ux%u+%u+%u encoding=Hextile\n",nrect,rectw,recth,rectx,recty); rectul = (1024 + rectw - 1) / rectw; if (rectul < 1) rectul = 1; aty0 = -1; atx = 0; aty = 0; vnc_step = &rect_hextile_tile; return(log_consumed(12,"Hextile rect (%d) %ux%u at %u,%u (%dx%d=%d tiles)",nrect,rectw,recth,rectx,recty,(rectw+15)>>4,(recth+15)>>4,((rectw+15)>>4)*((recth+15)>>4))); } static int rect_trle_tile(void); // forward static void trle_end_tile(int put) { int done; if (put) XPutImage(disp,shadow,wingc,img,rectx+atx,recty+aty,rectx+atx,recty+aty,tilew,tileh); done = rect_inc(16); if (atx == 0) { if ((aty >= aty0+rectul) || done) { if (aty > recth) aty = recth; XCopyArea(disp,shadow,mainwin,wingc,rectx,recty+aty0,rectw,aty-aty0,rectx,recty+aty0); aty0 = -1; } } if (done) end_rect(); else vnc_step = &rect_trle_tile; } static int trle_packed_palette(void) { int o; int sh; int ppb; // pixels per byte int psr; // pixel shift right unsigned char pm; // pixel mask int bsl; // byte shift left unsigned char pixels; int npix; int i; int j; /* * trle_palsiz sh ppb psr pm bsl * 2 3 8 7 1 1 * 3,4 2 4 6 3 2 * 5-16 1 2 4 15 4 */ switch (trle_palsiz) { case 2: sh = 3; if (0) { case 3: case 4: sh = 2; } if (0) { case 5 ... 16: sh = 1; } break; default: abort(); break; } ppb = 1 << sh; if (vncibl < ((tilew+ppb-1)>>sh)*tileh) return(0); o = 0; bsl = 8 >> sh; psr = 8 - bsl; pm = (1 << bsl) - 1; for (j=0;j>psr)&pm]); pixels <<= bsl; npix --; } } trle_end_tile(1); return(log_consumed(o,"TRLE palette pixels")); } static void trle_do_run(unsigned long int pix, int len, const char *what) { while (1) { if (len < 1) return; clipped_put_pixel(rectx+atx+tilex,recty+aty+tiley,pix); len --; if (tile_inc()) { if (len) { fprintf(stderr,"%s: %s/%s: TRLE %s overflows tile\n",__progname,&vnchost[0],&vncserv[0],what); exit(1); } trle_end_tile(1); return; } } } static int trle_plain_rle(void) { int len; int o; unsigned long int pix; unsigned char lenbyte; if (vncibl < bpcp+1) return(0); do <"startrun"> { o = bpcp; len = 1; while (o < vncibl) { lenbyte = vncib[o++]; len += lenbyte; if (lenbyte != 255) { pix = getcpixel(&vncib[0]); break <"startrun">; } } return(0); } while (0); trle_do_run(pix,len,"plain RLE"); return(log_consumed(o,"TRLE plain RLE pixels")); } static int trle_rle_palette(void) { int inx; int len; int o; unsigned char lenbyte; if (vncibl < 1) return(0); inx = vncib[0]; o = 1; if (inx & 128) { inx &= 127; do <"startrun"> { len = 1; while (o < vncibl) { lenbyte = vncib[o++]; len += lenbyte; if (lenbyte != 255) break <"startrun">; } return(0); } while (0); } else { len = 1; } trle_do_run(trle_palette[inx],len,"palette RLE"); return(log_consumed(o,"TRLE palette RLE pixels")); } static int rect_trle_tile(void) { unsigned char subenc; int i; int j; int o; int done; if (vncibl < 1) return(0); tilew = rectw - atx; if (tilew > 16) tilew = 16; tileh = recth - aty; if (tileh > 16) tileh = 16; if (aty0 < 0) aty0 = aty; subenc = vncib[0]; o = 1; tilex = 0; tiley = 0; switch (subenc) { case 0: if (vncibl < 1+(tilew*tileh*bpcp)) return(0); for (j=0;j= aty0+rectul) || done) { if (aty > recth) aty = recth; XCopyArea(disp,shadow,mainwin,wingc,rectx,recty+aty0,rectw,aty-aty0,rectx,recty+aty0); aty0 = -1; } } if (done) end_rect(); return(log_consumed(o,"TRLE tile, subenc %d",subenc)); } static int do_rect_trle(void) { if (VERB(RFB) || VERB(TRLE)) printf(" rect (%d): %ux%u+%u+%u encoding=TRLE\n",nrect,rectw,recth,rectx,recty); rectul = (1024 + rectw - 1) / rectw; if (rectul < 1) rectul = 1; aty0 = -1; atx = 0; aty = 0; vnc_step = &rect_trle_tile; return(log_consumed(12,"TRLE rect (%d) %ux%u at %u,%u",nrect,rectw,recth,rectx,recty)); } static int do_rect_desktop_size(void) { if (VERB(RFB)) printf(" rect (%d): %ux%u+%u+%u pseudo-encoding=DesktopSize\n",nrect,rectw,recth,rectx,recty); if (nrect > 1) { printf("Server sent DesktopSize pseudo-encoding as non-last rectangle\n"); exit(1); } if ((rectw != fbw) || (recth != fbh)) resize_fb(rectw,recth); end_rect(); return(log_consumed(12,"DesktopSize pseudo-rect (%d) %ux%u (at %u,%u)",nrect,rectw,recth,rectx,recty)); } static int vnc_framebuffer_update_recthdr(void) { unsigned long int enc; if (nrect < 1) abort(); if (vncibl < 12) return(0); rectx = get2be(&vncib[0]); recty = get2be(&vncib[2]); rectw = get2be(&vncib[4]); recth = get2be(&vncib[6]); enc = get4be(&vncib[8]); switch (enc) { case RFB_ENCODING_RAW: return(do_rect_raw()); break; case RFB_ENCODING_COPY_RECT: return(do_rect_copy_rect()); break; case RFB_ENCODING_RRE: return(do_rect_rre()); break; case RFB_ENCODING_CORRE: return(do_rect_corre()); break; case RFB_ENCODING_HEXTILE: return(do_rect_hextile()); break; case RFB_ENCODING_TRLE: return(do_rect_trle()); break; case RFB_PSEUDO_ENCODING_DESKTOP_SIZE: return(do_rect_desktop_size()); break; default: printf("Unknown FramebufferUpdate encoding %lu\n",enc); exit(1); break; } } static int vnc_running(void) { unsigned long int n; int i; if (vncibl < 1) return(0); switch (vncib[0]) { case RFBS_FRAMEBUFFER_UPDATE: pending_update = 0; if (rfblog) fprintf(rfblog,"FramebufferUpdate at %#x\n",(int)(vncib-vncib0)); if (vncibl < 4) return(0); n = get2be(&vncib[2]); if (VERB(RFB)) printf("FramebufferUpdate, nrect=%lu\n",n); if (n > 1048576) { fprintf(stderr,"%s: %s/%s: FramebufferUpdate ridiculously many rects (%lu)\n",__progname,&vnchost[0],&vncserv[0],n); exit(1); } if (n > 0) { nrect = n; vnc_step = &vnc_framebuffer_update_recthdr; } return(log_consumed(4,"FramebufferUpdate header, nrect=%d",nrect)); break; case RFBS_SET_COLOR_MAP_ENTRIES: { XColor *cv; int base; if (rfblog) fprintf(rfblog,"SetColorMapEntries at %#x\n",(int)(vncib-vncib0)); if (vncibl < 6) return(0); n = get2be(&vncib[4]); if (vncibl < 6+(6*n)) return(0); if (tcvis) { fprintf(stderr,"%s: %s/%s: SetColorMapEntries but using TrueColor\n",__progname,&vnchost[0],&vncserv[0]); exit(1); } base = get2be(&vncib[2]); if ((n > 256) || (base < 0) || (base > 256) || (base+n > 256)) { fprintf(stderr,"%s: %s/%s: SetColorMapEntries base=%d n=%lu out of range\n",__progname,&vnchost[0],&vncserv[0],base,n); exit(1); } if (VERB(RFB)) printf("SetColorMapEntries base=%d n=%lu\n",base,n); cv = malloc(n*sizeof(XColor)); for (i=0;i 1048576) { fprintf(stderr,"%s: %s/%s: ServerCutText ridiculously long (%lu)\n",__progname,&vnchost[0],&vncserv[0],n); exit(1); } if (vncibl < 8+n) return(0); if (VERB(RFB)) printf("ServerCutText len=%lu\n",n); return(log_consumed(8+n,"ServerCutText len=%lu",n)); break; } printf("Unrecognized server message type %d\n",vncib[0]); exit(1); } static void rd_vp(void *arg __attribute__((__unused__))) { struct timersock_event tse[64]; int nb; int nev; int any; any = 0; while (1) { nb = read(vpfd,&tse[0],sizeof(tse)); if (nb < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: if (any) pending_update = 0; return; break; } fprintf(stderr,"%s: timer socket read: %s\n",__progname,strerror(errno)); exit(1); } if (nb == 0) { fprintf(stderr,"%s: timer socket read EOF\n",__progname); exit(1); } nev = nb / sizeof(struct timersock_event); if (nb != nev * sizeof(struct timersock_event)) { fprintf(stderr,"%s: timer socket read returned %d, not a multiple of %d\n",__progname,nb,(int)sizeof(struct timersock_event)); exit(1); } any = 1; } } static int check_update(void *arg __attribute__((__unused__))) { if (pending_update) return(AIO_BLOCK_NIL); send_update_request(0,0,fbw,fbh,1); pending_update = 1; return(AIO_BLOCK_LOOP); } static int vnc_init(void) { int nl; unsigned char set_pixel_format[20]; unsigned char *set_encodings; S_M sm; struct itimerval itv; int i; if (vncibl < 2+2+16+4) return(0); nl = get4be(&vncib[2+2+16]); if (nl > 65536) { fprintf(stderr,"%s: %s/%s: name string ridiculously long (%d)\n",__progname,&vnchost[0],&vncserv[0],nl); exit(1); } if (vncibl < 2+2+16+4+nl) return(0); printf("Name %.*s\n",nl,&vncib[2+2+16+4]); fbw = get2be(&vncib[0]); fbh = get2be(&vncib[2]); printf("Size %dx%d\n",fbw,fbh); printf("Server native pixel format "); dump_pixel_format(&vncib[2+2]); printf("\n"); set_pixel_format[0] = RFBC_SET_PIXEL_FORMAT; set_pixel_format[1] = 0; set_pixel_format[2] = 0; set_pixel_format[3] = 0; switch (visinfo->class) { case PseudoColor: set_pixel_format[4] = 8; set_pixel_format[5] = 8; bzero(&set_pixel_format[6],14); cpt = CP1; bpp = 1; break; case TrueColor: switch (depth) { case 1 ... 8: set_pixel_format[4] = 8; cpt = CP1; bpp = 1; break; case 9 ... 16: set_pixel_format[4] = 16; cpt = CP2; bpp = 2; break; case 17 ... 32: set_pixel_format[4] = 32; cpt = CP4; bpp = 4; break; default: abort(); break; } set_pixel_format[5] = depth; set_pixel_format[6] = 1; set_pixel_format[7] = 1; sm = find_s_m(visinfo->red_mask); put2be(&set_pixel_format[8],sm.m); set_pixel_format[14] = sm.s; sm = find_s_m(visinfo->green_mask); put2be(&set_pixel_format[10],sm.m); set_pixel_format[15] = sm.s; sm = find_s_m(visinfo->blue_mask); put2be(&set_pixel_format[12],sm.m); set_pixel_format[16] = sm.s; set_pixel_format[17] = 0; set_pixel_format[18] = 0; set_pixel_format[19] = 0; break; default: abort(); break; } printf("Using pixel format "); dump_pixel_format(&set_pixel_format[4]); printf("\n"); bpcp = bpp; if ((visinfo->class == TrueColor) && (bpp == 4) && (depth <= 24)) { if (((visinfo->red_mask|visinfo->green_mask|visinfo->blue_mask) & 0xff000000UL) == 0) { cpt = CP3L; bpcp = 3; } else if (((visinfo->red_mask|visinfo->green_mask|visinfo->blue_mask) & 0x000000ffUL) == 0) { cpt = CP3H; bpcp = 3; } } imgdata = malloc(fbw*fbh*bpp); img = XCreateImage(disp,visual,depth,ZPixmap,0,imgdata,fbw,fbh,bpp*8,fbw*bpp); shadow = XCreatePixmap(disp,rootwin,fbw,fbh,depth); aio_oq_queue_copy(&vncoq,&set_pixel_format[0],20); default_encs(); printf("Pixel encoding count = %d:",n_enc); set_encodings = malloc(4+(4*(n_enc+1))); set_encodings[0] = RFBC_SET_ENCODINGS; set_encodings[1] = 0; put2be(set_encodings+2,n_enc+1); for (i=0;i> 7) & 1) | ((c >> 5) & 2) | ((c >> 3) & 4) | ((c >> 1) & 8) | ((c & 8) << 1) | ((c & 4) << 3) | ((c & 2) << 5) | ((c & 1) << 7); } } /* * One thing 6143 does not describe in enough detail is password * verification. It says: * The server sends a random 16-byte challenge: [...] The client encrypts the challenge with DES, using a password supplied by the user as the key. To form the key, the password is truncated to eight characters, or padded with null bytes on the right. The client then sends the resulting 16-byte response: [...] * * but (a) DES has a 64-bit blocksize and a 56-bit key, and the above * does not describe how to handle the 128 bits of data in the * challenge, nor does it describe how to convert the eight-byte key * buffer to the 56-bit DES key. * * By reference to another open-source implementation, I discovered * that the 128-bit block is handled by splitting it in the middle to * form two 64-bit blocks, each of which is encrypted ECB. The typed * password is converted to a DES key by (possibly padding with 0x00 * octets, then) bit-reversing each byte, then (as the DES spec says) * dropping the (then-)lowest bit of each byte. * * As a test vector, if the typed password is "abcdefgh" and the * challenge (in hex) is 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee * ff, the key bytes presented to DES (which then drops the LSB of * each byte) are 86 46 c6 26 a6 66 e6 16 and the data blocks are 00 * 11 22 33 44 55 66 77 (first half) and 88 99 aa bb cc dd ee ff * (second half); the encryptions, 8a d9 74 84 61 32 dc 3e and 06 c5 * 5b 08 ca 66 70 2f, are pasted together to form the response. */ static int vnc_sec_vnc(void) { char *ipass; unsigned char resp[16]; unsigned char pass[8]; unsigned char key[8]; if (vncibl < 16) return(0); ipass = getpass("Password: "); strncpy(&pass[0],ipass,8); bitrevkey(&pass[0],&key[0]); des(&resp[0],&vncib[0],&key[0],ENCRYPT); des(&resp[8],&vncib[8],&key[0],ENCRYPT); aio_oq_queue_copy(&vncoq,&resp[0],16); vnc_step = &vnc_sec_result; return(16); } static int vnc_getsec(void) { int i; unsigned int sec; if (vncibl < vncib[0]+1) return(0); if (vncib[0] == 0) { vnc_step = &vnc_badvers; return(1); } sec = 0; printf("Security types offered:\n"); for (i=0;i",data,len); } wr = aio_oq_writev(&vncoq,vncfd,-1); if (wr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: write to %s/%s: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(errno)); exit(1); } aio_oq_dropdata_cb(&vncoq,wr,&showdata,0); } static void rd_vnc(void *arg __attribute__((__unused__))) { int r; if (vnciba < vncibl+8192) vncib = realloc(vncib,vnciba=vncibl+8192); r = read(vncfd,vncib+vncibl,vnciba-vncibl); if (r < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: read from %s/%s: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(errno)); exit(1); } if (r == 0) { fprintf(stderr,"%s: read from %s/%s got EOF\n",__progname,&vnchost[0],&vncserv[0]); exit(1); } rfb_data("I<",vncib+vncibl,r); vncibl += r; vncib0 = vncib; while (1) { r = (*vnc_step)(); if (r == 0) { if (rfblog) fprintf(rfblog,"Want more\n"); break; } if ((r < 0) || (r > vncibl)) abort(); vncibl -= r; vncib += r; if (vncibl < 1) break; } if (vncib != vncib0) { if (vncibl > 0) bcopy(vncib,vncib0,vncibl); vncib = vncib0; if (rfblog) fprintf(rfblog,"Held over %d\n",vncibl); } } static void vnc_connect_success(void) { aio_oq_init(&vncoq); vncibl = 0; vncid = aio_add_poll(vncfd,&aio_rwtest_always,&wtest_vnc,&rd_vnc,&wr_vnc,0); vnc_step = &vnc_getvers; } static void vnc_connect_fail(void) { vncai = vncai->ai_next; if (! vncai) { fprintf(stderr,"%s: no more addresses to try\n",__progname); exit(1); } vncid = aio_add_block(&vnc_try_connect,0); } static void vnc_connect_check(void *arg __attribute__((__unused__))) { int e; socklen_t elen; aio_remove_poll(vncid); elen = sizeof(e); if (getsockopt(vncfd,SOL_SOCKET,SO_ERROR,&e,&elen) < 0) { fprintf(stderr,"%s: %s/%s: getsockopt SO_ERROR: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(errno)); vnc_connect_fail(); return; } if (elen != sizeof(e)) { fprintf(stderr,"%s: %s/%s: getsockopt SO_ERROR: error length is %d, wanted %d\n",__progname,&vnchost[0],&vncserv[0],(int)elen,(int)sizeof(e)); exit(1); } if (e == 0) { vnc_connect_success(); } else { fprintf(stderr,"%s: connect to %s/%s: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(e)); vnc_connect_fail(); } } static int vnc_try_connect(void *arg __attribute__((__unused__))) { int fd; int e; aio_remove_block(vncid); do <"fail"> { e = getnameinfo(vncai->ai_addr,vncai->ai_addrlen,&vnchost[0],sizeof(vnchost),&vncserv[0],sizeof(vncserv),NI_NUMERICHOST|NI_NUMERICSERV); if (e) { fprintf(stderr,"%s: can't get numeric form of address (af %d): %s\n",__progname,vncai->ai_family,gai_strerror(e)); break <"fail">; } fd = socket(vncai->ai_family,vncai->ai_socktype,vncai->ai_protocol); if (fd < 0) { fprintf(stderr,"%s: %s/%s: socket: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(errno)); break <"fail">; } fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); if (connect(fd,vncai->ai_addr,vncai->ai_addrlen) < 0) { if (errno == EINPROGRESS) { vncfd = fd; vncid = aio_add_poll(fd,&aio_rwtest_never,&aio_rwtest_always,0,&vnc_connect_check,0); return(AIO_BLOCK_LOOP); } fprintf(stderr,"%s: connect to %s/%s: %s\n",__progname,&vnchost[0],&vncserv[0],strerror(errno)); break <"fail">; } else { vncfd = fd; vnc_connect_success(); return(AIO_BLOCK_LOOP); } } while (0); vnc_connect_fail(); return(AIO_BLOCK_LOOP); } static void vnc_start_connect(void) { char *slash; const char *port; char *host; struct addrinfo hints; int e; slash = index(vncserver,'/'); if (slash) { port = slash + 1; host = blk_to_str(vncserver,(port-vncserver)-1); } else { port = "5900"; host = vncserver; } hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(host,port,&hints,&vncai0); if (e) { fprintf(stderr,"%s: can't find %s/%s: %s\n",__progname,host,port,gai_strerror(e)); exit(1); } vncai = vncai0; vncfd = -1; vncid = aio_add_block(&vnc_try_connect,0); } static void listen_accept(void *lv) { LISTENER *l; int s; struct sockaddr_storage ss; socklen_t sslen; l = lv; sslen = sizeof(ss); s = accept(l->fd,(void *)&ss,&sslen); if (s < 0) { fprintf(stderr,"%s: accept (on %s): %s\n",__progname,l->str,strerror(errno)); return; } vncfd = s; while ((l = listeners)) { listeners = l->link; aio_remove_poll(l->id); free(l->arg); free(l->str); close(l->fd); free(l); } vnc_connect_success(); } static void vnc_start_listen(void) { char *slash; const char *port; char *host; struct addrinfo hints; int e; int s; struct addrinfo *ai0; struct addrinfo *ai; char hstr[NI_MAXHOST]; char sstr[NI_MAXSERV]; LISTENER *l; slash = index(vncserver,'/'); if (slash) { port = slash + 1; host = (slash == vncserver) ? 0 : blk_to_str(vncserver,(port-vncserver)-1); } else { port = "5900"; host = vncserver; } hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(host,port,&hints,&ai0); if (e) { fprintf(stderr,"%s: can't find %s/%s: %s\n",__progname,host?:"",port,gai_strerror(e)); exit(1); } listeners = 0; for (ai=ai0;ai;ai=ai->ai_next) { e = getnameinfo(ai->ai_addr,ai->ai_addrlen,&hstr[0],sizeof(hstr),&sstr[0],sizeof(sstr),NI_NUMERICHOST|NI_NUMERICSERV); if (e) { fprintf(stderr,"%s: can't get numeric form of address (af %d): %s\n",__progname,ai->ai_family,gai_strerror(e)); continue; } s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) { fprintf(stderr,"%s: socket (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); continue; } if (bind(s,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: bind to %s/%s: %s\n",__progname,&hstr[0],&sstr[0],strerror(errno)); close(s); continue; } if (listen(s,10) < 0) { fprintf(stderr,"%s: listen on %s/%s: %s\n",__progname,&hstr[0],&sstr[0],strerror(errno)); close(s); continue; } l = malloc(sizeof(LISTENER)); l->arg = strdup(vncserver); asprintf(&l->str,"%s/%s",&hstr[0],&sstr[0]); l->fd = s; l->id = aio_add_poll(s,&aio_rwtest_always,&aio_rwtest_never,&listen_accept,0,l); l->link = listeners; listeners = l; } } static void setup_logfile(const char *fn, FILE **fp, const char *what) { int fd; if (fn) { fd = open(fn,O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,0666); if (fd < 0) { fprintf(stderr,"%s: can't open %s logfile %s: %s\n",__progname,what,fn,strerror(errno)); exit(1); } *fp = fdopen(fd,"w"); if (! *fp) { fprintf(stderr,"%s: can't fdopen %s logfile %s\n",__progname,what,fn); exit(1); } } else { *fp = 0; } } static void setup_log(void) { setup_logfile(rfblogfile,&rfblog,"RFB"); setup_logfile(klogfile,&klog,"keystroke"); if (klog) setlinebuf(klog); } static void setup_vnc(void) { if (dolisten) { vnc_start_listen(); } else { vnc_start_connect(); } } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); aio_poll_init(); disp = open_display(displayname); setup_errhand(); if (dosync) XSynchronize(disp,True); scr = XDefaultScreenOfDisplay(disp); scrno = XScreenNumberOfScreen(scr); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); rootwin = XRootWindowOfScreen(scr); setup_visual(); setup_db(); maybeset(&foreground,get_default_value("foreground","Foreground")); maybeset(&background,get_default_value("background","Background")); maybeset(&bordercstr,get_default_value("borderColor","BorderColor")); maybeset(&borderwstr,get_default_value("borderWidth","BorderWidth")); maybeset(&title,get_default_value("title","Title")); maybeset(&iconname,get_default_value("iconName","IconName")); maybeset(&uustr,get_default_value("updateUsec","UpdateUsec")); setup_colours(); setup_numbers(); setup_cursor(); setup_windows(); setup_aio(); setup_log(); setup_vnc(); while (1) { aio_pre_poll(); if (aio_do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } aio_post_poll(); } }