page@swan.ulowell.edu (Bob Page) (03/18/89)
Submitted-by: rminnich@super.org (Ronald G. Minnich) Posting-number: Volume 89, Issue 85 Archive-name: comm/amigatcp.6 # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # main.c # tcpin.c # newtelnetp.c # This archive created: Fri Mar 17 18:00:34 1989 cat << \SHAR_EOF > main.c /* * Amiga version changes are * * Copyright (c)1987 * Louis A. Mamakos * * For non-commercial use only. */ /* Main network program - provides both client and server functions */ #define NSESSIONS 10 /* Maximum interactive client sessions */ #define HOSTNAMELEN 32 /* changed from 16 by Bdale 860812 */ #ifndef STARTUP #ifdef AMIGA #define STARTUP "s:net-sequence" #else #define STARTUP "/autoexec.net" /* File to read startup commands from */ #endif #endif #include <stdio.h> #include "machdep.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "icmp.h" #include "iface.h" #include "ip.h" #include "tcp.h" #include "ftp.h" #include "telnet.h" #include "session.h" #include "cmdparse.h" #ifdef AMIGA #include "amiga.h" #else #include "pc.h" #endif extern struct interface *ifaces; struct session sessions[NSESSIONS]; struct session *current; extern char major_rev[], minor_rev[]; int mode; FILE *logfp; #ifdef TRACE #include "trace.h" int32 trace; #endif char hostname[HOSTNAMELEN]; int32 aton(); int16 lport = 1001; #ifdef AMIGA char prompt[] = "\33[1;33mnet>\33[m "; /* make it standout and stuff */ #else char prompt[] = "net> "; #endif char nospace[] = "No space!!\r\n"; /* Generic malloc fail message */ static char notval[] = "Not a valid TCB\r\n"; #ifndef MSDOS /* PC uses F-10 key always */ static char escape = 0x1d; /* default escape character is ^] */ #endif /* Command lookup and branch table */ int go(),cmdmode(),dodigipeat(), doipaddr(),dotelnet(),doexit(),doclose(),dohostname(), doreset(),dotcpstat(),dotrace(),doescape(),dospeed(),dohelp(), dowindow(),doroute(),doecho(), dolog(),doipstat(),doicmpstat(),doetherstat(),memstat(),doarp(), dosession(),doftp(),domss(),dostart(),dostop(),doattach(), domycall(),dosmtptick(),doudpstat(),dottl(),dokiss(),dotcpkick(), doeol(); #ifdef HAPN int dohapnstat(); #endif HAPN #ifdef AMIGADEVDRV int DriverInit(), DriverShutdown(), CheckTcp(); #endif static struct cmds cmds[] = { /* The "go" command must be first */ "", go, 0, NULLCHAR, NULLCHAR, "arp", doarp, 0, NULLCHAR, NULLCHAR, "attach", doattach, 2, "attach <hardware> <hw specific options>", NULLCHAR, "close", doclose, 0, NULLCHAR, NULLCHAR, #ifdef AX25 "digipeat", dodigipeat, 0, NULLCHAR, NULLCHAR, #endif #ifdef AMIGADEVDRV "checktcp", CheckTcp, 0, NULLCHAR, NULLCHAR, "driver", DriverInit, 0, NULLCHAR, NULLCHAR, "driveroff", DriverShutdown, 0, NULLCHAR, NULLCHAR, #endif "echo", doecho, 0, NULLCHAR, NULLCHAR, "eol", doeol, 0, NULLCHAR, "eol options: unix, standard", #ifndef MSDOS "escape", doescape, 0, NULLCHAR, NULLCHAR, #endif #ifdef PC_EC "etherstat", doetherstat, 0, NULLCHAR, NULLCHAR, #endif PC_EC "exit", doexit, 0, NULLCHAR, NULLCHAR, "ftp", doftp, 2, "ftp <address>", NULLCHAR, #ifdef HAPN "hapnstat", dohapnstat, 0, NULLCHAR, NULLCHAR, #endif HAPN "help", dohelp, 0, NULLCHAR, NULLCHAR, "hostname", dohostname, 0, NULLCHAR, NULLCHAR, "log", dolog, 0, NULLCHAR, NULLCHAR, "icmpstat", doicmpstat, 0, NULLCHAR, NULLCHAR, "ipaddr", doipaddr, 0, NULLCHAR, NULLCHAR, "ipstat", doipstat, 0, NULLCHAR, NULLCHAR, #ifdef AX25 "kiss", dokiss, 0, NULLCHAR, NULLCHAR, #endif "smtp", dosmtptick, 0, NULLCHAR, NULLCHAR, #ifndef AMIGA "memstat", memstat, 0, NULLCHAR, NULLCHAR, #endif "mss", domss, 0, NULLCHAR, NULLCHAR, #ifdef AX25 "mycall", domycall, 0, NULLCHAR, NULLCHAR, #endif "reset", doreset, 0, NULLCHAR, NULLCHAR, "route", doroute, 0, NULLCHAR, NULLCHAR, "session", dosession, 0, NULLCHAR, NULLCHAR, "speed", dospeed, 0, NULLCHAR, NULLCHAR, #ifdef SERVERS "start", dostart, 2, "start <servername>",NULLCHAR, "stop", dostop, 2, "stop <servername>", NULLCHAR, #endif "tcpstat", dotcpstat, 0, NULLCHAR, NULLCHAR, "tcpkick", dotcpkick, 0, NULLCHAR, NULLCHAR, "telnet", dotelnet, 2, "telnet <address>", NULLCHAR, "trace", dotrace, 0, NULLCHAR, NULLCHAR, "ttl", dottl, 0, NULLCHAR, NULLCHAR, "udpstat", doudpstat, 0, NULLCHAR, NULLCHAR, "window", dowindow, 0, NULLCHAR, NULLCHAR, "?", dohelp, 0, NULLCHAR, NULLCHAR, NULLCHAR, NULLFP, 0, "Unknown command; type \"?\" for list", NULLCHAR, }; /* "route" subcommands */ int doadd(),dodrop(); static struct cmds rtcmds[] = { "add", doadd, 3, "route add <dest addr>[/<bits>] <if name> [gateway] [metric]", "Add failed", "drop", dodrop, 2, "route drop <dest addr>[/<bits>]", "Not in table", NULLCHAR, NULLFP, 0, "route subcommands: add, drop", NULLCHAR, }; #ifdef SERVERS /* "start" and "stop" subcommands */ int ftp_start(),smtp_start(),discard_start(),echo_start(),telnet_start(); static struct cmds startcmds[] = { "discard", discard_start, 0, NULLCHAR, NULLCHAR, "echo", echo_start, 0, NULLCHAR, NULLCHAR, "ftp", ftp_start, 0, NULLCHAR, NULLCHAR, "smtp", smtp_start, 0, NULLCHAR, NULLCHAR, "telnet", telnet_start, 0, NULLCHAR, NULLCHAR, NULLCHAR, NULLFP, 0, "start options: discard, echo, ftp, smtp, telnet", NULLCHAR, }; int ftp_stop(),smtp_stop(),echo_stop(),discard_stop(),telnet_stop(); static struct cmds stopcmds[] = { "discard", discard_stop, 0, NULLCHAR, NULLCHAR, "echo", echo_stop, 0, NULLCHAR, NULLCHAR, "ftp", ftp_stop, 0, NULLCHAR, NULLCHAR, "smtp", smtp_stop, 0, NULLCHAR, NULLCHAR, "telnet", telnet_stop, 0, NULLCHAR, NULLCHAR, NULLCHAR, NULLFP, 0, "stop options: discard, echo, ftp, smtp, telnet", NULLCHAR, }; #endif main(argc,argv) int argc; char *argv[]; { static char inbuf[BUFSIZ]; /* keep it off the stack */ int c; char *ttybuf,*fgets(); int16 cnt; int ttydriv(); int cmdparse(); void check_time(); FILE *fp; struct interface *ifp; #ifdef AMIGA char *startup = STARTUP; #endif ioinit(); printf("KA9Q Internet Protocol Package, v%s.%s\n",major_rev,minor_rev); #ifdef AMIGA if (argc > 1) startup = argv[1]; if((fp = fopen(startup,"r")) != NULLFILE){ while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){ printf(prompt); printf(inbuf); cmdparse(cmds, inbuf); } fclose(fp); } else printf("Can't read inital commands from %s\n", startup); #else if((fp = fopen(STARTUP,"r")) != NULLFILE){ while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){ cmdparse(cmds,inbuf); } fclose(fp); } #endif cmdmode(); smtpclinit(); /* arm SMTP background client timer */ /* Main commutator loop */ for(;;){ /* Process any keyboard input */ while((c = kbread()) != -1){ #ifdef MSDOS /* c == -2 means the command escape key (F10) */ if(c == -2){ if(mode != CMD_MODE){ printf("\r\n"); cmdmode(); } continue; } #else if(c == escape && escape != 0 && mode != CMD_MODE) { printf("\r\n"); cmdmode(); continue; } #endif if((cnt = ttydriv(c,&ttybuf)) == 0) continue; switch(mode){ case CMD_MODE: (void)cmdparse(cmds,ttybuf); fflush(stdout); break; case CONV_MODE: #ifndef MSDOS if(ttybuf[0] == escape && escape != 0){ printf("\r\n"); cmdmode(); } else #endif MSDOS if(current->parse != NULLFP) (*current->parse)(ttybuf,cnt); break; } if(mode == CMD_MODE){ printf(prompt); fflush(stdout); } } /* Service the interfaces */ for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next){ if(ifp->recv != NULLFP) (*ifp->recv)(ifp); } /* Service the clock if it has ticked */ check_time(); #ifdef AMIGADEVDRV /* check device driver open request */ check_driver(); CheckTcp(); #else #ifdef MSDOS /* Tell DoubleDos to let the other task run for awhile. * If DoubleDos isn't active, this is a no-op */ giveup(); #else /* Wait until interrupt, then do it all over again */ eihalt(); #endif #endif AMIGADEVDRV } } /* Enter command mode */ int cmdmode() { if(mode != CMD_MODE){ mode = CMD_MODE; cooked(); printf(prompt); fflush(stdout); } return 0; } /* Select and display sessions */ static dosession(argc,argv) int argc; char *argv[]; { unsigned i; struct session *s; extern char *tcpstates[]; char *psocket(); if(argc < 2){ printf(" # Type Remote socket TCB State\r\n"); for(s=sessions;s < & sessions[NSESSIONS];s++){ switch(s->type){ case TELNET: #ifndef AMIGA printf("%c%-3d%Telnet %-23s%4x %-s\r\n", (current == s)? '*':' ', s - sessions, psocket(&s->cb.telnet->tcb->conn.remote), (int)s->cb.telnet->tcb, tcpstates[s->cb.telnet->tcb->state]); #else printf("%c%-3d%Telnet %-23s%6lx %-s\r\n", (current == s)? '*':' ', s - sessions, psocket(&s->cb.telnet->tcb->conn.remote), (unsigned long)s->cb.telnet->tcb, tcpstates[s->cb.telnet->tcb->state]); #endif break; case FTP: #ifndef AMIGA printf("%c%-3d%FTP %-23s%4x %-s\r\n", (current == s)? '*':' ', s - sessions, psocket(&s->cb.ftp->control->conn.remote), (int)s->cb.ftp->control, tcpstates[s->cb.ftp->control->state]); #else printf("%c%-3d%FTP %-23s%6lx %-s\r\n", (current == s)? '*':' ', s - sessions, psocket(&s->cb.ftp->control->conn.remote), (unsigned long)s->cb.ftp->control, tcpstates[s->cb.ftp->control->state]); #endif break; } } return 0; } i = atoi(argv[1]); if(i > NSESSIONS){ printf("Invalid session: %d\r\n",i); return 1; } if(sessions[i].type == FREE){ printf("Inactive session: %d\r\n",i); return 1; } current = &sessions[i]; go(); return 0; } /* Enter conversational mode with current session */ int go() { void rcv_char(),r_ctl(); if(current == NULLSESSION || current->type == FREE) return 0; mode = CONV_MODE; switch(current->type){ case TELNET: if(current->cb.telnet->remote[TN_ECHO]) raw(); /* Re-establish raw mode if it was set */ rcv_char(current->cb.telnet->tcb,0); /* Get any pending input */ break; case FTP: r_ctl(current->cb.ftp->control,0); break; } return 0; } #ifdef AX25 extern int digipeat; dodigipeat(argc,argv) int argc; char *argv[]; { if(argc == 1) { printf("digipeat %s\r\n",digipeat ? "on" : "off"); } else { if(strcmp(argv[1],"on") == 0) digipeat = 1; else digipeat = 0; } } #endif static doipaddr(argc,argv) int argc; char *argv[]; { char *inet_ntoa(); if(argc < 2) printf("%s\r\n",inet_ntoa(ip_addr)); else ip_addr = aton(argv[1]); return 0; } static doexit(argc,argv) int argc; char *argv[]; { iostop(); DriverShutdown(); exit(0); } static doclose(argc,argv) int argc; char *argv[]; { if(current == NULLSESSION){ printf("No current session\r\n"); return 0; } switch(current->type){ case TELNET: close_tcp(current->cb.telnet->tcb); break; case FTP: close_tcp(current->cb.ftp->control); break; } return 0; } static doreset(argc,argv) int argc; char *argv[]; { long htol(); struct tcb *tcb; if(argc < 2){ if(current == NULLSESSION){ printf("No current session\r\n"); return 1; } switch(current->type){ case TELNET: tcb = current->cb.telnet->tcb; break; case FTP: tcb = current->cb.ftp->control; break; } } else tcb = (struct tcb *)htol(argv[1]); if(!tcpval(tcb)){ printf(notval); return 1; } close_self(tcb,RESET); return 0; } static int dotcpstat(argc,argv) int argc; char *argv[]; { long htol(); register struct tcb *tcb; if(argc < 2){ tcpstat(); } else { tcb = (struct tcb *)htol(argv[1]); if(tcpval(tcb)) state_tcp(tcb); else printf(notval); } return 0; } static int dotcpkick(argc,argv) int argc; char *argv[]; { long htol(); register struct tcb *tcb; void tcp_timeout(); if(argc < 2){ if(current == NULLSESSION){ printf("No current session\r\n"); return 1; } switch(current->type){ case TELNET: tcb = current->cb.telnet->tcb; break; case FTP: tcb = current->cb.ftp->control; break; } } else tcb = (struct tcb *)htol(argv[1]); if(!tcpval(tcb)){ printf(notval); return 1; } if(argc > 2){ /* Optional value for SRTT */ tcb->srtt = atoi(argv[2]); } /* Don't actually do anything unless something is pending */ if(tcb->sndcnt != 0){ tcb->retry = 0; tcp_timeout((int *)tcb); } return 0; } static int dotrace(argc,argv) int argc; char *argv[]; { long htol(); #ifdef TRACE if(argc < 2) printf("trace level 0x%08lx\r\n",trace); else trace = htol(argv[1]); return 0; #else printf("Trace not enabled - recompile with TRACE defined\r\n"); #endif } static dohostname(argc,argv) int argc; char *argv[]; { char *strncpy(); if(argc < 2) printf("%s\r\n",hostname); else strncpy(hostname,argv[1],HOSTNAMELEN); return 0; } static int dolog(argc,argv) int argc; char *argv[]; { char *strncpy(); #ifdef AMIGA static char logname[45]; /* long enough for CON:10/10/300/300/title */ #else static char logname[15]; #endif if(argc < 2){ if(logfp) printf("Logging to %s\r\n",logname); else printf("Logging off\r\n"); return 0; } if(logfp){ fclose(logfp); logfp = NULLFILE; } if(strcmp(argv[1],"stop") != 0){ #ifdef AMIGA strncpy(logname,argv[1],45); #else strncpy(logname,argv[1],15); #endif logfp = fopen(logname,"a+"); } return 0; } #ifndef MSDOS static int doescape(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("0x%x\r\n",escape); else escape = *argv[1]; return 0; } #endif MSDOS static int dohelp(argc,argv) int argc; char *argv[]; { register struct cmds *cmdp; int i,j; printf("Main commands:\r\n"); for(i=0,cmdp = cmds;cmdp->name != NULL;cmdp++,i++){ printf("%s",cmdp->name); if((i % 4) == 3) printf("\r\n"); else { for(j=strlen(cmdp->name);j < 16; j++) putchar(' '); } } if((i % 4) != 0) printf("\r\n"); return 0; } static int domss(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("%d\r\n",tcp_mss); else tcp_mss = atoi(argv[1]); return 0; } static int dowindow(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("%d\r\n",tcp_window); else tcp_window = atoi(argv[1]); return 0; } static int dottl(argc,argv) char *argv[]; { if(argc < 2) printf("%u\r\n",ip_ttl & 0xff); else ip_ttl = atoi(argv[1]); return 0; } struct session * newsession() { register int i; for(i=0;i<NSESSIONS;i++) if(sessions[i].type == FREE) return &sessions[i]; return NULLSESSION; } freesession(s) struct session *s; { if(s != NULLSESSION) s->type = FREE; } /* Display and/or manipulate routing table */ int doroute(argc,argv) int argc; char *argv[]; { if(argc < 2){ dumproute(); return 0; } return subcmd(rtcmds,argc,argv); } /* Add an entry to the routing table * E.g., "add 1.2.3.4 ax0 5.6.7.8 3" */ int doadd(argc,argv) int argc; char *argv[]; { struct interface *ifp; int32 dest,gateway; unsigned bits; char *bitp,*index(); int metric; if(strcmp(argv[1],"default") == 0){ dest = 0; bits = 0; } else { dest = aton(argv[1]); /* If IP address is followed by an optional slash and * a length field, (e.g., 128.96/16) get it; * otherwise assume a full 32-bit address */ if((bitp = index(argv[1],'/')) != NULLCHAR){ bitp++; bits = atoi(bitp); } else bits = 32; } for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){ if(strcmp(argv[2],ifp->name) == 0) break; } if(ifp == NULL){ printf("Interface \"%s\" unknown\r\n",argv[2]); return 1; } if(argc > 3) gateway = aton(argv[3]); else gateway = 0; if(argc > 4) metric = atoi(argv[4]); else metric = 0; rt_add(dest,bits,gateway,metric,ifp); return 0; } /* Drop an entry from the routing table * E.g., "drop 128.96/16 */ int dodrop(argc,argv) int argc; char *argv[]; { char *bitp,*index(); unsigned bits; /* If IP address is followed by an optional slash and length field, * (e.g., 128.96/16) get it; otherwise assume a full 32-bit address */ if((bitp = index(argv[1],'/')) != NULLCHAR){ bitp++; bits = atoi(bitp); } else bits = 32; return rt_drop(aton(argv[1]),bits); } #ifdef SERVERS dostart(argc,argv) int argc; char *argv[]; { return subcmd(startcmds,argc,argv); } dostop(argc,argv) int argc; char *argv[]; { return subcmd(stopcmds,argc,argv); } #endif SERVERS doecho(argc,argv) int argc; char *argv[]; { extern int refuse_echo; if(argc < 2){ if(refuse_echo) printf("Refuse\r\n"); else printf("Accept\r\n"); } else { if(strcmp(argv[1],"refuse") == 0) refuse_echo = 1; else if(strcmp(argv[1],"accept") == 0) refuse_echo = 0; else return -1; } return 0; } /* set for unix end of line for remote echo mode telnet */ doeol(argc,argv) int argc; char *argv[]; { extern int unix_line_mode; if(argc < 2){ if(unix_line_mode) printf("Unix\r\n"); else printf("Standard\r\n"); } else { if(strcmp(argv[1],"unix") == 0) unix_line_mode = 1; else if(strcmp(argv[1],"standard") == 0) unix_line_mode = 0; else { return -1; } } return 0; } /* List of supported hardware devices */ #ifdef PC_EC int ec_attach(); #endif int asy_attach(); #ifdef PC100 int pc_attach(); #endif #ifdef HAPN int hapn_attach(); #endif #ifdef NETROM int netrom_attach(); #endif struct cmds attab[] = { #ifdef NETROM /* NET/ROM virtual interface using companion asy interface in AX.25 mode */ "netrom", netrom_attach, 4, "attach netrom <asy-intf> ax25 <label> [mtu [ upd-freq ]]", "Could not attach NET/ROM virtual interface", #endif #ifdef PC_EC /* 3-Com Ethernet interface */ "3c500", ec_attach, 7, "attach 3c500 <address> <vector> arpa <label> <buffers> <mtu>", "Could not attach 3c500", #endif /* Ordinary PC asynchronous adaptor */ "asy", asy_attach, 8, "attach asy <address> <vector> slip|ax25 <label> <buffers> <mtu> <speed>", "Could not attach asy", #ifdef PC100 /* PACCOMM PC-100 8530 HDLC adaptor */ "pc100", pc_attach, 8, "attach pc100 <address> <vector> ax25 <label> <buffers> <mtu> <speed>", "Could not attach pc100", #endif #ifdef HAPN /* Hamilton Area Packet Radio (HAPN) 8273 HDLC adaptor */ "hapn", hapn_attach, 8, "attach hapn <address> <vector> ax25 <label> <rx bufsize> <mtu> csma|full", "Could not attach hapn", #endif NULLCHAR, NULLFP, 0, "Unknown device", NULLCHAR, }; /* Attach an interface * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<speed>] */ doattach(argc,argv) int argc; char *argv[]; { return subcmd(attab,argc,argv); } int dospeed(argc,argv) int argc; char *argv[]; { int i; if(argc < 3){ for(i=0;i < nasy; i++){ printf("%d: %d bps\r\n",i,asy[i].speed); } return 0; } i = atoi(argv[1]); if(i >= nasy){ printf("Line %d out of range\r\n",i); return 0; } asy_speed(i,atoi(argv[2])); return i; } /* Log messages of the form * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP */ /*VARARGS2*/ log(tcb,fmt,arg1,arg2,arg3,arg4) struct tcb *tcb; char *fmt; int arg1,arg2,arg3,arg4; { char *cp,*cp1,*ctime(),*index(); long t; if(logfp == NULLFILE) return; time(&t); cp = ctime(&t); if((cp1 = index(cp,'\n')) != NULLCHAR) *cp1 = '\0'; fprintf(logfp,"%s %s - ",cp,psocket(&tcb->conn.remote)); fprintf(logfp,fmt,arg1,arg2,arg3,arg4); fprintf(logfp,"\n"); fflush(logfp); } /* Define null definitions for unused interfaces to avoid pulling in code * from the library */ #ifndef ETHER int ec_output() {} int pether() {} int gether() {} char *ether_bdcst = NULLCHAR; #endif ETHER #ifndef AX25 int setcall() {} int psax25() {} struct ax25_addr ax25_bdcst,mycall; #endif AX25 SHAR_EOF cat << \SHAR_EOF > tcpin.c /* Process incoming TCP segments. Page number references are to ARPA RFC-793, * the TCP specification. */ #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "tcp.h" #include "icmp.h" #include "ip.h" struct tcp_stat tcp_stat; /* This function is called from IP with the IP header in machine byte order, * along with a mbuf chain pointing to the TCP header. */ void tcp_input(bp,protocol,source,dest,tos,length,rxbroadcast) struct mbuf *bp; /* Data field, if any */ char protocol; /* Should always be TCP_PTCL */ int32 source; /* Remote IP address */ int32 dest; /* Our IP address */ char tos; /* Type of Service */ int16 length; /* Length of data field */ char rxbroadcast; /* Incoming broadcast - discard if true */ { void reset(),update(); void proc_syn(),send_syn(),add_reseq(),get_reseq(); register struct tcb *tcb; /* TCP Protocol control block */ struct tcp_header seg; /* Local copy of segment header */ int16 mss; /* Incoming MSS, if any */ struct connection conn; /* Local copy of addresses */ struct pseudo_header ph; /* Pseudo-header for checksumming */ int16 optlen; /* Length of TCP options */ if(bp == NULLBUF) return; if(rxbroadcast){ /* Any TCP packet arriving as a broadcast is * to be completely IGNORED!! */ tcp_stat.bdcsts++; free_p(bp); } ph.source = source; ph.dest = dest; ph.protocol = protocol; ph.zero = 0; ph.length = length; if(cksum(&ph,bp,length) != 0){ /* Checksum failed, ignore segment completely */ tcp_stat.checksum++; free_p(bp); return; } /* Form local copy of TCP header in host byte order */ if(pullup(&bp,(char *)&seg,sizeof(struct tcp_header)) != sizeof(struct tcp_header)){ /* TCP header is too small */ tcp_stat.runt++; free_p(bp); return; } length -= sizeof(struct tcp_header); seg.source = ntohs(seg.source); seg.dest = ntohs(seg.dest); seg.seq = ntohl(seg.seq); seg.ack = ntohl(seg.ack); seg.wnd = ntohs(seg.wnd); seg.up = ntohs(seg.up); /* Unused as of yet */ /* Examine options, if any (there can only be MSS). */ optlen = hinibble(seg.offset) * sizeof(long) - sizeof(struct tcp_header); if(optlen != 0){ struct mss *mssp; mssp = (struct mss *)bp->data; if(mssp->kind == MSS_KIND && mssp->length == MSS_LENGTH){ mss = ntohs(mssp->mss); } pullup(&bp,NULLCHAR,optlen); length -= optlen; } else { mss = 0; } /* Fill in connection structure and find TCB */ conn.local.address = dest; conn.local.port = seg.dest; conn.remote.address = source; conn.remote.port = seg.source; if((tcb = lookup_tcb(&conn)) == NULLTCB){ struct tcb *ntcb; char *malloc(); void link_tcb(); /* Check that this segment carries a SYN, and that * there's a LISTEN on this socket with * unspecified source address and port */ conn.remote.address = 0; conn.remote.port = 0; if(!(seg.flags & SYN) || (tcb = lookup_tcb(&conn)) == NULLTCB){ /* No unspecified LISTEN either, so reject */ free_p(bp); reset(source,dest,tos,length,&seg); return; } /* We've found an unspecified remote socket, so clone the TCB * and stuff the foreign socket into the clone. */ if((ntcb = (struct tcb *)malloc(sizeof (struct tcb))) == NULLTCB){ free_p(bp); /* This may fail, but we should at least try */ reset(source,dest,tos,length,&seg); return; } bcopy((char *)tcb,(char *)ntcb,sizeof(struct tcb)); tcb = ntcb; tcb->conn.remote.address = source; tcb->conn.remote.port = seg.source; tcb->timer.arg = (int *)tcb; link_tcb(tcb); } /* Do unsynchronized-state processing (p. 65-68) */ switch(tcb->state){ case CLOSED: free_p(bp); reset(source,dest,tos,length,&seg); return; case LISTEN: if(seg.flags & RST){ free_p(bp); return; } if(seg.flags & ACK){ free_p(bp); reset(source,dest,tos,length,&seg); return; } if(seg.flags & SYN){ /* (Security check is bypassed) */ /* page 66 */ tcp_stat.conin++; proc_syn(tcb,tos,&seg,mss); send_syn(tcb); setstate(tcb,SYN_RECEIVED); if(length != 0 || seg.flags & FIN) { break; /* Continue processing if there's more */ } tcp_output(tcb); } free_p(bp); /* Unlikely to get here directly */ return; case SYN_SENT: if(seg.flags & ACK){ if(!seq_within(seg.ack,tcb->iss+1,tcb->snd.nxt)){ free_p(bp); reset(source,dest,tos,length,&seg); return; } } if(seg.flags & RST){ /* p 67 */ if(seg.flags & ACK){ /* The ack must be acceptable since we just checked it. * This is how the remote side refuses connect requests. */ close_self(tcb,RESET); } free_p(bp); return; } /* (Security check skipped here) */ /* Check incoming precedence; it must match if there's an ACK */ if((seg.flags & ACK) && PREC(tos) != PREC(tcb->tos)){ free_p(bp); reset(source,dest,tos,length,&seg); return; } if(seg.flags & SYN){ proc_syn(tcb,tos,&seg,mss); if(seg.flags & ACK){ /* Our SYN has been acked, otherwise the ACK * wouldn't have been valid. */ update(tcb,&seg); setstate(tcb,ESTABLISHED); } else { setstate(tcb,SYN_RECEIVED); } if(length != 0 || (seg.flags & FIN)) { break; /* Continue processing if there's more */ } tcp_output(tcb); } else { free_p(bp); /* Ignore if neither SYN or RST is set */ } return; } /* We reach this point directly in any synchronized state. Note that * if we fell through from LISTEN or SYN_SENT processing because of a * data-bearing SYN, window trimming and sequence testing "cannot fail". */ /* Trim segment to fit receive window. */ if(trim(tcb,&seg,&bp,&length) == -1){ /* Segment is unacceptable */ if(!(seg.flags & RST)){ tcb->force = 1; tcp_output(tcb); } return; } /* If segment isn't the next one expected, and there's data * or flags associated with it, put it on the resequencing * queue and return. Don't send anything in reply. * * Processing the ACK in an out-of-sequence segment without * flags or data should be safe, however. */ if(seg.seq != tcb->rcv.nxt && (length != 0 || (seg.flags & (SYN|FIN)) )){ add_reseq(tcb,tos,&seg,bp,length); return; } /* This loop first processes the current segment, and then * repeats if it can process the resequencing queue. */ for(;;){ /* We reach this point with an acceptable segment; all data and flags * are in the window, and the starting sequence number equals rcv.nxt * (p. 70) */ if(seg.flags & RST){ switch(tcb->state){ case SYN_RECEIVED: setstate(tcb,LISTEN); free_p(bp); return; default: close_self(tcb,RESET); free_p(bp); return; } } /* (Security check skipped here) p. 71 */ /* Check for precedence mismatch or erroneous extra SYN */ if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){ free_p(bp); reset(source,dest,tos,length,&seg); return; } /* Check ack field p. 72 */ if(!(seg.flags & ACK)){ free_p(bp); /* All segments after synchronization must have ACK */ return; } /* Process ACK */ switch(tcb->state){ case SYN_RECEIVED: if(seq_within(seg.ack,tcb->snd.una+1,tcb->snd.nxt)){ update(tcb,&seg); setstate(tcb,ESTABLISHED); } else { free_p(bp); reset(source,dest,tos,length,&seg); return; } break; case ESTABLISHED: case CLOSE_WAIT: update(tcb,&seg); break; case FINWAIT1: /* p. 73 */ update(tcb,&seg); if(tcb->sndcnt == 0){ /* Our FIN is acknowledged */ setstate(tcb,FINWAIT2); } break; case FINWAIT2: update(tcb,&seg); break; case CLOSING: update(tcb,&seg); if(tcb->sndcnt == 0){ /* Our FIN is acknowledged */ setstate(tcb,TIME_WAIT); tcb->timer.start = MSL2; start_timer(&tcb->timer); } break; case LAST_ACK: update(tcb,&seg); if(tcb->sndcnt == 0){ /* Our FIN is acknowledged, close connection */ close_self(tcb,NORMAL); return; } case TIME_WAIT: tcb->force = 1; start_timer(&tcb->timer); } /* (URGent bit processing skipped here) */ /* Process the segment text, if any, beginning at rcv.nxt (p. 74) */ if(length != 0){ switch(tcb->state){ case SYN_RECEIVED: case ESTABLISHED: case FINWAIT1: case FINWAIT2: /* Place on receive queue */ append(&tcb->rcvq,bp); tcb->rcvcnt += length; tcb->rcv.nxt += length; tcb->rcv.wnd -= length; tcb->force = 1; break; default: /* Ignore segment text */ free_p(bp); break; } } /* If the user has set up a r_upcall function, AND * the receive window is more than half full OR * has the urg, push or fin flags set, notify him. * * All this is done before sending an acknowledgement, * to give the user a chance to piggyback some reply data. * It's also done before processing FIN so that the state * change upcall will occur after the user has had a chance * to read the last of the incoming data. */ if(tcb->r_upcall){ if(tcb->rcvcnt >= tcb->rcv.wnd || (seg.flags & (FIN|URG|PSH))){ (*tcb->r_upcall)(tcb,tcb->rcvcnt); } } /* process FIN bit (p 75) */ if(seg.flags & FIN){ tcb->force = 1; /* Always respond with an ACK */ switch(tcb->state){ case SYN_RECEIVED: case ESTABLISHED: tcb->rcv.nxt++; setstate(tcb,CLOSE_WAIT); break; case FINWAIT1: tcb->rcv.nxt++; if(tcb->sndcnt == 0){ /* Our FIN has been acked; bypass CLOSING state */ setstate(tcb,TIME_WAIT); tcb->timer.start = MSL2; start_timer(&tcb->timer); } else { setstate(tcb,CLOSING); } break; case FINWAIT2: tcb->rcv.nxt++; setstate(tcb,TIME_WAIT); tcb->timer.start = MSL2; start_timer(&tcb->timer); break; case CLOSE_WAIT: case CLOSING: case LAST_ACK: break; /* Ignore */ case TIME_WAIT: /* p 76 */ start_timer(&tcb->timer); break; } } /* Scan the resequencing queue, looking for a segment we can handle, * and freeing all those that are now obsolete. */ while(tcb->reseq != NULLRESEQ && seq_ge(tcb->rcv.nxt,tcb->reseq->seg.seq)){ get_reseq(tcb,&tos,&seg,&bp,&length); if(trim(tcb,&seg,&bp,&length) == 0) goto gotone; /* Segment is an old one; trim has freed it */ } break; gotone: ; } tcp_output(tcb); /* Send any necessary ack */ } /* Process an incoming ICMP response */ tcp_icmp(source,dest,type,code,data) int32 source; /* Original IP datagram source (i.e. us) */ int32 dest; /* Original IP datagram dest (i.e., them) */ char type,code; /* ICMP error codes */ char *data; /* First 8 bytes of TCP header */ { struct tcp_header *tcph; struct connection conn; register struct tcb *tcb; /* Extract the socket info from the returned TCP header fragment * Note that since this is a datagram we sent, the source fields * refer to the local side. */ tcph = (struct tcp_header *)data; conn.local.address = source; conn.local.port = ntohs(tcph->source); conn.remote.address = dest; conn.remote.port = ntohs(tcph->dest); if((tcb = lookup_tcb(&conn)) == NULLTCB) return; /* Unknown connection, ignore */ /* Verify that the sequence number in the returned segment corresponds * to something currently unacknowledged. If not, it can safely * be ignored. */ if(!seq_within(ntohl(tcph->seq),tcb->snd.una,tcb->snd.nxt)) return; /* The strategy here is that Destination Unreachable and Time Exceeded * messages that occur after a connection has been established are likely * to be transient events, and shouldn't kill our connection (at least * until after we've tried a few more times). On the other hand, if * they occur on our very first attempt to send a datagram on a new * connection, they're probably "for real". In any event, the info * is saved. */ switch(type){ case DEST_UNREACH: case TIME_EXCEED: tcb->type = type; tcb->code = code; if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED){ close_self(tcb,NETWORK); } break; case QUENCH: break; /* I really ought to implement this */ } } /* Send an acceptable reset (RST) response for this segment */ static void reset(source,dest,tos,length,seg) int32 source; /* Remote IP address */ int32 dest; /* Our IP address */ char tos; /* Type of Service */ int16 length; /* Length of data portion */ register struct tcp_header *seg; /* Offending TCP header */ { struct mbuf *hbp; struct pseudo_header ph; register struct tcp_header *tcph; if(seg->flags & RST) return; /* Never send an RST in response to an RST */ tcp_stat.resets++; /* Compose the RST IP pseudo-header, swapping addresses */ ph.source = dest; ph.dest = source; ph.protocol = TCP_PTCL; ph.zero = 0; ph.length = sizeof(struct tcp_header); /* Allocate mbuf for the RST TCP header and fill it in */ if((hbp = alloc_mbuf(ph.length)) == NULLBUF) return; /* Can't do nothin' */ hbp->cnt = ph.length; tcph = (struct tcp_header *)hbp->data; tcph->source = htons(seg->dest); tcph->dest = htons(seg->source); tcph->flags = RST; if(seg->flags & ACK){ /* This reset is being sent to clear a half-open connection. * Set the sequence number of the RST to the incoming ACK * so it will be acceptable. */ tcph->seq = htonl(seg->ack); tcph->ack = 0; } else { /* We're rejecting a connect request (SYN) from LISTEN state * so we have to "acknowledge" their SYN. */ tcph->seq = 0; if(seg->flags & SYN) length++; if(seg->flags & FIN) length++; tcph->ack = htonl((int32)(seg->seq + length)); tcph->flags |= ACK; } tcph->offset = (ph.length/sizeof(long)) << DSHIFT; tcph->wnd = 0; tcph->checksum = 0; tcph->up = 0; tcph->checksum = cksum(&ph,hbp,ph.length); /* Ship it out (note swap of addresses) */ ip_send(dest,source,TCP_PTCL,tos,0,hbp,ph.length,0,0); } /* Process an incoming acknowledgement and window indication. * From page 72. */ static void update(tcb,seg) register struct tcb *tcb; register struct tcp_header *seg; { int16 acked; int32 rtt; acked = 0; if(seq_gt(seg->ack,tcb->snd.nxt)){ tcb->force = 1; /* Acks something not yet sent */ return; } /* Round trip time estimation */ if(seq_gt(tcb->rttseq,tcb->snd.una)){ /* A sequence number is actively being timed */ if(tcb->retry == 0 && seq_ge(seg->ack,tcb->rttseq)){ /* This packet was sent only once and now * it's been acked, so compute the new smoothed * estimate. The RT time is just the current time * on the retransmission timer. */ rtt = tcb->timer.start - tcb->timer.count; rtt *= MSPTICK; /* convert to ticks */ /* Fast attack/slow decay algorithm by Dave Mills * (see RFC-889) */ if(rtt > tcb->srtt){ /* RTT is increasing, use fast attack */ tcb->srtt = (ALPHA1*tcb->srtt + rtt)/(ALPHA1+1); } else { /* RTT is decreasing, use slow decay */ tcb->srtt = (ALPHA2*tcb->srtt + rtt)/(ALPHA2+1); } /* Set new retransmission timeout */ tcb->timer.start = (BETA * tcb->srtt)/MSPTICK; } } /* If the remote window is closed, reset the retry counter even if * this packet doesn't acknowledge anything new as long as the ACK * is current. * This allows closed-window probes to go on indefinitely. */ if(seg->wnd == 0 && seq_ge(seg->ack,tcb->snd.una)) tcb->retry = 0; /* See if anything new is being acknowledged */ if(seq_gt(seg->ack,tcb->snd.una)){ acked = seg->ack - tcb->snd.una; /* If we're waiting for an ack of our SYN, process it */ switch(tcb->state){ case SYN_SENT: case SYN_RECEIVED: acked--; tcb->sndcnt--; } /* Remove acknowledged bytes from the send queue and update the * unacknowledged pointer. If a FIN is being acked, * pullup won't be able to remove it from the queue. */ pullup(&tcb->sndq,NULLCHAR,acked); /* This will include the FIN if there is one */ tcb->sndcnt -= acked; tcb->snd.una = seg->ack; /* If retransmissions have been occurring, make sure the * send pointer doesn't repeat ancient history */ if(seq_lt(tcb->snd.ptr,tcb->snd.una)) tcb->snd.ptr = tcb->snd.una; /* Reset the retry counter */ tcb->retry = 0; /* Stop retransmission timer, but restart it if there is still * unacknowledged data */ stop_timer(&tcb->timer); if(tcb->snd.una != tcb->snd.nxt){ start_timer(&tcb->timer); } } /* Decide if we need to do a window update. * This is always checked whenever a legal ACK is received, * because it might be a spontaneous window reopening. */ if(seq_gt(seg->seq,tcb->snd.wl1) || ((seg->seq == tcb->snd.wl1) && seq_ge(seg->ack,tcb->snd.wl2))){ /* If the window had been closed, crank back the * send pointer so we'll immediately resume transmission. * Otherwise we'd have to wait until the next probe. */ if(tcb->snd.wnd == 0 && seg->wnd != 0) tcb->snd.ptr = tcb->snd.una; tcb->snd.wnd = seg->wnd; tcb->snd.wl1 = seg->seq; tcb->snd.wl2 = seg->ack; } /* If outgoing data was acked, notify the user so he can send more * unless we've already sent a FIN. */ if(acked != 0 && tcb->t_upcall){ switch(tcb->state){ case ESTABLISHED: case CLOSE_WAIT: (*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt); } } } /* Determine if the given sequence number is in our receiver window. * NB: must not be used when window is closed! */ static int in_window(tcb,seq) struct tcb *tcb; int32 seq; { return seq_within(seq,tcb->rcv.nxt,(int32)(tcb->rcv.nxt+tcb->rcv.wnd-1)); } /* Process an incoming SYN */ static void proc_syn(tcb,tos,seg,mss) register struct tcb *tcb; char tos; struct tcp_header *seg; int16 mss; { int16 mtu,ip_mtu(); tcb->force = 1; /* Always send a response */ /* Note: It's not specified in RFC 793, but SND.WL1 and * SND.WND are initialized here since it's possible for the * window update routine in update() to fail depending on the * IRS if they are left unitialized. */ /* Check incoming precedence and increase if higher */ if(PREC(tos) > PREC(tcb->tos)) tcb->tos = tos; tcb->rcv.nxt = seg->seq + 1; /* p 68 */ tcb->snd.wl1 = tcb->irs = seg->seq; tcb->snd.wnd = seg->wnd; if(mss != 0) tcb->mss = mss; /* Check the MTU of the interface we'll use to reach this guy * and lower the MSS so that unnecessary fragmentation won't occur */ if((mtu = ip_mtu(tcb->conn.remote.address)) != 0){ /* Allow space for the TCP and IP headers */ mtu -= sizeof(struct tcp_header) + sizeof(struct ip_header); tcb->mss = min(mtu,tcb->mss); } } /* Generate an initial sequence number and put a SYN on the send queue */ void send_syn(tcb) register struct tcb *tcb; { tcb->iss = iss(); tcb->rttseq = tcb->snd.ptr = tcb->snd.wl2 = tcb->snd.nxt = tcb->snd.una = tcb->iss; tcb->sndcnt++; tcb->force = 1; } /* Add an entry to the resequencing queue in the proper place */ static void add_reseq(tcb,tos,seg,bp,length) struct tcb *tcb; char tos; struct tcp_header *seg; struct mbuf *bp; int16 length; { register struct reseq *rp,*rp1; char *malloc(); /* Allocate reassembly descriptor */ if((rp = (struct reseq *)malloc(sizeof (struct reseq))) == NULLRESEQ){ /* No space, toss on floor */ free_p(bp); return; } bcopy((char *)seg,(char *)&rp->seg,sizeof(struct tcp_header)); rp->tos = tos; rp->bp = bp; rp->length = length; /* Place on reassembly list sorting by starting seq number */ rp1 = tcb->reseq; if(rp1 == NULLRESEQ || seq_lt(seg->seq,rp1->seg.seq)){ /* Either the list is empty, or we're less than all other * entries; insert at beginning. */ rp->next = rp1; tcb->reseq = rp; } else { /* Find the last entry less than us */ for(;;){ if(rp1->next == NULLRESEQ || seq_lt(seg->seq,rp1->next->seg.seq)){ /* We belong just after this one */ rp->next = rp1->next; rp1->next = rp; break; } rp1 = rp1->next; } } } /* Fetch the first entry off the resequencing queue */ static void get_reseq(tcb,tos,seg,bp,length) register struct tcb *tcb; char *tos; struct tcp_header *seg; struct mbuf **bp; int16 *length; { register struct reseq *rp; if((rp = tcb->reseq) == NULLRESEQ) return; tcb->reseq = rp->next; *tos = rp->tos; bcopy((char *)&rp->seg,(char *)seg,sizeof(struct tcp_header)); *bp = rp->bp; *length = rp->length; free((char *)rp); } /* Trim segment to fit window. Return 0 if OK, -1 if segment is * unacceptable. */ static int trim(tcb,seg,bp,length) register struct tcb *tcb; register struct tcp_header *seg; struct mbuf **bp; int16 *length; { struct mbuf *nbp; long dupcnt,excess; int16 len; /* Segment length including flags */ char accept; accept = 0; len = *length; if(seg->flags & SYN) len++; if(seg->flags & FIN) len++; /* Acceptability tests */ if(tcb->rcv.wnd == 0){ /* Only in-order, zero-length segments are acceptable when our window * is closed. */ if(seg->seq == tcb->rcv.nxt && len == 0){ return 0; /* Acceptable, no trimming needed */ } } else { /* Some part of the segment must be in the window */ if(in_window(tcb,seg->seq)){ accept++; /* Beginning is */ } else if(len != 0){ if(in_window(tcb,(int32)(seg->seq+len-1)) || /* End is */ seq_within(tcb->rcv.nxt,seg->seq,(int32)(seg->seq+len-1))){ /* Straddles */ accept++; } } } if(!accept){ free_p(*bp); return -1; } dupcnt = tcb->rcv.nxt - seg->seq; if(dupcnt > 0){ /* Trim off SYN if present */ if(seg->flags & SYN){ /* SYN is before first data byte */ seg->flags &= ~SYN; seg->seq++; dupcnt--; } if(dupcnt > 0){ pullup(bp,NULLCHAR,(int16)dupcnt); seg->seq += dupcnt; *length -= dupcnt; } } excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd); if(excess > 0){ /* Trim right edge */ *length -= excess; nbp = copy_p(*bp,*length); free_p(*bp); *bp = nbp; seg->flags &= ~FIN; /* FIN follows last data byte */ } return 0; } SHAR_EOF cat << \SHAR_EOF > newtelnetp.c #include <stdio.h> #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/tasks.h> #include <exec/ports.h> #include <exec/libraries.h> #include <exec/io.h> #include <exec/devices.h> #include <exec/errors.h> #include <proto/exec.h> #include <devices/console.h> #include <libraries/dos.h> #include <libraries/dosextens.h> #include <intuition/intuition.h> #include <dos.h> #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" #include "inetdev.h" #include "inetlib.h" #define DEBUG struct Process *mytask; APTR oldwindowptr; struct IntuitionBase *IntuitionBase; char banner[80] = "telnet window"; static struct NewWindow nw = { 0, 0, 640, 200, /* left, top, (max) width, (max) height */ 0, 1, /* detail pen, block pen */ 0, /* IDCMP flags */ SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING | SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH, /* window flags */ NULL, NULL, /* gadget, checkmark */ (UBYTE *)&banner[0], /* title of window */ NULL, NULL, /* screen, bitmap */ 200, 50, -1, -1, /* sizing limits */ WBENCHSCREEN, /* on the workbench */ }; struct Window *win; struct MsgPort *keyboard, *consinp, *consoutp, *tcpinp, *tcpoutp; struct IOStdReq consin, consout; char InputCharacter; int deviceopened = 0; struct IOINETReq tnreq, tninreq, tnoutreq; char recv[512], snd[512], recvbuf[512], tcpinbuf[512]; struct telnet *tn; #ifdef LATTICE extern struct { short error; char *msg; } os_errlist[]; extern int _OSERR, os_nerr; #endif static /* * wait for something to happen */ eihalt() { register struct IntuiMessage *msg; static ULONG mask = 0; if (mask == 0L) mask = 1L << consinp->mp_SigBit | 1L << tcpinp->mp_SigBit; (void) Wait(mask); } clean(why) char *why; { int i; if (win) CloseWindow(win); if (consinp) DeletePort(consinp); if (consoutp) DeletePort(consoutp); if (tcpinp) DeletePort(tcpinp); if (tcpoutp) DeletePort(tcpoutp); if (deviceopened) CloseDevice(&tnreq); mytask->pr_WindowPtr = oldwindowptr; if (why) { myoserr(why); } exit(0); } printlist(l) struct List *l; { printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, l->lh_TailPred); } myoserr(why) char *why; { int i; fprintf(stderr, "%s: ", why); #ifdef LATTICE fprintf(stderr, "%d: ", _OSERR); for(i = 0; os_errlist[i].error < os_nerr; i++) if (os_errlist[i].error == _OSERR) fprintf(stderr, os_errlist[i].msg); #endif fprintf(stderr, "\r\n"); } /* Called at startup time to set up console I/O, memory heap */ ioinit() { struct Screen *scr; mytask = (struct Process *) FindTask((char *) NULL); oldwindowptr = mytask->pr_WindowPtr; mytask->pr_WindowPtr = (APTR) -1; /* disable DOS requestors */ if ((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 33L)) == NULL) clean("No intuition: Version 1.2 of Amiga Systems Software required"); /* * Try to determine the size of the workbench screen */ scr = malloc(sizeof(struct Screen)); if (scr==NULL) clean("Can't alloc screen"); if (GetScreenData(scr, (ULONG) sizeof(struct Screen), WBENCHSCREEN, NULL) == TRUE) { nw.Width = scr->Width; nw.Height = scr->Height-20; nw.TopEdge = 19; } else fprintf(stderr, "Can't GetScreenData()\n"); free((char *)scr); if ((win = OpenWindow(&nw)) == NULL) clean("Can't open window"); if ((consinp = CreatePort("telnet:console in", 0L)) == NULL) clean("Can't create console port"); if ((tcpinp = CreatePort("telnet:tcp in", 0L)) == NULL) clean("Can't create telnet tcp input port"); if ((tcpoutp = CreatePort("telnet:tcp out", 0L)) == NULL) clean("Can't create telnet tcp output port"); consin.io_Data = (APTR) win; consin.io_Length = sizeof(struct Window); _OSERR = OpenDevice("console.device", 0L, &consin, 0L); if (_OSERR != 0L){ printf("opendevice returned %d\n", _OSERR); myoserr("could not get console"); clean("Can't open console device"); } consout = consin; consin.io_Message.mn_ReplyPort = consinp; consin.io_Length = 512; consin.io_Data = (APTR) recvbuf; consin.io_Command = CMD_READ; SendIO(&consin); consout.io_Message.mn_ReplyPort = consoutp; consout.io_Command = CMD_WRITE; } /* Read characters from the keyboard, translating them to "real" ASCII * If none are ready, return the -1 from kbraw() */ kbread() { char c; int amount; if (CheckIO(&consin)) { WaitIO(&consin); amount = consin.io_Actual; if (amount > 512) amount = 511; strncpy(recv, recvbuf, amount); SendIO(&consin); return (amount); } return -1; /* nuthin here */ } extern char nospace[]; int refuse_echo = 0; int unix_line_mode = 0; /* if true turn <cr> to <nl> when in line mode */ #ifdef DEBUG char *t_options[] = { "Transmit Binary", "Echo", "", "Suppress Go Ahead", "", "Status", "Timing Mark" }; #endif /* Telnet receiver upcall routine */ void rcv_char() { tel_input(tn,tninreq.io_Data, tninreq.io_Actual); fflush(stdout); } brk() { clean("ok i iwll quit\n"); } /* TCP connection states */ char *tcpstates[] = { "Closed", "Listen", "SYN sent", "SYN received", "Established", "FIN wait 1", "FIN wait 2", "Close wait", "Closing", "Last ACK", "Time wait" }; /* TCP segment header flags */ char *tcpflags[] = { "FIN", /* 0x01 */ "SYN", /* 0x02 */ "RST", /* 0x04 */ "PSH", /* 0x08 */ "ACK", /* 0x10 */ "URG" /* 0x20 */ }; /* TCP closing reasons */ char *reasons[] = { "Normal", "Reset", "Timeout", "ICMP" }; char old = LISTEN; int done = 0; char *hostname=""; int hostport=0; char *bannerfmt = "telnet %10s %4d %10s"; void showstate(old, new) char old, new; { /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ sprintf(banner, bannerfmt, hostname, hostport, tcpstates[new]); SetWindowTitles(win, banner, -1); switch(new){ case CLOSE_WAIT: done = 1; break; case CLOSED: /* court adjourned */ done = 1; break; default: break; } fflush(stdout); } /* Execute user telnet command */ main(argc,argv) int argc; char *argv[]; { extern int _OSERR; struct InternetBase *InternetBase; int send_tel(); int unix_send_tel(); struct session *s; int amount; struct socket lsocket,fsocket; ioinit(); hostname = argv[1]; old = LISTEN; showstate(old, LISTEN); tnreq.io_fsocket.address = aton(argv[1]); if(argc < 3) tnreq.io_fsocket.port = TELNET_PORT; else tnreq.io_fsocket.port = atoi(argv[2]); tnreq.io_Device = NULL; tnreq.io_Unit = NULL; tnreq.io_Flags = 0; tnreq.io_Error = 0; InternetBase = (struct InternetBase *) OpenDevice("internet.device", (long) INET_UNIT_TCP, &tnreq, 0L); if (InternetBase != 0L){ printf("it did not open %d\n",InternetBase); clean("i quit"); } hostport = tnreq.io_lsocket.port; deviceopened = 1; tninreq = tnreq; /* possible lettuce bug ?*/ tnoutreq = tnreq; tninreq.io_Length = 512; tnoutreq.io_Length = 1; tninreq.io_Data = tcpinbuf; tnoutreq.io_Data = snd; tnoutreq.io_Length = 0; tninreq.io_Command = CMD_READ; tnoutreq.io_Command = CMD_WRITE; tninreq.io_Message.mn_ReplyPort = tcpinp; tnoutreq.io_Message.mn_ReplyPort = tcpoutp; /* Create and initialize a Telnet protocol descriptor */ if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){ myoserr("calloc faiuled\n"); goto done; } tn->session = s; /* Upward pointer */ tn->state = TS_DATA; SendIO(&tninreq); onbreak(&brk); InputCharacter = ' '; while (! done) { eihalt(); if ((amount = kbread()) >= 0){ unix_send_tel(recv, amount);} if (CheckIO(&tninreq)) { chkabort(); WaitIO(&tninreq); rcv_char(); if (tninreq.io_State != old) { showstate(old, tninreq.io_State); old = tninreq.io_State; } SendIO(&tninreq); } } done: clean("All done"); } /* Process typed characters */ int unix_send_tel(buf,n) char *buf; int16 n; { int i; for (i=0; (i<n) && (buf[i] != '\r'); i++) ; if (buf[i] == '\r') { buf[i] = '\n'; n = i+1; } send_tel(buf,n); } int send_tel(buf,n) char *buf; int16 n; { int i; tnoutreq.io_Data = buf; tnoutreq.io_Length = n; SendIO(&tnoutreq); i = WaitIO(&tnoutreq); if (tnoutreq.io_State != old) { showstate(old, tnoutreq.io_State); old = tnoutreq.io_State; } } /* Process incoming TELNET characters */ int tel_input(tn,bp, len) register struct telnet *tn; char *bp; int len; { 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(len--){ c = *bp++; 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; } } } #ifdef NOTDEF /* 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); } #endif /* Delete telnet structure */ static free_telnet(tn) struct telnet *tn; { 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; tnoutreq.io_Data = s; tnoutreq.io_Length = 3; DoIO(&tnoutreq); /* bp = qdata(s,(int16)3); send_tcp(tn->tcb,bp); */ } #define BUFMAXCNT 150 static char conbuf[BUFMAXCNT]; static int concnt = 0; int amigaputchar(c) char c; { conbuf[concnt++] = c; if ((c == '\n') || (concnt == BUFMAXCNT)) amigaflush(); return c; } amigaflush() { if (concnt == 0) return; consout.io_Data = (APTR) conbuf; consout.io_Length = concnt; consout.io_Command = CMD_WRITE; DoIO(&consout); concnt = 0; } /* * Begin terrible, horrible hack. All output should be printed upon (into?) * the window we opened before. Here goes nothing... */ printf(a, b, c, d, e, f, g, h, i, j, k) char *a; int b, c, d, e, f, g, h, i, j, k; { if (concnt) amigaflush(); sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k); consout.io_Data = (APTR) conbuf; consout.io_Length = strlen(conbuf); consout.io_Command = CMD_WRITE; DoIO(&consout); /* no use in doing this async */ } SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.