#include #include #include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "msgs.h" #include "cmdline.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; } ; 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) { fprintf(stderr,"%s: protocol error: OPEN_CONFIRMATION for nonexistent channel %u\n",__progname,lclno); exit(1); } /* Theoretically, we have to know the channel type to check the rest of the packet. But all channel types we support have zero-length additional data in their open confirmations... */ if (restlen != 0) { fprintf(stderr,"%s: protocol error: junk (len %d) at end of OPEN_CONFIRMATION packet\n",__progname,restlen); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN_SENT) { fprintf(stderr,"%s: protocol error: OPEN_CONFIRMATION already-open channel\n",__progname); exit(1); } c->flags |= CF_OPEN_RCVD; c->remnum = remno; c->maxsend = maxpkt; c->sendwin = winsz; (*c->ops->opendone)(c->cbarg,c->lclnum,OPENSTAT_SUCCESS); (*c->ops->morewin)(c->cbarg,c->lclnum,winsz,winsz); } 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) { fprintf(stderr,"%s; protocol error: REQUEST_FAILURE with body\n",__progname); exit(1); } r = s->grqcb; if (r == 0) { fprintf(stderr,"%s: protocol error: %s to nonexistent GLOBAL_REQUEST\n",__progname,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) { fprintf(stderr,"%s: protocol error: OPEN_FAILURE for nonexistent channel %u\n",__progname,lclno); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN_SENT) { fprintf(stderr,"%s: protocol error: OPEN_FAILURE already-open channel\n",__progname); exit(1); } switch (rcode) { case SSH_OPEN_ADMINISTRATIVELY_PROHIBITED: rcode = OPENSTAT_ADMIN_PROHIB; break; case SSH_OPEN_CONNECT_FAILED: rcode = OPENSTAT_CONNECT_FAILED; break; case SSH_OPEN_UNKNOWN_CHANNEL_TYPE: rcode = OPENSTAT_BAD_TYPE; break; case SSH_OPEN_RESOURCE_SHORTAGE: rcode = OPENSTAT_RESOURCE; break; default: rcode = OPENSTAT_UNKNOWN; break; } channel_destroy(s,c); (*c->ops->opendone)(c->cbarg,c->lclnum,rcode); 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) { fprintf(stderr,"%s: protocol error: data for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: data for unopened channel\n",__progname); exit(1); } if (c->flags & CF_RCVD_EOF) { fprintf(stderr,"%s: protocol error: data after EOF\n",__progname); exit(1); } if (c->flags & CF_RCVD_CLOSE) { fprintf(stderr,"%s: protocol error: data after close\n",__progname); exit(1); } if (data.len > 0) (*c->ops->gotdata)(c->cbarg,chan,ext,code,data.data,data.len); free(data.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) { fprintf(stderr,"%s: protocol error: WINDOW_ADJUST for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: WINDOW_ADJUST for unopened channel\n",__progname); exit(1); } if (! (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE))) { c->sendwin += addl; (*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) { fprintf(stderr,"%s: protocol error: %s for nonexistent channel %u\n",__progname,ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE",chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: %s for unopened channel\n",__progname,ok?"CHANNEL_SUCCESS":"CHANNEL_FAILURE"); exit(1); } r = c->rqcb; if (r == 0) { fprintf(stderr,"%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_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; 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) { fprintf(stderr,"%s: protocol error: CHANNEL_REQUEST for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: CHANNEL_REQUEST for unopened channel\n",__progname); exit(1); } switch ((*c->ops->chanreq)(c->cbarg,chan,str_to_rostr(request),wantreply,rest,restlen)) { 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: fprintf(stderr,"%s: unrecognized CHANNEL_REQUEST `%.*s'\n",__progname,request.len,request.data); if (wantreply) send_reqrep(l,c,0); break; default: abort(); break; } free(request.data); } 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) { fprintf(stderr,"%s: protocol error: CHANNEL_EOF for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: CHANNEL_EOF for unopened channel\n",__progname); exit(1); } if (c->flags & CF_RCVD_EOF) { fprintf(stderr,"%s: protocol error: duplicate CHANNEL_EOF\n",__progname); exit(1); } if (c->flags & CF_RCVD_CLOSE) { fprintf(stderr,"%s: protocol error: EOF after close\n",__progname); 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) { fprintf(stderr,"%s: protocol error: CHANNEL_CLOSE for nonexistent channel %u\n",__progname,chan); exit(1); } if ((c->flags & CF_OPEN) != CF_OPEN) { fprintf(stderr,"%s: protocol error: CHANNEL_CLOSE for unopened channel\n",__progname); 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; if (! s->globalops) abort(); parse_packet(l->b,pp_fail, PP_IGNORE(1), PP_STRING(&name), PP_BOOL(&wantrep), PP_REST(&rest,&restlen) ); switch ((*s->globalops->globalreq)(str_to_rostr(name),wantrep,rest,restlen)) { 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: abort(); 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)); cls.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) abort(); 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; c->maxsend = maxpkt; c->sendwin = initwin; (*s->globalops->chanopen)(str_to_rostr(type),c->lclnum,rest,restlen); } 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN_RCVD) abort(); 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,SSH_MAX_PAYLOAD_LEN); 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_fail(int cno, unsigned int rcode, ROSTR addl, ROSTR lang) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) abort(); if ((c->flags & CF_OPEN) != CF_OPEN_RCVD) abort(); 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); } 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__))) { abort(); } LAYERDESC layer_channels = { &chan_init, &chan_i, &chan_o }; int chan_open(int type, void *arg, const CHANOPS *ops, ...) { va_list ap; unsigned char *opp; CHANNEL *c; opp = &cls.layer->b->opkt[0]; *opp++ = SSH_MSG_CHANNEL_OPEN; /* Note when adding a new channel type: if it doesn't have zero-length channel-type-specific data in its OPEN_CONFIRMATION, open_confirmation() will need work. */ switch (type) { case CHANTYPE_SESSION: opp = put_string(opp,"session",-1); break; case CHANTYPE_X: opp = put_string(opp,"x11",-1); break; case CHANTYPE_FWD_TCP: opp = put_string(opp,"forwarded-tcpip",-1); break; case CHANTYPE_DIR_TCP: opp = put_string(opp,"direct-tcpip",-1); break; default: return(-1); break; } c = nascent_channel(&cls); c->flags |= CF_OPEN_SENT; c->cbarg = arg; c->ops = ops; opp = put_uint32(opp,c->lclnum); opp = put_uint32(opp,c->recvwin); opp = put_uint32(opp,SSH_MAX_PAYLOAD_LEN); va_start(ap,ops); switch (type) { case CHANTYPE_SESSION: break; case CHANTYPE_X: { const char *srcaddr; unsigned short int srcport; srcaddr = va_arg(ap,const char *); srcport = va_arg(ap,unsigned short int); opp = put_string(opp,srcaddr,-1); opp = put_uint32(opp,srcport); } break; case CHANTYPE_FWD_TCP: { const char *lcladdr; unsigned short int lclport; const char *origaddr; unsigned short int origport; lcladdr = va_arg(ap,const char *); lclport = va_arg(ap,unsigned short int); origaddr = va_arg(ap,const char *); origport = va_arg(ap,unsigned short int); opp = put_string(opp,lcladdr,-1); opp = put_uint32(opp,lclport); opp = put_string(opp,origaddr,-1); opp = put_uint32(opp,origport); } break; case CHANTYPE_DIR_TCP: { const char *remaddr; unsigned short int remport; const char *origaddr; unsigned short int origport; remaddr = va_arg(ap,const char *); remport = va_arg(ap,unsigned short int); origaddr = va_arg(ap,const char *); origport = va_arg(ap,unsigned short int); opp = put_string(opp,remaddr,-1); opp = put_uint32(opp,remport); opp = put_string(opp,origaddr,-1); opp = put_uint32(opp,origport); } break; default: return(-1); break; } va_end(ap); cls.layer->b->oplen = 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_RCVD_CLOSE) abort(); return(c->recvwin); } void chan_add_rwin(int cno, unsigned int addl) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & (CF_RCVD_EOF|CF_RCVD_CLOSE|CF_SENT_CLOSE)) abort(); if (addl > 0xffffffff-c->recvwin) abort(); 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_SENT_CLOSE) abort(); 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_SENT_CLOSE) abort(); 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_SENT_CLOSE) abort(); 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; if (len > 30000) abort(); c = channel_by_num(&cls,cno); if (c == 0) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE)) abort(); if (len > c->sendwin) abort(); 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,len); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; c->sendwin -= len; below_opkt(cls.layer); } void chan_send_eof(int cno) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & (CF_SENT_EOF|CF_SENT_CLOSE)) abort(); 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) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_SENT_CLOSE) abort(); send_reqrep(cls.layer,c,success); } int chan_close(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) abort(); if ((c->flags & CF_OPEN) != CF_OPEN) abort(); if (c->flags & CF_SENT_CLOSE) abort(); 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); }