#define IDENT_PORT 113 #define BASE_X_PORT 6000 #define IOBUFSIZE 8192 #define MAXBUFFERED 8192 #define BELL_ALLOWANCE 10 #ifdef NEED_SYS_TYPES_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_IOV_LEN 16 /* should be in an include file somewhere */ #include #include #include #include #include #include #include #if !defined(USE_GCC_EXTENSIONS) && !defined(NO_GCC_EXTENSIONS) #if defined(__GNUC__) && \ ( (__GNUC__ > 2) || \ ( (__GNUC__ == 2) && \ defined(__GNUC_MINOR__) && \ (__GNUC_MINOR__ >= 7) ) ) #define USE_GCC_EXTENSIONS #else #define NO_GCC_EXTENSIONS #endif #endif #if defined(USE_GCC_EXTENSIONS) && defined(NO_GCC_EXTENSIONS) #undef USE_GCC_EXTENSIONS #endif #if defined(USE_GCC_EXTENSIONS) #define attribute(x) __attribute__(x) #define inline __inline__ #define UNUSED_ARG(x) x __attribute__((__unused__)) #endif #if defined(NO_GCC_EXTENSIONS) #define attribute(x) #define inline #define UNUSED_ARG(x) x #endif extern const char *__progname; #ifdef NO_PROGNAME const char *__progname; #endif static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *BorderMargin: 2\n\ *Name: xconns\n\ *IconName: xconns\n\ "; static char *displayname; static char *geometryspec; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *bordermstr; static char *name; static char *visualstr; static char *iconname; static char *fontname; static char *configfile; static int synch = 0; /* grrr, preempts sync */ static char *xdisplay = 0; static int dispno = -1; static int dofork; static int disphunt; static int dispforce; static int debug; typedef struct fd FD; typedef struct pm PM; typedef struct conn CONN; typedef struct mask MASK; typedef struct ioseg IOSEG; struct pm { const char *str; XCharStruct strbound; int w; int h; Pixmap pixmap; } ; struct ioseg { IOSEG *link; const char *buf; char *free; unsigned int nb : 31; } ; struct fd { int fd; const char *debugtag; unsigned int ateof : 1; CONN *conn; IOSEG *wq; IOSEG **wqt; unsigned int wqbytes; char rbuf[IOBUFSIZE]; unsigned int rbhead; unsigned int rbtail; unsigned int rbfill; } ; struct mask { MASK *link; unsigned int action : 3; #define MA_TRUST 1 #define MA_ALLOW 2 #define MA_ALERT 3 #define MA_DENY 4 unsigned long int mask; unsigned long int value; } ; struct conn { CONN *flink; CONN *blink; char *debugtag; unsigned int dead : 1; unsigned int iip : 2; /* IDENT in progress: */ #define CIIP_NOT 0 /* not in progress */ #define CIIP_CONNECTING 1 /* waiting for connect to complete */ #define CIIP_CONNECTED 2 /* connect() done, nothing sent yet */ #define CIIP_SENT 3 /* sent query, waiting for reply */ unsigned int xcip : 1; /* waiting for X connect to complete */ unsigned int cfirst : 1; /* reading client's initial 12 bytes */ unsigned int cauthn : 1; /* reading client's authorization name */ unsigned int cauthd : 1; /* reading client's authorization data */ unsigned int xfirst : 1; /* reading server's connection reply */ unsigned int xrfail : 1; /* reading server's "failed" reply */ unsigned int xrsucc : 1; /* reading server's "succeeded" reply */ unsigned int frozen : 1; /* connection is frozen - no reads */ unsigned int alert : 1; /* alert up (also implies no reads) */ unsigned int connalert : 1; /* alert is for new connection */ unsigned int menuup : 1; /* menu is up */ unsigned int creqhdr : 1; /* reading request header */ unsigned int cblind : 1; /* blindly copying request client->server */ unsigned int cskip : 1; /* skipping request body */ unsigned int noalerts : 1; /* no more alerts for this connection */ char *identreply; int identreplen; FD c; FD x; int ifd; struct sockaddr_in caddr; char *cuser; Window top; int topw; Window texttop; int textw; int topy; Window text; char *listtext; XCharStruct textbound; Window freeze_win; Window menu_top; int menu_w; Window menu_dismiss; Window menu_freeze; Window menu_thaw; Window menu_kill; Window menu_noop; Window menu_allow; Window menu_disable; Window alert_text; int alert_w; void *creadptr; unsigned int creadleft; void *xreadptr; unsigned int xreadleft; unsigned char bytesex; #define BYTESEX_LSB 0x6c /* ASCII l */ #define BYTESEX_MSB 0x42 /* ASCII M */ unsigned int c_proto_major; unsigned int c_proto_minor; unsigned int x_proto_major; unsigned int x_proto_minor; unsigned int auth_n_len; unsigned int auth_d_len; char *auth_n; char *auth_d; unsigned int setupbytes; unsigned char *setupbuf; unsigned char *reqbuf; unsigned int reqlen; unsigned int reqbuflen; unsigned long int resid_base; unsigned long int resid_mask; unsigned int nscreens; unsigned long int *roots; unsigned char reqhdr[4]; struct timeval lastbell; int bellcount; } ; static const char *resname = "xconns"; static const char *resclass = "FrontEnd"; static char *xrmstr = 0; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap defcmap; static Visual *visual; static int (*preverr)(Display *, XErrorEvent *); static int (*prevIOerr)(Display *); static XColor fgcolor; static XColor bgcolor; static XColor bdcolor; static GC wingc; static GC revgc; static Window topwin; static int topwin_mapped; static Colormap wincmap; static int margin; static int borderwidth; static XFontStruct *font; static int deffont; static int baselineskip; static CONN *conns_h; static CONN *conns_t; static int nconns; static int x_disp; static struct in_addr x_addr; static int accfd; static Xauth *authdata; static IOSEG *freeiosegs = 0; static const char *padding = "\0\0\0\0"; static PM pm_frozen = { "(Frozen)" }; static PM pm_dismiss = { "Dismiss" }; static PM pm_freeze = { "Freeze" }; static PM pm_thaw = { "Thaw" }; static PM pm_kill = { "Kill" }; static PM pm_noop = { "NoOp" }; static PM pm_allow = { "Allow" }; static PM pm_disable = { "AllOK" }; static int junk_direction; static int junk_ascent; static int junk_descent; #define XTE_JUNK &junk_direction,&junk_ascent,&junk_descent static void process_client_input(CONN *); /* forward */ static inline unsigned int pad4(unsigned int) attribute ((const)); static inline unsigned int pad4(unsigned int x) { return((x+3)&~3); } static inline unsigned int pad4npad(unsigned int) attribute ((const)); static inline unsigned int pad4npad(unsigned int x) { return((4-(x&3))&3); } static inline int max(int, int) attribute ((const)); static inline int max(int a, int b) { return((a>b)?a:b); } /* I _think_ ANSI realloc and free are already supposed to behave this way. These are in case (a) I'm wrong and/or (b) someone is stuck with defective libraries. */ static void *xrealloc(void *old, unsigned int nb) { return(old?realloc(old,nb):malloc(nb)); } static void xfree(void *old) { if (old) free(old); } static unsigned short int get2(CONN *c, const void *bparg) #define bp ((const unsigned char *)bparg) { switch (c->bytesex) { case BYTESEX_LSB: return(bp[0]|(bp[1]<<8)); break; case BYTESEX_MSB: return(bp[1]|(bp[0]<<8)); break; } abort(); } #undef bp static void set2(CONN *c, void *bparg, unsigned short int val) #define bp ((unsigned char *)bparg) { switch (c->bytesex) { case BYTESEX_LSB: bp[0] = val & 0xff; bp[1] = val >> 8; break; case BYTESEX_MSB: bp[1] = val & 0xff; bp[0] = val >> 8; break; } } #undef bp static unsigned long int get4(CONN *c, const void *bparg) #define bp ((const unsigned char *)bparg) { switch (c->bytesex) { case BYTESEX_LSB: return(bp[0]|(bp[1]<<8)|(bp[2]<<16)|(bp[3]<<24)); break; case BYTESEX_MSB: return(bp[3]|(bp[2]<<8)|(bp[1]<<16)|(bp[0]<<24)); break; } abort(); } #undef bp static char *deconst_(int x, ...) { char *rv; va_list ap; va_start(ap,x); rv = va_arg(ap,char *); va_end(ap); return(rv); } static char *deconst(const char *s) { return(deconst_(0,s)); } static char *copyofstr(const char *s) { char *t; t = malloc(strlen(s)+1); if (t) strcpy(t,s); return(t); } static void nap(double sec) { struct timeval tv; struct timeval now; struct timeval then; int n; gettimeofday(&now,0); tv.tv_sec = (int) sec; tv.tv_usec = (int) (1000000 * (sec - tv.tv_sec)); then.tv_sec = now.tv_sec + tv.tv_sec; then.tv_usec = now.tv_usec + tv.tv_usec; while (then.tv_usec >= 1000000) { then.tv_usec -= 1000000; then.tv_sec ++; } while (1) { n = select(0,0,0,0,&tv); gettimeofday(&now,0); tv.tv_sec = then.tv_sec - now.tv_sec; tv.tv_usec = then.tv_usec - now.tv_usec; while (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec --; } if (tv.tv_sec < 0) { return; } } } static void dprintf(CONN *, const char *, ...) attribute ((format(printf,2,3))); static void dprintf(CONN *c, const char *fmt, ...) { va_list ap; if (! debug) return; if (c) printf("%s: ",c->debugtag); va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); if (c) printf("\n"); } 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 0) { skip --; continue; } if (**av != '-') { 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,"-X")) { WANTARG(); xdisplay = av[skip]; continue; } if (!strcmp(*av,"-dispno")) { WANTARG(); dispno = atoi(av[skip]); continue; } if (!strcmp(*av,"-hunt")) { disphunt = 1; continue; } if (!strcmp(*av,"-force")) { dispforce = 1; continue; } if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = 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,"-bd")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-bordermargin") || !strcmp(*av,"-bm")) { WANTARG(); bordermstr = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-font") || !strcmp(*av,"-fn")) { WANTARG(); fontname = 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"); strappend(&xrmstr,av[skip]); continue; } if (!strcmp(*av,"-config")) { WANTARG(); configfile = av[skip]; continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } if (!strcmp(*av,"-debug")) { debug = 1; continue; } if (!strcmp(*av,"-fork")) { dofork = 1; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) { exit(1); } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static void setup_limit(void) { #ifdef RLIMIT_NOFILE { struct rlimit rl; getrlimit(RLIMIT_NOFILE,&rl); rl.rlim_cur = rl.rlim_max; setrlimit(RLIMIT_NOFILE,&rl); } #endif } 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 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_color(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } while (1) { if (XAllocColor(disp,wincmap,col) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for color `%s'\n",__progname,str); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); continue; } break; } } static void setup_visual(void) { char *cp; XVisualInfo *xvi; int nvi; XVisualInfo template; long int mask; int i; int vpref; visual = 0; depth = XDefaultDepthOfScreen(scr); if (visualstr == 0) return; template.screen = XScreenNumberOfScreen(scr); mask = VisualScreenMask | VisualClassMask; for (cp=visualstr;*cp;cp++) *cp = tolower(*cp); if (!strcmp(visualstr,"staticgray")) template.class = StaticGray; else if (!strcmp(visualstr,"grayscale")) template.class = GrayScale; else if (!strcmp(visualstr,"staticcolor")) template.class = StaticColor; else if (!strcmp(visualstr,"pseudocolor")) template.class = PseudoColor; else if (!strcmp(visualstr,"directcolor")) template.class = DirectColor; else if (!strcmp(visualstr,"truecolor")) template.class = TrueColor; else { unsigned long int id; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; mask |= VisualIDMask; mask &= ~VisualClassMask; } xvi = XGetVisualInfo(disp,mask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no matching visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } vpref = 0; for (i=1;i xvi[vpref].depth) vpref = i; visual = xvi[vpref].visual; depth = xvi[vpref].depth; XFree((char *)xvi); } static void setup_font(void) { deffont = 1; if (fontname) { font = XLoadQueryFont(disp,fontname); if (! font) { fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); } else { deffont = 0; } } if (deffont) { font = XQueryFont(disp,XGContextFromGC(XDefaultGCOfScreen(scr))); } baselineskip = font->ascent + font->descent; } static void setup_colors(void) { wincmap = defcmap; if (visual) wincmap = XCreateColormap(disp,rootwin,visual,AllocNone); setup_color(foreground,&fgcolor); setup_color(background,&bgcolor); setup_color(bordercstr,&bdcolor); } static void setup_numbers(void) { if (bordermstr) margin = atoi(bordermstr); if (borderwstr) borderwidth = atoi(borderwstr); } static void setup_windows(void) { int i; int x; int y; unsigned int w; unsigned int h; int bits; unsigned long int attrmask; unsigned long int gcvalmask; XGCValues gcval; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; static struct { int value; int gravity; } gravities[] = { { XValue|YValue, NorthWestGravity }, { XValue|YValue|XNegative, NorthEastGravity }, { XValue|YValue|YNegative, SouthWestGravity }, { XValue|YValue|XNegative|YNegative, SouthEastGravity } }; x = 0; y = 0; bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (bits & XNegative) x = width + x - w; if (bits & YNegative) y = height + y - h; w = 1; h = 1; attrmask = 0; attr.background_pixel = bdcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = KeyPressMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; attrmask |= CWDontPropagate; attr.colormap = wincmap; attrmask |= CWColormap; topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visual?visual:CopyFromParent,attrmask,&attr); wn_prop.value = (unsigned char *) name; 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 = PMinSize | PResizeInc | PWinGravity; normal_hints.x = x; normal_hints.y = y; normal_hints.win_gravity = CenterGravity; for (i=0;i<(sizeof(gravities)/sizeof(gravities[0]));i++) { if ((bits & (XValue|YValue|XNegative|YNegative)) == gravities[i].value) { normal_hints.win_gravity = gravities[i].gravity; } } normal_hints.flags |= (bits & (XValue|YValue)) ? USPosition : PPosition; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= PSize; 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 = deconst(resname); class_hints.res_class = deconst(resclass); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); gcvalmask = 0; if (! deffont) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.foreground = fgcolor.pixel; gcvalmask |= GCForeground; gcval.background = bgcolor.pixel; gcvalmask |= GCBackground; wingc = XCreateGC(disp,topwin,gcvalmask,&gcval); gcvalmask = 0; if (! deffont) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.foreground = bgcolor.pixel; gcvalmask |= GCForeground; gcval.background = fgcolor.pixel; gcvalmask |= GCBackground; revgc = XCreateGC(disp,topwin,gcvalmask,&gcval); topwin_mapped = 0; XRaiseWindow(disp,topwin); } static void setup_text_pixmap(PM *pm) { XTextExtents(font,pm->str,strlen(pm->str),XTE_JUNK,&pm->strbound); pm->w = (pm->strbound.rbearing - pm->strbound.lbearing) + (2 * margin); pm->h = baselineskip + (2 * margin); pm->pixmap = XCreatePixmap(disp,rootwin,pm->w,pm->h,depth); XFillRectangle(disp,pm->pixmap,revgc,0,0,pm->w,pm->h); XDrawString(disp,pm->pixmap,wingc,margin-pm->strbound.lbearing,margin+font->ascent,pm->str,strlen(pm->str)); } static void setup_pixmaps(void) { setup_text_pixmap(&pm_frozen); setup_text_pixmap(&pm_dismiss); setup_text_pixmap(&pm_freeze); setup_text_pixmap(&pm_thaw); setup_text_pixmap(&pm_kill); setup_text_pixmap(&pm_noop); setup_text_pixmap(&pm_allow); setup_text_pixmap(&pm_disable); } static void setup_addr(void) { char *dv; char *cp; dv = xdisplay; if (dv == 0) dv = getenv("DISPLAY"); if (dv == 0) { fprintf(stderr,"no $DISPLAY\n"); exit(1); } for (cp=dv;*cp&&(*cp!=':');cp++) ; *cp++ = '\0'; if (cp == dv+1) { x_addr.s_addr = htonl(0x7f000001); } else { x_addr.s_addr = inet_addr(dv); } x_disp = atoi(cp); if (x_addr.s_addr == -1) { struct hostent *hp; hp = gethostbyname(dv); if (hp == 0) { fprintf(stderr,"%s: unknown\n",dv); exit(1); } if ((hp->h_addrtype != AF_INET) || (hp->h_length != sizeof(x_addr))) { fprintf(stderr,"%s: on strange network\n",dv); exit(1); } x_addr = *(struct in_addr *)hp->h_addr; } } static void setup_signals(void) { signal(SIGPIPE,SIG_IGN); /* get EPIPE instead */ } static void setup_accept(void) { struct sockaddr_in sin; if (dispno < 0) { if (disphunt) { dispno = 25; } else { fprintf(stderr,"%s: must specify -dispno or -hunt\n",__progname); exit(1); } } top:; bzero((char *)&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(BASE_X_PORT+dispno); accfd = socket(AF_INET,SOCK_STREAM,0); if (accfd < 0) { fprintf(stderr,"%s: can't create accept socket: %s\n",__progname,strerror(errno)); exit(1); } if (dispforce) { int val; val = 1; if (setsockopt(accfd,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) < 0) { fprintf(stderr,"%s: warning: can't set SO_REUSEADDR: %s\n",__progname,strerror(errno)); } } if (bind(accfd,(struct sockaddr *)&sin,sizeof(sin)) < 0) { if ((errno == EADDRINUSE) && disphunt) { dispno ++; close(accfd); goto top; } fprintf(stderr,"%s: can't bind accept socket: %s\n",__progname,strerror(errno)); exit(1); } if (disphunt) { printf("%d\n",dispno); fflush(stdout); } listen(accfd,10); } static void setup_conns(void) { conns_h = 0; conns_t = 0; nconns = 0; } static void setup_auth(void) { char dispno[64]; sprintf(&dispno[0],"%d",x_disp); authdata = XauGetAuthByAddr( (unsigned short int) FamilyInternet, (unsigned short int) sizeof(x_addr), (char *) &x_addr, (unsigned short int) strlen(&dispno[0]), &dispno[0], (unsigned short int) 0, (char *) 0 ); if (authdata == 0) { static Xauth noauth; authdata = &noauth; noauth.name_length = 0; noauth.data_length = 0; } } static void setup_fork(void) { if (dofork) { if (fork() != 0) exit(0); close(0); close(1); close(2); open("/",O_RDONLY,0); dup(0); dup(0); } } static void ensure_freeze_win(CONN *c) { if (c->freeze_win == None) { unsigned long int attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixmap = pm_frozen.pixmap; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; c->freeze_win = XCreateWindow(disp,c->top,-pm_frozen.w,0,pm_frozen.w,pm_frozen.h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,c->freeze_win); } } static Window setup_menuwin(CONN *c, PM *pm) { Window w; unsigned long int attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixmap = pm->pixmap; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; w = XCreateWindow(disp,c->menu_top,-pm->w,0,pm->w,pm->h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); return(w); } static void queue_write_buf(FD *f, const void *buf, int nb, int how) #define QWB_COPY 1 #define QWB_STATIC 2 #define QWB_FREE 3 { IOSEG *i; if (nb < 1) return; i = freeiosegs; if (i) { freeiosegs = i->link; } else { i = malloc(sizeof(IOSEG)); if (i == 0) { printf("xc: queue_write_buf: nil malloc\n"); abort(); } } switch (how) { case QWB_COPY: i->free = malloc(nb); bcopy(buf,i->free,nb); i->buf = i->free; break; case QWB_STATIC: i->buf = buf; i->free = 0; break; case QWB_FREE: i->buf = buf; i->free = deconst(buf); break; } i->nb = nb; i->link = 0; *f->wqt = i; f->wqt = &i->link; f->wqbytes += nb; } static void ensure_menu_wins(CONN *c) { if (c->menu_top == None) { unsigned long int attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixel = bdcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask; attrmask |= CWEventMask; c->menu_top = XCreateWindow(disp,c->top,-1,0,1,baselineskip+(2*margin),0,depth,InputOutput,CopyFromParent,attrmask,&attr); c->menu_w = 1; c->menu_dismiss = setup_menuwin(c,&pm_dismiss); c->menu_freeze = setup_menuwin(c,&pm_freeze); c->menu_thaw = setup_menuwin(c,&pm_thaw); c->menu_kill = setup_menuwin(c,&pm_kill); c->menu_noop = setup_menuwin(c,&pm_noop); c->menu_allow = setup_menuwin(c,&pm_allow); c->menu_disable = setup_menuwin(c,&pm_disable); XMapSubwindows(disp,c->menu_top); XMapWindow(disp,c->menu_top); } } static void relayout_menu(CONN *c) { int x; XWindowChanges chg; x = borderwidth; #define FOO(test,win,pm) \ do { if (test) { chg.x = x; XConfigureWindow(disp,c->win,CWX,&chg); \ x += pm.w + borderwidth; } else { chg.x = - pm.w; \ XConfigureWindow(disp,c->win,CWX,&chg); } } while (0) FOO(c->menuup,menu_dismiss,pm_dismiss); FOO(!c->frozen,menu_freeze,pm_freeze); FOO(c->frozen&&!c->alert,menu_thaw,pm_thaw); FOO(1,menu_kill,pm_kill); FOO(c->alert&&!c->connalert,menu_noop,pm_noop); FOO(c->alert,menu_allow,pm_allow); FOO(c->alert,menu_disable,pm_disable); #undef FOO c->menu_w = x; chg.width = x; XConfigureWindow(disp,c->menu_top,CWWidth,&chg); } static void relayout_windows(CONN *carg) { CONN *c; int maxw; int txtw; int topw; int w; int y; XWindowChanges chg; int nrect; XRectangle *rects_b; XRectangle *rects_c; maxw = 0; for (c=conns_h;c;c=c->flink) { if (c->textbound.rbearing-c->textbound.lbearing > maxw) { maxw = c->textbound.rbearing - c->textbound.lbearing; } } txtw = maxw + (2 * margin); rects_b = malloc(2*(nconns+1)*sizeof(XRectangle)); rects_c = rects_b + nconns + 1; nrect = 1; rects_c[0].x = 0; rects_c[0].y = 0; rects_c[0].width = txtw; rects_c[0].height = (nconns * (baselineskip + (2*margin) + borderwidth)) - borderwidth; rects_b[0].x = - borderwidth; rects_b[0].y = - borderwidth; rects_b[0].width = txtw + (2 * borderwidth); rects_b[0].height = rects_c[0].height + (2 * borderwidth); topw = maxw; y = 0; for (c=conns_h;c;c=c->flink) { if ((c == carg) || (c->textw != txtw)) { c->textw = txtw; chg.width = txtw; XConfigureWindow(disp,c->texttop,CWWidth,&chg); chg.x = (txtw - (c->textbound.rbearing-c->textbound.lbearing)) / 2; XConfigureWindow(disp,c->text,CWX,&chg); w = txtw; if (c->menuup || c->alert) { ensure_menu_wins(c); relayout_menu(c); w += borderwidth; chg.x = w; XConfigureWindow(disp,c->menu_top,CWX,&chg); w += c->menu_w; } else if (c->menu_top != None) { chg.x = - c->menu_w; XConfigureWindow(disp,c->menu_top,CWX,&chg); } if (c->alert) { w += borderwidth; chg.x = w; XConfigureWindow(disp,c->alert_text,CWX,&chg); w += c->alert_w; } else if (c->alert_text != None) { XDestroyWindow(disp,c->alert_text); c->alert_text = None; } if (c->frozen) { ensure_freeze_win(c); w += borderwidth; chg.x = w; XConfigureWindow(disp,c->freeze_win,CWX,&chg); w += pm_frozen.w; } else if (c->freeze_win != None) { chg.x = - pm_frozen.w; XConfigureWindow(disp,c->freeze_win,CWX,&chg); } c->topw = w; } if (c->topy != y) { chg.y = y; XConfigureWindow(disp,c->top,CWY,&chg); c->topy = y; } if (c->topw != maxw) { XRectangle *r; r = &rects_c[nrect]; r->x = 0; r->y = c->topy; r->width = c->topw; r->height = baselineskip + (2 * margin); r = &rects_b[nrect]; r->x = - borderwidth; r->y = c->topy - borderwidth; r->width = c->topw + (2 * borderwidth); r->height = baselineskip + (2 * margin) + (2 * borderwidth); nrect ++; } if (c->topw > topw) topw = c->topw; y += baselineskip + (2 * margin) + borderwidth; } if (conns_h) { XShapeCombineRectangles(disp,topwin,ShapeBounding,0,0,rects_b,nrect,ShapeSet,Unsorted); XShapeCombineRectangles(disp,topwin,ShapeClip,0,0,rects_c,nrect,ShapeSet,Unsorted); XResizeWindow(disp,topwin,topw,y-borderwidth); if (! topwin_mapped) { XMapWindow(disp,topwin); topwin_mapped = 1; } } else { if (topwin_mapped) { XUnmapWindow(disp,topwin); topwin_mapped = 0; } } xfree(rects_b); } static void raisetop(void) { XWindowChanges chg; chg.stack_mode = Above; XReconfigureWMWindow(disp,topwin,XScreenNumberOfScreen(scr),CWStackMode,&chg); } static void popup_menu(CONN *c) { c->menuup = 1; raisetop(); relayout_windows(c); } static void popdown_menu(CONN *c) { c->menuup = 0; relayout_windows(c); } static void begin_request(CONN *c) { c->creadleft = 4; c->creadptr = &c->reqhdr[0]; c->creqhdr = 1; c->cblind = 0; c->cskip = 0; } static void blind_request(CONN *c, int skip) { if (! skip) { if (c->creqhdr) { queue_write_buf(&c->x,&c->reqhdr[0],4,QWB_COPY); c->creadleft = (get2(c,&c->reqhdr[2]) - 1) * 4; } else { queue_write_buf(&c->x,c->reqbuf,c->reqbuflen,QWB_FREE); c->reqbuf = 0; c->creadleft = c->reqlen - c->reqbuflen; } } if (c->creadleft) { c->creqhdr = 0; c->cblind = ! skip; c->cskip = skip; } else { begin_request(c); } } static void pending_noop(CONN *c) { char noopreq[4]; noopreq[0] = 127; noopreq[1] = 0; set2(c,&noopreq[2],1); queue_write_buf(&c->x,&noopreq[0],4,QWB_COPY); blind_request(c,1); } static void pending_allow(CONN *c) { blind_request(c,0); } static void popup_alert(CONN *c, const char *tag) { XCharStruct xcs; Pixmap pm; int h; unsigned long int attrmask; XSetWindowAttributes attr; c->alert = 1; XTextExtents(font,tag,strlen(tag),XTE_JUNK,&xcs); c->alert_w = (xcs.rbearing - xcs.lbearing) + (2 * margin); h = baselineskip + (2 * margin); pm = XCreatePixmap(disp,rootwin,c->alert_w,h,depth); XFillRectangle(disp,pm,revgc,0,0,c->alert_w,h); XDrawString(disp,pm,wingc,margin-xcs.lbearing,margin+font->ascent,tag,strlen(tag)); if (c->alert_text != None) XDestroyWindow(disp,c->alert_text); attrmask = 0; attr.background_pixmap = pm; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; c->alert_text = XCreateWindow(disp,c->top,-c->alert_w,0,c->alert_w,h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,c->alert_text); raisetop(); relayout_windows(c); } static void send_initial_client(CONN *c) { char hdr[12]; hdr[0] = c->bytesex; set2(c,&hdr[2],c->c_proto_major); set2(c,&hdr[4],c->c_proto_minor); set2(c,&hdr[6],authdata->name_length); set2(c,&hdr[8],authdata->data_length); queue_write_buf(&c->x,&hdr[0],12,QWB_COPY); queue_write_buf(&c->x,authdata->name,authdata->name_length,QWB_STATIC); queue_write_buf(&c->x,padding,pad4npad(authdata->name_length),QWB_STATIC); queue_write_buf(&c->x,authdata->data,authdata->data_length,QWB_STATIC); queue_write_buf(&c->x,padding,pad4npad(authdata->data_length),QWB_STATIC); } static void conn_allow(CONN *c) { send_initial_client(c); begin_request(c); } static void buttonpress(Window win, Window sub, UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(unsigned int state), UNUSED_ARG(unsigned int button), UNUSED_ARG(Time time)) { CONN *c; for (c=conns_h;c;c=c->flink) { if (win == c->texttop) { popup_menu(c); return; } if (win == c->menu_top) { if (c->menuup && (sub == c->menu_dismiss)) { popdown_menu(c); } else if (!c->frozen && (sub == c->menu_freeze)) { c->frozen = 1; popdown_menu(c); } else if (c->frozen && !c->alert && (sub == c->menu_thaw)) { c->frozen = 0; popdown_menu(c); } else if (sub == c->menu_kill) { c->dead = 1; } else if (c->alert && !c->connalert && (sub == c->menu_noop)) { c->alert = 0; relayout_windows(c); pending_noop(c); process_client_input(c); } else if (c->alert && (sub == c->menu_allow)) { c->alert = 0; relayout_windows(c); if (c->connalert) { conn_allow(c); } else { pending_allow(c); } process_client_input(c); } else if (c->alert && (sub == c->menu_disable)) { c->noalerts = 1; c->alert = 0; relayout_windows(c); if (c->connalert) { conn_allow(c); } else { pending_allow(c); } process_client_input(c); } return; } } } /* XXX wish we didn't have to depend on Window and Drawable being "the same thing", in redisplay() and handle_event() */ static void redisplay(Drawable win, UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), int count) { CONN *c; if (count != 0) return; for (c=conns_h;c;c=c->flink) { if (win == (Drawable)c->text) { XDrawString(disp,c->text,wingc,-c->textbound.lbearing,font->ascent,c->listtext,strlen(c->listtext)); } } } static void handle_event(XEvent *e) { switch (e->type) { default: break; case Expose: /* XExposeEvent - xexpose */ redisplay((Drawable)e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; case GraphicsExpose: /* XGraphicsExposeEvent - xgraphicsexpose */ redisplay(e->xgraphicsexpose.drawable,e->xgraphicsexpose.x,e->xgraphicsexpose.y,e->xgraphicsexpose.width,e->xgraphicsexpose.height,e->xgraphicsexpose.count); break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ if (e->xbutton.send_event) return; buttonpress(e->xbutton.window,e->xbutton.subwindow,e->xbutton.x,e->xbutton.y,e->xbutton.state,e->xbutton.button,e->xbutton.time); break; } } static void closefd(FD *f) { IOSEG *i; for (i=f->wq;i;i=i->link) if (i->free) xfree(i->free); *f->wqt = freeiosegs; freeiosegs = f->wq; close(f->fd); } static void setup_fd(CONN *c, FD *f, int fd, const char *tag) { f->fd = fd; f->debugtag = tag; f->ateof = 0; f->conn = c; f->wq = 0; f->wqt = &f->wq; f->wqbytes = 0; f->rbhead = 0; f->rbtail = 0; f->rbfill = 0; } static void create_conn_windows(CONN *c) { unsigned long int attrmask; XSetWindowAttributes attr; int y; attrmask = 0; attr.background_pixel = bdcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; y = baselineskip + (2 * margin); c->top = XCreateWindow(disp,topwin,0,-y,65535,y,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,c->top); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ButtonPressMask; attrmask |= CWEventMask; c->texttop = XCreateWindow(disp,c->top,0,0,1,baselineskip+(2*margin),0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,c->texttop); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; c->text = XCreateWindow(disp,c->texttop,0,margin,1,baselineskip,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,c->text); c->textw = -1; c->topy = -1; } static void changed_listtext(CONN *c) { XWindowChanges chg; XTextExtents(font,c->listtext,strlen(c->listtext),XTE_JUNK,&c->textbound); chg.width = c->textbound.rbearing - c->textbound.lbearing; XConfigureWindow(disp,c->text,CWWidth,&chg); } static CONN *accept_conn(void) { int newfd; int xfd; int ifd; struct sockaddr_in sin; struct sockaddr_in csin; int sinlen; CONN *c; int val; sinlen = sizeof(sin); newfd = accept(accfd,(struct sockaddr *)&sin,&sinlen); if (newfd < 0) return(0); val = 1; ioctl(newfd,FIONBIO,&val); ifd = socket(AF_INET,SOCK_STREAM,0); if (ifd < 0) { fprintf(stderr,"%s: can't set up ident query: socket: %s\n",__progname,strerror(errno)); close(newfd); return(0); } xfd = socket(AF_INET,SOCK_STREAM,0); if (xfd < 0) { fprintf(stderr,"%s: can't make X connection: socket: %s\n",__progname,strerror(errno)); close(newfd); close(ifd); return(0); } c = malloc(sizeof(CONN)); c->debugtag = malloc(25); sprintf(c->debugtag,"%s/%hu",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port)); dprintf(c,"new connection"); val = 1; ioctl(xfd,FIONBIO,&val); bzero((char *)&csin,sizeof(csin)); csin.sin_family = AF_INET; csin.sin_addr = x_addr; csin.sin_port = htons(BASE_X_PORT+x_disp); if (connect(xfd,(struct sockaddr *)&csin,sizeof(csin)) < 0) { if (errno == EINPROGRESS) { dprintf(c,"X connection in progress"); c->xcip = 1; } else { const char *s; s = strerror(errno); dprintf(c,"X connection failed: %s",s); fprintf(stderr,"%s: can't make X connection: connect: %s\n",__progname,s); close(newfd); close(xfd); close(ifd); xfree(c); return(0); } } else { dprintf(c,"X connection succeeded"); c->xcip = 0; } c->ifd = ifd; c->identreply = malloc(1); c->identreplen = 0; c->cuser = 0; val = 1; ioctl(ifd,FIONBIO,&val); /* we have to make sure the ident connection originates from the address we received the connection on, or it won't work */ sinlen = sizeof(csin); if (getsockname(newfd,(struct sockaddr *)&csin,&sinlen) < 0) { const char *s; s = strerror(errno); dprintf(c,"getsockname failed: %s",s); fprintf(stderr,"%s: getsockname failed: %s\n",__progname,s); close(newfd); close(xfd); close(ifd); xfree(c); return(0); } csin.sin_port = 0; if (bind(ifd,(struct sockaddr *)&csin,sizeof(csin)) < 0) { const char *s; s = strerror(errno); dprintf(c,"local bind failed: %s",s); fprintf(stderr,"%s: local bind failed: %s\n",__progname,s); close(newfd); close(xfd); close(ifd); xfree(c); return(0); } bzero((char *)&csin,sizeof(csin)); csin.sin_family = AF_INET; csin.sin_addr = sin.sin_addr; csin.sin_port = htons(IDENT_PORT); if (connect(ifd,(struct sockaddr *)&csin,sizeof(csin)) < 0) { if (errno == EINPROGRESS) { dprintf(c,"ident connection CONNECTING"); c->iip = CIIP_CONNECTING; } else { dprintf(c,"ident connection failed: %s",strerror(errno)); c->iip = CIIP_NOT; close(ifd); } } else { dprintf(c,"ident connection CONNECTED"); c->iip = CIIP_CONNECTED; } c->dead = 0; c->cfirst = 1; c->cauthn = 0; c->cauthd = 0; c->xfirst = 1; c->xrfail = 0; c->xrsucc = 0; c->frozen = 0; c->alert = 0; c->connalert = 0; c->menuup = 0; c->creqhdr = 0; c->cblind = 0; c->cskip = 0; c->noalerts = 0; c->menu_top = None; c->alert_text = None; c->freeze_win = None; setup_fd(c,&c->c,newfd,"client"); setup_fd(c,&c->x,xfd,"server"); c->caddr = sin; c->topw = 1; c->listtext = copyofstr(inet_ntoa(sin.sin_addr)); c->setupbuf = 0; c->auth_n_len = 0; c->auth_d_len = 0; c->auth_n = 0; c->auth_d = 0; c->reqbuf = 0; c->roots = 0; c->bellcount = BELL_ALLOWANCE; c->lastbell.tv_sec = 0; c->flink = 0; c->blink = conns_t; conns_t = c; if (c->blink) c->blink->flink = c; else conns_h = c; nconns ++; create_conn_windows(c); changed_listtext(c); return(c); } static void destroy_conn(CONN *c) { dprintf(c,"destroying"); if (c->flink) c->flink->blink = c->blink; else conns_t = c->blink; if (c->blink) c->blink->flink = c->flink; else conns_h = c->flink; nconns --; xfree(c->cuser); if (c->iip != CIIP_NOT) { close(c->ifd); xfree(c->identreply); } closefd(&c->c); closefd(&c->x); xfree(c->auth_n); xfree(c->auth_d); XDestroyWindow(disp,c->top); xfree(c->listtext); xfree(c->setupbuf); xfree(c->roots); xfree(c->debugtag); xfree(c); } static void tryread(FD *f) { int sp; int n; sp = IOBUFSIZE - f->rbhead; if (sp > IOBUFSIZE-f->rbfill) sp = IOBUFSIZE - f->rbfill; if (sp == 0) abort(); n = read(f->fd,&f->rbuf[f->rbhead],sp); if ((n < 0) && (errno == EWOULDBLOCK)) { dprintf(f->conn,"%s read EWOULDBLOCK",f->debugtag); return; } if (n <= 0) { if (n < 0) { dprintf(f->conn,"%s read failed: %s",f->debugtag,strerror(errno)); } else { dprintf(f->conn,"%s read EOF",f->debugtag); } f->ateof = 1; return; } dprintf(f->conn,"%s read %d",f->debugtag,n); f->rbfill += n; f->rbhead += n; if (f->rbhead >= IOBUFSIZE) f->rbhead = 0; } static void trywrite(FD *f) { struct iovec iov[MAX_IOV_LEN]; int niov; struct iovec *ivp; IOSEG *i; int trylen; int n; niov = 0; trylen = 0; for (i=f->wq;i&&(niovlink) { ivp = &iov[niov++]; ivp->iov_base = (caddr_t) deconst(i->buf); ivp->iov_len = i->nb; trylen += i->nb; } n = writev(f->fd,&iov[0],niov); if ((n < 0) && (errno == EWOULDBLOCK)) { dprintf(f->conn,"%s write EWOULDBLOCK",f->debugtag); return; } if (n < 0) { dprintf(f->conn,"%s write failed: %s",f->debugtag,strerror(errno)); f->conn->dead = 1; return; } dprintf(f->conn,"%s write %d",f->debugtag,n); f->wqbytes -= n; i = f->wq; while (i && (i->nb <= n)) { IOSEG *i2; i2 = i->link; n -= i->nb; if (i->free) xfree(i->free); i->link = freeiosegs; freeiosegs = i; i = i2; } if (n > 0) { i->buf += n; i->nb -= n; } f->wq = i; if (! i) f->wqt = &f->wq; } static void process_ident_reply(CONN *c) { char *cp; c->identreply[c->identreplen] = '\0'; dprintf(c,"processing ident reply `%s'",c->identreply); cp = index(c->identreply,':'); if (cp) { for (cp++;*cp&&isspace(*cp);cp++) ; if ( (cp[0] == 'U') && (cp[1] == 'S') && (cp[2] == 'E') && (cp[3] == 'R') && (cp[4] == 'I') && (cp[5] == 'D') ) { cp = index(cp+6,':'); if (cp) { cp = index(cp+1,':'); if (cp) { char *ia; int n; char *beg; beg = ++cp; while (1) { if (!cp[0]) break; if ((cp[0] == '\r') && (cp[1] == '\n')) break; cp ++; } xfree(c->cuser); c->cuser = malloc((cp-beg)+1); bcopy(beg,c->cuser,cp-beg); c->cuser[cp-beg] = '\0'; ia = inet_ntoa(c->caddr.sin_addr); n = strlen(c->cuser) + 1 + strlen(ia); xfree(c->listtext); c->listtext = malloc(n+1); sprintf(c->listtext,"%s@%s",c->cuser,ia); dprintf(c,"listtext now `%s'",c->listtext); changed_listtext(c); relayout_windows(c); } } } } xfree(c->identreply); } static void fdread(FD *f, void *buf, int nb) { int n; if (nb > f->rbfill) abort(); n = IOBUFSIZE - f->rbtail; if (nb > n) { if (n > 0) { bcopy(&f->rbuf[f->rbtail],buf,n); nb -= n; buf = ((char *)buf) + n; f->rbfill -= n; } f->rbtail = 0; } bcopy(&f->rbuf[f->rbtail],buf,nb); f->rbtail += nb; f->rbfill -= nb; if (f->rbfill == 0) { f->rbhead = 0; f->rbtail = 0; } } static int handle_ChangeWindowAttributes(CONN *c) { #if 0 static const char *bitnames[] = { "background pixmap", "background pixel", "border pixmap", "border pixel", "bit gravity", "win gravity", "backing store", "backing planes", "backing pixel", "override redirect", "save under", "event mask", "don't-propagate mask", "colormap", "cursor" }; #endif unsigned long int window; unsigned long int mask; unsigned long int values[15]; int n; int o; int i; if ((c->reqlen < 12) || (c->reqlen % 4)) { return(-1); } n = (c->reqlen - 12) / 4; window = get4(c,c->reqbuf+4); mask = get4(c,c->reqbuf+8); if (mask & 0xffff8000) return(-1); o = 12; for (i=0;i<15;i++) { if ((mask >> i) & 1) { values[i] = get4(c,c->reqbuf+o); o += 4; n --; } } if (n != 0) return(-1); /* printf("CWA: window %08lx mask %08lx base %08lx roots",window,c->resid_mask,c->resid_base); for (i=c->nscreens-1;i>=0;i--) printf(" %08lx",c->roots[i]); printf("\n"); */ if ((window & ~c->resid_mask) != c->resid_base) { for (i=c->nscreens-1;i>=0;i--) if (window == c->roots[i]) break; if (i < 0) { popup_alert(c,"dubious ChangeWindowAttributes"); return(1); } } pending_allow(c); return(0); } static void skipspaces(FILE *f) { int c; while (1) { c = getc(f); if (c == EOF) return; if (! isspace(c)) { ungetc(c,f); return; } } } static void loadmask(MASK *m, unsigned int *vv) { m->mask = (vv[4] << 24) | (vv[5] << 16) | (vv[6] << 8) | vv[7]; m->value = ((vv[0] << 24) | (vv[1] << 16) | (vv[2] << 8) | vv[3]) & m->mask; } static int readmask(FILE *f, MASK *m) { int c; unsigned int v; int star; unsigned int val[8]; int x; static char sep[8] = ".../...\0"; top:; skipspaces(f); switch (getc(f)) { case 'a': if (getc(f) != 'l') return(0); switch (getc(f)) { case 'l': if (getc(f) != 'o') return(0); if (getc(f) != 'w') return(0); m->action = MA_ALLOW; break; case 'e': if (getc(f) != 'r') return(0); if (getc(f) != 't') return(0); m->action = MA_ALERT; break; default: return(0); break; } break; case 'd': if (getc(f) != 'e') return(0); if (getc(f) != 'n') return(0); if (getc(f) != 'y') return(0); m->action = MA_DENY; break; case 't': if (getc(f) != 'r') return(0); if (getc(f) != 'u') return(0); if (getc(f) != 's') return(0); if (getc(f) != 't') return(0); m->action = MA_TRUST; break; case '#': while (1) { c = getc(f); if (c == '\n') goto top; if (c == EOF) return(0); } break; default: return(0); break; } skipspaces(f); star = 0; for (x=0;;x++) { c = getc(f); if (c == '*') { if (x > 3) return(0); star |= 1 << x; c = getc(f); } else { if (! isdigit(c)) return(0); v = 0; do { v = (10 * v) + (c - '0'); c = getc(f); } while (isdigit(c)); if (v > 255) return(0); val[x] = v; } switch (sep[x]) { case '.': if (c != '.') return(0); break; case '/': if (star) { int i; for (i=0;i<4;i++) { if (star & (1 << i)) { val[i] = 0; val[i+4] = 0; } else { val[i+4] = 255; } } loadmask(m,&val[0]); return(1); } if (c != '/') { ungetc(c,f); skipspaces(f); c = getc(f); } if (c != '/') { ungetc(c,f); val[4] = 255; val[5] = 255; val[6] = 255; val[7] = 255; loadmask(m,&val[0]); return(1); } skipspaces(f); break; default: loadmask(m,&val[0]); return(1); } } } static int initial_action(CONN *c) { static time_t mtime = 0; static MASK *list = 0; MASK *t; unsigned long int a; struct stat stb; if (configfile == 0) { char *home; home = getenv("HOME"); if (home == 0) return(MA_ALLOW); configfile = malloc(strlen(home)+1+3+1); sprintf(configfile,"%s/.xc",home); } if (stat(configfile,&stb) < 0) { if (errno == ENOENT) return(MA_ALLOW); return(MA_ALERT); } if (stb.st_mtime != mtime) { MASK **tail; FILE *f; MASK m; f = fopen(configfile,"r"); if (f) { mtime = stb.st_mtime; while (list) { t = list; list = t->link; free(t); } tail = &list; while (readmask(f,&m)) { t = malloc(sizeof(MASK)); *t = m; *tail = t; tail = &t->link; #if 0 printf("[read %ld.%ld.%ld.%ld/%ld.%ld.%ld.%ld action ", m.value>>24,(m.value>>16)&0xff,(m.value>>8)&0xff,m.value&0xff, m.mask>>24,(m.mask>>16)&0xff,(m.mask>>8)&0xff,m.mask&0xff); switch (m.action) { case MA_ALLOW: printf("allow"); break; case MA_ALERT: printf("alert"); break; case MA_DENY: printf("deny"); case MA_TRUST: printf("trust"); break; default: printf("%d",m.action); break; } printf("]\n"); #endif } fclose(f); *tail = 0; } } a = ntohl(c->caddr.sin_addr.s_addr); for (t=list;t;t=t->link) { if ((a & t->mask) == t->value) { return(t->action); } } return(MA_ALERT); } static int skip_bell(CONN *c) { struct timeval now; gettimeofday(&now,0); /* this comparison is > rather than != to defend against timewarps. note that now is copied to lastbell before returning, so a timewarp will affect at most one bell. */ if (now.tv_sec > c->lastbell.tv_sec) { if (now.tv_sec > c->lastbell.tv_sec+BELL_ALLOWANCE) { c->bellcount = BELL_ALLOWANCE; } else { c->bellcount += now.tv_sec - c->lastbell.tv_sec; if (c->bellcount > BELL_ALLOWANCE) { c->bellcount = BELL_ALLOWANCE; } } } if (c->bellcount < 1) { c->lastbell = now; return(1); } else { c->bellcount --; c->lastbell = now; return(0); } } static void process_client_input(CONN *c) { if (c->c.ateof && (c->cfirst || c->cauthn || c->cauthd)) { c->dead = 1; return; } if (c->cfirst) { char firstblk[12]; if (c->c.rbfill < 12) return; fdread(&c->c,&firstblk[0],12); switch (firstblk[0]) { case 0x42: c->bytesex = BYTESEX_MSB; break; case 0x6c: c->bytesex = BYTESEX_LSB; break; default: c->dead = 1; return; } c->c_proto_major = get2(c,&firstblk[2]); c->c_proto_minor = get2(c,&firstblk[4]); c->auth_n_len = get2(c,&firstblk[6]); c->auth_d_len = get2(c,&firstblk[8]); c->auth_n = malloc(max(pad4(c->auth_n_len),c->auth_n_len+1)); c->auth_d = malloc(max(pad4(c->auth_d_len),c->auth_d_len+1)); c->creadleft = pad4(c->auth_n_len); c->creadptr = c->auth_n; c->cfirst = 0; c->cauthn = 1; } while (1) { int n; n = (c->creadleft < c->c.rbfill) ? c->creadleft : c->c.rbfill; if (c->cblind || c->cskip) { int m; if (c->c.rbfill < 1) return; m = IOBUFSIZE - c->c.rbtail; if (n >= m) { if (! c->cskip) { queue_write_buf(&c->x,&c->c.rbuf[c->c.rbtail],m,QWB_COPY); queue_write_buf(&c->x,&c->c.rbuf[0],n-m,QWB_COPY); } c->c.rbtail = n - m; } else { if (! c->cskip) { queue_write_buf(&c->x,&c->c.rbuf[c->c.rbtail],n,QWB_COPY); } c->c.rbtail += n; } c->c.rbfill -= n; c->creadleft -= n; if (c->creadleft < 1) { c->cblind = 0; c->cskip = 0; begin_request(c); } continue; } fdread(&c->c,c->creadptr,n); c->creadleft -= n; c->creadptr = ((char *)c->creadptr) + n; if (c->creadleft > 0) return; if (c->cauthn) { c->auth_n[c->auth_n_len] = '\0'; c->creadleft = pad4(c->auth_d_len); c->creadptr = c->auth_d; c->cauthn = 0; c->cauthd = 1; } else if (c->cauthd) { c->auth_d[c->auth_d_len] = '\0'; c->cauthd = 0; switch (initial_action(c)) { case MA_TRUST: c->noalerts = 1; conn_allow(c); break; case MA_ALLOW: conn_allow(c); break; case MA_ALERT: c->connalert = 1; popup_alert(c,"New connection"); return; break; case MA_DENY: c->dead = 1; return; break; default: abort(); break; } } else if (c->creqhdr) { const char *dubious; int prelim; int handled; c->reqlen = get2(c,&c->reqhdr[2]) * 4; dubious = 0; prelim = 0; handled = 0; switch (c->reqhdr[0]) { case 104: /* Bell */ if (skip_bell(c)) { pending_noop(c); handled = 1; } break; } if (! handled) { if (! c->noalerts) { switch (c->reqhdr[0]) { case 2: /* ChangeWindowAttributes */ prelim = c->reqlen; break; case 109: /* ChangeHosts */ dubious = "Attempt to change host list"; break; case 111: /* SetAccessControl */ dubious = "Attempt to enable/disable access control"; break; } } if (dubious) { popup_alert(c,dubious); return; } if (prelim) { if (prelim < 4) abort(); c->reqbuflen = prelim; c->creqhdr = 0; xfree(c->reqbuf); c->reqbuf = malloc(c->reqbuflen); c->reqbuf[0] = c->reqhdr[0]; c->reqbuf[1] = c->reqhdr[1]; c->reqbuf[2] = c->reqhdr[2]; c->reqbuf[3] = c->reqhdr[3]; c->creadptr = c->reqbuf + 4; c->creadleft = c->reqbuflen - 4; } else { blind_request(c,0); } } } else { int code; switch (c->reqhdr[0]) { case 2: /* ChangeWindowAttributes */ code = handle_ChangeWindowAttributes(c); break; default: abort(); break; } switch (code) { case -1: c->dead = 1; /* fall through */ case 1: return; break; } } } } static int unpack_setup(CONN *c) { int n; int v; int p; int m; int o; int i; int j; int k; if (c->setupbytes < 32) return(-1); v = get2(c,c->setupbuf+16); n = c->setupbuf[21]; p = pad4npad(v); m = c->setupbytes - (32 + v + p + (8 * n)); if ((m < 0) || (m % 4)) return(-1); c->resid_base = get4(c,c->setupbuf+4); c->resid_mask = get4(c,c->setupbuf+8); c->nscreens = c->setupbuf[20]; c->roots = malloc(c->nscreens*sizeof(unsigned long int)); o = 32 + v + p + (8 * n); for (i=0;inscreens;i++) { if (o+40 > c->setupbytes) return(-1); c->roots[i] = get4(c,c->setupbuf+o); j = c->setupbuf[o+39]; o += 40; for (;j>0;j--) { if (o+8 > c->setupbytes) return(-1); k = get2(c,c->setupbuf+o+2); o += 8 + (24 * k); } } if (o != c->setupbytes) return(-1); return(0); } static void process_server_input(CONN *c) { if (c->c.ateof && (c->xfirst || c->xrfail || c->xrsucc)) { c->dead = 1; return; } if (c->xfirst) { char rbuf[8]; int nl; if (c->x.rbfill < 8) return; fdread(&c->x,&rbuf[0],8); queue_write_buf(&c->c,&rbuf[0],8,QWB_COPY); c->x_proto_major = get2(c,&rbuf[2]); c->x_proto_minor = get2(c,&rbuf[4]); nl = get2(c,&rbuf[6]); c->setupbytes = 4 * nl; c->setupbuf = malloc(c->setupbytes); c->xreadleft = c->setupbytes; c->xreadptr = c->setupbuf; c->xfirst = 0; switch (rbuf[0]) { case 0: c->xrfail = 1; break; case 1: c->xrsucc = 1; break; default: c->dead = 1; return; break; } } if (c->xrfail) { int n; n = (c->xreadleft < c->x.rbfill) ? c->xreadleft : c->x.rbfill; fdread(&c->x,c->xreadptr,n); c->xreadleft -= n; c->xreadptr = ((char *)c->xreadptr) + n; if (c->xreadleft > 0) return; queue_write_buf(&c->c,c->setupbuf,c->setupbytes,QWB_STATIC); c->xrfail = 0; c->x.ateof = 1; c->x.rbfill = 0; c->x.rbhead = 0; c->x.rbtail = 0; return; } if (c->xrsucc) { int n; n = (c->xreadleft < c->x.rbfill) ? c->xreadleft : c->x.rbfill; fdread(&c->x,c->xreadptr,n); c->xreadleft -= n; c->xreadptr = ((char *)c->xreadptr) + n; if (c->xreadleft > 0) return; if (unpack_setup(c) < 0) { c->dead = 1; return; } queue_write_buf(&c->c,c->setupbuf,c->setupbytes,QWB_STATIC); c->xrsucc = 0; } /* * reply: 32 bytes, including a length field, maybe more --> first byte 1 * error: 32 bytes --> first byte 0, second byte is error number * event: 32 bytes --> first byte is event code */ if (c->x.rbfill < 1) return; { int n; n = IOBUFSIZE - c->x.rbtail; if (c->x.rbfill >= n) { queue_write_buf(&c->c,&c->x.rbuf[c->x.rbtail],n,QWB_COPY); queue_write_buf(&c->c,&c->x.rbuf[0],c->x.rbfill-n,QWB_COPY); } else { queue_write_buf(&c->c,&c->x.rbuf[c->x.rbtail],c->x.rbfill,QWB_COPY); } c->x.rbtail = 0; c->x.rbhead = 0; c->x.rbfill = 0; } } static void run(void) { int xfd; int selcnt; int selrv; CONN *c; int any; fd_set rfds; fd_set wfds; xfd = XConnectionNumber(disp); selcnt = 0; while (1) { if (selcnt > 600) { fprintf(stderr,"infinite select loop\n"); exit(1); } XFlush(disp); if (XQLength(disp) > 0) { do { XEvent e; XNextEvent(disp,&e); handle_event(&e); } while (XQLength(disp) > 0); selcnt = 0; continue; } if (selcnt > 0) nap(.1); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(accfd,&rfds); FD_SET(xfd,&rfds); c = conns_h; any = 0; while (c) { if ((c->iip != CIIP_NOT) || c->xcip) { switch (c->iip) { case CIIP_NOT: break; case CIIP_CONNECTING: FD_SET(c->ifd,&rfds); FD_SET(c->ifd,&wfds); break; case CIIP_CONNECTED: { char qbuf[64]; sprintf(&qbuf[0],"%hu, %d",ntohs(c->caddr.sin_port),BASE_X_PORT+dispno); write(c->ifd,&qbuf[0],strlen(&qbuf[0])); write(c->ifd,"\r\n",2); dprintf(c,"ident query `%s' sent",&qbuf[0]); shutdown(c->ifd,1); } c->iip = CIIP_SENT; /* fall through */ case CIIP_SENT: FD_SET(c->ifd,&rfds); break; } if (c->xcip) { FD_SET(c->x.fd,&rfds); FD_SET(c->x.fd,&wfds); } c = c->flink; } else { if (c->dead || (c->c.ateof && !c->x.wq && c->x.ateof && !c->c.wq)) { CONN *f; f = c->flink; destroy_conn(c); any ++; c = f; } else { if (c->c.wq) FD_SET(c->c.fd,&wfds); if (c->x.wq) FD_SET(c->x.fd,&wfds); if (c->c.ateof && !c->x.wq) shutdown(c->x.fd,1); if (c->x.ateof && !c->c.wq) shutdown(c->c.fd,1); if (!c->frozen && !c->alert) { if ( !c->c.ateof && (c->c.rbfill < IOBUFSIZE) && (c->x.wqbytes < MAXBUFFERED) ) FD_SET(c->c.fd,&rfds); if ( !c->x.ateof && (c->x.rbfill < IOBUFSIZE) && (c->c.wqbytes < MAXBUFFERED) ) FD_SET(c->x.fd,&rfds); } c = c->flink; } } } if (any) { relayout_windows(0); continue; } selrv = select(FD_SETSIZE,&rfds,&wfds,(fd_set *)0,(struct timeval *)0); if (selrv < 0) { if (errno != EINTR) { fprintf(stderr,"%s: select error: %s\n",__progname,strerror(errno)); exit(1); } continue; } if (selrv == 0) continue; if (FD_ISSET(xfd,&rfds)) { int bpend; ioctl(xfd,FIONREAD,&bpend); if (bpend == 0) /* socket EOF */ { fprintf(stderr,"%s: X EOF\n",__progname); exit(0); } XEventsQueued(disp,QueuedAfterReading); selcnt ++; } if (FD_ISSET(accfd,&rfds)) { c = accept_conn(); if (c) { relayout_windows(0); relayout_windows(c); } continue; } for (c=conns_h;c;c=c->flink) { if ((c->iip != CIIP_NOT) || c->xcip) { switch (c->iip) { case CIIP_NOT: break; case CIIP_CONNECTING: if (FD_ISSET(c->ifd,&rfds) || FD_ISSET(c->ifd,&wfds)) { int err; int errsize; errsize = sizeof(err); getsockopt(c->ifd,SOL_SOCKET,SO_ERROR,&err,&errsize); if (err) { dprintf(c,"ident connection failed: %s",strerror(err)); close(c->ifd); c->iip = CIIP_NOT; } else { dprintf(c,"ident connection succeeded"); c->iip = CIIP_CONNECTED; } } break; case CIIP_SENT: if (FD_ISSET(c->ifd,&rfds)) { int nb; int n; ioctl(c->ifd,FIONREAD,&nb); if (nb == 0) { dprintf(c,"ident EOF (FIONREAD)"); close(c->ifd); c->iip = CIIP_NOT; process_ident_reply(c); } else { c->identreply = xrealloc(c->identreply,c->identreplen+nb+1); n = read(c->ifd,c->identreply+c->identreplen,nb); if (n < 0) { dprintf(c,"ident read error %s",strerror(errno)); close(c->ifd); c->iip = CIIP_NOT; xfree(c->identreply); } else if (n == 0) { dprintf(c,"ident EOF (read)"); close(c->ifd); c->iip = CIIP_NOT; process_ident_reply(c); } else { dprintf(c,"ident read %d",n); c->identreplen += n; } } } break; } if (c->xcip) { if (FD_ISSET(c->x.fd,&rfds) || FD_ISSET(c->x.fd,&wfds)) { int err; int errsize; errsize = sizeof(err); getsockopt(c->x.fd,SOL_SOCKET,SO_ERROR,&err,&errsize); if (err) { dprintf(c,"X connection failed: %s",strerror(err)); c->dead = 1; } else { dprintf(c,"X connection succeeded"); c->xcip = 0; } } } continue; } if (FD_ISSET(c->c.fd,&rfds)) { tryread(&c->c); process_client_input(c); } if (FD_ISSET(c->x.fd,&rfds)) { tryread(&c->x); process_server_input(c); } if (c->c.wq && FD_ISSET(c->c.fd,&wfds)) { trywrite(&c->c); } if (c->x.wq && FD_ISSET(c->x.fd,&wfds)) { trywrite(&c->x); } } } } 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 err(Display *d, XErrorEvent *ee) { return((*preverr)(d,ee)); } static int ioerr(Display *d) { return((*prevIOerr)(d)); } int main(int, char **); int main(int ac, char **av) { #ifdef NO_PROGNAME __progname = av[0]; #endif saveargv(ac,av); handleargs(ac,av); XrmInitialize(); disp = open_display(displayname); if (synch) XSynchronize(disp,True); preverr = XSetErrorHandler(err); prevIOerr = XSetIOErrorHandler(ioerr); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); setup_limit(); setup_db(); maybeset(&geometryspec,get_default_value("geometry","Geometry")); maybeset(&fontname,get_default_value("font","Font")); 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(&bordermstr,get_default_value("borderMargin","BorderMargin")); maybeset(&name,get_default_value("name","Name")); maybeset(&iconname,get_default_value("iconName","IconName")); maybeset(&visualstr,get_default_value("visual","Visual")); maybeset(&configfile,get_default_value("configFile","ConfigFile")); setup_visual(); setup_font(); setup_colors(); setup_numbers(); setup_windows(); setup_pixmaps(); setup_addr(); setup_signals(); setup_accept(); setup_conns(); setup_auth(); setup_fork(); run(); exit(0); }