/* * 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 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 "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\ "; typedef struct s_m S_M; 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 int dosync = 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 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 int vnciba = 0; static int vncibl; static int (*vnc_step)(void); static int bpp; static int fbw; static int fbh; static XImage *img; static char *imgdata; static int nrect; static int rectx; static int recty; static int rectw; static int recth; static int rectul; static int aty0; static int atx; static int aty; static int vpfd; static int vpid; 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 get4be(const unsigned char *d) { return( (d[0] * 0x01000000) + (d[1] * 0x00010000) + (d[2] * 0x00000100) + 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 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; return(0); } } fprintf(stderr,"%s: unrecognized verbosity `%s'\n",__progname,s); fprintf(stderr,"%s: use `-verbose list' for a list\n",__progname); return(1); } 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,"-vnc")) { WANTARG(); vncserver = 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 (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) { if (borderwstr) borderwidth = atoi(borderwstr); } 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,0); XDrawPoint(disp,pm,gc,2,0); 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); send_update_request(x,y,w,h,0); } } 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); 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); } 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 int vnc_running(void); // forward static int vnc_framebuffer_update_recthdr(void); // forward static int rect_inc(void) { atx ++; if (atx >= rectw) { atx = 0; aty ++; if (aty >= recth) { 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 int vnc_rect_raw_pixels(void) { int npix; int i; int o; int done; unsigned long int pix; if (vncibl < bpp) return(0); npix = vncibl / bpp; o = 0; for (i=npix;i>0;i--) { switch (bpp) { case 1: pix = vncib[o++]; break; case 2: pix = get2be(vncib+o); o += 2; break; case 4: pix = get4be(vncib+o); o += 4; break; default: abort(); break; } XPutPixel(img,rectx+atx,recty+aty,pix); if (aty0 < 0) aty0 = aty; done = rect_inc(); if (atx == 0) { if ((aty >= aty0+rectul) || done) { XPutImage(disp,mainwin,wingc,img,rectx,recty+aty0,rectx,recty+aty0,rectw,aty-aty0); aty0 = -1; } } if (done) { end_rect(); break; } } return(o); } static int vnc_framebuffer_update_recthdr(void) { unsigned int x; unsigned int y; unsigned int w; unsigned int h; unsigned long int enc; unsigned int len; if (nrect < 1) abort(); if (vncibl < 12) return(0); x = get2be(&vncib[0]); y = get2be(&vncib[2]); w = get2be(&vncib[4]); h = get2be(&vncib[6]); enc = get4be(&vncib[8]); switch (enc) { case RFB_ENCODING_RAW: len = w * h * bpp; if (VERB(RFB)) printf(" rect: %ux%u+%u+%u encoding Raw\n",w,h,x,y); rectx = x; recty = y; rectw = w; recth = h; rectul = (1024 + w - 1) / w; if (rectul < 1) rectul = 1; aty0 = -1; atx = 0; aty = 0; vnc_step = &vnc_rect_raw_pixels; return(12); 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: 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(4); break; case RFBS_SET_COLOR_MAP_ENTRIES: { XColor *cv; int base; 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 < 4+n) return(0); if (VERB(RFB)) printf("ServerCutText len=%lu\n",n); return(4+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) send_update_request(0,0,fbw,fbh,1); 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 vnc_init(void) { int nl; unsigned char set_pixel_format[20]; unsigned char set_encodings[8]; S_M sm; struct itimerval itv; 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); bpp = 1; break; case TrueColor: switch (depth) { case 1 ... 8: set_pixel_format[4] = 8; bpp = 1; break; case 9 ... 16: set_pixel_format[4] = 16; bpp = 2; break; case 17 ... 32: set_pixel_format[4] = 32; 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"); imgdata = malloc(fbw*fbh*bpp); img = XCreateImage(disp,visual,depth,ZPixmap,0,imgdata,fbw,fbh,bpp*8,fbw*bpp); aio_oq_queue_copy(&vncoq,&set_pixel_format[0],20); set_encodings[0] = RFBC_SET_ENCODINGS; set_encodings[1] = 0; put2be(&set_encodings[2],1); put4be(&set_encodings[4],RFB_ENCODING_RAW); aio_oq_queue_copy(&vncoq,&set_encodings[0],8); XResizeWindow(disp,mainwin,fbw,fbh); XResizeWindow(disp,topwin,fbw,fbh); XMapWindow(disp,mainwin); XMapRaised(disp,topwin); vnc_step = &vnc_running; vpfd = socket(AF_TIMER,SOCK_STREAM,0); fcntl(vpfd,F_SETFL,fcntl(vpfd,F_GETFL,0)|O_NONBLOCK); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 1000000 / 10; itv.it_value = itv.it_interval; write(vpfd,&itv,sizeof(itv)); vpid = aio_add_poll(vpfd,&aio_rwtest_always,&aio_rwtest_never,&rd_vp,0,0); return(2+2+16+4+nl); } static int vnc_sec_result(void) { unsigned long int err; if (vncibl < 4) return(0); err = get4be(&vncib[0]); if (err == 0) { aio_oq_queue_point(&vncoq,"\1",1); vnc_step = &vnc_init; } else { vnc_step = &vnc_sec_fail; } return(4); } static void bitrevkey(const void *in, unsigned char *out) { int i; unsigned char c; for (i=0;i<8;i++) { c = ((const unsigned char *)in)[i]; out[i] = ((c >> 7) & 1) | ((c >> 5) & 2) | ((c >> 3) & 4) | ((c >> 1) & 8) | ((c & 8) << 1) | ((c & 4) << 3) | ((c & 2) << 5) | ((c & 1) << 7); } } 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 vncibl)) abort(); vncibl -= r; vncib += r; if (vncibl < 1) break; } if (vncib != vncib0) { if (vncibl > 0) bcopy(vncib,vncib0,vncibl); vncib = vncib0; } } 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 { 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 setup_vnc(void) { 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")); setup_colours(); setup_numbers(); setup_cursor(); setup_windows(); setup_aio(); 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(); } }