msa@clinet.FI (Markku Savela) (01/23/88)
This is part 2 of my CMU PC-IP Telnet modifications containing: \PC-IP\SRCCMD\BTN - Modified Basic TN MAKEFILE - Build BTN.EXE (3COM variant only) TELNET.H - "tnsemulate" added TELNET.C - (modified from original TELNET.C) BTN.C - (modified from original TN.C) -- -- Markku Savela, UUCP: msa@clinet.fi -- Nokia Information Systems (or Nokia Data Systems?) -- P.O.BOX 780 SF-00101 HELSINKI, FINLAND -- -*cut here*------------ \pc-ip\srccmd\btn\makefile ----------------- telnet.obj: telnet.c ..\..\include\tftp.h ..\..\include\em.h telnet.h \ ..\..\include\custom.h ..\..\include\ip.h cl /c /DMSC /Gs /I..\..\include /Zd /Zl telnet.c btn.obj: btn.c ..\..\include\custom.h ..\..\include\ip.h telnet.h cl /c /DMSC /Gs /I..\..\include /Zd /Gs btn.c btn.exe: telnet.obj btn.obj link btn telnet,btn,btn/map/noi,tftp4n tcp4n domain4n udp4n ip4n net4n 3com4n task4n pc4n int144n mapsym btn exemod btn.exe /stack 0a00 -*cut here*------------ \pc-ip\srccmd\btn\telnet.h ----------------- /* Copyright 1986 by Carnegie Mellon */ /* Copyright 1986 by Carnegie Mellon */ /* See permission and disclaimer notice in file "cmu-note.h" */ #include <cmu-note.h> /* Copyright 1984 by the Massachusetts Institute of Technology */ /* See permission and disclaimer notice in file "notice.h" */ #include <notice.h> /* 11-5-85 Added terminal type information, and option negotiation code from Jacob Rekhter, IBM Corp. <Drew D. Perkins> */ /* Definitions for telnet */ struct ucb { int u_state; /* ESTAB or CLOSING */ int u_tcpfull; /* is tcp's output buffer full? */ int u_tftp; /* tftp acceptance */ int u_rstate; /* Read terminal state */ /* NORMALMODE */ /* BLOCK - don't read terminal */ int u_rspecial; /* Read terminal character handling */ /* NORMALMODE */ /* SPECIAL - processing char after prompt */ /* CONFIRM - quit confirmation */ /* TCPFULL - tcp output buffer full */ int u_wstate; /* Write terminal state */ /* NORMALMODE */ /* URGENTM - urgent mode ignore nonspecial chars */ int u_wspecial; /* Write terminal character handling */ /* NORMALMODE */ /* '\r' (13), IAC, WILL, WONT, DO, DONT - processing special chars */ int u_prompt; /* Prompt char */ int u_sendm; /* Send mode */ /* EVERYC - send to net on every char */ /* NEWLINE - send to net on newline */ int u_echom; /* Echo mode */ /* LOCAL - local echo */ /* REMOTE - remote echo */ int u_echongo; /* Echo negotiation request outstanding */ /* NORMALMODE */ /* LECHOREQ - IAC DONT ECHO was sent */ /* RECHOREQ - IAC DO ECHO was sent */ int u_mode; /* status line on or off flag */ int u_ask; /* ask about tftp transfers */ int u_terminal; /* DDP - ASCII or 3278 */ }; #define NORMALMODE 0 #define SPECIAL 1 /* #define TEST 2 */ #define CONFIRM 3 #define HOLD 4 #define TFUNKNOWN 1 #define TFWAITING 2 #define TFYES 3 #define TFNO 4 #define ASCII_TERM 1 /* DDP - ASCII terminal emulator */ #define E3278_TERM 2 /* DDP - 3278 emulator */ #define BLOCK 1 #define NOBLOCK 2 #define URGENTM 1 #define EVERYC 1 #define NEWLINE 2 #define LOCAL 1 #define REMOTE 2 #define LECHOREQ 1 #define RECHOREQ 2 #define IAC 255 #define WILL 251 #define WONT 252 #define DO 253 #define DONT 254 #define DM 242 #define INTP 244 #define AO 245 #define AYT 246 #define GA 249 #define OPTECHO 1 #define OPTSPGA 3 #define TERMTYPE 24 /* DDP - TERMINALtype */ #define TRANS_BIN 0 /* DDP - Transmit Binary */ #define SBNVT 250 /* DDP - SBnvt*/ #define SENVT 240 /* DDP - SEnvt */ #define IS 0 /* DDP - ISchar */ #define SEND 1 /* DDP - SEnvt */ #define EOR 239 /* DDP - End-of-Record */ #define DFESC '\036' /* default escape char */ #define ESTAB 1 #define CLOSING 2 #define CLOSED 3 #define TELNETSOCK 23 /* Telnet well known socket no. */ extern struct ucb ucb; extern struct task *TNsend; /* Telnet send task. */ extern int speed; extern char tnshost[]; /* foreign host's string name */ extern char tnsemulate[]; /* terminal emulation command */ -*cut here*------------ \pc-ip\srccmd\btn\telnet.c ----------------- /* Copyright 1986 by Carnegie Mellon */ /* See permission and disclaimer notice in file "cmu-note.h" */ #include <cmu-note.h> /* Copyright 1984 by the Massachusetts Institute of Technology */ /* See permission and disclaimer notice in file "notice.h" */ #include <notice.h> /* Modified to send octal internet address on F10/control-I, to put a blank after octal and decimal internet address, to leave the line-25 message containing "My internet address" displayed indefinitely, to leave the message "file transfer in progress" on line 25 for the duration of the transfer, to accept an upcall from the tftp server when the transfer is complete, to display a message on line 25 indicating success or failure of that transfer, and to leave the line-25 message "closing connection" displayed indefinitely, 12/23/83. F10/A toggles TCP tracing, 12/28/83. Line 25 message for tftp now gives name of file and host, 1/2/84. <J. H. Saltzer> 2/3/84 - changed to allow the status line (25th line) display to be turned off. <John Romkey> 2/8/84 - commented out call to scr_close() and _curse() because the emulator's stdne() already does it. <John Romkey> 2/24/84 - changed code to use new printf internet address fields instead of having duplicated code all over the place. <John Romkey> 8/12/84 - added a space in the new printf() calls after the IP address. <John Romkey> 8/30/84 - added F10A to turn off TFTP server asking. <John Romkey> 6/2/85 - added F10/control menu and commands to toggle all debugging switches. <J. H. Saltzer> 10/14/85 - changed putchar("$") to putchar('$'). Made conditional changes for use with Microsoft C V3.00. <Drew D. Perkins> 11/14/85 - merged in changes for IBM 3278 emulation from Jacob Rekhter IBM/ACIS. <Drew D. Perkins> 1/9/86 - Send IAC DO ECHO upon opening of the connection. <Drew D. Perkins> 3/21/86 - Merge in color support for IBM 3279 emulation from Jacob Rekhter IBM/ACIS. <Drew D. Perkins> 3/32/86 - Break up gt_usr routine so MSC can optimize it. It got too big. <Drew D. Perkins> -------------------------------------------------------------------------- 6/21/87 - Strip down to naked TELNET and prepare for external terminal emulation (via INT14 catcher). <Markku Savela> */ #include <stdio.h> #include <types.h> #include <task.h> #include <q.h> #include <netq.h> #include <net.h> #include <custom.h> #include <netbuf.h> #include <icmp.h> #include <ip.h> #include "telnet.h" #include <em.h> #include <tftp.h> #ifdef MSC /* DDP */ #include <process.h> /* DDP */ #endif /* DDP */ extern long cticks; /* number of clock ticks since program start */ extern in_name tnhost; extern int TIMERDEBUG; extern int NBUF; int chartest; int scrntest; int ct1time; int ct2time; int ctcount; int st1time; int st2time; char buf1[80]; char numchar[10]; static char *term_types[] = { "ZENITH-H19", NULL }; /* * INT14 Interface area */ #define int14_high 2000 int int14_count = 0; /* Number of characters in buffer */ int int14_tcpfull = 0; /* Copied here for INT14TN access */ int int14_channel = 1; /* Channel to intercept */ static char int14_buffer[int14_high]; /* Received data from telnet */ static char *int14_sp = &int14_buffer[1]; /* Next position to store */ static char *int14_fp = &int14_buffer[0]; /* Last position fetched */ tel_init() { register struct ucb *pucb; pucb = &ucb; pucb->u_state = ESTAB; pucb->u_tftp = TFUNKNOWN; pucb->u_tcpfull = 0; pucb->u_rstate = NORMALMODE; pucb->u_rspecial = NORMALMODE; pucb->u_wstate = NORMALMODE; pucb->u_wspecial = NORMALMODE; pucb->u_sendm = custom.c_1custom & NLSET ? NEWLINE : EVERYC; pucb->u_echom = REMOTE; pucb->u_echongo = NORMALMODE; pucb->u_mode = TRUE; /*ms pucb->u_ask = custom.c_1custom & TN_TFTP_ASK ? FALSE : TRUE; */ pucb->u_ask = FALSE; chartest = 0; scrntest = 0; ct1time = 0; ct2time = 0; st1time = 0; st2time = 0; } tel_exit() { } /* Return true if telnet must run; false otherwise. */ mst_run() { return 1; } bfr() { ucb.u_tcpfull = 0; int14_tcpfull = 0; } /* gt_usr Read nch chars from user's terminal * When prompt char is encountered, go into * SPECIAL read mode and handle char following * prompt specially. * Tc_put puts telnet chars into an output * to net packet. */ #define int_stack_size 2000 gt_usr() { register struct ucb *pucb; int c; int i; char *stk; extern char *sys_errlist[]; extern int errno; int retcode; if (!(stk = (char *)stk_alloc(int_stack_size))) { printf("\nCould not allocate internal stack\n"); return(0); } _stk_fill(int_stack_size,stk); int14_enter(&stk[int_stack_size]); pucb = &ucb; tk_yield(); if (tnsemulate[0]) system(tnsemulate); else { printf("\nEntering inferior command interpreter. Do not execute any network programs.\n"); printf("Use the EXIT command to return to telnet\n"); #ifdef MSC /* DDP */ retcode = spawnlp(P_WAIT, getenv("COMSPEC"), getenv("COMSPEC"), NULL); /* DDP */ #else /* DDP */ retcode = system(0); #endif /* DDP */ }; if(retcode < 0) printf("Can't exec command interpreter: %s\n", sys_errlist[errno]); else puts("Exiting Telnet\n"); int14_exit(); } /* * This is called from INT14h to give a chance to the TCP/IP drivers... */ int14_yield () { tk_yield(); } /* * This is called from INT14h handler to retrieve the next character * from the int14_buffer */ char int14_fetch() { int14_count--; if (int14_fp == &int14_buffer[int14_high]) int14_fp = &int14_buffer[0]; else int14_fp++; return *int14_fp; } /* * Store character into int14_buffer (ignored, if the buffer is * already full). */ int14_store(c) register char c; { if (int14_count <= int14_high) { *int14_sp = c & 0177; if (int14_sp == &int14_buffer[int14_high]) int14_sp = &int14_buffer[0]; else int14_sp++; int14_count++; } } /* * This routine is called from INT14h-catcher, when the external * emulation wants to output a character to the communication line. */ int14_write(c) register char c; { register struct ucb *pucb; pucb = &ucb; if(pucb->u_tcpfull) { tcpfull(); return; } if(c == IAC) tc_put(IAC); else if(c == C_BREAK) { tc_put(IAC); tc_put(INTP); tc_put(IAC); tc_put(DM); tcpurgent(); return; } if(pucb->u_sendm == EVERYC) { if(tc_fput(c)) tcpfull(); } else { if(tc_put(c)) { tcpfull(); return; } if(c == '\n') tcp_ex(); } } /* * wr_local is to be used for writing locally generated messages * and texts to the local screen. Currently this just fills the * same buffer as telnet connection (wr_usr). A separate window * for these messages would be nice... */ wr_local(s) char *s; { register char *p; for (p = s; *p != 0; p++) { if (*p == '\n') int14_store('\r'); int14_store(*p); } } /* wr_usr manage chars coming from net and * going to user * Process received telnet special chars and * option negotiation. When wstate is URGENTM, * only process special chars. * All interrupts should be turned off * when in this routine - write to terminal * may block; an interrupt would cause * an error return from write with resulting * loss of chars to terminal */ wr_usr(buf, len, urg) char *buf; int len; int urg; { register struct ucb *pucb; register char *p; int c; /* DDP - Begin changes */ int i; static char **terminal = term_types; char *cp; pucb = &ucb; for(p = buf; p < buf+len; p++) { c = (*p & 0377); switch(pucb->u_wspecial) { case NORMALMODE: switch(c) { case IAC: pucb->u_wspecial = IAC; break; default: if(pucb->u_wstate != URGENTM) int14_store(c); } break; case IAC: switch(c) { case IAC: if(pucb->u_wstate != URGENTM) putchar(c); pucb->u_wspecial = NORMALMODE; break; case AO: tc_put(IAC); tc_put(DM); tcpurgent(); pucb->u_wspecial = NORMALMODE; break; case WILL: case WONT: case DO: case DONT: pucb->u_wspecial = c; break; /* DDP - Begin changes */ case SBNVT: pucb->u_wspecial = c; break; case SENVT: tc_put(IAC); tc_put(SBNVT); tc_put(TERMTYPE); tc_put(IS); if(NDEBUG & APTRACE) wr_local("Telnet: Sending terminal type %s\n", *terminal); for (cp = *terminal; *cp; cp++) tc_put(*cp); if(terminal[1] != NULL) terminal++; tc_put(IAC); tc_fput(SENVT); pucb->u_wspecial = NORMALMODE; break; /* Ignore IAC x */ default: pucb->u_wspecial = NORMALMODE; break; } break; case WILL: switch(c) { case OPTECHO: switch(pucb->u_echongo) { /* This host did not initiate echo negot - so respond */ case NORMALMODE: if(pucb->u_echom != REMOTE) echoremote(pucb); break; /* Rejecting my IAC DONT ECHO (illegit) */ case LECHOREQ: ttechoremote(pucb); break; } pucb->u_echongo = NORMALMODE; break; case OPTSPGA: /* suppress GA's */ tc_put(IAC); tc_put(DO); tc_fput(c); break; default: tc_put(IAC); tc_put(DONT); tc_fput(c); break; } pucb->u_wspecial = NORMALMODE; break; case WONT: switch(c) { case OPTECHO: switch(pucb->u_echongo) { /* This host did not initiate echo negot - so respond */ case NORMALMODE: if(pucb->u_echom != LOCAL) echolocal(pucb); break; /* Rejecting my IAC DO ECHO */ case RECHOREQ: ttecholocal(pucb); break; } pucb->u_echongo = NORMALMODE; break; } pucb->u_wspecial = NORMALMODE; break; case DO: /* DDP - Begin changes */ switch (c) { case TERMTYPE: tc_put(IAC); tc_put(WILL); tc_fput(c); break; default: tc_put(IAC); tc_put(WONT); tc_fput(c); pucb->u_wspecial = NORMALMODE; break; } /* DDP - End changes */ case DONT: pucb->u_wspecial = NORMALMODE; break; /* DDP - Begin changes */ case SBNVT: if ( c != TERMTYPE) printf(" SBNVT c = %x\n", c & 0xff); pucb->u_wspecial = c; break; case TERMTYPE: if (c != SEND) printf(" TERMTYPE c = %x\n", c& 0xff); pucb->u_wspecial = c; break; case SEND: if (c != IAC) printf(" SEND c = %x\n", c & 0xff); pucb->u_wspecial = c; break; /* DDP - End changes */ } } } echolocal(pucb) register struct ucb *pucb; { tc_put(IAC); tc_put(DONT); tc_fput(OPTECHO); pucb->u_echom = LOCAL; /* foptions(STECHO); */ } ttecholocal(pucb) register struct ucb *pucb; { pucb->u_echom = LOCAL; /* foptions(STECHO); */ } echoremote(pucb) register struct ucb *pucb; { tc_put(IAC); tc_put(DO); tc_fput(OPTECHO); pucb->u_echom = REMOTE; /* foptions(STNECHO); */ } ttechoremote(pucb) register struct ucb *pucb; { pucb->u_echom = REMOTE; /* foptions(STNECHO); */ } opn_usr() { echoremote(); /* DDP - Attempt to change to REMOTE echo */ wr_local("Open\n"); } cls_usr() { wr_local("Closed\n"); /* tel_exit(); */ exit(); } du_usr() { wr_local("Destination Unreachable\n"); /* tel_exit(); */ exit(); } tmo_usr() { wr_local("Host not responding\n"); /* tel_exit(); let exit_hook do the work */ exit(); } pr_dot() { wr_local("."); } tcpfull() { ucb.u_tcpfull = 1; int14_tcpfull = 1; /* fullbell(); */ } tntftp(host, file, dir) in_name host; char *file; unsigned dir; { char buffer[80]; char c; ring(); if(host == tnhost){ sprintf(buffer,"Host %s wants to %s file %s; OK? [F10/y or F10/n]", tnshost, dir == PUT ? "read" : "write",file); sprintf(buf1,"Host %s is %s file %s\n", tnshost, dir == PUT ? "reading" : "writing", file); } else { sprintf(buffer, "Host %a wants to %s file %s; OK? [F10/y or F10/n]", host, dir == PUT ? "read" : "write",file); sprintf(buf1, "Host %a is %s file %s\n", host, dir == PUT ? "reading" : "writing", file); } if(ucb.u_ask) wr_local(buffer); else wr_local(buf1); /*if(FALSE)return true;*/ /*TEMPORARY DEBUGGING AID*/ if(ucb.u_ask) { ucb.u_tftp = TFWAITING; while(ucb.u_tftp == TFWAITING) tk_yield(); if(ucb.u_tftp == TFYES) return TRUE; else return FALSE; } else return TRUE; } /* end of tntftp() */ /* function called when file transfer is done. */ tntfdn(success) int success; { if (success) wr_local("File transfer successful.\n"); else wr_local("File transfer failed.\n"); ring(); *buf1 = '\0'; } -*cut here*------------ \pc-ip\srccmd\btn\btn.c -------------------- /* Copyright 1986 by Carnegie Mellon */ /* See permission and disclaimer notice in file "cmu-note.h" */ #include <cmu-note.h> /* Copyright 1984 by the Massachusetts Institute of Technology */ /* See permission and disclaimer notice in file "notice.h" */ #include <notice.h> #include <stdio.h> #include <string.h> #include <types.h> #include <task.h> #include <q.h> #include <netq.h> #include <net.h> #include <custom.h> #include <netbuf.h> #include <icmp.h> #include <ip.h> #include "telnet.h" /* Modified 12/23/83 to include upcalled "done" function in initialization of tftp server. <J. H. Saltzer> Modified 1/2/84 to get window and low window sizes from the custom structure. <John Romkey> 11/15/84 - fixed screen condition on error exit. <John Romkey> 3/21/85 - Decreased initial stack size in tcp_init. We no longer need as much stack becuase the large array in rst_screen was made static instead of auto, saving 2000 bytes. <Drew D. Perkins> 10/30/86 - Added destination unreachable upcall. <Drew D. Perkins> 6/21/87 - Made a new version of the original tn.c for "naked" telnet connection. Terminal emulation is done outside this utility. <Markku Savela> 10/20/87 - Program argument handling made more robust. <Markku Savela> */ #define WAITCLS 5 /* close wait time */ struct ucb ucb; struct task *TNsend; /* telnet data sending task */ int speed; in_name tnhost; extern int wr_usr(), mst_run(), opn_usr(), cls_usr(), tmo_usr(), pr_dot(); extern int du_usr(); /* DDP */ extern int bfr(); extern int tn_flash(); extern int tntftp(); extern int tntfdn(); char usage[] = "usage: telnet [-p port] host [-e emulator command list]\n"; char tnshost[40]; /* Host Name string */ char tnsemulate[80]; /* Terminal Emulation Program */ main(argc, argv) int argc; char *argv[]; { if(!main_init(argc, argv)) exit(1); tel_init(); gt_usr(); } int tel_exit(); main_init(argc, argv) int argc; char *argv[]; { in_name fhost; unsigned sock; unsigned fsock = TELNETSOCK; char **temp = argv; char *s; exit_hook(tel_exit); while (--argc > 0) { if ((*++temp)[0] == '-') for (s = temp[0]+1; *s != '\0'; s++) { switch (*s) { case 'p': if (--argc > 0) fsock = atoi(*++temp); else { printf("telnet: port number expected.\n"); printf(usage); return FALSE; }; break; case 'd': if (--argc > 0) NDEBUG = atoi(*++temp); else { printf("telnet: debug parameter expected.\n"); printf(usage); return FALSE; }; break; case 'e': s = tnsemulate; *s = '\0'; while (--argc > 0) { *s++ = ' '; strcpy(s,*++temp); s = strchr(s,'\0'); }; s--; break; default: printf("telnet: unknow flag.\n"); printf(usage); return FALSE; }; } else { if (tnshost[0]) { printf("telnet: improper arguments.\n"); printf(usage); return FALSE; }; strcpy(tnshost,*temp); }; }; /* Initialize tasking and save our task's name. */ NBUF = 14; /* need lots of stack because of terminal emulator */ #ifndef MSC /* DDP - No longer true for MSC. */ tcp_init(5500, opn_usr, wr_usr, mst_run, cls_usr, tmo_usr, pr_dot, bfr); #else /* DDP */ tcp_init(2000, opn_usr, wr_usr, mst_run, cls_usr, tmo_usr, pr_dot, bfr); /* DDP */ #endif /* DDP */ tcp_duinit(du_usr); /* DDP */ fhost = resolve_name(tnshost); if(fhost == 0) { printf("Foreign host %s not known.\n", tnshost); return FALSE; } if(fhost == 1) { printf("Name servers not responding.\n"); return FALSE; } tnhost = fhost; /* start the tftp server and turn it on with full buffering*/ /*ms tfsinit(tntftp, tntfdn, FALSE); */ tfsinit(tntftp, tntfdn, TRUE); tfs_on(); printf("Trying..."); sock = tcp_sock(); /* DDP */ tcp_open(&fhost, fsock, sock, custom.c_telwin, custom.c_tellowwin); tk_yield(); /* let it start */ return(TRUE); }