#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ext.h" #include "sets.h" #include "atoms.h" #include "structs.h" #include "fmt-ext.h" #include "fmt-core.h" #include "fmt-util.h" extern const char *__progname; #ifdef OWN_PROGNAME const char *__progname; #endif static char *xdisplay; static int dumphex = 0; static int dohunt = 0; static int doforce = 0; static int dispno = -1; static int dosplit = 0; static int dofork = 0; static int dostamp = 0; static int printpid = 0; static int idledie = 0; static const char *outname = 0; #define BASE_TCP_X_PORT 6000 typedef struct pktopriv PKTOPRIV; struct pktopriv { FLOW *f; int atbol; } ; static struct sockaddr_in x_addr_in; static struct sockaddr_un x_addr_un; static struct sockaddr *x_addr; static int x_addr_len; static struct sockaddr_in x_addr_in_tmp; static struct sockaddr_un x_addr_un_tmp; static struct sockaddr *x_addr_tmp; static int acc_tcp; static int acc_tcp_px; static int acc_local; static int acc_local_px; static CONN *conns; static CONN *conntail; static unsigned int connserial; static struct pollfd *pv = 0; static int pa = 0; static int pn; static struct iovec *iov = 0; static int iova = 0; static int iovn; static void (*packet_print_abort)(void); static int outfd = -1; static void *dequal(const volatile void *v) { return((((const volatile char *)v)-(const volatile char *)0)+(char *)0); } 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 != '-') { 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,"-hex")) { dumphex = 1; continue; } if (!strcmp(*av,"-hunt")) { dohunt = 1; continue; } if (!strcmp(*av,"-force")) { doforce = 1; continue; } if (!strcmp(*av,"-split")) { dosplit = 1; continue; } if (!strcmp(*av,"-fork")) { dofork = 1; continue; } if (!strcmp(*av,"-stamp")) { dostamp = 1; continue; } if (!strcmp(*av,"-pid")) { printpid = 1; continue; } if (!strcmp(*av,"-idle")) { idledie = 1; continue; } if (!strcmp(*av,"-o")) { WANTARG(); outname = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (dosplit && !outname) outname = "xscope.out."; if (errs) exit(1); } 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_addr(void) { char *cp; int dispno; if (xdisplay == 0) xdisplay = getenv("DISPLAY"); if (xdisplay == 0) { fprintf(stderr,"%s: no $DISPLAY, you must use -X\n",__progname); exit(1); } for (cp=xdisplay;*cp&&(*cp!=':');cp++) ; if (!*cp) { fprintf(stderr,"%s: no colon in display name: %s\n",__progname,xdisplay); exit(1); } *cp++ = '\0'; dispno = atoi(cp); if (!strcmp(xdisplay,"") || !strcmp(xdisplay,"unix")) { bzero(&x_addr_un,sizeof(x_addr_un)); x_addr_un.sun_family = AF_LOCAL; sprintf(&x_addr_un.sun_path[0],"/tmp/.X11-unix/X%d",dispno); x_addr = (struct sockaddr *)&x_addr_un; x_addr_tmp = (struct sockaddr *)&x_addr_un_tmp; x_addr_len = sizeof(x_addr_un) - sizeof(x_addr_un.sun_path) + strlen(&x_addr_un.sun_path[0]) + 1; } else { bzero(&x_addr_in,sizeof(x_addr_in)); x_addr_in.sin_family = AF_INET; x_addr_in.sin_port = htons((short int)(BASE_TCP_X_PORT+dispno)); x_addr_in.sin_addr.s_addr = inet_addr(xdisplay); if (x_addr_in.sin_addr.s_addr == -1) { struct hostent *hp; hp = gethostbyname(xdisplay); if (hp == 0) { fprintf(stderr,"%s: unknown host %s\n",__progname,xdisplay); exit(1); } if ((hp->h_addrtype != AF_INET) || (hp->h_length != sizeof(struct in_addr))) { fprintf(stderr,"%s: host %s on strange network\n",__progname,xdisplay); exit(1); } bcopy(hp->h_addr,(char *)&x_addr_in.sin_addr,sizeof(struct in_addr)); } x_addr = (struct sockaddr *)&x_addr_in; x_addr_tmp = (struct sockaddr *)&x_addr_in_tmp; x_addr_len = sizeof(struct sockaddr_in); } cp[-1] = ':'; } static void setup_accept(void) { struct sockaddr_in sin; char *sunpath; int sunlen; struct sockaddr_un *sun; if (dispno < 0) { if (dohunt) { dispno = 25; } else { fprintf(stderr,"%s: must specify a display number with -dispno, or use -hunt\n",__progname); exit(1); } } sunpath = 0; sun = 0; acc_tcp = -1; acc_local = -1; while (1) { free(sunpath); sunpath = 0; free(sun); sun = 0; if (acc_tcp >= 0) close(acc_tcp); if (acc_local >= 0) close(acc_local); asprintf(&sunpath,"/tmp/.X11-unix/X%d",dispno); sunlen = sizeof(struct sockaddr_un) - sizeof(sun->sun_path) + strlen(sunpath) + 1; sun = malloc(sunlen); sun->sun_family = AF_LOCAL; sun->sun_len = sunlen; strcpy(&sun->sun_path[0],sunpath); if (! doforce) { acc_local = socket(AF_LOCAL,SOCK_STREAM,0); if (acc_local < 0) { fprintf(stderr,"%s: can't create local accept socket: %s\n",__progname,strerror(errno)); exit(1); } if (connect(acc_local,(struct sockaddr *)sun,sunlen) >= 0) { close(acc_local); if (dohunt) { dispno ++; continue; } fprintf(stderr,"%s: can't bind local accept socket: %s\n",__progname,strerror(EADDRINUSE)); exit(1); } close(acc_local); } unlink(sunpath); acc_local = socket(AF_LOCAL,SOCK_STREAM,0); if (acc_local < 0) { fprintf(stderr,"%s: can't create local accept socket: %s\n",__progname,strerror(errno)); exit(1); } if (bind(acc_local,(struct sockaddr *)sun,sunlen) < 0) { if (dohunt && (errno == EADDRINUSE)) { dispno ++; continue; } fprintf(stderr,"%s: can't bind local accept socket: %s\n",__progname,strerror(errno)); exit(1); } bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons((short int)(BASE_TCP_X_PORT+dispno)); acc_tcp = socket(AF_INET,SOCK_STREAM,0); if (acc_tcp < 0) { fprintf(stderr,"%s: can't create TCP accept socket: %s\n",__progname,strerror(errno)); exit(1); } if (doforce) { int val; val = 1; if (setsockopt(acc_tcp,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) < 0) { fprintf(stderr,"%s: warning: can't set REUSEADDR: %s\n",__progname,strerror(errno)); } } if (bind(acc_tcp,(struct sockaddr *)&sin,sizeof(sin)) < 0) { if (dohunt && (errno == EADDRINUSE)) { close(acc_tcp); dispno ++; continue; } fprintf(stderr,"%s: can't bind TCP accept socket: %s\n",__progname,strerror(errno)); exit(1); } break; } if (dohunt) printf("%d\n",dispno); free(sunpath); free(sun); listen(acc_tcp,10); listen(acc_local,10); } static void setup_signals(void) { signal(SIGPIPE,SIG_IGN); /* we'd rather get EPIPE instead */ } static void setup_conns(void) { conns = 0; conntail = 0; connserial = 1; } static void pop_oq(FLOW *f) { OBLK *p; p = f->oq; f->oq = p->link; if (! f->oq) f->oqt = &f->oq; switch (p->type) { default: abort(); break; case OBT_EOF: break; case OBT_DATA: free(p->data.tofree); break; } free(p); } static void close_flow(FLOW *f) { get_free(f); free(f->ibuf); while (f->oq) pop_oq(f); } static EXPREP *pop_repq(CONN *c) { EXPREP *er; er = c->repq; if (! er) abort(); c->repq = er->link; if (! c->repq) c->repqt = &c->repq; return(er); } static void destroy_conn(CONN *c) { if (c->flink) c->flink->blink = c->blink; else conntail = c->blink; if (c->blink) c->blink->flink = c->flink; else conns = c->flink; fclose(c->out); close(c->client_fd); close(c->X_fd); free(c->remstr); close_flow(&c->ctox); close_flow(&c->xtoc); while (c->repq) { EXPREP *er; er = pop_repq(c); (*er->fn)(0,0,er->arg); free(er); } atoms_done(c->atoms); while (c->extensions) { EXTINFO *ei; ei = c->extensions; c->extensions = ei->link; free(ei); } free(c->ext_opc_vec); free(c->ext_ev_vec); free(c->ext_err_vec); free(c); } static int connect_to_X(void) { int xfd; xfd = socket(x_addr->sa_family,SOCK_STREAM,0); if (xfd < 0) { fprintf(stderr,"%s: can't create socket for X connection: %s\n",__progname,strerror(errno)); return(-1); } bcopy(x_addr,x_addr_tmp,x_addr_len); if (connect(xfd,x_addr_tmp,x_addr_len) < 0) { fprintf(stderr,"%s: can't connect to X: %s\n",__progname,strerror(errno)); return(-1); } return(xfd); } static void init_flow( FLOW *f, CONN *c, const char *dir, int srcb, int dstb, int srcfd, int dstfd, int init_n, void (*whendone)(FLOW *) ) { f->conn = c; f->dir = dir; f->srcbits = srcb; f->dstbits = dstb; f->srcfd = srcfd; f->dstfd = dstfd; f->ibuf = malloc(init_n); f->iballoc = init_n; f->ibfill = 0; f->ibfull = init_n; get_init(f); f->readdone = whendone; f->oq = 0; f->oqt = &f->oq; } static int packet_output_w(void *pv, const char *buf, int len) { PKTOPRIV *p; int i; FILE *f; p = pv; f = p->f->conn->out; for (i=0;iatbol) { fprintf(f,"%d %s\t",p->f->conn->serial,p->f->dir); } putc(buf[i],f); p->atbol = (buf[i] == '\n'); } return(len); } static int packet_output_c(void *pv) { PKTOPRIV *p; p = pv; fflush(p->f->conn->out); free(p); return(0); } static FILE *open_packet_output(FLOW *f) { PKTOPRIV *p; p = malloc(sizeof(PKTOPRIV)); p->f = f; p->atbol = 1; get_clear(f); packet_print_abort = 0; return(funopen(p,0,&packet_output_w,0,&packet_output_c)); } static void setfull(FLOW *f, unsigned int n) { if (n < f->ibfill) abort(); f->ibfull = n; if (f->ibfull > f->iballoc) { f->iballoc = f->ibfull; if (f->ibfill > 0) { f->ibuf = realloc(f->ibuf,f->ibfull); } else { free(f->ibuf); f->ibuf = malloc(f->ibfull); } } } static void queue_eof_blk(FLOW *f) { OBLK *b; b = malloc(sizeof(OBLK)); b->type = OBT_EOF; b->link = 0; *f->oqt = b; f->oqt = &b->link; } static void queue_data_copy(FLOW *f, const void *data, int len) { OBLK *b; b = malloc(sizeof(OBLK)); b->type = OBT_DATA; b->data.tofree = malloc(len); bcopy(data,b->data.tofree,len); b->data.b = b->data.tofree; b->data.l = len; b->data.p = 0; b->link = 0; *f->oqt = b; f->oqt = &b->link; } static void queue_ibuf_copy(FLOW *f) { queue_data_copy(f,f->ibuf,f->ibfill); } static void new_request(FLOW *); /* forward */ static void new_reply(FLOW *); /* forward */ void abort_packet_print(void) { (*packet_print_abort)(); } static void new_beginning(FLOW *f) { struct tm tm; time_t tt; char fbuf[32]; fprintf(f->conn->out,"----"); if (dostamp) { tt = f->stamp_tv.tv_sec; localtime_r(&tt,&tm); if ( (tm.tm_mday != f->conn->laststamp_tm.tm_mday) || (tm.tm_mon != f->conn->laststamp_tm.tm_mon) || (tm.tm_year != f->conn->laststamp_tm.tm_year) ) { strftime(&fbuf[0],sizeof(fbuf),"%Y-%m-%d %H:%M:%S",&tm); } else { strftime(&fbuf[0],sizeof(fbuf),"%H:%M:%S",&tm); } f->conn->laststamp_tm = tm; fprintf(f->conn->out," %lu.%06lu %s", (unsigned long int) f->stamp_tv.tv_sec, (unsigned long int) f->stamp_tv.tv_usec, &fbuf[0] ); } fprintf(f->conn->out,"\n"); } static void ctox_req(FLOW *f) { __label__ throw_to; FILE *o; int opc; int inc; unsigned char save[4]; void throw_out(void) { goto throw_to; } new_beginning(f); o = open_packet_output(f); packet_print_abort = &throw_out; if ((f->conn->flags & CF_BIGREQ) && (get2u(f,2) == 0)) { bcopy(f->ibuf+4,&save[0],4); bcopy(f->ibuf,f->ibuf+4,2); inc = 1; f->ibuf += 4; f->ibfill -= 4; f->ibfull -= 4; } else { inc = 0; } get2u(f,2); opc = get1u(f,0); if ((opc >= 1) && (opc <= 127)) { ctox_core(f,o); } else { ctox_ext(f,o); } if (dumphex) print_raw_data(o,f); throw_to:; get_done(f,o); fclose(o); if (inc) { bcopy(&save[0],f->ibuf,4); f->ibuf -= 4; } new_request(f); } static void regen_ext_vectors(CONN *c) { EXTINFO *ei; int n; EXTINFO *v[258]; void nonefill(unsigned char *tbl, int len) { int i; for (i=len-1;i>=0;i--) if (tbl[i] != TBL_UNK) tbl[i] = TBL_NONE; } void set_vec(EXTINFO ***vp) { EXTINFO **vc; if (n == 0) { vc = 0; } else { vc = malloc(n*sizeof(EXTINFO *)); bcopy(&v[0],vc,n*sizeof(EXTINFO *)); } free(*vp); *vp = vc; } nonefill(&c->ext_opc_tbl[0],128); nonefill(&c->ext_ev_tbl[0],64); nonefill(&c->ext_err_tbl[0],128); n = 0; for (ei=c->extensions;ei;ei=ei->link) { if (ei->opcode) { c->ext_opc_tbl[ei->opcode-128] = n; v[n++] = ei; } } set_vec(&c->ext_opc_vec); n = 0; for (ei=c->extensions;ei;ei=ei->link) { if (ei->eventbase) { c->ext_ev_tbl[ei->eventbase-64] = n; v[n++] = ei; } } set_vec(&c->ext_ev_vec); n = 0; for (ei=c->extensions;ei;ei=ei->link) { if (ei->errorbase) { c->ext_err_tbl[ei->errorbase-128] = n; v[n++] = ei; } } set_vec(&c->ext_err_vec); } void ext_values(FLOW *f, FILE *o, const char *name, int namelen, unsigned char present, unsigned char opc, unsigned char ev, unsigned char err) { int i; EXTINFO *ei; EXTDEF *d; if (! present) return; if ( ((opc != 0) && (opc < 128)) || ((ev != 0) && ((ev < 64) || (ev > 127))) || ((err != 0) && (err < 128)) ) abort(); f->conn->ext_ev_tbl[ev-64] = TBL_UNK; f->conn->ext_err_tbl[err-128] = TBL_UNK; for (i=n_exts-1;i>=0;i--) { d = exts[i]; if ((*d->match)(name,namelen)) { for (ei=f->conn->extensions;ei;ei=ei->link) { if (ei->def == d) { if ((ei->opcode != opc) || (ei->eventbase != ev) || (ei->errorbase != err)) { fprintf(o,"*** Extension %s changed values (opc/ev/err %u/%u/%u -> %u/%u/%u)\n", d->name, ei->opcode, ei->eventbase, ei->errorbase, opc, ev, err); ei->opcode = opc; ei->eventbase = ev; ei->errorbase = err; regen_ext_vectors(f->conn); } return; } } ei = malloc(sizeof(EXTINFO)); ei->def = d; ei->opcode = opc; ei->eventbase = ev; ei->errorbase = err; ei->link = f->conn->extensions; f->conn->extensions = ei; regen_ext_vectors(f->conn); return; } } fprintf(o,"*** Unrecognized extension, will not be handled\n"); f->conn->ext_opc_tbl[opc-128] = TBL_UNK; f->conn->ext_ev_tbl[ev-64] = TBL_UNK; f->conn->ext_err_tbl[err-128] = TBL_UNK; regen_ext_vectors(f->conn); } void set_eof(FLOW *f) { f->conn->flags |= f->srcbits & CF_EOF; shutdown(f->srcfd,SHUT_RD); queue_eof_blk(f); } void expect_reply(CONN *c, void (*fn)(FLOW *, FILE *, void *), void *arg) { EXPREP *er; er = malloc(sizeof(EXPREP)); er->seq = c->reqseq & 0xffff; er->fn = fn; er->arg = arg; er->link = 0; *c->repqt = er; c->repqt = &er->link; } void expect_another_reply(CONN *c, unsigned int seq, void (*fn)(FLOW *, FILE *, void *), void *arg) { EXPREP *er; er = malloc(sizeof(EXPREP)); er->seq = seq; er->fn = fn; er->arg = arg; er->link = c->repq; c->repq = er; if (c->repqt == &c->repq) c->repqt = &er->link; } static void ctox_reqhdr(FLOW *f) { int nw; FILE *o; f->conn->reqseq ++; nw = get2u(f,2); if ((nw == 0) && (f->conn->flags & CF_BIGREQ)) { nw = get2u(f,4); } if ((nw < 1) || (nw > f->conn->maxreq)) { o = open_packet_output(f); fprintf(o,"Request, sequence %d: length %u (INVALID)\n",f->conn->reqseq,nw); print_raw_data(o,f); get_disable(f); fclose(o); set_eof(f); return; } setfull(f,nw*4); if (nw > 1) { f->readdone = &ctox_req; } else { ctox_req(f); } } static void xtoc_error(FLOW *f) { __label__ throw_to; FILE *o; unsigned int ec; void throw_out(void) { goto throw_to; } new_beginning(f); o = open_packet_output(f); packet_print_abort = &throw_out; if (get1u(f,0) != 0) abort(); ec = get1u(f,1); if (ec < 128) { error_core(f,o); } else { error_ext(f,o); } if (dumphex) print_raw_data(o,f); throw_to:; get_done(f,o); fclose(o); new_reply(f); } static void xtoc_reply(FLOW *f) { __label__ throw_to; unsigned int seq; EXPREP *er; FILE *o; void throw_out(void) { goto throw_to; } while <"top"> (1) { if (get1u(f,0) != 1) abort(); seq = get2u(f,2); er = f->conn->repq; if ( er && (er->seq != seq) && (((seq+0x10000-er->seq)&0xffff) < 0x1000) ) { er = pop_repq(f->conn); fprintf(f->conn->out,"----\n"); o = open_packet_output(f); packet_print_abort = &throw_out; fprintf(o,"Lost reply, sequence %08x\n",er->seq); (*er->fn)(0,0,er->arg); free(er); fclose(o); continue <"top">; } break; } new_beginning(f); o = open_packet_output(f); packet_print_abort = &throw_out; get1u(f,0); if (er && (seq == er->seq)) { er = pop_repq(f->conn); (*er->fn)(f,o,er->arg); free(er); if (dumphex) print_raw_data(o,f); } else { fprintf(o,"Unexpected reply, length %u\n",f->ibfill); print_raw_data(o,f); } throw_to:; get_done(f,o); fclose(o); new_reply(f); } int format_event(FLOW *f, FILE *o) { __label__ throw_to; void (*prevthrow)(void); int rv; void throw_out(void) { goto throw_to; } prevthrow = packet_print_abort; packet_print_abort = &throw_out; if ((get1u(f,0) & 0x7f) < 2) { fprintf(o,"*** `Event' with non-event first byte 0x%02x\n",get1u(f,0)); print_raw_data(o,f); abort_packet_print(); } else if ((get1u(f,0) & 0x7f) < 64) { event_core(f,o); } else { event_ext(f,o); } rv = 0; if (0) { throw_to:; rv = 1; } packet_print_abort = prevthrow; return(rv); } static void xtoc_event(FLOW *f) { FILE *o; new_beginning(f); o = open_packet_output(f); if (! format_event(f,o)) { if (dumphex) print_raw_data(o,f); } get_done(f,o); fclose(o); new_reply(f); } static void xtoc_reehdr(FLOW *f) { unsigned int len; switch (f->ibuf[0]) { case 0: xtoc_error(f); break; case 1: len = get4u(f,4); if (len == 0) { xtoc_reply(f); } else { setfull(f,32+(4*len)); f->readdone = &xtoc_reply; return; } break; default: xtoc_event(f); break; } } static void new_request(FLOW *f) { if (f->conn->flags & f->srcbits & CF_EOF) return; queue_ibuf_copy(f); f->ibfill = 0; setfull(f,4); f->readdone = &ctox_reqhdr; } static void new_reply(FLOW *f) { if (f->conn->flags & f->srcbits & CF_EOF) return; queue_ibuf_copy(f); f->ibfill = 0; setfull(f,32); f->readdone = &xtoc_reehdr; } static void print_1_124(FILE *to, unsigned char v) { fprintf(to,"%u",v); switch (v) { case 8: case 16: case 32: break; default: fprintf(to," (INVALID)"); break; } } static void ctox_first(FLOW *f) { FILE *o; int pmaj; int pmin; int n; int d; new_beginning(f); o = open_packet_output(f); fprintf(o,"New connection: %s\n",f->conn->remstr); fprintf(o,"Connection setup: length %d\n",f->ibfull); fprintf(o," Endian: 0x%02x",get1u(f,0)); get_pad(f,1,1); switch (f->conn->endian) { default: abort(); break; case END_L: fprintf(o," (little-endian)"); break; case END_B: fprintf(o," (big-endian)"); break; } fprintf(o,"\n"); pmaj = get2u(f,2); pmin = get2u(f,4); fprintf(o," Protocol version: %u.%u",pmaj,pmin); if ((pmaj != 11) && (pmin != 0)) { fprintf(o," (UNSUPPORTED)"); print_raw_data(o,f); fclose(o); f->conn->flags |= CF_EOF_BOTH; return; } fprintf(o,"\n"); n = get2u(f,6); d = get2u(f,8); get_pad(f,10,2); if (f->ibfull != 12+padded(n)+padded(d)) abort(); print_padded_text_blk(f,o,12,n," Auth proto: ",0); print_bin_blk(f,o,12+padded(n),d," Auth data: ",0); get_pad(f,12+padded(n)+d,pad(d)); if (dumphex) print_raw_data(o,f); get_done(f,o); fclose(o); f->conn->reqseq = 0; new_request(f); } static void ctox_first_hdr(FLOW *f) { FILE *o; switch (get1u(f,0)) { case 0x42: f->conn->endian = END_B; break; case 0x6c: f->conn->endian = END_L; break; default: o = open_packet_output(f); fprintf(o,"Connection setup\n"); fprintf(o," Endian\t0x%02x (INVALID)",get1u(f,0)); print_raw_data(o,f); get_done(f,o); fclose(o); f->conn->flags |= CF_EOF_BOTH; return; } setfull(f,12+padded(get2u(f,6))+padded(get2u(f,8))); /* ibfull was 10 coming in, so can't be full already */ f->readdone = &ctox_first; } static void xtoc_first(FLOW *f) { __label__ ret; FILE *o; int n; int v; int m; int maxreq; unsigned int val; int off; int i; int j; int k; unsigned int pmaj; unsigned int pmin; void fail(const char *s) { fprintf(o,"%s\n",s); print_raw_data(o,f); fclose(o); f->conn->flags |= CF_EOF_BOTH; goto ret; } new_beginning(f); o = open_packet_output(f); fprintf(o,"Connection setup: length %d\n",f->ibfull); switch (get1u(f,0)) { case 0: fprintf(o," Success code: 0x00 (failure)\n"); break; case 1: fprintf(o," Success code: 0x01 (success)\n"); break; default: fprintf(o," Success code: 0x%02x (INVALID)\n",get1u(f,0)); break; } pmaj = get2u(f,2); pmin = get2u(f,4); fprintf(o," Protocol version: %u.%u",pmaj,pmin); if ((pmaj != 11) && (pmin != 0)) fail(" (UNSUPPORTED)"); switch (get1u(f,0)) { case 0: n = get1u(f,1); if (f->ibfill != 8+padded(n)) fail("*** Length wrong (for reason length)!"); if (f->ibfill != 8+(4*get2u(f,6))) fail("*** Length wrong (for data length)!"); print_padded_text_blk(f,o,8,n," Reason: ",0); break; case 1: get_pad(f,1,1); n = get2u(f,6); if (f->ibfill != 8+(4*n)) fail("*** Length wrong!"); v = get2u(f,24); m = get1u(f,28); n = get1u(f,29); if (f->ibfull < 8+32+padded(v)+(8*n)) fail("*** Too short!"); fprintf(o," Vendor release: %u\n",get4u(f,8)); fprintf(o," Resource ID 0x%08x, mask 0x%08x\n",get4u(f,12),get4u(f,16)); fprintf(o," Motion buffer size: %u\n",get4u(f,20)); maxreq = get2u(f,26); fprintf(o," Maximum request length: %u (%u bytes)",maxreq,maxreq*4); if (maxreq < 4096) fprintf(o," (INVALID)"); fprintf(o,"\n"); f->conn->maxreq = maxreq; fprintf(o," Image byte order: "); val = get1u(f,30); switch (val) { case 0: fprintf(o,"LSBFirst"); break; case 1: fprintf(o,"MSBFirst"); break; default: fprintf(o,"%d (INVALID)",val); break; } fprintf(o,"\n"); fprintf(o," Bitmap bit order: "); val = get1u(f,31); switch (val) { case 0: fprintf(o,"LeastSignificant"); break; case 1: fprintf(o,"MostSignificant"); break; default: fprintf(o,"%d (INVALID)",val); break; } fprintf(o,"\n"); fprintf(o," Image scanline-unit "); print_1_124(o,get1u(f,32)); fprintf(o,", scanline-pad "); print_1_124(o,get1u(f,33)); if (get1u(f,32) > get1u(f,32)) fprintf(o," (NOTE: unit>pad)"); fprintf(o,"\n"); fprintf(o," Keycode range: %u..%u",get1u(f,34),get1u(f,35)); get_pad(f,36,4); if ((get1u(f,34) < 8) || (get1u(f,34) > get1u(f,35))) fprintf(o," (INVALID)"); fprintf(o,"\n"); print_padded_text_blk(f,o,40,v," Vendor: ",0); off = 40 + padded(v); fprintf(o," Pixmap format count: %d\n",n); for (i=n;i>0;i--) { fprintf(o," Depth %u bpp %u stride %u\n",get1u(f,off),get1u(f,off+1),get1u(f,off+2)); get_pad(f,off+3,5); off += 8; } fprintf(o," Screen count: %d\n",m); for (i=0;i f->ibfull) fail("*** Too short!"); fprintf(o," Root window 0x%x\n",get4u(f,off)); fprintf(o," Default colourmap 0x%x\n",get4u(f,off+4)); fprintf(o," Pixels: white %u, black %u\n",get4u(f,off+8),get4u(f,off+12)); print_set(o," Current event mask: "," ",get4u(f,off+16),SETofEVENT); fprintf(o," Size: %ux%u pixels, %ux%u mm\n",get2u(f,off+20),get2u(f,off+22),get2u(f,off+24),get2u(f,off+26)); fprintf(o," Installed colourmaps: %u..%u\n",get2u(f,off+28),get2u(f,off+30)); fprintf(o," Root visual 0x%x\n",get4u(f,off+32)); fprintf(o," Backing-store: "); val = get1u(f,off+36); switch (val) { case 0: fprintf(o,"Never"); break; case 1: fprintf(o,"WhenMapped"); break; case 2: fprintf(o,"Always"); break; default: fprintf(o,"%d (INVALID)",val); break; } fprintf(o,"\n"); fprintf(o," Save-under support: "); print_bool(o,get1u(f,off+37)); fprintf(o,"\n"); fprintf(o," Root depth: %u\n",get1u(f,off+38)); n = get1u(f,off+39); fprintf(o," Supported depth count: %u\n",n); off += 40; for (j=0;jibfull < off+8) fail("*** Too short!"); v = get2u(f,off+2); fprintf(o," Depth %u: visual count %u\n",get1u(f,off),v); get_pad(f,off+1,1); get_pad(f,off+4,4); off += 8; if (f->ibfull < off+(24*v)) fail("*** Too short!"); for (k=0;kibfull > f->ibfill) { f->readdone = &xtoc_first; } else { xtoc_first(f); } } static FILE *open_output(unsigned int ser) { int fd; char *path; FILE *o; if (! outname) { fd = dup(1); if (fd < 0) { fprintf(stderr,"%s: dup(1): %s\n",__progname,strerror(errno)); return(0); } } else if (! dosplit) { fd = dup(outfd); if (fd < 0) { fprintf(stderr,"%s: dup(%d): %s\n",__progname,outfd,strerror(errno)); return(0); } } else { asprintf(&path,"%s%u",outname,ser); fd = open(path,O_WRONLY|O_CREAT|O_TRUNC,0666); if (fd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,path,strerror(errno)); return(0); } } o = fdopen(fd,"w"); if (! o) { fprintf(stderr,"%s: fdopen: %s\n",__progname,strerror(errno)); close(fd); return(0); } return(o); } static CONN *add_conn(struct sockaddr_in *addr, int client_fd) { int xfd; FILE *o; CONN *c; xfd = connect_to_X(); if (xfd < 0) { close(client_fd); return(0); } o = open_output(connserial); if (! o) { close(xfd); close(client_fd); return(0); } c = malloc(sizeof(CONN)); if (addr) { asprintf(&c->remstr,"%s/%d",inet_ntoa(addr->sin_addr),ntohs(addr->sin_port)); } else { asprintf(&c->remstr,"(local)"); } c->flink = 0; c->blink = conntail; if (conns) { conntail->flink = c; } else { conns = c; } conntail = c; c->out = o; c->serial = connserial ++; c->flags = 0; c->maxreq = 0; c->client_fd = client_fd; c->client_px = -1; c->X_fd = xfd; c->X_px = -1; { static int on = 1; ioctl(c->client_fd,FIONBIO,&on); ioctl(c->X_fd,FIONBIO,&on); } c->laststamp_tm.tm_mday = -1; init_flow(&c->ctox,c,"C->S",CF_C,CF_X,c->client_fd,c->X_fd,10,&ctox_first_hdr); init_flow(&c->xtoc,c,"S->C",CF_X,CF_C,c->X_fd,c->client_fd,8,&xtoc_first_hdr); c->repq = 0; c->repqt = &c->repq; c->atoms = atoms_init(); c->extensions = 0; memset(&c->ext_opc_tbl[0],TBL_NONE,128); c->ext_opc_vec = 0; memset(&c->ext_ev_tbl[0],TBL_NONE,64); c->ext_ev_vec = 0; memset(&c->ext_err_tbl[0],TBL_NONE,128); c->ext_err_vec = 0; return(c); } void set_big_requests(CONN *c, unsigned int max) { c->maxreq = max; c->flags |= CF_BIGREQ; } static void accept_tcp(void) { int newfd; struct sockaddr_in sin; socklen_t sinlen; sinlen = sizeof(sin); newfd = accept(acc_tcp,(struct sockaddr *)&sin,&sinlen); if (newfd < 0) return; add_conn(&sin,newfd); } static void accept_local(void) { int newfd; struct sockaddr_un sun; int sunlen; sunlen = sizeof(sun); newfd = accept(acc_local,(struct sockaddr *)&sun,&sunlen); if (newfd < 0) return; add_conn(0,newfd); } static void tryread(FLOW *f) { int rv; rv = read(f->srcfd,f->ibuf+f->ibfill,f->ibfull-f->ibfill); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: read error on %s connection for %s\n",__progname,f->dir,f->conn->remstr); f->conn->flags |= CF_EOF_BOTH; return; } if (f->ibfill == 0) gettimeofday(&f->stamp_tv,0); if (rv == 0) { FILE *o; if (f->ibfill) fprintf(stderr,"%s: unexpected EOF on %s connection for %s\n",__progname,f->dir,f->conn->remstr); set_eof(f); new_beginning(f); o = open_packet_output(f); fprintf(o,"EOF\n"); get_disable(f); fclose(o); return; } f->ibfill += rv; if (f->ibfill < f->ibfull) return; (*f->readdone)(f); } static int iov_full(void) { static int iov_max = -1; if (iov_max < 0) { int mib[2]; size_t oldlen; mib[0] = CTL_KERN; mib[1] = KERN_IOV_MAX; oldlen = sizeof(iov_max); if (sysctl(&mib[0],2,&iov_max,&oldlen,0,0) < 0) { fprintf(stderr,"%s: sysctl kern.iov_max: %s\n",__progname,strerror(errno)); exit(1); } if (iov_max > 1024) iov_max = 1024; if (iov_max < 1) iov_max = 1; iova = iov_max; iov = malloc(iova*sizeof(struct iovec)); } return(iovn>=iova); } static void iov_add(const void *data, int len) { if (iovn >= iova) abort(); iov[iovn++] = (struct iovec) { .iov_base = dequal(data), .iov_len = len }; } static void trywrite(FLOW *f) { OBLK *b; int n; switch (f->oq->type) { default: abort(); break; case OBT_EOF: shutdown(f->dstfd,SHUT_WR); pop_oq(f); return; break; case OBT_DATA: break; } iovn = 0; for (b=f->oq;b&&(b->type==OBT_DATA)&&!iov_full();b=b->link) { iov_add(b->data.b+b->data.p,b->data.l-b->data.p); } n = writev(f->dstfd,iov,iovn); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: write error on %s connection for %s\n",__progname,f->dir,f->conn->remstr); f->conn->flags |= CF_EOF_BOTH; return; } while (n > 0) { if (f->oq->type != OBT_DATA) abort(); if (f->oq->data.l-f->oq->data.p <= n) { n -= f->oq->data.l - f->oq->data.p; pop_oq(f); } else { f->oq->data.p += n; n = 0; } } } static int add_poll(int fd, unsigned int ev) { if (pn >= pa) pv = realloc(pv,(pa=pn+8)*sizeof(*pv)); pv[pn] = (struct pollfd) { .fd = fd, .events = ev }; return(pn++); } static void run(void) __attribute__((__noreturn__)); static void run(void) { int prv; CONN *c; while <"run"> (1) { fflush(0); if (idledie && !conns && (connserial > 1)) exit(0); pn = 0; acc_tcp_px = add_poll(acc_tcp,POLLIN|POLLRDNORM); acc_local_px = add_poll(acc_local,POLLIN|POLLRDNORM); for (c=conns;c;c=c->flink) { unsigned int ev; if ((c->flags & CF_EOF) == CF_EOF_BOTH) { destroy_conn(c); continue <"run">; } ev = 0; if (! (c->flags & CF_EOF_C)) ev |= POLLIN | POLLRDNORM; if (c->xtoc.oq) ev |= POLLOUT | POLLWRNORM; c->client_px = ev ? add_poll(c->client_fd,ev) : -1; ev = 0; if (! (c->flags & CF_EOF_X)) ev |= POLLIN | POLLRDNORM; if (c->ctox.oq) ev |= POLLOUT | POLLWRNORM; c->X_px = ev ? add_poll(c->X_fd,ev) : -1; } prv = poll(pv,pn,INFTIM); if (prv < 0) { if (errno != EINTR) { perror("poll"); exit(1); } continue; } if (pv[acc_tcp_px].revents & (POLLIN|POLLRDNORM)) accept_tcp(); if (pv[acc_local_px].revents & (POLLIN|POLLRDNORM)) accept_local(); for (c=conns;c;c=c->flink) { if (c->client_px >= 0) { if (pv[c->client_px].revents & (POLLIN|POLLRDNORM)) { tryread(&c->ctox); } if (pv[c->client_px].revents & (POLLOUT|POLLWRNORM)) { trywrite(&c->xtoc); } } if (c->X_px >= 0) { if (pv[c->X_px].revents & (POLLIN|POLLRDNORM)) { tryread(&c->xtoc); } if (pv[c->X_px].revents & (POLLOUT|POLLWRNORM)) { trywrite(&c->ctox); } } } } } static void setup_output(void) { if (outname) { if (! dosplit) { outfd = open(outname,O_WRONLY|O_CREAT|O_TRUNC,0666); if (outfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,outname,strerror(errno)); exit(1); } } fflush(0); close(1); open("/dev/null",O_WRONLY,0); } } static void setup_fork(void) { if (dostamp) tzset(); if (dofork) { pid_t kid; fflush(0); kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid > 0) exit(0); } if (printpid) printf("%d\n",(int)getpid()); } int main(int, char **); int main(int ac, char **av) { #ifdef OWN_PROGNAME __progname = av[0]; #endif handleargs(ac,av); setup_limit(); setup_addr(); setup_accept(); setup_fork(); setup_output(); setup_signals(); setup_conns(); run(); }