/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "oq.h" #include "pp.h" #include "errf.h" #include "msgs.h" #include "util.h" #include "panic.h" #include "client.h" #include "config.h" #include "nested.h" #include "verbose.h" #include "pkt-util.h" #include "pollloop.h" #include "connshare.h" enum fwdd_state { FWDDS_REQ = 1, /* requested, no response yet */ FWDDS_UP, /* all set up */ FWDDS_CANCEL, /* cancel requested, no response yet */ FWDDS_BLIP /* requested, no response yet, canceled */ } ; typedef enum fwdd_state FWDD_STATE; typedef struct client CLIENT; typedef struct chanmap_head CHANMAP_HEAD; typedef struct chanmap CHANMAP; typedef struct ssgrqq SSGRQQ; typedef struct sscocb SSCOCB; typedef struct serialmap SERIALMAP; typedef struct mapentry MAPENTRY; typedef struct crqq CRQQ; typedef struct fwdd FWDD; struct crqq { CRQQ *link; unsigned int flags; #define CRQF_STARTED 0x00000001 unsigned char *data; int len; } ; struct mapentry { unsigned int client; unsigned int server; } ; struct serialmap { MAPENTRY *map; int n; int a; } ; struct sscocb { SSCOCB *link; void (*succ)(void *, unsigned int, unsigned int, unsigned int, const void *, int); void (*fail)(void *, unsigned int, ROSTR, ROSTR); void *arg; } ; struct ssgrqq { SSGRQQ *link; CLIENT *c; int set; void *data; int len; } ; struct chanmap_head { CHANMAP *h; CHANMAP *t; } ; struct chanmap { CLIENT *client; CHANMAP *flink; CHANMAP *blink; unsigned int flags; #define CMF_S2C 0x00000001 #define CMF_C2S 0x00000002 #define CMF_EOF 0x00000004 unsigned int client_id; unsigned int channel_id; unsigned int my_id; } ; struct fwdd { FWDD *flink; FWDD *blink; CLIENT *c; SSGRQQ *rq; FWDD_STATE state; STR addr; unsigned int port; unsigned int scookie; unsigned int ccookie; } ; struct client { CLIENT *flink; CLIENT *blink; unsigned int flags; #define CF_DEAD 0x00000001 int refs; SSGRQQ *grqq; SSGRQQ **grqqt; SSCOCB *cocb; SSCOCB **cocbt; CRQQ *crqq; CRQQ **crqqt; FWDD *fwdings; CHANMAP_HEAD chans; SERIALMAP tcpfwdmap; SERIALMAP xfwdmap; SERIALMAP afwdmap; int fd; int rwid; int grid; OQ oq; int ifill; int ilen; unsigned char ibuf[sizeof(int)+SSH_MAX_PAYLOAD_LEN]; unsigned char obuf[SSH_MAX_PAYLOAD_LEN]; } ; static const char *share_server_path; static int autosocket = -1; static int autopipe; static int autotimeout = -1; static int stopping = 0; static int sfd; static int sid; static CLIENT *clienth; static CLIENT *clientt; static int timeout_fd = -1; static int timeout_id; static CHANMAP_HEAD deadmap; static CHANMAP **idmap; static int nidmap; static PEERID *remid; #define IDGROW 8 static void (*ss_parse_throw)(const char *, va_list *); #define CM_HEAD(cm) ((cm)->client?&(cm)->client->chans:&deadmap) static void chanmap_head_init(CHANMAP_HEAD *hd) { hd->h = 0; hd->t = 0; } static void client_link(CLIENT *c) { c->flink = clienth; clienth = c; c->blink = 0; if (c->flink) c->flink->blink = c; else clientt = c; } static void client_unlink(CLIENT *c) { if (c->flink) c->flink->blink = c->blink; else clientt = c->blink; if (c->blink) c->blink->flink = c->flink; else clienth = c->flink; } static void chanmap_link(CHANMAP *cm, CHANMAP_HEAD *hd) { cm->flink = hd->h; hd->h = cm; cm->blink = 0; if (cm->flink) cm->flink->blink = cm; else hd->t = cm; } static void chanmap_unlink(CHANMAP *cm, CHANMAP_HEAD *hd) { if (cm->flink) cm->flink->blink = cm->blink; else hd->t = cm->blink; if (cm->blink) cm->blink->flink = cm->flink; else hd->h = cm->flink; } static void chanmap_destroy(CHANMAP *cm) { if (cm->my_id >= nidmap) panic("impossible id"); if (idmap[cm->my_id] != cm) panic("backpointer wrong"); idmap[cm->my_id] = 0; free(cm); } static void serialmap_init(SERIALMAP *m) { m->map = 0; m->n = 0; m->a = 0; } static int serialmap_lookup_server(SERIALMAP *m, unsigned int v, unsigned int *cp) { int i; for (i=m->n-1;i>=0;i--) { if (m->map[i].server == v) { *cp = m->map[i].client; return(1); } } return(0); } static void serialmap_add(SERIALMAP *m, unsigned int c, unsigned int s) { if (m->n >= m->a) m->map = realloc(m->map,(m->a=m->n+8)*sizeof(*m->map)); m->map[m->n++] = (MAPENTRY){ .client=c, .server=s }; } static void serialmap_remove(SERIALMAP *m, unsigned int c, unsigned int s) { int i; for (i=m->n-1;i>=0;i--) { if ((m->map[i].client == c) && (m->map[i].server == s)) { m->n --; if (i < m->n) m->map[i] = m->map[m->n]; return; } } panic("can't find it"); } static void serialmap_destroy(SERIALMAP *m) { free(m->map); } static void unlink_fwdd(FWDD *fw) { if (fw->flink) fw->flink->blink = fw->blink; if (fw->blink) fw->blink->flink = fw->flink; else if (fw->c) fw->c->fwdings = fw->flink; fw->c = 0; fw->flink = 0; fw->blink = 0; } static void cancel_fwdd(FWDD *fw) { if (fw->c) serialmap_remove(&fw->c->tcpfwdmap,fw->ccookie,fw->scookie); unlink_fwdd(fw); free_str(fw->addr); free(fw); } static void client_ref(CLIENT *c) { c->refs ++; if (c->refs < 1) panic("ref overflow"); } static void client_deref(CLIENT *c) { c->refs --; if (c->refs > 0) return; if (c->refs < 0) panic("ref underflow"); serialmap_destroy(&c->tcpfwdmap); serialmap_destroy(&c->xfwdmap); serialmap_destroy(&c->afwdmap); if (c->rwid != PL_NOID) remove_poll_id(c->rwid); if (c->grid != PL_NOID) remove_block_id(c->grid); if (c->fd >= 0) close(c->fd); if (c->grqq) panic("pending global requests"); free(c); } static void dead_forward_reply(int ok, void *fwv, const void *body __attribute__((__unused__)), int bodylen __attribute__((__unused__))) { CLIENT *c; switch (ok) { default: panic("bad status"); break; case GREQ_OK: case GREQ_FAIL: break; } /* put c in a variable to avoid use-after-free */ c = ((FWDD *)fwv)->c; cancel_fwdd(fwv); if (c) client_deref(c); } static void ss_dead_cancel(FWDD *fw) { unsigned char *opp; opp = global_req_hdr(cstr_to_rostr("fixed-cancel-tcpip@rodents.montreal.qc.ca")); opp = put_string(opp,fw->addr.data,fw->addr.len); opp = put_uint32(opp,fw->port); opp = put_uint32(opp,fw->scookie); global_send_req(opp,&dead_forward_reply,fw); if (fw->c) client_ref(fw->c); fw->state = FWDDS_CANCEL; } static void client_destroy(CLIENT *c) { if (! (c->flags & CF_DEAD)) panic("destroying live client"); while (c->fwdings) { FWDD *fw; fw = c->fwdings; unlink_fwdd(fw); switch (fw->state) { default: panic("bad state"); break; case FWDDS_REQ: case FWDDS_CANCEL: case FWDDS_BLIP: break; case FWDDS_UP: ss_dead_cancel(fw); break; } } while (c->chans.h) { CHANMAP *cm; cm = c->chans.h; chanmap_unlink(cm,&c->chans); switch (cm->flags & (CMF_S2C|CMF_C2S)) { default: panic("bad flags"); break; case CMF_S2C: /* Received CHANNEL_OPEN from server, told client, no reply */ /* Fail the open. None of the available open failure codes are really appropriate. We use CONNECT_FAILED as perhaps the least inappropriate approximation. */ if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: failing half-done open, channel_id %d\n",cm->channel_id); chan_open_fail(cm->channel_id,SSH_OPEN_CONNECT_FAILED, cstr_to_rostr("Proxy connection lost"),cstr_to_rostr("en")); chanmap_destroy(cm); break; case CMF_C2S: /* Received CHANNEL_OPEN from client, told server, no reply */ /* There's no way to cancel a pending open, so just move this to the deadmap, which will DTRT when the server responds. */ cm->client = 0; chanmap_link(cm,&deadmap); break; case CMF_S2C|CMF_C2S: /* Fully up. */ /* Send a close, and either tear down or move to deadmap to await the server's answering close. */ if (chan_close(cm->channel_id)) { chanmap_destroy(cm); } else { cm->client = 0; chanmap_link(cm,&deadmap); } break; } } } static void new_ipkt(CLIENT *c) { c->ifill = 0; c->ilen = sizeof(int); } static void client_opkt_buf(CLIENT *c, const void *data, int len) { if (VERB(SHAREDATA)) { verb(SHAREDATA,"Share server out (%d):\n",len); verb_data_block(VERBOSE_SHAREDATA,data,len); } oq_queue_copy(&c->oq,&len,sizeof(int)); oq_queue_copy(&c->oq,data,len); } static void client_opkt(CLIENT *c, int len) { client_opkt_buf(c,&c->obuf[0],len); } static void chop_shared_opensucc(void *cmv, int cno, ROSTR rest) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { cm->flags |= CMF_S2C; opp = &cm->client->obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; opp = put_uint32(opp,cm->client_id); opp = put_uint32(opp,cm->my_id); opp = put_uint32(opp,chan_get_wwin(cm->channel_id)); opp = put_uint32(opp,chan_get_maxpkt(cm->channel_id)); opp = put_block(opp,rest.data,rest.len); client_opkt(cm->client,opp-&cm->client->obuf[0]); } else { /* Client went away while we were waiting for server response. Just close it down; when the server responds we'll do teardown. We should never get 1 back here 'cause we're in the open success callback, so a close from the server for this channel can't have happened yet. */ if (chan_close(cno)) panic("close failed"); } } static void chop_shared_openfail(void *cmv, int cno, unsigned int rcode, ROSTR reason, ROSTR lang) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { opp = &cm->client->obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN_FAILURE; opp = put_uint32(opp,cm->client_id); opp = put_uint32(opp,rcode); opp = put_string(opp,reason.data,reason.len); opp = put_string(opp,lang.data,lang.len); client_opkt(cm->client,opp-&cm->client->obuf[0]); } chanmap_unlink(cm,CM_HEAD(cm)); chanmap_destroy(cm); } static void chop_shared_gotdata(void *cmv, int cno, int ext, unsigned int code, const void *buf, int len) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { opp = &cm->client->obuf[0]; if (len == 0) { *opp++ = SSH_MSG_CHANNEL_EOF; opp = put_uint32(opp,cm->client_id); cm->flags |= CMF_EOF; } else if (ext) { *opp++ = SSH_MSG_CHANNEL_EXTENDED_DATA; opp = put_uint32(opp,cm->client_id); opp = put_uint32(opp,code); opp = put_string(opp,buf,len); } else { *opp++ = SSH_MSG_CHANNEL_DATA; opp = put_uint32(opp,cm->client_id); opp = put_string(opp,buf,len); } client_opkt(cm->client,opp-&cm->client->obuf[0]); } } static void chop_shared_morewin(void *cmv, int cno, unsigned int addl, unsigned int new __attribute__((__unused__))) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { opp = &cm->client->obuf[0]; *opp++ = SSH_MSG_CHANNEL_WINDOW_ADJUST; opp = put_uint32(opp,cm->client_id); opp = put_uint32(opp,addl); client_opkt(cm->client,opp-&cm->client->obuf[0]); } } static int chop_shared_chanreq(void *cmv, int cno, ROSTR req, int wantrep, const void *rest, int restlen) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { opp = &cm->client->obuf[0]; *opp++ = SSH_MSG_CHANNEL_REQUEST; opp = put_uint32(opp,cm->client_id); opp = put_string(opp,req.data,req.len); opp = put_bool(opp,wantrep); opp = put_block(opp,rest,restlen); client_opkt(cm->client,opp-&cm->client->obuf[0]); return(CHANREQRET_DONE); } else { return(CHANREQRET_FAIL); } } static void chop_shared_closed(void *cmv, int cno, int final) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cno != cm->channel_id) panic("channel wrong"); if (cm->client) { opp = &cm->client->obuf[0]; *opp++ = SSH_MSG_CHANNEL_CLOSE; opp = put_uint32(opp,cm->client_id); client_opkt(cm->client,opp-&cm->client->obuf[0]); cm->flags |= CMF_EOF; } if (!cm->client && !final) { /* Client's gone, but we haven't chan_close()d yet - I'm not sure this is possible, but it's clear enough what to do if it is. */ if (! chan_close(cno)) panic("close failed"); final = 1; } if (final) { chanmap_unlink(cm,CM_HEAD(cm)); chanmap_destroy(cm); } } static const CHANOPS chops_shared = { &chop_shared_opensucc, &chop_shared_openfail, &chop_shared_gotdata, &chop_shared_morewin, &chop_shared_chanreq, &chop_shared_closed }; static void ss_parse_failure(const void *errat __attribute__((__unused__)), const char *fmt, ...) { va_list ap; va_start(ap,fmt); (*ss_parse_throw)(fmt,&ap); va_end(ap); panic("ss_parse_throw returned"); } static void allocate_my_id(CHANMAP *cm) { int i; for (i=nidmap-1;i>=0;i--) { if (idmap[i] == 0) { idmap[i] = cm; cm->my_id = i; return; } } idmap = realloc(idmap,(nidmap+IDGROW)*sizeof(*idmap)); for (i=0;imy_id = nidmap - 1; } static CHANMAP *chanmap_by_myid(unsigned int id) { return((id < nidmap) ? idmap[id] : 0); } static SSGRQQ *pending_global_new(CLIENT *c) { SSGRQQ *q; q = malloc(sizeof(SSGRQQ)); q->c = c; q->set = 0; q->link = 0; *c->grqqt = q; c->grqqt = &q->link; return(q); } static void pending_global_reply(SSGRQQ *q, int len) { q->set = 1; q->data = malloc(len); q->len = len; bcopy(&q->c->obuf[0],q->data,len); } static void ss_forward_reply(int ok, void *fwv, const void *body, int bodylen) { FWDD *fw; CLIENT *c; unsigned char *opp; fw = fwv; c = fw->c; opp = &c->obuf[0]; switch (ok) { default: panic("bad status"); break; case GREQ_OK: if (c->flags & CF_DEAD) { ss_dead_cancel(fw); } else { *opp++ = SSH_MSG_REQUEST_SUCCESS; opp = put_block(opp,body,bodylen); pending_global_reply(fw->rq,opp-&c->obuf[0]); fw->state = FWDDS_UP; } break; case GREQ_FAIL: cancel_fwdd(fw); if (! (c->flags & CF_DEAD)) { *opp++ = SSH_MSG_REQUEST_FAILURE; pending_global_reply(fw->rq,opp-&c->obuf[0]); } break; } client_deref(c); } static void ss_cancel_reply(int ok, void *fwv, const void *body, int bodylen) { FWDD *fw; CLIENT *c; unsigned char *opp; fw = fwv; if (fw->state != FWDDS_CANCEL) panic("bad state"); c = fw->c; opp = &c->obuf[0]; switch (ok) { default: panic("bad status"); break; case GREQ_OK: *opp++ = SSH_MSG_REQUEST_SUCCESS; opp = put_block(opp,body,bodylen); pending_global_reply(fw->rq,opp-&c->obuf[0]); break; case GREQ_FAIL: *opp++ = SSH_MSG_REQUEST_FAILURE; pending_global_reply(fw->rq,opp-&c->obuf[0]); break; } cancel_fwdd(fw); client_deref(c); } static void ss_global_request(CLIENT *c, unsigned char *data, int len) { STR name; int wantrep; const void *rest; int restlen; unsigned char *opp; SSGRQQ *rq; rq = pending_global_new(c); parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_STRING(&name), PP_BOOL(&wantrep), PP_REST(&rest,&restlen) ); if (str_equalsC(name,"fixed-tcpip-forward@rodents.montreal.qc.ca")) { STR addr; unsigned int port; unsigned int ccookie; unsigned int scookie; FWDD *fw; parse_data(rest,restlen,&ss_parse_failure, PP_STRING(&addr), PP_UINT32(&port), PP_UINT32(&ccookie), PP_ENDSHERE ); scookie = nextfwdreq_serial(); opp = global_req_hdr(cstr_to_rostr("fixed-tcpip-forward@rodents.montreal.qc.ca")); opp = put_string(opp,addr.data,addr.len); opp = put_uint32(opp,port); opp = put_uint32(opp,scookie); fw = malloc(sizeof(FWDD)); fw->c = c; fw->rq = rq; fw->state = FWDDS_REQ; fw->addr = addr; fw->port = port; fw->scookie = scookie; fw->ccookie = ccookie; fw->flink = c->fwdings; fw->blink = 0; if (fw->flink) fw->flink->blink = fw; c->fwdings = fw; global_send_req(opp,&ss_forward_reply,fw); client_ref(c); serialmap_add(&c->tcpfwdmap,ccookie,scookie); free_str(name); return; } else if (str_equalsC(name,"fixed-cancel-tcpip@rodents.montreal.qc.ca")) { STR addr; unsigned int port; unsigned int ccookie; FWDD *fw; parse_data(rest,restlen,&ss_parse_failure, PP_STRING(&addr), PP_UINT32(&port), PP_UINT32(&ccookie), PP_ENDSHERE ); for (fw=c->fwdings;fw;fw=fw->flink) { if (fw->ccookie == ccookie) { switch (fw->state) { default: panic("bad state"); break; case FWDDS_REQ: fw->state = FWDDS_BLIP; break; case FWDDS_UP: opp = global_req_hdr(cstr_to_rostr("fixed-cancel-tcpip@rodents.montreal.qc.ca")); opp = put_string(opp,fw->addr.data,fw->addr.len); opp = put_uint32(opp,fw->port); opp = put_uint32(opp,fw->scookie); global_send_req(opp,&ss_cancel_reply,fw); client_ref(fw->c); fw->state = FWDDS_CANCEL; fw->rq = rq; free_str(name); return; break; case FWDDS_CANCEL: case FWDDS_BLIP: break; } opp = &c->obuf[0]; *opp++ = SSH_MSG_REQUEST_SUCCESS; pending_global_reply(rq,opp-&c->obuf[0]); free_str(name); return; } } } /* if (str_equalcC(name,"server-debug@rodents.montreal.qc.ca")) */ printf("[globalreq: name %.*s wantreply %d restlen %d]\n",name.len,name.data,wantrep,restlen); opp = &c->obuf[0]; *opp++ = SSH_MSG_REQUEST_FAILURE; pending_global_reply(rq,opp-&c->obuf[0]); free_str(name); } static void ss_channel_open(CLIENT *c, unsigned char *data, int len) { STR type; unsigned int remchan; unsigned int initwin; unsigned int maxpkt; const void *rest; int restlen; CHANMAP *cm; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_STRING(&type), PP_UINT32(&remchan), PP_UINT32(&initwin), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); cm = malloc(sizeof(CHANMAP)); cm->client = c; chanmap_link(cm,&c->chans); cm->flags = CMF_C2S; cm->client_id = remchan; cm->channel_id = chan_open_detail( str_to_rostr(type), maxpkt, initwin, rest, restlen, &chops_shared, cm ); allocate_my_id(cm); } static void ss_open_failure(CLIENT *c, unsigned char *data, int len) { unsigned int schan; unsigned int rcode; STR reason; STR lang; SSCOCB *cb; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&schan), PP_UINT32(&rcode), PP_STRING(&reason), PP_STRING(&lang), PP_ENDSHERE ); cb = c->cocb; if (! cb) { fprintf(errf,"%s: protocol error: OPEN_FAILURE without pending open\n",__progname); c->flags |= CF_DEAD; return; } if (! (c->cocb = cb->link)) c->cocbt = &c->cocb; (*cb->fail)(cb->arg,rcode,str_to_rostr(reason),str_to_rostr(lang)); free_str(reason); free_str(lang); free(cb); } static void ss_open_confirmation(CLIENT *c, unsigned char *data, int len) { unsigned int schan; unsigned int cchan; unsigned int iwin; unsigned int maxpkt; const void *rest; int restlen; SSCOCB *cb; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&schan), PP_UINT32(&cchan), PP_UINT32(&iwin), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); cb = c->cocb; if (! cb) { fprintf(errf,"%s: protocol error: OPEN_CONFIRMATION without pending open\n",__progname); c->flags |= CF_DEAD; return; } if (! (c->cocb = cb->link)) c->cocbt = &c->cocb; (*cb->succ)(cb->arg,cchan,iwin,maxpkt,rest,restlen); free(cb); } static void ss_data_packet(CLIENT *c, unsigned char *data, int len, int ext) { unsigned int chan; unsigned int code; STR body; CHANMAP *cm; if (ext) { parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&code), PP_STRING(&body), PP_ENDSHERE ); } else { parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&body), PP_ENDSHERE ); code = 0; } cm = chanmap_by_myid(chan); if (cm == 0) { fprintf(errf,"%s: protocol error: data for nonexistent channel %u\n",__progname,chan); c->flags |= CF_DEAD; return; } chan_send_data(cm->channel_id,ext,code,body.data,body.len); free_str(body); } static void ss_window_adjust(CLIENT *c, unsigned char *data, int len) { unsigned int chan; unsigned int addl; CHANMAP *cm; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&addl), PP_ENDSHERE ); cm = chanmap_by_myid(chan); if (cm == 0) { fprintf(errf,"%s: protocol error: WINDOW_ADJUST for nonexistent channel %u\n",__progname,chan); c->flags |= CF_DEAD; return; } if (! (cm->flags & CMF_EOF)) chan_add_rwin(cm->channel_id,addl); } static void crqq_drop(CLIENT *c) { CRQQ *q; q = c->crqq; if (! q) panic("queue empty"); if (! (c->crqq=q->link)) c->crqqt = &c->crqq; free(q->data); free(q); } static void ss_chanreq_respond(int ok, void *cmv, int fromqueue) { CHANMAP *cm; unsigned char *opp; cm = cmv; if (cm->client) { opp = &cm->client->obuf[0]; switch (ok) { default: panic("bad status"); break; case CREQ_OK: *opp++ = SSH_MSG_CHANNEL_SUCCESS; break; case CREQ_FAIL: *opp++ = SSH_MSG_CHANNEL_FAILURE; break; case CREQ_CLOSE: /* Don't send along anything here; the channel is going away. Let its going away produce CREQ_CLOSE in connshare-c.c. */ return; break; } opp = put_uint32(opp,cm->client_id); client_opkt(cm->client,opp-&cm->client->obuf[0]); if (fromqueue) crqq_drop(cm->client); } } static void ss_chanreq_reply(int ok, void *cmv) { ss_chanreq_respond(ok,cmv,1); } static void queue_channel_request(CLIENT *c, unsigned char *data, int len, unsigned int flags) { CRQQ *e; e = malloc(sizeof(CRQQ)); e->link = 0; *c->crqqt = e; c->crqqt = &e->link; e->flags = flags; if (len) { e->data = malloc(len); bcopy(data,e->data,len); } else { e->data = 0; } e->len = len; } static int handle_channel_request(CLIENT *c, unsigned char *data, int len) { unsigned int chan; STR request; int wantreply; const void *rest; int restlen; CHANMAP *cm; unsigned char *opp; int pending; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&request), PP_BOOL(&wantreply), PP_REST(&rest,&restlen) ); cm = chanmap_by_myid(chan); if (cm == 0) { fprintf(errf,"%s: protocol error: CHANNEL_REQUEST for nonexistent channel %u\n",__progname,chan); c->flags |= CF_DEAD; free_str(request); return(0); } pending = 0; if (str_equalsC(request,"fixed-auth-agent-req@rodents.montreal.qc.ca")) { unsigned int ccookie; unsigned int scookie; parse_data(rest,restlen,&ss_parse_failure, PP_UINT32(&ccookie), PP_ENDSHERE ); scookie = nextfwdreq_serial(); opp = chan_req_hdr(cm->channel_id,str_to_rostr(request)); opp = put_uint32(opp,scookie); if (wantreply) { chan_send_req_reply(cm->channel_id,opp,&ss_chanreq_reply,cm); pending = 1; } else { chan_send_req_blind(cm->channel_id,opp); } serialmap_add(&c->afwdmap,ccookie,scookie); } else if (str_equalsC(request,"fixed-auth-agent-req-2@rodents.montreal.qc.ca")) { unsigned int ccookie; unsigned int scookie; int single; parse_data(rest,restlen,&ss_parse_failure, PP_BOOL(&single), PP_UINT32(&ccookie), PP_ENDSHERE ); scookie = nextfwdreq_serial(); opp = chan_req_hdr(cm->channel_id,str_to_rostr(request)); opp = put_bool(opp,single); opp = put_uint32(opp,scookie); if (wantreply) { chan_send_req_reply(cm->channel_id,opp,&ss_chanreq_reply,cm); pending = 1; } else { chan_send_req_blind(cm->channel_id,opp); } serialmap_add(&c->afwdmap,ccookie,scookie); } else if (str_equalsC(request,"x11-req")) { if (wantreply) ss_chanreq_respond(CREQ_FAIL,cm,0); } else if (str_equalsC(request,"fixed-x11-req@rodents.montreal.qc.ca")) { unsigned int ccookie; unsigned int scookie; const void *xstuff; int xstufflen; parse_data(rest,restlen,&ss_parse_failure, PP_UINT32(&ccookie), PP_REST(&xstuff,&xstufflen) ); scookie = nextfwdreq_serial(); opp = chan_req_hdr(cm->channel_id,str_to_rostr(request)); opp = put_uint32(opp,scookie); opp = put_block(opp,xstuff,xstufflen); if (wantreply) { chan_send_req_reply(cm->channel_id,opp,&ss_chanreq_reply,cm); pending = 1; } else { chan_send_req_blind(cm->channel_id,opp); } serialmap_add(&c->xfwdmap,ccookie,scookie); } else if (str_equalsC(request,"fixed-x11-req-2@rodents.montreal.qc.ca")) { unsigned int ccookie; unsigned int scookie; const void *xstuff; int xstufflen; int single; parse_data(rest,restlen,&ss_parse_failure, PP_BOOL(&single), PP_UINT32(&ccookie), PP_REST(&xstuff,&xstufflen) ); scookie = nextfwdreq_serial(); opp = chan_req_hdr(cm->channel_id,str_to_rostr(request)); opp = put_bool(opp,single); opp = put_uint32(opp,scookie); opp = put_block(opp,xstuff,xstufflen); if (wantreply) { chan_send_req_reply(cm->channel_id,opp,&ss_chanreq_reply,cm); pending = 1; } else { chan_send_req_blind(cm->channel_id,opp); } serialmap_add(&c->xfwdmap,ccookie,scookie); } else if (str_equalsC(request,"auth-agent-req")) { if (wantreply) ss_chanreq_respond(CREQ_FAIL,cm,0); } else if ( str_equalsC(request,"window-change") || str_equalsC(request,"shell") || str_equalsC(request,"exec") || str_equalsC(request,"pty-req") || str_equalsC(request,"missing-pty-modes@rodents.montreal.qc.ca") ) { opp = chan_req_hdr(cm->channel_id,str_to_rostr(request)); opp = put_block(opp,rest,restlen); if (wantreply) { chan_send_req_reply(cm->channel_id,opp,&ss_chanreq_reply,cm); pending = 1; } else { chan_send_req_blind(cm->channel_id,opp); } } else if (str_equalsC(request,"keepalive@openssh.com")) { if (wantreply) ss_chanreq_respond(CREQ_FAIL,cm,0); } else { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: unrecognized CHANNEL_REQUEST `%.*s'\n",__progname,request.len,request.data); if (wantreply) ss_chanreq_respond(CREQ_FAIL,cm,0); } free_str(request); return(pending); } static void ss_channel_request(CLIENT *c, unsigned char *data, int len) { if (c->crqq) { queue_channel_request(c,data,len,0); } else if (handle_channel_request(c,data,len)) { queue_channel_request(c,0,0,CRQF_STARTED); } } static void ss_channel_eof(CLIENT *c, unsigned char *data, int len) { unsigned int chan; CHANMAP *cm; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); cm = chanmap_by_myid(chan); if (cm == 0) { fprintf(errf,"%s: protocol error: CHANNEL_EOF for nonexistent channel %u\n",__progname,chan); c->flags |= CF_DEAD; return; } chan_send_eof(cm->channel_id); } static void ss_channel_close(CLIENT *c, unsigned char *data, int len) { unsigned int chan; CHANMAP *cm; parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); cm = chanmap_by_myid(chan); if (cm == 0) { fprintf(errf,"%s: protocol error: CHANNEL_CLOSE for nonexistent channel %u\n",__progname,chan); c->flags |= CF_DEAD; return; } if (chan_close(cm->channel_id)) { chanmap_unlink(cm,&c->chans); chanmap_destroy(cm); } } static void ss_private_proto(CLIENT *c, unsigned char *data, int len) { unsigned int sig[4]; STR proto; const void *rest; int restlen; if (len < 17) ss_parse_failure(0,"too short"); parse_data(data,len,&ss_parse_failure, PP_IGNORE(1), PP_UINT32(&sig[0]), PP_UINT32(&sig[1]), PP_UINT32(&sig[2]), PP_UINT32(&sig[3]), PP_REST(&rest,&restlen) ); if ( (sig[0] != moussh_private_proto_signature[0]) || (sig[1] != moussh_private_proto_signature[1]) || (sig[2] != moussh_private_proto_signature[2]) || (sig[3] != moussh_private_proto_signature[3]) ) ss_parse_failure(0,"signature wrong"); parse_data(rest,restlen,&ss_parse_failure, PP_STRING(&proto), PP_REST(&rest,&restlen) ); if (str_equalsC(proto,"share-op")) { unsigned char kind; parse_data(rest,restlen,&ss_parse_failure, PP_BYTE(&kind), PP_ENDSHERE ); switch (kind) { case MOUSSH_SHARE_OP_KILL: if (VERB(SHARECONN_S)) verb(SHARECONN_S,"share-op kill from fd %d: ",c->fd); if (! config_bool("ignore-kill")) { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"exiting\n"); exit(0); } if (VERB(SHARECONN_S)) verb(SHARECONN_S,"ignoring\n"); c->flags |= CF_DEAD; return; break; case MOUSSH_SHARE_OP_STOP: if (VERB(SHARECONN_S)) verb(SHARECONN_S,"share-op stop from fd %d: ",c->fd); if (! config_bool("ignore-stop")) { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"stopping\n"); if (sfd >= 0) { unlink(share_server_path); /* Don't close(sfd) here, for the benefit of auto-sharing code that expects this fd to remain open. Since we've unlinked the path, leaving the fd open doesn't hurt. */ sfd = -1; remove_poll_id(sid); sid = PL_NOID; spt_av0(config_str("host"),"sharing server [stopping]"); stopping = 1; } } else { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"ignoring\n"); } c->flags |= CF_DEAD; return; break; case MOUSSH_SHARE_OP_DROP: if (VERB(SHARECONN_S)) verb(SHARECONN_S,"share-op drop from fd %d: ",c->fd); if (! config_bool("ignore-drop")) { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"dropping\n"); for (c=clienth;c;c=c->flink) c->flags |= CF_DEAD; } else { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"ignoring\n"); c->flags |= CF_DEAD; } return; break; default: if (VERB(SHARECONN_S)) verb(SHARECONN_S,"share-op %d from fd %d: error\n",kind,c->fd); logmsg(LM_WARN|LM_PEER,"share-kill %d",kind); c->flags |= CF_DEAD; return; break; } } ss_parse_failure(0,"unknown proto %.*s",proto.len,proto.data); } static void client_input(CLIENT *c, unsigned char *data, int len) { __label__ ret; const char *pkttype; NESTED void throw(const char *fmt, va_list *app) { if (fmt) { fprintf(errf,"%s: shared client protocol error: bad %s packet: ",__progname,pkttype); vfprintf(errf,fmt,*app); fprintf(errf,"\r\n"); } c->flags |= CF_DEAD; goto ret; } if (VERB(SHAREDATA)) { verb(SHAREDATA,"Share server in (%d):\n",len); verb_data_block(VERBOSE_SHAREDATA,&data[0],len); } ss_parse_throw = &throw; switch (data[0]) { default: fprintf(errf,"%s: sharing client protocol error: packet type %d\n",__progname,data[0]); c->flags |= CF_DEAD; return; break; case SSH_MSG_GLOBAL_REQUEST: pkttype = "GLOBAL_REQUEST"; ss_global_request(c,data,len); break; #if 0 case SSH_MSG_REQUEST_SUCCESS: ss_global_reply(c,data,len,1); break; case SSH_MSG_REQUEST_FAILURE: ss_global_reply(c,data,len,0); break; #endif case SSH_MSG_CHANNEL_OPEN: pkttype = "CHANNEL_OPEN"; ss_channel_open(c,data,len); break; case SSH_MSG_CHANNEL_OPEN_FAILURE: pkttype = "CHANNEL_OPEN_FAILURE"; ss_open_failure(c,data,len); break; case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: pkttype = "CHANNEL_OPEN_CONFIRMATION"; ss_open_confirmation(c,data,len); break; case SSH_MSG_CHANNEL_DATA: pkttype = "CHANNEL_DATA"; ss_data_packet(c,data,len,0); break; case SSH_MSG_CHANNEL_EXTENDED_DATA: pkttype = "CHANNEL_EXTENDED_DATA"; ss_data_packet(c,data,len,1); break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: pkttype = "CHANNEL_WINDOW_ADJUST"; ss_window_adjust(c,data,len); break; #if 0 case SSH_MSG_CHANNEL_SUCCESS: s_channel_request_reply(s,l,1); break; case SSH_MSG_CHANNEL_FAILURE: s_channel_request_reply(s,l,0); break; #endif case SSH_MSG_CHANNEL_REQUEST: pkttype = "CHANNEL_REQUEST"; ss_channel_request(c,data,len); break; case SSH_MSG_CHANNEL_EOF: pkttype = "CHANNEL_EOF"; ss_channel_eof(c,data,len); break; case SSH_MSG_CHANNEL_CLOSE: pkttype = "CHANNEL_CLOSE"; ss_channel_close(c,data,len); break; case MOUSSH_PRIVATE_PROTO: pkttype = "MOUSSH_PRIVATE_PROTO"; ss_private_proto(c,data,len); break; } ret:; ss_parse_throw = 0; } static void ifilled(CLIENT *c) { if (c->ilen == sizeof(int)) { int len; bcopy(&c->ibuf[0],&len,sizeof(int)); if ((len <= 0) || (len > SSH_MAX_PAYLOAD_LEN)) { fprintf(errf,"%s: sharing client protocol error: len=%d\n",__progname,len); c->flags |= CF_DEAD; return; } c->ilen = sizeof(int) + len; return; } else { client_input(c,&c->ibuf[sizeof(int)],c->ilen-sizeof(int)); if (! (c->flags & CF_DEAD)) new_ipkt(c); } } static void client_rd(int id __attribute__((__unused__)), void *arg) { CLIENT *c; int n; int i; unsigned char buf[8192]; c = arg; if (c->flags & CF_DEAD) return; n = read(c->fd,&buf[0],sizeof(buf)); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: sharing client read: %s\n",__progname,strerror(errno)); c->flags |= CF_DEAD; return; } if (n == 0) { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: sharing client read EOF\n",__progname); c->flags |= CF_DEAD; return; } i = 0; while (n > 0) { if (n < c->ilen-c->ifill) { bcopy(&buf[i],&c->ibuf[c->ifill],n); c->ifill += n; break; } bcopy(&buf[i],&c->ibuf[c->ifill],c->ilen-c->ifill); i += c->ilen - c->ifill; n -= c->ilen - c->ifill; c->ifill = c->ilen; ifilled(c); if (c->flags & CF_DEAD) return; } } static void client_wr(int id __attribute__((__unused__)), void *cv) { CLIENT *c; int w; c = cv; if (c->flags & CF_DEAD) return; w = oq_writev(&c->oq,c->fd,&oq_canthappen); if (w < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: sharing client write: %s\n",__progname,strerror(errno)); c->flags |= CF_DEAD; return; } if (w == 0) { if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: sharing client zero write\n",__progname); c->flags |= CF_DEAD; return; } oq_dropdata(&c->oq,w); } static int client_rtest(int id __attribute__((__unused__)), void *cv) { return(!(((CLIENT *)cv)->flags&CF_DEAD)); } static int client_wtest(int id __attribute__((__unused__)), void *cv) { CLIENT *c; c = cv; return( ! (c->flags & CF_DEAD) && oq_nonempty(&c->oq) ); } static void send_rem_id(CLIENT *c) { unsigned char *opp; int namelen; int iplen; namelen = strlen(remid->name); iplen = strlen(remid->ip); opp = &c->obuf[0]; opp = put_uint32(opp,namelen); opp = put_uint32(opp,iplen); opp = put_uint32(opp,remid->port); oq_queue_copy(&c->oq,&c->obuf[0],opp-&c->obuf[0]); if (namelen > 0) oq_queue_point(&c->oq,remid->name,namelen); if (iplen > 0) oq_queue_point(&c->oq,remid->ip,iplen); } static void stop_timeout(void) { if (timeout_fd >= 0) { close(timeout_fd); remove_poll_id(timeout_id); timeout_fd = -1; } } static void timeout_fire(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { stop_timeout(); share_auto_timed_out("idle timeout"); } #ifndef AF_TIMER static void start_timeout(void) { /* Not much we can do without AF_TIMER! */ (void)&timeout_fire; /* pacify -Wunused */ } #else static void start_timeout(void) { struct itimerval itv; stop_timeout(); if (autotimeout < 0) return; timeout_fd = socket(AF_TIMER,SOCK_STREAM,0); if (timeout_fd < 0) { fprintf(errf,"%s: can't create timer socket: %s\n",__progname,strerror(errno)); fprintf(errf,"%s: auto-share timeout will not occur\n",__progname); return; } itv.it_value.tv_sec = autotimeout; itv.it_value.tv_usec = 1; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; write(timeout_fd,&itv,sizeof(itv)); timeout_id = add_poll_fd(timeout_fd,&rwtest_always,&rwtest_never,&timeout_fire,0,0); } #endif static int client_pending_globals(void *cv) { CLIENT *c; SSGRQQ *q; c = cv; q = c->grqq; if (q && q->set) { do { if (! (c->flags & CF_DEAD)) client_opkt_buf(c,q->data,q->len); free(q->data); c->grqq = q->link; free(q); q = c->grqq; } while (q && q->set); return(BLOCK_LOOP); } return(BLOCK_NIL); } static void accept_client(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { int fd; struct sockaddr_un sun; socklen_t slen; CLIENT *c; slen = sizeof(sun); fd = accept(sfd,(void *)&sun,&slen); if (fd < 0) { fprintf(stderr,"%s: accept: %s\n",__progname,strerror(errno)); exit(1); } if (VERB(SHARECONN_S)) verb(SHARECONN_S,"S: %s: accepted client fd %d\n",__progname,fd); c = malloc(sizeof(CLIENT)); c->flags = 0; c->refs = 0; c->grqq = 0; c->grqqt = &c->grqq; c->cocb = 0; c->cocbt = &c->cocb; c->crqq = 0; c->crqqt = &c->crqq; c->fwdings = 0; chanmap_head_init(&c->chans); serialmap_init(&c->tcpfwdmap); serialmap_init(&c->xfwdmap); serialmap_init(&c->afwdmap); c->fd = fd; fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); c->rwid = add_poll_fd(c->fd,&client_rtest,&client_wtest,&client_rd,&client_wr,c); c->grid = add_block_fn(&client_pending_globals,c); oq_init(&c->oq); new_ipkt(c); client_link(c); client_ref(c); send_rem_id(c); stop_timeout(); } static int shareserver_idle(void *arg __attribute__((__unused__))) { CLIENT *c; CLIENT *nx; int any; c = clienth; any = 0; while (c) { nx = c->flink; if (c->flags & CF_DEAD) { client_unlink(c); client_destroy(c); client_deref(c); any = 1; } c = nx; } if (any) { if (clienth == 0) { if (stopping) { if (autosocket >= 0) share_auto_timed_out("stopped"); exit(0); } start_timeout(); } return(BLOCK_LOOP); } for (c=clienth;c;c=c->flink) { CRQQ *q; while ( ! (c->flags & CF_DEAD) && (q=c->crqq) && ! (q->flags & CRQF_STARTED) ) { any = 1; if (handle_channel_request(c,q->data,q->len)) { q->flags |= CRQF_STARTED; } else { crqq_drop(c); } } } return(any?BLOCK_LOOP:BLOCK_NIL); } void share_listen(const char *path, PEERID *rem) { struct sockaddr_un *sun; int pl; if (autosocket < 0) { if (unlink(path) < 0) { switch (errno) { case ENOENT: break; default: fprintf(errf,"%s: note: unlink %s: %s\n",__progname,path,strerror(errno)); break; } } pl = strlen(path); sun = malloc(sizeof(*sun)-sizeof(sun->sun_path)+pl); sun->sun_family = AF_LOCAL; sun->sun_len = sizeof(*sun) - sizeof(sun->sun_path) + pl; bcopy(path,&sun->sun_path[0],pl); sfd = socket(AF_LOCAL,SOCK_STREAM,0); if (sfd < 0) { fprintf(errf,"%s: socket; %s\n",__progname,strerror(errno)); exit(1); } if (bind(sfd,(void *)sun,sun->sun_len) < 0) { fprintf(errf,"%s: bind %s: %s\n",__progname,path,strerror(errno)); exit(1); } if (listen(sfd,10) < 0) { fprintf(errf,"%s: listen %s: %s\n",__progname,path,strerror(errno)); exit(1); } autotimeout = -1; } else { sfd = autosocket; } spt_av0(config_str("host"),"sharing server"); share_server_path = path; clienth = 0; clientt = 0; chanmap_head_init(&deadmap); idmap = 0; nidmap = 0; remid = rem; sid = add_poll_fd(sfd,&rwtest_always,&rwtest_never,&accept_client,0,0); add_block_fn(&shareserver_idle,0); signal(SIGPIPE,SIG_IGN); } void share_unlisten(void) { unlink(share_server_path); } static void fwded_conn_open_succ(void *cmv, unsigned int chan, unsigned int iwin, unsigned int maxpkt, const void *body, int bodylen) { CHANMAP *cm; cm = cmv; cm->client_id = chan; cm->flags |= CMF_C2S; chan_open_ok_detail(cm->channel_id,iwin,maxpkt,blk_to_rostr(body,bodylen),&chops_shared,cm); } static void fwded_conn_open_fail(void *cmv, unsigned int rcode, ROSTR reason, ROSTR lang) { CHANMAP *cm; cm = cmv; chanmap_unlink(cm,CM_HEAD(cm)); chan_open_fail(cm->channel_id,rcode,reason,lang); chanmap_destroy(cm); } static int share_fwd_common( const char *rqname, int chan, int (*check)(CLIENT *), unsigned char *(*pktfn)(unsigned char *) ) { CLIENT *c; unsigned char *opp; CHANMAP *cm; for (c=clienth;c;c=c->flink) { if ((*check)(c)) { SSCOCB *cb; cm = malloc(sizeof(CHANMAP)); cm->client = c; chanmap_link(cm,&c->chans); cm->flags = CMF_S2C; cm->channel_id = chan; allocate_my_id(cm); opp = &c->obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN; opp = put_string(opp,rqname,-1); opp = put_uint32(opp,cm->my_id); opp = put_uint32(opp,chan_get_wwin(chan)); opp = put_uint32(opp,chan_get_maxpkt(chan)); opp = (*pktfn)(opp); client_opkt(c,opp-&c->obuf[0]); cb = malloc(sizeof(SSCOCB)); cb->link = 0; *c->cocbt = cb; c->cocbt = &cb->link; cb->succ = &fwded_conn_open_succ; cb->fail = &fwded_conn_open_fail; cb->arg = cm; return(1); } } return(0); } int share_fwded_tcpip(int chan, ROSTR connhost, unsigned int connport, ROSTR clienthost, unsigned int clientport, unsigned int id) { unsigned int cc; NESTED int check(CLIENT *c) { return(serialmap_lookup_server(&c->tcpfwdmap,id,&cc)); } NESTED unsigned char *pktfn(unsigned char *opp) { opp = put_string(opp,connhost.data,connhost.len); opp = put_uint32(opp,connport); opp = put_string(opp,clienthost.data,clienthost.len); opp = put_uint32(opp,clientport); opp = put_uint32(opp,cc); return(opp); } if (VERB(SHARECONN_S) || VERB(FWD)) { pverb(VERBOSE_SHARECONN_S|VERBOSE_FWD, "share_fwded_tcpip: chan=%d conn=%.*s/%u client=%.*s/%u id=%u\n", chan, connhost.len, connhost.data, connport, clienthost.len, clienthost.data, clientport, id); } return(share_fwd_common( "fixed-forwarded-tcpip@rodents.montreal.qc.ca", chan, &check, &pktfn )); } int share_x(int chan, ROSTR how, const void *rest, int restlen, unsigned int id) { unsigned int cc; NESTED int check(CLIENT *c) { return(serialmap_lookup_server(&c->xfwdmap,id,&cc)); } NESTED unsigned char *pktfn(unsigned char *opp) { opp = put_uint32(opp,cc); opp = put_string(opp,how.data,how.len); opp = put_block(opp,rest,restlen); return(opp); } if (VERB(FWD) || VERB(SHARECONN_S)) { FILE *to; to = verb_fopen(VERBOSE_FWD|VERBOSE_SHARECONN_S); fprintf(to,"share_x: id %u method %.*s",id,how.len,how.data); if (str_equalcC(how,"tcp")) { __label__ throw_to; STR addr; unsigned int port; NESTED void fail(const void *errat __attribute__((__unused__)), const char *fmt, ...) { va_list ap; fprintf(to," [parse error: "); va_start(ap,fmt); vfprintf(to,fmt,ap); va_end(ap); fprintf(to,"]"); goto throw_to; } parse_data(rest,restlen,&fail, PP_STRING(&addr), PP_UINT32(&port), PP_ENDSHERE ); fprintf(to," from %.*s/%u",addr.len,addr.data,port); free_str(addr); throw_to:; } fprintf(to,"\n"); fclose(to); } return(share_fwd_common( "fixed-x11@rodents.montreal.qc.ca", chan, &check, &pktfn )); } int share_auth_agent(int chan, unsigned int id) { unsigned int cc; NESTED int check(CLIENT *c) { return(serialmap_lookup_server(&c->afwdmap,id,&cc)); } NESTED unsigned char *pktfn(unsigned char *opp) { opp = put_uint32(opp,cc); return(opp); } if (VERB(FWD) || VERB(SHARECONN_S)) pverb(VERBOSE_FWD|VERBOSE_SHARECONN_S,"share_auth_agent: id %u\n",id); return(share_fwd_common( "fixed-auth-agent@rodents.montreal.qc.ca", chan, &check, &pktfn )); } static void autopipe_signal(void) { char flag; int devnull; flag = 0; write(autopipe,&flag,1); close(autopipe); signal(SIGTSTP,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTTOU,SIG_IGN); devnull = open("/dev/null",O_RDWR,0); if (devnull >= 0) { if (devnull != 0) { dup2(devnull,0); close(devnull); } dup2(0,1); } setsid(); } void share_server_auto(int socketfd, int pipefd, int timeout) { autosocket = socketfd; autopipe = pipefd; #ifndef AF_TIMER if (timeout >= 0) { fprintf(errf,"%s: warning: auto-share timeouts not supported in this build (no AF_TIMER)\n",__progname); } #else autotimeout = timeout; #endif client_session_up(&autopipe_signal); }