/* This file is in the public domain. */ #include #include #include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "msgs.h" #include "errf.h" #include "panic.h" #include "params.h" #include "config.h" #include "pkt-util.h" #include "channels.h" #define MAX_RECV_WIN 32768 typedef struct chanlayer_state CHANLAYER_STATE; typedef struct channel CHANNEL; typedef struct crqcb CRQCB; typedef struct grqcb GRQCB; struct chanlayer_state { LAYER *layer; int cvn; CHANNEL **cv; const NONCHANOPS *globalops; GRQCB *grqcb; GRQCB **grqcbt; int chanopen_off; } ; struct channel { int flags; #define CF_OPEN_SENT 0x00000001 #define CF_OPEN_RCVD 0x00000002 #define CF_OPEN (CF_OPEN_SENT|CF_OPEN_RCVD) #define CF_SENT_EOF 0x00000004 #define CF_RCVD_EOF 0x00000008 #define CF_SENT_CLOSE 0x00000010 #define CF_RCVD_CLOSE 0x00000020 void *cbarg; const CHANOPS *ops; int lclnum; int remnum; int maxsend; int sendwin; int recvwin; CRQCB *rqcb; CRQCB **rqcbt; int chanreq_wr_off; } ; struct crqcb { CRQCB *link; void (*cb)(int, void *); void *arg; } ; struct grqcb { GRQCB *link; void (*cb)(int, void *, const void *, int); void *arg; } ; static CHANLAYER_STATE cls; static void *chan_init(LAYER *l) { cls.layer = l; cls.cvn = 0; cls.cv = 0; cls.grqcb = 0; cls.grqcbt = &cls.grqcb; cls.globalops = 0; return(&cls); } static CHANNEL *channel_by_num(CHANLAYER_STATE *s, unsigned int lclno) { if (lclno >= s->cvn) return(0); return(s->cv[lclno]); } static void open_confirmation(CHANLAYER_STATE *s, LAYER *l) { unsigned int lclno; unsigned int remno; unsigned int winsz; unsigned int maxpkt; const void *rest; int restlen; CHANNEL *c; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&lclno), PP_UINT32(&remno), PP_UINT32(&winsz), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); c = channel_by_num(s,lclno); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: OPEN_CONFIRMATION for nonexistent channel %u",lclno); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN_SENT) { logmsg(LM_WARN|LM_PEER,"protocol error: OPEN_CONFIRMATION for already-open channel"); exit(1); } c->flags |= CF_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 channel_destroy(CHANLAYER_STATE *s, CHANNEL *c) { s->cv[c->lclnum] = 0; while (c->rqcb) { CRQCB *r; r = c->rqcb; c->rqcb = r->link; (*r->cb)(CREQ_CLOSE,r->arg); free(r); } } static void global_reply(CHANLAYER_STATE *s, LAYER *l, int ok) { GRQCB *r; const void *rest; int restlen; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_REST(&rest,&restlen) ); if (!ok && restlen) { logmsg(LM_WARN|LM_PEER,"protocol error: REQUEST_FAILURE with body"); exit(1); } r = s->grqcb; if (r == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: %s to nonexistent GLOBAL_REQUEST",ok?"REQUEST_SUCCESS":"REQUEST_FAILURE"); exit(1); } if (! (s->grqcb = r->link)) s->grqcbt = &s->grqcb; (*r->cb)(ok?GREQ_OK:GREQ_FAIL,r->arg,rest,restlen); free(r); } static void open_failure(CHANLAYER_STATE *s, LAYER *l) { unsigned int lclno; unsigned int rcode; STR reason; STR lang; CHANNEL *c; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&lclno), PP_UINT32(&rcode), PP_STRING(&reason), PP_STRING(&lang), PP_ENDSHERE ); c = channel_by_num(s,lclno); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: OPEN_FAILURE for nonexistent channel %u",lclno); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN_SENT) { logmsg(LM_WARN|LM_PEER,"protocol error: OPEN_FAILURE already-open channel"); exit(1); } channel_destroy(s,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 data_packet(CHANLAYER_STATE *s, LAYER *l, int ext) { unsigned int chan; unsigned int code; STR data; CHANNEL *c; if (ext) { parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&code), PP_STRING(&data), PP_ENDSHERE ); } else { parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&data), PP_ENDSHERE ); } c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: data for nonexistent channel %u",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: data for unopened channel"); exit(1); } if (c->flags & CF_RCVD_EOF) { logmsg(LM_WARN|LM_PEER,"protocol error: data after EOF"); exit(1); } if (c->flags & CF_RCVD_CLOSE) { logmsg(LM_WARN|LM_PEER,"protocol error: data after close"); exit(1); } if (data.len > 0) { if (data.len > c->recvwin) { logmsg(LM_WARN|LM_PEER,"data beyond window"); exit(1); } c->recvwin -= data.len; (*c->ops->gotdata)(c->cbarg,chan,ext,code,data.data,data.len); } free_str(data); } static void window_adjust(CHANLAYER_STATE *s, LAYER *l) { unsigned int chan; unsigned int addl; CHANNEL *c; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_UINT32(&addl), PP_ENDSHERE ); c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: WINDOW_ADJUST for nonexistent channel %u",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: WINDOW_ADJUST for unopened channel"); exit(1); } if (! (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE))) { c->sendwin += addl; if (c->ops->morewin) (*c->ops->morewin)(c->cbarg,c->lclnum,addl,c->sendwin); } } static void channel_request_reply(CHANLAYER_STATE *s, LAYER *l, int ok) { unsigned int chan; CHANNEL *c; CRQCB *r; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: %s for nonexistent channel %u",ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: %s for unopened channel",ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE"); exit(1); } r = c->rqcb; if (r == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: %s to nonexistent CHANNEL_REQUEST",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_reqrep(LAYER *l, CHANNEL *c, int success) { unsigned char *opp; opp = &l->b->opkt[0]; *opp++ = success ? SSH_MSG_CHANNEL_SUCCESS : SSH_MSG_CHANNEL_FAILURE; opp = put_uint32(opp,c->remnum); l->b->oplen = opp - &l->b->opkt[0]; below_opkt(l); } static void channel_request(CHANLAYER_STATE *s, LAYER *l) { unsigned int chan; STR request; int wantreply; const void *rest; int restlen; CHANNEL *c; int rv; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_STRING(&request), PP_BOOL(&wantreply), PP_REST(&rest,&restlen) ); c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_REQUEST for nonexistent channel %u",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_REQUEST for unopened channel"); 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_reqrep(l,c,1); break; case CHANREQRET_FAIL: if (wantreply) send_reqrep(l,c,0); break; case CHANREQRET_UNK: logmsg(LM_NOTE|LM_PEER,"unrecognized CHANNEL_REQUEST `%.*s'",request.len,request.data); if (wantreply) send_reqrep(l,c,0); break; default: panic("bad chanreq status %d",rv); break; } free_str(request); } static void channel_eof(CHANLAYER_STATE *s, LAYER *l) { unsigned int chan; CHANNEL *c; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_EOF for nonexistent channel %u",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_EOF for unopened channel"); exit(1); } if (c->flags & CF_RCVD_EOF) { logmsg(LM_WARN|LM_PEER,"protocol error: duplicate CHANNEL_EOF"); exit(1); } if (c->flags & CF_RCVD_CLOSE) { logmsg(LM_WARN|LM_PEER,"protocol error: EOF after close"); exit(1); } (*c->ops->gotdata)(c->cbarg,chan,0,0,0,0); c->flags |= CF_RCVD_EOF; } static void send_close(LAYER *l, CHANNEL *c) { unsigned char *opp; opp = &l->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_CLOSE; opp = put_uint32(opp,c->remnum); l->b->oplen = opp - &l->b->opkt[0]; below_opkt(l); } static void channel_close(CHANLAYER_STATE *s, LAYER *l) { unsigned int chan; CHANNEL *c; parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_UINT32(&chan), PP_ENDSHERE ); c = channel_by_num(s,chan); if (c == 0) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_CLOSE for nonexistent channel %u",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { logmsg(LM_WARN|LM_PEER,"protocol error: CHANNEL_CLOSE for unopened channel"); exit(1); } if (c->flags & CF_SENT_CLOSE) { channel_destroy(s,c); (*c->ops->closed)(c->cbarg,chan,1); free(c); } else { c->flags |= CF_RCVD_CLOSE; (*c->ops->closed)(c->cbarg,chan,0); } } static void global_request(CHANLAYER_STATE *s, LAYER *l) { STR name; int wantrep; const void *rest; int restlen; int rv; if (! s->globalops) panic("no globalops"); parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_STRING(&name), PP_BOOL(&wantrep), PP_REST(&rest,&restlen) ); rv = (*s->globalops->globalreq)(str_to_rostr(name),wantrep,rest,restlen); switch (rv) { case GLOBALREQ_DONE: break; case GLOBALREQ_OK: if (wantrep) { l->b->opkt[0] = SSH_MSG_REQUEST_SUCCESS; l->b->oplen = 1; below_opkt(l); } break; case GLOBALREQ_FAIL: case GLOBALREQ_UNK: l->b->opkt[0] = SSH_MSG_REQUEST_FAILURE; l->b->oplen = 1; below_opkt(l); break; default: panic("bad globalreq status %d",rv); break; } } static CHANNEL *nascent_channel(CHANLAYER_STATE *s) { int n; CHANNEL *c; for (n=s->cvn-1;n>=0;n--) if (s->cv[n] == 0) break; if (n < 0) { s->cv = realloc(s->cv,(s->cvn+1)*sizeof(*s->cv)); s->cv[s->cvn] = 0; n = s->cvn ++; } c = malloc(sizeof(CHANNEL)); s->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 channel_open(CHANLAYER_STATE *s, LAYER *l) { STR type; unsigned int remchan; unsigned int initwin; unsigned int maxpkt; CHANNEL *c; const void *rest; int restlen; if (! s->globalops) panic("no globalops"); parse_packet(l->b,&pp_fail, PP_IGNORE(1), PP_STRING(&type), PP_UINT32(&remchan), PP_UINT32(&initwin), PP_UINT32(&maxpkt), PP_REST(&rest,&restlen) ); c = nascent_channel(s); c->flags |= CF_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; (*s->globalops->chanopen)(str_to_rostr(type),c->lclnum,rest,restlen); free_str(type); } void chan_open_ok(int cno, ROSTR body, const CHANOPS *ops, void *arg) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN_RCVD) panic("channel state wrong"); c->flags |= CF_OPEN_SENT; cls.layer->b->opkt[0] = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; opp = put_uint32(&cls.layer->b->opkt[1],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; cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); } void chan_open_ok_detail(int cno, unsigned int iwin, unsigned int maxpkt, ROSTR body, const CHANOPS *ops, void *arg) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN_RCVD) panic("channel state wrong"); c->flags |= CF_OPEN_SENT; cls.layer->b->opkt[0] = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; opp = put_uint32(&cls.layer->b->opkt[1],c->remnum); opp = put_uint32(opp,c->lclnum); opp = put_uint32(opp,iwin); opp = put_uint32(opp,maxpkt); opp = put_block(opp,body.data,body.len); c->ops = ops; c->cbarg = arg; c->recvwin = iwin; cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); } void chan_open_fail(int cno, unsigned int rcode, ROSTR addl, ROSTR lang) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN_RCVD) panic("channel state wrong"); cls.layer->b->opkt[0] = SSH_MSG_CHANNEL_OPEN_FAILURE; opp = put_uint32(&cls.layer->b->opkt[1],c->remnum); opp = put_uint32(opp,rcode); opp = put_string(opp,addl.data,addl.len); opp = put_string(opp,lang.data,lang.len); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); channel_destroy(&cls,c); free(c); } void chan_set_ops(int cno, const CHANOPS *ops, void *arg) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); c->ops = ops; c->cbarg = arg; } static void chan_i(LAYER *l, void *arg) { CHANLAYER_STATE *s; BPP *b; s = arg; b = l->b; switch (b->ipkt[0]) { default: above_ipkt(l); break; case SSH_MSG_GLOBAL_REQUEST: global_request(s,l); break; case SSH_MSG_REQUEST_SUCCESS: global_reply(s,l,1); break; case SSH_MSG_REQUEST_FAILURE: global_reply(s,l,0); break; case SSH_MSG_CHANNEL_OPEN: channel_open(s,l); break; case SSH_MSG_CHANNEL_OPEN_FAILURE: open_failure(s,l); break; case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: open_confirmation(s,l); break; case SSH_MSG_CHANNEL_DATA: data_packet(s,l,0); break; case SSH_MSG_CHANNEL_EXTENDED_DATA: data_packet(s,l,1); break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: window_adjust(s,l); break; case SSH_MSG_CHANNEL_SUCCESS: channel_request_reply(s,l,1); break; case SSH_MSG_CHANNEL_FAILURE: channel_request_reply(s,l,0); break; case SSH_MSG_CHANNEL_REQUEST: channel_request(s,l); break; case SSH_MSG_CHANNEL_EOF: channel_eof(s,l); break; case SSH_MSG_CHANNEL_CLOSE: channel_close(s,l); break; } } static void chan_o(LAYER *l __attribute__((__unused__)), void *arg __attribute__((__unused__))) { panic("called"); } LAYERDESC layer_channels = { &chan_init, &chan_i, &chan_o }; void *chan_open_hdr(ROSTR type) { unsigned char *opp; opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_OPEN; opp = put_string(opp,type.data,type.len); cls.chanopen_off = opp - &cls.layer->b->opkt[0]; opp = put_uint32(opp,0); opp = put_uint32(opp,0); opp = put_uint32(opp,MAX_PACKET_SIZE_VALUE); return(opp); } int chan_open_send(void *msgend, const CHANOPS *ops, void *arg) { CHANNEL *c; unsigned char *opp; c = nascent_channel(&cls); c->flags |= CF_OPEN_SENT; c->cbarg = arg; c->ops = ops; opp = put_uint32(&cls.layer->b->opkt[cls.chanopen_off],c->lclnum); put_uint32(opp,c->recvwin); cls.layer->b->oplen = (unsigned char *)msgend - &cls.layer->b->opkt[0]; below_opkt(cls.layer); return(c->lclnum); } int chan_open_detail(ROSTR type, int maxpkt, unsigned int rwin, const void *data, int datalen, const CHANOPS *ops, void *arg) { CHANNEL *c; unsigned char *opp; c = nascent_channel(&cls); c->flags |= CF_OPEN_SENT; c->cbarg = arg; c->ops = ops; c->recvwin = rwin; opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_OPEN; opp = put_string(opp,type.data,type.len); opp = put_uint32(opp,c->lclnum); opp = put_uint32(opp,rwin); opp = put_uint32(opp,maxpkt); opp = put_block(opp,data,datalen); cls.layer->b->oplen = (unsigned char *)opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); return(c->lclnum); } unsigned int chan_get_rwin(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & (CF_OPEN|CF_RCVD_CLOSE)) != CF_OPEN) panic("channel not open"); return(c->recvwin); } unsigned int chan_get_maxpkt(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if (! (c->flags & CF_OPEN_RCVD)) panic("channel state wrong"); return(c->maxsend); } void chan_add_rwin(int cno, unsigned int addl) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & (CF_OPEN|CF_RCVD_CLOSE|CF_SENT_CLOSE)) != CF_OPEN) panic("channel state wrong"); if (addl > 0xffffffff-c->recvwin) panic("rwin too large"); c->recvwin += addl; opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_WINDOW_ADJUST; opp = put_uint32(opp,c->remnum); opp = put_uint32(opp,addl); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); } unsigned int chan_get_wwin(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if (! (c->flags & CF_OPEN_RCVD)) panic("channel state wrong"); return((c->flags&(CF_SENT_EOF|CF_SENT_CLOSE))?0:c->sendwin); } unsigned char *chan_req_hdr(int cno, ROSTR type) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & CF_SENT_CLOSE) panic("channel closed"); opp = &cls.layer->b->opkt[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 - &cls.layer->b->opkt[0]; *opp++ = 2; /* not a legal want-reply; corrected before sending */ return(opp); } void chan_send_req_reply(int cno, unsigned char *opp, void (*cb)(int, void *), void *cbarg) { CHANNEL *c; CRQCB *r; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & CF_SENT_CLOSE) panic("channel closed"); cls.layer->b->opkt[c->chanreq_wr_off] = 1; cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; r = malloc(sizeof(CRQCB)); r->link = 0; r->cb = cb; r->arg = cbarg; *c->rqcbt = r; c->rqcbt = &r->link; below_opkt(cls.layer); } void chan_send_req_blind(int cno, unsigned char *opp) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & CF_SENT_CLOSE) panic("channel closed"); cls.layer->b->opkt[c->chanreq_wr_off] = 0; cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); } void chan_send_data(int cno, int ext, unsigned int code, const void *buf, int len) { CHANNEL *c; unsigned char *opp; int n; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE)) panic("data not flowing"); if (len > c->sendwin) panic("window overrun"); while (len > 0) { n = (len < c->maxsend) ? len : c->maxsend; opp = &cls.layer->b->opkt[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,n); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; c->sendwin -= n; below_opkt(cls.layer); buf = ((const char *)buf) + n; len -= n; } } void chan_send_eof(int cno) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE)) panic("data not flowing"); opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_EOF; opp = put_uint32(opp,c->remnum); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; below_opkt(cls.layer); c->flags |= CF_SENT_EOF; } void chan_reqrep(int cno, int success) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & CF_SENT_CLOSE) panic("channel closed"); send_reqrep(cls.layer,c,success); } int chan_close(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) panic("channel not found"); if ((c->flags & CF_OPEN) != CF_OPEN) panic("channel not open"); if (c->flags & CF_SENT_CLOSE) panic("channel closed"); send_close(cls.layer,c); if (c->flags & CF_RCVD_CLOSE) { channel_destroy(&cls,c); free(c); return(1); } else { c->flags |= CF_SENT_CLOSE; return(0); } } LAYER *chan_layer(void) { return(cls.layer); } void set_globalops(const NONCHANOPS *ops) { cls.globalops = ops; } unsigned char *global_req_hdr(ROSTR name) { unsigned char *opp; opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_GLOBAL_REQUEST; opp = put_string(opp,name.data,name.len); *opp++ = 1; return(opp); } void global_send_req(unsigned char *pktend, void (*cb)(int, void *, const void *, int), void *cbarg) { GRQCB *r; cls.layer->b->oplen = pktend - &cls.layer->b->opkt[0]; r = malloc(sizeof(GRQCB)); r->link = 0; r->cb = cb; r->arg = cbarg; *cls.grqcbt = r; cls.grqcbt = &r->link; below_opkt(cls.layer); } unsigned char *global_rep_hdr(int ok) { unsigned char *opp; opp = &cls.layer->b->opkt[0]; switch (ok) { case GREQ_OK: *opp++ = SSH_MSG_REQUEST_SUCCESS; return(opp); break; case GREQ_FAIL: *opp++ = SSH_MSG_REQUEST_FAILURE; cls.layer->b->oplen = 1; below_opkt(cls.layer); return(0); break; } panic("bad status"); } void global_send_rep(unsigned char *pktend) { cls.layer->b->oplen = pktend - &cls.layer->b->opkt[0]; below_opkt(cls.layer); } void print_openfail_rcode(FILE *to, unsigned int rcode) { switch (rcode) { case SSH_OPEN_ADMINISTRATIVELY_PROHIBITED: fprintf(to,"admin-prohib"); break; case SSH_OPEN_CONNECT_FAILED: fprintf(to,"connect-failed"); break; case SSH_OPEN_UNKNOWN_CHANNEL_TYPE: fprintf(to,"unknown-type"); break; case SSH_OPEN_RESOURCE_SHORTAGE: fprintf(to,"resource-shortage"); break; default: fprintf(to,"?%u",rcode); break; } }