#include #include "machdep.h" #include "mbuf.h" #include "timer.h" #include "internet.h" #include "icmp.h" #include "netuser.h" #include "tcp.h" #include "telnet.h" #include "session.h" extern char nospace[]; int refuse_echo = 0; int unix_line_mode = 0; /* if true turn to when in line mode */ #ifdef DEBUG char *t_options[] = { "Transmit Binary", "Echo", "", "Suppress Go Ahead", "", "Status", "Timing Mark" }; #endif /* Execute user telnet command */ int dotelnet(argc,argv) int argc; char *argv[]; { void t_state(),rcv_char(); char *inet_ntoa(),*calloc(); int32 aton(); int send_tel(); int unix_send_tel(); struct session *s,*newsession(); struct telnet *tn; struct tcb *tcb; struct socket lsocket,fsocket; lsocket.address = ip_addr; lsocket.port = lport++; fsocket.address = aton(argv[1]); if(argc < 3) fsocket.port = TELNET_PORT; else fsocket.port = atoi(argv[2]); /* Allocate a session descriptor */ if((s = newsession()) == NULLSESSION){ printf("Too many sessions\r\n"); return 1; } s->type = TELNET; if ((refuse_echo == 0) && (unix_line_mode != 0)) { s->parse = unix_send_tel; } else { s->parse = send_tel; } current = s; /* Create and initialize a Telnet protocol descriptor */ if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){ printf(nospace); s->type = FREE; return 1; } tn->session = s; /* Upward pointer */ tn->state = TS_DATA; s->cb.telnet = tn; /* Downward pointer */ tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,0, rcv_char,NULLVFP,t_state,0,(int *)tn); if(tcb == NULLTCB || tcb->state == CLOSED){ /* This is actually a bit dirty here. About the only time the * state will be closed here is if we tried to connect to * ourselves and got RST'ed. If this is true then the close * upcall will already have freed the TCB and telnet block, * so we're looking at the TCB after it's back on the heap. */ return 0; } tn->tcb = tcb; /* Downward pointer */ go(); return 0; } /* Process typed characters */ int unix_send_tel(buf,n) char *buf; int16 n; { int i; for (i=0; (icb.telnet == NULLTN || current->cb.telnet->tcb == NULLTCB) return; bp = qdata(buf,n); send_tcp(current->cb.telnet->tcb,bp); } /* Process incoming TELNET characters */ int tel_input(tn,bp) register struct telnet *tn; struct mbuf *bp; { char c; int ci; void doopt(),dontopt(),willopt(),wontopt(),answer(); #ifdef FAST /* DON'T USE -- Aztec memchr() routine is broken */ char *memchr(); /* Optimization for very common special case -- no command chars */ if(tn->state == TS_DATA){ while(bp != NULLBUF && memchr(bp->data,IAC,bp->cnt) == NULLCHAR){ fflush(stdout); write(1,bp->data,bp->cnt); bp = free_mbuf(bp); } } #endif while(pullup(&bp,&c,1) == 1){ ci = c & 0xff; switch(tn->state){ case TS_DATA: if(ci == IAC){ tn->state = TS_IAC; } else { if(!tn->remote[TN_TRANSMIT_BINARY]) c &= 0x7f; putchar(c); } break; case TS_IAC: switch(ci){ case WILL: tn->state = TS_WILL; break; case WONT: tn->state = TS_WONT; break; case DO: tn->state = TS_DO; break; case DONT: tn->state = TS_DONT; break; case IAC: putchar(c); tn->state = TS_DATA; break; default: tn->state = TS_DATA; break; } break; case TS_WILL: willopt(tn,ci); tn->state = TS_DATA; break; case TS_WONT: wontopt(tn,ci); tn->state = TS_DATA; break; case TS_DO: doopt(tn,ci); tn->state = TS_DATA; break; case TS_DONT: dontopt(tn,ci); tn->state = TS_DATA; break; } } } /* Telnet receiver upcall routine */ void rcv_char(tcb,cnt) register struct tcb *tcb; int16 cnt; { struct mbuf *bp; struct telnet *tn; if((tn = (struct telnet *)tcb->user) == NULLTN){ /* Unknown connection; ignore it */ return; } /* Hold output if we're not the current session */ if(mode != CONV_MODE || current == NULLSESSION || current->cb.telnet != tn) return; if(recv_tcp(tcb,&bp,cnt) > 0) tel_input(tn,bp); fflush(stdout); } /* State change upcall routine */ void t_state(tcb,old,new) register struct tcb *tcb; char old,new; { struct telnet *tn; char notify = 0; extern char *tcpstates[]; extern char *reasons[]; extern char *unreach[]; extern char *exceed[]; /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ tn = (struct telnet *)tcb->user; if(current != NULLSESSION && current->type == TELNET && current->cb.telnet == tn) notify = 1; switch(new){ case CLOSE_WAIT: if(notify) printf("%s\r\n",tcpstates[new]); close_tcp(tcb); break; case CLOSED: /* court adjourned */ if(notify){ printf("%s (%s",tcpstates[new],reasons[tcb->reason]); if(tcb->reason == NETWORK){ switch(tcb->type){ case DEST_UNREACH: printf(": %s unreachable",unreach[tcb->code]); break; case TIME_EXCEED: printf(": %s time exceeded",exceed[tcb->code]); break; } } printf(")\r\n"); cmdmode(); } del_tcp(tcb); if(tn != NULLTN) free_telnet(tn); break; default: if(notify) printf("%s\r\n",tcpstates[new]); break; } fflush(stdout); } /* Delete telnet structure */ static free_telnet(tn) struct telnet *tn; { if(tn->session != NULLSESSION) freesession(tn->session); if(tn != NULLTN) free((char *)tn); } /* The guts of the actual Telnet protocol: negotiating options */ static void willopt(tn,opt) struct telnet *tn; int opt; { int ack; void answer(); #ifdef DEBUG printf("recv: will "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif switch(opt){ case TN_TRANSMIT_BINARY: case TN_ECHO: case TN_SUPPRESS_GA: if(tn->remote[opt] == 1) return; /* Already set, ignore to prevent loop */ if(opt == TN_ECHO){ if(refuse_echo){ /* User doesn't want to accept */ ack = DONT; break; } else raw(); /* Put tty into raw mode */ } tn->remote[opt] = 1; ack = DO; break; default: ack = DONT; /* We don't know what he's offering; refuse */ } answer(tn,ack,opt); } static void wontopt(tn,opt) struct telnet *tn; int opt; { void answer(); #ifdef DEBUG printf("recv: wont "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif if(opt <= NOPTIONS){ if(tn->remote[opt] == 0) return; /* Already clear, ignore to prevent loop */ tn->remote[opt] = 0; if(opt == TN_ECHO) cooked(); /* Put tty into cooked mode */ } answer(tn,DONT,opt); /* Must always accept */ } static void doopt(tn,opt) struct telnet *tn; int opt; { void answer(); int ack; #ifdef DEBUG printf("recv: do "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif switch(opt){ #ifdef FUTURE /* Use when local options are implemented */ if(tn->local[opt] == 1) return; /* Already set, ignore to prevent loop */ tn->local[opt] = 1; ack = WILL; break; #endif default: ack = WONT; /* Don't know what it is */ } answer(tn,ack,opt); } static void dontopt(tn,opt) struct telnet *tn; int opt; { void answer(); #ifdef DEBUG printf("recv: dont "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif if(opt <= NOPTIONS){ if(tn->local[opt] == 0){ /* Already clear, ignore to prevent loop */ return; } tn->local[opt] = 0; } answer(tn,WONT,opt); } static void answer(tn,r1,r2) struct telnet *tn; int r1,r2; { struct mbuf *bp,*qdata(); char s[3]; #ifdef DEBUG switch(r1){ case WILL: printf("sent: will "); break; case WONT: printf("sent: wont "); break; case DO: printf("sent: do "); break; case DONT: printf("sent: dont "); break; } if(r2 <= 6) printf("%s\r\n",t_options[r2]); else printf("%u\r\n",r2); #endif s[0] = IAC; s[1] = r1; s[2] = r2; bp = qdata(s,(int16)3); send_tcp(tn->tcb,bp); }