#include #include #include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "msgs.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 rqcb RQCB; struct chanlayer_state { LAYER *layer; int cvn; CHANNEL **cv; } ; struct channel { int flags; #define CF_OPENED 0x00000001 #define CF_SENT_EOF 0x00000002 #define CF_RCVD_EOF 0x00000004 #define CF_SENT_CLOSE 0x00000008 #define CF_RCVD_CLOSE 0x00000010 const CHANOPS *ops; int lclnum; int remnum; int maxsend; int sendwin; int recvwin; RQCB *rqcb; RQCB **rqcbt; } ; struct rqcb { RQCB *link; void (*cb)(int); } ; static CHANLAYER_STATE cls; static void *chan_init(LAYER *l) { cls.layer = l; cls.cvn = 0; cls.cv = 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_OPENED) { fprintf(stderr,"%s: protocol error: OPEN_CONFIRMATION already-open channel\n",__progname); exit(1); } c->flags |= CF_OPENED; c->remnum = remno; c->maxsend = maxpkt; c->sendwin = winsz; (*c->ops->opendone)(c->lclnum,OPENSTAT_SUCCESS); (*c->ops->morewin)(c->lclnum,winsz,winsz); } 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_OPENED)) { 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)(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_OPENED)) { 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->lclnum,addl,c->sendwin); } } static void channel_request_reply(CHANLAYER_STATE *s, LAYER *l, int ok) { unsigned int chan; CHANNEL *c; RQCB *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_OPENED)) { 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); 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_OPENED)) { fprintf(stderr,"%s: protocol error: CHANNEL_REQUEST for unopened channel\n",__progname); exit(1); } switch ((*c->ops->chanreq)(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); send_reqrep(l,c,SSH_MSG_CHANNEL_FAILURE); 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_OPENED)) { 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)(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_OPENED)) { fprintf(stderr,"%s: protocol error: CHANNEL_CLOSE for unopened channel\n",__progname); exit(1); } if (c->flags & CF_SENT_CLOSE) { s->cv[c->lclnum] = 0; while (c->rqcb) { RQCB *r; r = c->rqcb; c->rqcb = r->link; (*r->cb)(CREQ_CLOSE); free(r); } (*c->ops->closed)(chan,1); free(c); } else { c->flags |= CF_RCVD_CLOSE; (*c->ops->closed)(chan,0); } } static void chan_i(LAYER *l, void *arg) { CHANLAYER_STATE *s; BPP *b; s = arg; b = l->b; switch (b->ipkt[0]) { default: case SSH_MSG_GLOBAL_REQUEST: case SSH_MSG_REQUEST_SUCCESS: case SSH_MSG_REQUEST_FAILURE: case SSH_MSG_CHANNEL_OPEN: case SSH_MSG_CHANNEL_OPEN_FAILURE: above_ipkt(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 }; static int new_channel_number(CHANLAYER_STATE *s) { int i; for (i=0;icvn;i++) if (s->cv[i] == 0) return(i); s->cv = realloc(s->cv,(s->cvn+1)*sizeof(*s->cv)); s->cv[s->cvn] = 0; return(s->cvn++); } int chan_open(int type, const CHANOPS *ops, ...) { va_list ap; unsigned char *opp; int n; 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; } n = new_channel_number(&cls); c = malloc(sizeof(CHANNEL)); cls.cv[n] = c; c->flags = 0; c->ops = ops; c->lclnum = n; c->remnum = -1; c->maxsend = -1; c->sendwin = -1; c->recvwin = 0; c->rqcb = 0; c->rqcbt = &c->rqcb; opp = put_uint32(opp,n); 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,"x11",-1); 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,"forwarded-tcpip",-1); 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,"direct-tcpip",-1); 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(n); } unsigned int chan_get_rwin(int cno) { CHANNEL *c; c = channel_by_num(&cls,cno); if (c == 0) abort(); if (! (c->flags & CF_OPENED)) 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_OPENED)) 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_OPENED)) abort(); return((c->flags&(CF_SENT_EOF|CF_SENT_CLOSE))?0:c->sendwin); } unsigned char *chan_rq_hdr(int cno) { CHANNEL *c; unsigned char *opp; c = channel_by_num(&cls,cno); if (c == 0) abort(); if (! (c->flags & CF_OPENED)) 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); return(opp); } void chan_send_req(int cno, unsigned char *opp, void (*cb)(int)) { CHANNEL *c; RQCB *r; c = channel_by_num(&cls,cno); if (c == 0) abort(); if (! (c->flags & CF_OPENED)) abort(); if (c->flags & CF_SENT_CLOSE) abort(); cls.layer->b->oplen = opp - &cls.layer->b->opkt[0]; r = malloc(sizeof(RQCB)); r->link = 0; r->cb = cb; *c->rqcbt = r; c->rqcbt = &r->link; 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_OPENED)) 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_OPENED)) 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_OPENED)) 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_OPENED)) abort(); if (c->flags & CF_SENT_CLOSE) abort(); send_close(cls.layer,c); if (c->flags & CF_RCVD_CLOSE) { cls.cv[c->lclnum] = 0; while (c->rqcb) { RQCB *r; r = c->rqcb; c->rqcb = r->link; (*r->cb)(CREQ_CLOSE); free(r); } free(c); return(1); } else { c->flags |= CF_SENT_CLOSE; return(0); } }