/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #undef sun /* just in case */ extern const char *__progname; #include "oq.h" #include "pp.h" #include "str.h" #include "errf.h" #include "msgs.h" #include "panic.h" #include "params.h" #include "config.h" #include "nested.h" #include "verbose.h" #include "pkt-util.h" #include "channels.h" #include "pollloop.h" #include "connshare.h" typedef struct sharestate SHARESTATE; typedef struct schan SCHAN; typedef struct scrqcb SCRQCB; typedef struct sgrqcb SGRQCB; struct scrqcb { SCRQCB *link; void (*cb)(int, void *); void *arg; } ; struct sgrqcb { SGRQCB *link; void (*cb)(int, void *, const void *, int); void *arg; } ; struct sharestate { int fd; int id; int cvn; SCHAN **cv; const NONCHANOPS *globalops; SGRQCB *grqcb; SGRQCB **grqcbt; void (*input)(unsigned char *, int); int chanopen_off; OQ oq; int ifill; int ilen; unsigned char obuf[SSH_MAX_PAYLOAD_LEN]; unsigned char ibuf[sizeof(int)+SSH_MAX_PAYLOAD_LEN]; } ; struct schan { int flags; #define SCF_OPEN_SENT 0x00000001 #define SCF_OPEN_RCVD 0x00000002 #define SCF_OPEN (SCF_OPEN_SENT|SCF_OPEN_RCVD) #define SCF_SENT_EOF 0x00000004 #define SCF_RCVD_EOF 0x00000008 #define SCF_SENT_CLOSE 0x00000010 #define SCF_RCVD_CLOSE 0x00000020 void *cbarg; const CHANOPS *ops; int lclnum; int remnum; int maxsend; int sendwin; int recvwin; SCRQCB *rqcb; SCRQCB **rqcbt; int chanreq_wr_off; } ; static int autofd = -1; static SHARESTATE st; static PEERID *remid; static int remid_namelen; static int remid_iplen; static int remid_sread; static void (*after_id)(unsigned char *, int); const char share_op_desc[] = "share-op"; const int share_op_desc_len = sizeof(share_op_desc) - 1; static void (*sc_parse_throw)(const char *, va_list *); static void sc_parse_fail(const void *errat __attribute__((__unused__)), const char *fmt, ...) { va_list ap; va_start(ap,fmt); (*sc_parse_throw)(fmt,&ap); va_end(ap); exit(1); } static SCHAN *schan_by_num(unsigned int lclno) { if (lclno >= st.cvn) return(0); return(st.cv[lclno]); } static void sendpkt(unsigned char *endptr) { int len; len = endptr - &st.obuf[0]; if (VERB(SHAREDATA)) { verb(SHAREDATA,"Share client out (%d):\n",len); verb_data_block(VERBOSE_SHAREDATA,&st.obuf[0],len); } oq_queue_copy(&st.oq,&len,sizeof(int)); oq_queue_copy(&st.oq,&st.obuf[0],len); } static void sc_global_request(unsigned char *data, int len) { STR name; int wantrep; const void *rest; int restlen; int rv; if (! st.globalops) panic("no globalops"); parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_STRING(&name), PP_BOOL(&wantrep), PP_REST(&rest,&restlen) ); rv = (*st.globalops->globalreq)(str_to_rostr(name),wantrep,rest,restlen); switch (rv) { case GLOBALREQ_DONE: break; case GLOBALREQ_OK: if (wantrep) { st.obuf[0] = SSH_MSG_REQUEST_SUCCESS; sendpkt(&st.obuf[1]); } break; case GLOBALREQ_FAIL: case GLOBALREQ_UNK: st.obuf[0] = SSH_MSG_REQUEST_FAILURE; sendpkt(&st.obuf[1]); break; default: panic("bad status"); break; } } static void sc_global_reply(unsigned char *data, int len, int ok) { SGRQCB *r; const void *rest; int restlen; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_REST(&rest,&restlen) ); if (!ok && restlen) { fprintf(errf,"%s; protocol error: REQUEST_FAILURE with body\n",__progname); exit(1); } r = st.grqcb; if (r == 0) { fprintf(errf,"%s: protocol error: %s to nonexistent GLOBAL_REQUEST\n",__progname,ok?"REQUEST_SUCCESS":"REQUEST_FAILURE"); exit(1); } if (! (st.grqcb = r->link)) st.grqcbt = &st.grqcb; (*r->cb)(ok?GREQ_OK:GREQ_FAIL,r->arg,rest,restlen); free(r); } static SCHAN *nascent_schan(void) { int n; SCHAN *c; for (n=st.cvn-1;n>=0;n--) if (st.cv[n] == 0) break; if (n < 0) { st.cv = realloc(st.cv,(st.cvn+1)*sizeof(*st.cv)); st.cv[st.cvn] = 0; n = st.cvn ++; } c = malloc(sizeof(SCHAN)); st.cv[n] = c; c->lclnum = n; c->recvwin = 0; c->rqcb = 0; c->rqcbt = &c->rqcb; /* the rest are actually our caller's responsibility */ c->flags = 0; c->ops = 0; c->cbarg = 0; c->remnum = -1; c->maxsend = -1; c->sendwin = -1; return(c); } static void sc_channel_open(unsigned char *data, int len) { STR type; unsigned int remchan; unsigned int initwin; unsigned int maxpkt; const void *rest; int restlen; SCHAN *c; if (! st.globalops) panic("no globalops"); parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_STRING(&type), PP_UINT32(&remchan), PP_UINT32(&initwin), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); c = nascent_schan(); c->flags |= SCF_OPEN_RCVD; c->remnum = remchan; if (maxpkt > SSH_MAX_PAYLOAD_LEN-MAX_PACKET_SIZE_VALUE) { maxpkt -= SSH_MAX_PAYLOAD_LEN - MAX_PACKET_SIZE_VALUE; } c->maxsend = maxpkt; c->sendwin = initwin; (*st.globalops->chanopen)(str_to_rostr(type),c->lclnum,rest,restlen); free_str(type); } static void sc_open_confirmation(unsigned char *data, int len) { unsigned int lclno; unsigned int remno; unsigned int winsz; unsigned int maxpkt; const void *rest; int restlen; SCHAN *c; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&lclno), PP_UINT32(&remno), PP_UINT32(&winsz), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); c = schan_by_num(lclno); if (c == 0) { fprintf(errf,"%s: protocol error: OPEN_CONFIRMATION for nonexistent channel %u\n",__progname,lclno); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN_SENT) { fprintf(errf,"%s: protocol error: OPEN_CONFIRMATION already-open channel\n",__progname); exit(1); } c->flags |= SCF_OPEN_RCVD; c->remnum = remno; if (maxpkt > SSH_MAX_PAYLOAD_LEN-MAX_PACKET_SIZE_VALUE) { maxpkt -= SSH_MAX_PAYLOAD_LEN - MAX_PACKET_SIZE_VALUE; } c->maxsend = maxpkt; c->sendwin = winsz; (*c->ops->opensucc)(c->cbarg,c->lclnum,blk_to_rostr(rest,restlen)); } static void schan_destroy(SCHAN *c) { if ( (c->lclnum < 0) || (c->lclnum >= st.cvn) || (st.cv[c->lclnum] != c) ) panic("backpointer wrong"); st.cv[c->lclnum] = 0; while (c->rqcb) { SCRQCB *r; r = c->rqcb; c->rqcb = r->link; (*r->cb)(CREQ_CLOSE,r->arg); free(r); } } static void sc_open_failure(unsigned char *data, int len) { unsigned int chan; unsigned int rcode; STR reason; STR lang; SCHAN *c; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&rcode), PP_STRING(&reason), PP_STRING(&lang), PP_ENDSHERE ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: OPEN_FAILURE for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN_SENT) { fprintf(errf,"%s: protocol error: OPEN_FAILURE already-open channel\n",__progname); exit(1); } schan_destroy(c); (*c->ops->openfail)(c->cbarg,c->lclnum,rcode,str_to_rostr(reason),str_to_rostr(lang)); free_str(reason); free_str(lang); free(c); } static void sc_data_packet(unsigned char *data, int len, int ext) { unsigned int chan; unsigned int code; STR body; SCHAN *c; if (ext) { parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&code), PP_STRING(&body), PP_ENDSHERE ); } else { parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&body), PP_ENDSHERE ); } c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: data for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: data for unopened channel\n",__progname); exit(1); } if (c->flags & SCF_RCVD_EOF) { fprintf(errf,"%s: protocol error: data after EOF\n",__progname); exit(1); } if (c->flags & SCF_RCVD_CLOSE) { fprintf(errf,"%s: protocol error: data after close\n",__progname); exit(1); } if (body.len > 0) { if (body.len > c->recvwin) { fprintf(errf,"%s: data beyond window\n",__progname); exit(1); } c->recvwin -= body.len; (*c->ops->gotdata)(c->cbarg,chan,ext,code,body.data,body.len); } free_str(body); } static void sc_window_adjust(unsigned char *data, int len) { unsigned int chan; unsigned int addl; SCHAN *c; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&addl), PP_ENDSHERE ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: WINDOW_ADJUST for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: WINDOW_ADJUST for unopened channel\n",__progname); exit(1); } if (! (c->flags & (SCF_SENT_EOF|SCF_SENT_CLOSE))) { c->sendwin += addl; if (c->ops->morewin) (*c->ops->morewin)(c->cbarg,c->lclnum,addl,c->sendwin); } } static void sc_channel_request_reply(unsigned char *data, int len, int ok) { unsigned int chan; SCHAN *c; SCRQCB *r; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: %s for nonexistent channel %u\n",__progname,ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE",chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: %s for unopened channel\n",__progname,ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE"); exit(1); } r = c->rqcb; if (r == 0) { fprintf(errf,"%s: protocol error: %s to nonexistent CHANNEL_REQUEST\n",__progname,ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE"); exit(1); } if (! (c->rqcb = r->link)) c->rqcbt = &c->rqcb; (*r->cb)(ok?CREQ_OK:CREQ_FAIL,r->arg); free(r); } static void send_sc_reqrep(SCHAN *c, int success) { unsigned char *opp; opp = &st.obuf[0]; *opp++ = success ? SSH_MSG_CHANNEL_SUCCESS : SSH_MSG_CHANNEL_FAILURE; opp = put_uint32(opp,c->remnum); sendpkt(opp); } static void sc_channel_request(unsigned char *data, int len) { unsigned int chan; STR request; int wantreply; const void *rest; int restlen; SCHAN *c; int rv; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&request), PP_BOOL(&wantreply), PP_REST(&rest,&restlen) ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: CHANNEL_REQUEST for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: CHANNEL_REQUEST for unopened channel\n",__progname); exit(1); } rv = c->ops->chanreq ? (*c->ops->chanreq)( c->cbarg, chan, str_to_rostr(request), wantreply, rest, restlen ) : CHANREQRET_UNK; switch (rv) { case CHANREQRET_DONE: break; case CHANREQRET_OK: if (wantreply) send_sc_reqrep(c,1); break; case CHANREQRET_FAIL: if (wantreply) send_sc_reqrep(c,0); break; case CHANREQRET_UNK: fprintf(errf,"%s: unrecognized CHANNEL_REQUEST `%.*s'\n",__progname,request.len,request.data); if (wantreply) send_sc_reqrep(c,0); break; default: panic("bad status"); break; } free_str(request); } static void sc_channel_eof(unsigned char *data, int len) { unsigned int chan; SCHAN *c; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: CHANNEL_EOF for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: CHANNEL_EOF for unopened channel\n",__progname); exit(1); } if (c->flags & SCF_RCVD_EOF) { fprintf(errf,"%s: protocol error: duplicate CHANNEL_EOF\n",__progname); exit(1); } if (c->flags & SCF_RCVD_CLOSE) { fprintf(errf,"%s: protocol error: EOF after close\n",__progname); exit(1); } (*c->ops->gotdata)(c->cbarg,chan,0,0,0,0); c->flags |= SCF_RCVD_EOF; } static void sc_channel_close(unsigned char *data, int len) { unsigned int chan; SCHAN *c; parse_data(data,len,&sc_parse_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = schan_by_num(chan); if (c == 0) { fprintf(errf,"%s: protocol error: CHANNEL_CLOSE for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & SCF_OPEN) != SCF_OPEN) { fprintf(errf,"%s: protocol error: CHANNEL_CLOSE for unopened channel\n",__progname); exit(1); } if (c->flags & SCF_SENT_CLOSE) { schan_destroy(c); (*c->ops->closed)(c->cbarg,chan,1); free(c); } else { c->flags |= SCF_RCVD_CLOSE; (*c->ops->closed)(c->cbarg,chan,0); } } static void new_ipkt(void) { st.ifill = 0; st.ilen = sizeof(int); } static void shared_input(unsigned char *data, int len) { const char *pkttype; NESTED void throw(const char *fmt, va_list *app) { fprintf(errf,"%s: protocol error: bad %s packet: ",__progname,pkttype); vfprintf(errf,fmt,*app); fprintf(errf,"\r\n"); exit(1); } if (len <= sizeof(int)) panic("too small"); data += sizeof(int); len -= sizeof(int); if (VERB(SHAREDATA)) { verb(SHAREDATA,"Share client in (%d):\n",len); verb_data_block(VERBOSE_SHAREDATA,&data[0],len); } sc_parse_throw = &throw; switch (data[0]) { default: fprintf(errf,"%s: sharing connection protocol error: packet type %d\n",__progname,data[0]); exit(1); break; case SSH_MSG_GLOBAL_REQUEST: sc_global_request(data,len); break; case SSH_MSG_REQUEST_SUCCESS: sc_global_reply(data,len,1); break; case SSH_MSG_REQUEST_FAILURE: sc_global_reply(data,len,0); break; case SSH_MSG_CHANNEL_OPEN: sc_channel_open(data,len); break; case SSH_MSG_CHANNEL_OPEN_FAILURE: pkttype = "CHANNEL_OPEN_FAILURE"; sc_open_failure(data,len); break; case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: pkttype = "CHANNEL_OPEN_CONFIRMATION"; sc_open_confirmation(data,len); break; case SSH_MSG_CHANNEL_DATA: sc_data_packet(data,len,0); break; case SSH_MSG_CHANNEL_EXTENDED_DATA: sc_data_packet(data,len,1); break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: sc_window_adjust(data,len); break; case SSH_MSG_CHANNEL_SUCCESS: sc_channel_request_reply(data,len,1); break; case SSH_MSG_CHANNEL_FAILURE: sc_channel_request_reply(data,len,0); break; case SSH_MSG_CHANNEL_REQUEST: sc_channel_request(data,len); break; case SSH_MSG_CHANNEL_EOF: sc_channel_eof(data,len); break; case SSH_MSG_CHANNEL_CLOSE: sc_channel_close(data,len); break; } sc_parse_throw = 0; new_ipkt(); } static void ibfilled(void) { if (st.ilen == sizeof(int)) { int len; bcopy(&st.ibuf[0],&len,sizeof(int)); if ((len <= 0) || (len > SSH_MAX_PAYLOAD_LEN)) { fprintf(errf,"%s: sharing connection protocol error: len=%d\n",__progname,len); exit(1); } st.ilen = sizeof(int) + len; return; } else { (*st.input)(&st.ibuf[0],st.ilen); } } static int wtest_cs(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { return(oq_nonempty(&st.oq)); } static void rd_cs(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { unsigned char buf[8192]; int n; int i; n = read(st.fd,&buf[0],sizeof(buf)); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(errf,"%s: sharing connection read: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(errf,"%s: sharing connection read EOF\n",__progname); exit(1); } i = 0; while (n > 0) { if (n < st.ilen-st.ifill) { bcopy(&buf[i],&st.ibuf[st.ifill],n); st.ifill += n; break; } bcopy(&buf[i],&st.ibuf[st.ifill],st.ilen-st.ifill); i += st.ilen - st.ifill; n -= st.ilen - st.ifill; st.ifill = st.ilen; ibfilled(); } } static void wr_cs(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { int w; w = oq_writev(&st.oq,st.fd,&oq_canthappen); if (w < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(errf,"%s: sharing connection write: %s\n",__progname,strerror(errno)); exit(1); } if (w == 0) { fprintf(errf,"%s: sharing connection zero write\n",__progname); exit(1); } oq_dropdata(&st.oq,w); } static void rem_id_2_more(void) { if (remid_sread >= remid_namelen+remid_iplen) { remid->name[remid_namelen] = '\0'; remid->ip[remid_iplen] = '\0'; st.input = after_id; new_ipkt(); } else { st.ifill = 0; st.ilen = remid_namelen+remid_iplen - remid_sread; if (st.ilen > sizeof(st.ibuf)) st.ilen = sizeof(st.ibuf); } } static void rem_id_input_2(unsigned char *data, int len) { int n; if (len < 1) return; if (remid_sread < remid_namelen) { n = remid_namelen - remid_sread; if (n > len) n = len; bcopy(data,&remid->name[remid_sread],n); data += n; len -= n; remid_sread += n; } if (len < 1) return; if (remid_sread < remid_namelen+remid_iplen) { n = remid_namelen+remid_iplen - remid_sread; if (n > len) n = len; bcopy(data,&remid->ip[remid_sread-remid_namelen],n); data += n; len -= n; remid_sread += n; } if (len) panic("leftover length"); rem_id_2_more(); } static void rem_id_input_1(unsigned char *data, int len) { unsigned int namelen; unsigned int iplen; unsigned int port; NESTED void fail(const void *errat __attribute__((__unused__)), const char *fmt __attribute__((__unused__)), ...) { panic("impossible"); } if (len != 12) panic("length wrong"); parse_data(data,12,&fail, PP_UINT32(&namelen), PP_UINT32(&iplen), PP_UINT32(&port), PP_ENDSHERE ); remid->name = malloc(namelen+1); remid->ip = malloc(iplen+1); remid->port = port; remid_namelen = namelen; remid_iplen = iplen; remid_sread = 0; st.input = &rem_id_input_2; rem_id_2_more(); } static int make_connection(const char *path, void (*fail)(const char *, ...)) { int fd; int pl; struct sockaddr_un *sun; fd = socket(AF_LOCAL,SOCK_STREAM,0); if (fd < 0) (*fail)("socket: %s",strerror(errno)); 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); if (connect(fd,(void *)sun,sun->sun_len) < 0) (*fail)("connect: %s",strerror(errno)); free(sun); return(fd); } void share_open(const char *path, PEERID *id) { NESTEDFWD void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); NESTEDDEF void fail(const char *fmt, ...) { va_list ap; fprintf(errf,"%s: can't connect to share socket %s: ",__progname,path); va_start(ap,fmt); vfprintf(errf,fmt,ap); va_end(ap); fprintf(errf,"\n"); exit(1); } if (autofd < 0) { st.fd = make_connection(path,&fail); if (VERB(SHARECONN_C)) verb(SHARECONN_C,"C: sharing connection to %s opened\n",path); } else { st.fd = autofd; if (VERB(SHARECONN_C)) verb(SHARECONN_C,"C: using auto-sharing connection\n"); } st.id = add_poll_fd(st.fd,&rwtest_always,&wtest_cs,&rd_cs,&wr_cs,0); st.cvn = 0; st.cv = 0; st.globalops = 0; st.grqcb = 0; st.grqcbt = &st.grqcb; st.input = &rem_id_input_1; oq_init(&st.oq); st.ifill = 0; st.ilen = 12; remid = id; after_id = &shared_input; } void *share_chan_open_hdr(ROSTR type) { unsigned char *opp; opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN; opp = put_string(opp,type.data,type.len); st.chanopen_off = opp - &st.obuf[0]; opp = put_uint32(opp,0); opp = put_uint32(opp,0); opp = put_uint32(opp,MAX_PACKET_SIZE_VALUE); return(opp); } int share_chan_open_send(void *msgend, const CHANOPS *ops, void *arg) { SCHAN *c; unsigned char *opp; c = nascent_schan(); c->flags |= SCF_OPEN_SENT; c->cbarg = arg; c->ops = ops; opp = put_uint32(&st.obuf[st.chanopen_off],c->lclnum); put_uint32(opp,c->recvwin); sendpkt(msgend); return(c->lclnum); } void share_chan_open_ok(int cno, ROSTR body, const CHANOPS *ops, void *arg) { SCHAN *c; unsigned char *opp; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & SCF_OPEN) != SCF_OPEN_RCVD) panic("channel state wrong"); c->flags |= SCF_OPEN_SENT; opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; opp = put_uint32(opp,c->remnum); opp = put_uint32(opp,c->lclnum); opp = put_uint32(opp,0); opp = put_uint32(opp,MAX_PACKET_SIZE_VALUE); opp = put_block(opp,body.data,body.len); c->ops = ops; c->cbarg = arg; sendpkt(opp); } void share_chan_open_fail(int cno, unsigned int rcode, ROSTR addl, ROSTR lang) { SCHAN *c; unsigned char *opp; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & SCF_OPEN) != SCF_OPEN_RCVD) panic("channel state wrong"); c->flags |= SCF_OPEN_SENT; opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_OPEN_FAILURE; opp = put_uint32(opp,c->remnum); opp = put_uint32(opp,rcode); opp = put_string(opp,addl.data,addl.len); opp = put_string(opp,lang.data,lang.len); schan_destroy(c); sendpkt(opp); free(c); } void share_chan_set_ops(int cno, const CHANOPS *ops, void *arg) { SCHAN *c; c = schan_by_num(cno); if (c == 0) panic("channel not found"); c->ops = ops; c->cbarg = arg; } unsigned int share_chan_get_rwin(int cno) { SCHAN *c; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_RCVD_CLOSE)) != SCF_OPEN) panic("channel not open"); return(c->recvwin); } void share_chan_add_rwin(int cno, unsigned int addl) { SCHAN *c; unsigned char *opp; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_RCVD_CLOSE|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel state wrong"); if (addl > 0xffffffff-c->recvwin) panic("window overflow"); c->recvwin += addl; opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_WINDOW_ADJUST; opp = put_uint32(opp,c->remnum); opp = put_uint32(opp,addl); sendpkt(opp); } unsigned int share_chan_get_wwin(int cno) { SCHAN *c; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & SCF_OPEN) != SCF_OPEN) panic("channel not open"); return((c->flags&(SCF_SENT_EOF|SCF_SENT_CLOSE))?0:c->sendwin); } unsigned char *share_chan_req_hdr(int cno, ROSTR type) { SCHAN *c; unsigned char *opp; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel not open"); opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_REQUEST; opp = put_uint32(opp,c->remnum); opp = put_string(opp,type.data,type.len); c->chanreq_wr_off = opp - &st.obuf[0]; *opp++ = 2; /* not a legal want-reply; corrected before sending */ return(opp); } void share_chan_send_req_reply(int cno, unsigned char *opp, void (*cb)(int, void *), void *cbarg) { SCHAN *c; SCRQCB *r; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel not open"); st.obuf[c->chanreq_wr_off] = 1; r = malloc(sizeof(SCRQCB)); r->link = 0; r->cb = cb; r->arg = cbarg; *c->rqcbt = r; c->rqcbt = &r->link; sendpkt(opp); } void share_chan_send_req_blind(int cno, unsigned char *opp) { SCHAN *c; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel not open"); st.obuf[c->chanreq_wr_off] = 0; sendpkt(opp); } void share_chan_send_data(int cno, int ext, unsigned int code, const void *buf, int len) { SCHAN *c; unsigned char *opp; if (len > 30000) panic("too much data"); c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_EOF|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel state wrong"); if (len > c->sendwin) panic("window overflow"); opp = &st.obuf[0]; *opp++ = ext ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA; opp = put_uint32(opp,c->remnum); if (ext) opp = put_uint32(opp,code); opp = put_string(opp,buf,len); c->sendwin -= len; sendpkt(opp); } void share_chan_send_eof(int cno) { SCHAN *c; unsigned char *opp; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_EOF|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel state wrong"); opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_EOF; opp = put_uint32(opp,c->remnum); sendpkt(opp); c->flags |= SCF_SENT_EOF; } static void send_close(SCHAN *c) { unsigned char *opp; opp = &st.obuf[0]; *opp++ = SSH_MSG_CHANNEL_CLOSE; opp = put_uint32(opp,c->remnum); sendpkt(opp); } int share_chan_close(int cno) { SCHAN *c; c = schan_by_num(cno); if (c == 0) panic("channel not found"); if ((c->flags & (SCF_OPEN|SCF_SENT_CLOSE)) != SCF_OPEN) panic("channel not open"); send_close(c); if (c->flags & SCF_RCVD_CLOSE) { schan_destroy(c); free(c); return(1); } else { c->flags |= SCF_SENT_CLOSE; return(0); } } void share_set_globalops(const NONCHANOPS *ops) { st.globalops = ops; } unsigned char *share_global_req_hdr(ROSTR name) { unsigned char *opp; opp = &st.obuf[0]; *opp++ = SSH_MSG_GLOBAL_REQUEST; opp = put_string(opp,name.data,name.len); *opp++ = 1; return(opp); } void share_global_send_req(unsigned char *opp, void (*cb)(int, void *, const void *, int), void *cbarg) { SGRQCB *r; r = malloc(sizeof(SGRQCB)); r->link = 0; r->cb = cb; r->arg = cbarg; *st.grqcbt = r; st.grqcbt = &r->link; sendpkt(opp); } int share_oq_empty(void) { return(oq_empty(&st.oq)); } void share_client_auto(int fd) { autofd = fd; } void share_op(int op) { const char *path; int fd; int len; unsigned char *mp; struct iovec iov[2]; unsigned char msg[1+16+4+share_op_desc_len+1]; NESTEDFWD void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); NESTEDDEF void fail(const char *fmt, ...) { va_list ap; fprintf(errf,"%s: can't connect to share socket %s: ",__progname,path); va_start(ap,fmt); vfprintf(errf,fmt,ap); va_end(ap); fprintf(errf,"\n"); exit(1); } path = config_str("share-path"); if (! path) { fprintf(errf,"%s: no shared-client path specified\n",__progname); exit(1); } fd = make_connection(path,&fail); if (VERB(SHARECONN_C)) verb(SHARECONN_C,"C: sharing connection to %s opened\n",path); mp = &msg[0]; *mp++ = MOUSSH_PRIVATE_PROTO; mp = put_uint32(mp,moussh_private_proto_signature[0]); mp = put_uint32(mp,moussh_private_proto_signature[1]); mp = put_uint32(mp,moussh_private_proto_signature[2]); mp = put_uint32(mp,moussh_private_proto_signature[3]); mp = put_string(mp,&share_op_desc[0],share_op_desc_len); *mp++ = op; if (mp != &msg[sizeof(msg)]) panic("message size mismatch"); len = sizeof(msg); iov[0].iov_base = &len; iov[0].iov_len = sizeof(int); iov[1].iov_base = &msg[0]; iov[1].iov_len = sizeof(msg); writev(fd,&iov[0],2); close(fd); exit(0); } static void share_status_input(unsigned char *data, int len) { unsigned int sig[4]; const void *rest; int restlen; STR proto; unsigned char kind; unsigned int msscount; unsigned int *mssvalue; int i; NESTEDFWD void throw(const void *, const char *, ...) __attribute__((__format__(__printf__,2,3))); NESTEDDEF void throw(const void *errat __attribute__((__unused__)), const char *fmt, ...) { va_list ap; fprintf(errf,"%s: protocol error: bad share-status response packet: ",__progname); va_start(ap,fmt); vfprintf(errf,fmt,ap); va_end(ap); fprintf(errf,"\r\n"); exit(1); } if (len <= sizeof(int)) panic("too small"); data += sizeof(int); len -= sizeof(int); if (VERB(SHAREDATA)) { verb(SHAREDATA,"Share client in (%d):\n",len); verb_data_block(VERBOSE_SHAREDATA,&data[0],len); } switch (data[0]) { default: fprintf(errf,"%s: sharing connection protocol error: packet type %d\n",__progname,data[0]); exit(1); break; case MOUSSH_PRIVATE_PROTO: parse_data(data,len,&throw, 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]) ) throw(0,"signature wrong"); parse_data(rest,restlen,&throw, PP_STRING(&proto), PP_REST(&rest,&restlen) ); if (str_equalsC(proto,"share-op")) { parse_data(rest,restlen,&throw, PP_BYTE(&kind), PP_REST(&rest,&restlen) ); if (kind == MOUSSH_SHARE_OP_STATREP) { parse_data(rest,restlen,&throw, PP_UINT32(&msscount), PP_REST(&rest,&restlen) ); if (msscount < 1) { throw(0,"impossible server count 0 in share-status response"); } i = msscount; if ((i < 0) || (i != msscount)) { throw(0,"ridiculous server count %u in share-status response",msscount); } i *= sizeof(*mssvalue); if ((i < 0) || (i/sizeof(*mssvalue) != msscount)) { throw(0,"ridiculous server count %u in share-status response",msscount); } mssvalue = malloc(i); if (! mssvalue) { throw(0,"can't malloc for server count %u",msscount); } for (i=0;i