page@swan.ulowell.edu (Bob Page) (03/18/89)
Submitted-by: rminnich@super.org (Ronald G. Minnich) Posting-number: Volume 89, Issue 81 Archive-name: comm/amigatcp.2 # 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: # icmp.c # mbuf.c # slip.c # smtpcli.c # tcpout.c # tcpuser.c # telnet.c # This archive created: Fri Mar 17 17:57:17 1989 cat << \SHAR_EOF > icmp.c /* Internet Control Message Protocol */ #include "machdep.h" #include "internet.h" #include "timer.h" #include "ip.h" #include "icmp.h" #include "mbuf.h" int (*echo_proc)(); /* Handler for Echo Reply messages */ struct icmp_errors icmp_errors; struct icmp_stats icmp_stats; /* Process an incoming ICMP packet */ void icmp_input(bp,protocol,source,dest,tos,length,rxbroadcast) struct mbuf *bp; /* Pointer to ICMP message */ char protocol; /* Should always be ICMP_PTCL */ int32 source; /* Sender of ICMP message */ int32 dest; /* Us */ char tos; /* Type of Service */ int16 length; /* Length of ICMP message */ char rxbroadcast; { struct icmp *icmph; /* Pointer to ICMP message */ struct ip_header *iph; /* Offending datagram header */ int16 type; /* Type of ICMP message */ int16 ip_len; if(rxbroadcast){ /* Broadcast ICMP packets are to be IGNORED !! */ icmp_errors.bdcsts++; free_p(bp); return; } if(cksum(NULLHEADER,bp,length) != 0){ /* Bad ICMP checksum; discard */ icmp_errors.checksum++; free_p(bp); return; } /* If the message is fragmented, copy to a contiguous mbuf */ if(bp->next != NULLBUF){ struct mbuf *nbp; nbp = copy_p(bp,length); free_p(bp); if(nbp == NULLBUF){ icmp_errors.nospace++; return; } bp = nbp; } icmph = (struct icmp *)bp->data; /* Process the message. Some messages are passed up to the protocol * module for handling, others are handled here. */ type = icmph->type & 0xff; if(type < ICMP_TYPES) icmp_stats.input[type]++; switch(type){ case TIME_EXCEED: /* Time-to-live Exceeded */ case DEST_UNREACH: /* Destination Unreachable */ case QUENCH: /* Source Quench */ iph = (struct ip_header *)(icmph + 1); ip_len = (iph->v_ihl & 0xf) * sizeof(int32); switch(iph->protocol){ case TCP_PTCL: tcp_icmp(ntohl(iph->source),ntohl(iph->dest), icmph->type,icmph->code,(char *)iph + ip_len); break; } break; case ECHO: /* Echo Request */ /* Change type to ECHO_REPLY, recompute checksum, * and return datagram. */ icmph->type = ECHO_REPLY; icmph->checksum = 0; icmph->checksum = cksum(NULLHEADER,bp,length); icmp_stats.output[ECHO_REPLY]++; ip_send(dest,source,ICMP_PTCL,tos,0,bp,length,0,0); return; case REDIRECT: /* Redirect */ case PARAM_PROB: /* Parameter Problem */ break; case ECHO_REPLY: /* Echo Reply */ if(echo_proc){ (*echo_proc)(ntohl(iph->source),ntohl(iph->dest), icmph->type,icmph->code,(char *)iph + ip_len); } break; case TIMESTAMP: /* Timestamp */ case TIME_REPLY: /* Timestamp Reply */ case INFO_RQST: /* Information Request */ case INFO_REPLY: /* Information Reply */ break; } free_p(bp); } /* Return an ICMP response to the sender of a datagram */ icmp_output(bp,type,code,args) struct mbuf *bp; /* Pointer to offending IP header + data */ char type,code; /* Codes to send */ union icmp_args *args; { struct ip_header *iph; /* Offending IP header */ int16 ip_len; /* Length of offending IP header */ struct mbuf *reply; /* Buffer with ICMP reply */ struct icmp *icmph; /* ICMP protocol header */ struct mbuf *data; /* Returned portion of offending packet */ int16 dlen; /* Length of data portion of offending pkt */ int16 length; /* Total length of reply */ extern int32 ip_addr; /* Our IP address */ if(type < ICMP_TYPES) icmp_stats.output[type]++; iph = (struct ip_header *)bp->data; if(iph->protocol == ICMP_PTCL){ icmp_errors.noloop++; return; /* Never send an ICMP message about another ICMP message */ } /* Compute amount of original datagram to return. * We return the original IP header, and up to 8 bytes past that. */ ip_len = (iph->v_ihl & 0xf) * sizeof(int32); dlen = ntohs(iph->length); if(dlen > ip_len + 8) dlen = ip_len + 8; length = sizeof(struct icmp) + dlen; /* Allocate ICMP header and fill in */ if((reply = alloc_mbuf(sizeof(struct icmp))) == NULLBUF){ /* No space; don't bother */ icmp_errors.nospace++; return; } reply->cnt = sizeof(struct icmp); icmph = (struct icmp *)reply->data; icmph->type = type; icmph->code = code; if(args != (union icmp_args *)NULL) icmph->args.unused = args->unused; /* copies whole union */ else icmph->args.unused = 0; /* Link in a copy of the beginning of the original datagram */ data = copy_p(bp,dlen); reply->next = data; /* Could be NULL if copy fails */ /* Compute ICMP checksum and send */ icmph->checksum = 0; icmph->checksum = cksum(NULLHEADER,reply,length); ip_send(ip_addr,ntohl(iph->source),ICMP_PTCL,iph->tos,0,reply,length,0,0); } #ifdef TRACE /* ICMP message types */ char *icmptypes[] = { "Echo Reply", NULLCHAR, NULLCHAR, "Unreachable", "Source Quench", "Redirect", NULLCHAR, NULLCHAR, "Echo Request", NULLCHAR, NULLCHAR, "Time Exceeded", "Parameter Problem", "Timestamp", "Timestamp Reply", "Information Request", "Information Reply" }; /* ICMP unreachable messages */ char *unreach[] = { "Network", "Host", "Protocol", "Port", "Fragmentation", "Source route" }; /* ICMP Time exceeded messages */ char *exceed[] = { "Time-to-live", "Fragment reassembly" }; /* ICMP redirect messages */ char *redirect[] = { "Network", "Host", "TOS & Network", "TOS & Host" }; int doicmpstat(argc,argv) int argc; char *argv[]; { extern struct icmp_errors icmp_errors; extern struct icmp_stats icmp_stats; register int i; printf("chksum err %u no space %u icmp %u bdcsts %u\r\n", icmp_errors.checksum,icmp_errors.nospace,icmp_errors.noloop, icmp_errors.bdcsts); printf("type rcvd sent\r\n"); for(i=0;i<ICMP_TYPES;i++){ if(icmp_stats.input[i] == 0 && icmp_stats.output[i] == 0) continue; printf("%-6u%-6u%-6u",i,icmp_stats.input[i], icmp_stats.output[i]); if(icmptypes[i] != NULLCHAR) printf(" %s",icmptypes[i]); printf("\r\n"); } return 0; } /* Dump an ICMP header */ void icmp_dump(bp,source,dest,check) struct mbuf *bp; int32 source,dest; int check; /* If 0, bypass checksum verify */ { register struct icmp *icmp; char *codemsg; struct mbuf *ibp; int i; char tmpbuf; if(bp == NULLBUF) return; /* If packet isn't in a single buffer, make a temporary copy and * note the fact so we free it later */ if(bp->next != NULLBUF){ bp = copy_p(bp,len_mbuf(bp)); tmpbuf = 1; } else tmpbuf = 0; codemsg = NULLCHAR; icmp = (struct icmp *)bp->data; if(icmp->type <= 16 && icmptypes[icmp->type] != NULLCHAR) printf("ICMP: %s",icmptypes[icmp->type]); else printf("ICMP: type %u",icmp->type); switch(icmp->type){ case DEST_UNREACH: if(icmp->code <= 5) codemsg = unreach[icmp->code]; break; case REDIRECT: if(icmp->code <= 3) codemsg = redirect[icmp->code]; break; case TIME_EXCEED: if(icmp->code <= 1) codemsg = exceed[icmp->code]; break; } if(codemsg != NULLCHAR) printf(" %s",codemsg); else printf(" code %u",icmp->code); /* Special case for parameter problem message */ if(icmp->type == PARAM_PROB) printf(" pointer = 0x%x",icmp->args.pointer); if(check){ /* Verify checksum */ if((i = cksum(NULLHEADER,bp,len_mbuf(bp))) != 0) printf(" CHECKSUM ERROR (%u)",i); } printf("\r\n"); /* Dump the offending IP header, if any */ switch(icmp->type){ case DEST_UNREACH: case TIME_EXCEED: case PARAM_PROB: case QUENCH: case REDIRECT: printf("Returned "); dup_p(&ibp,bp,sizeof(struct icmp), len_mbuf(bp) - sizeof(struct icmp)); ip_dump(ibp); free_p(ibp); } if(tmpbuf) free_p(bp); } #endif SHAR_EOF cat << \SHAR_EOF > mbuf.c /* Primitive mbuf allocate/free routines */ #ifdef TRACE #include <stdio.h> #endif #include "machdep.h" #include "mbuf.h" /* Allocate mbuf with associated buffer of 'size' bytes */ struct mbuf * alloc_mbuf(size) register int16 size; { register struct mbuf *bp; char *malloc(); if((bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)))) == NULLBUF) return NULLBUF; bp->next = bp->anext = NULLBUF; if(size != 0){ bp->data = (char *)(bp + 1); } else { bp->data = NULLCHAR; } bp->cnt = 0; return bp; } /* Free all resources associated with mbuf * Return pointer to next mbuf in packet chain */ struct mbuf * free_mbuf(bp) register struct mbuf *bp; { register struct mbuf *bp1 = NULLBUF; if(bp != NULLBUF){ bp1 = bp->next; free((char *)bp); } return bp1; } /* Free packet (a chain of mbufs). Return pointer to next packet on queue, * if any */ struct mbuf * free_p(bp) register struct mbuf *bp; { struct mbuf *abp; if(bp == NULLBUF) return NULLBUF; abp = bp->anext; while(bp != NULLBUF) bp = free_mbuf(bp); return abp; } /* Free entire queue of packets (of mbufs) */ free_q(q) struct mbuf **q; { register struct mbuf *bp; while((bp = dequeue(q)) != NULLBUF) free_p(bp); } /* Count up the total number of bytes in an mbuf */ int16 len_mbuf(bp) register struct mbuf *bp; { int cnt; cnt = 0; while(bp != NULLBUF){ cnt += bp->cnt; bp = bp->next; } return cnt; } /* Count up the number of packets in a queue */ int16 len_q(bp) register struct mbuf *bp; { register int cnt; for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext) ; return cnt; } /* Duplicate/enqueue/dequeue operations based on mbufs */ /* Duplicate first 'cnt' bytes of packet starting at 'offset'. * This is done without copying data; only the headers are duplicated, * but without data segments of their own. The pointers are set up to * share the data segments of the original copy. The return pointer is * passed back through the first argument, and the return value is the * number of bytes actually duplicated. */ int16 dup_p(hp,bp,offset,cnt) struct mbuf **hp; register struct mbuf *bp; register int16 offset; register int16 cnt; { register struct mbuf *cp; int16 tot; if(cnt == 0 || bp == NULLBUF || hp == (struct mbuf **)NULL){ if(hp != (struct mbuf **)NULL) *hp = NULLBUF; return 0; } if((*hp = cp = alloc_mbuf(0)) == NULLBUF){ return 0; } /* Skip over leading mbufs that are smaller than the offset */ while(bp != NULLBUF && bp->cnt <= offset){ offset -= bp->cnt; bp = bp->next; } if(bp == NULLBUF){ free_mbuf(cp); *hp = NULLBUF; return 0; /* Offset was too big */ } tot = 0; for(;;){ cp->data = bp->data + offset; cp->cnt = min(cnt,bp->cnt - offset); offset = 0; cnt -= cp->cnt; tot += cp->cnt; bp = bp->next; if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF) break; cp = cp->next; } return tot; } /* Copy first 'cnt' bytes of packet into a new, single mbuf */ struct mbuf * copy_p(bp,cnt) register struct mbuf *bp; register int16 cnt; { register struct mbuf *cp; register char *wp; register int16 n; if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF) return NULLBUF; wp = cp->data; while(cnt != 0 && bp != NULLBUF){ n = min(cnt,bp->cnt); bcopy(bp->data,wp,n); wp += n; cp->cnt += n; cnt -= n; bp = bp->next; } return cp; } /* Copy and delete "cnt" bytes from beginning of packet. Return number of * bytes actually pulled off */ int16 pullup(bph,buf,cnt) struct mbuf **bph; char *buf; int16 cnt; { register struct mbuf *bp; int16 n,tot; tot = 0; if(bph == (struct mbuf **)NULL) return 0; while(*bph != NULLBUF && cnt != 0){ bp = *bph; n = min(cnt,bp->cnt); if(buf != NULLCHAR){ bcopy(bp->data,buf,n); buf += n; } tot += n; cnt -= n; bp->data += n; bp->cnt -= n; if(bp->cnt == 0){ *bph = free_mbuf(bp); } } return tot; } /* Append mbuf to end of mbuf chain */ int16 append(bph,bp) struct mbuf **bph; struct mbuf *bp; { register struct mbuf *p; if(bph == (struct mbuf **)NULL || bp == NULLBUF) return; if(*bph == NULLBUF){ /* First one on chain */ *bph = bp; } else { for(p = *bph ; p->next != NULLBUF ; p = p->next) ; p->next = bp; } } /* Append packet to end of packet queue */ void enqueue(q,bp) struct mbuf **q; struct mbuf *bp; { register struct mbuf *p; char i_state; if(q == (struct mbuf **)NULL || bp == NULLBUF) return; i_state = disable(); if(*q == NULLBUF){ /* List is empty, stick at front */ *q = bp; } else { for(p = *q ; p->anext != NULLBUF ; p = p->anext) ; p->anext = bp; } restore(i_state); } /* Unlink a packet from the head of the queue */ struct mbuf * dequeue(q) register struct mbuf **q; { register struct mbuf *bp; char i_state; if(q == (struct mbuf **)NULL) return NULLBUF; i_state = disable(); if((bp = *q) != NULLBUF){ *q = bp->anext; bp->anext = NULLBUF; } restore(i_state); return bp; } /* Copy user data into an mbuf */ struct mbuf * qdata(data,cnt) char *data; int16 cnt; { register struct mbuf *bp; if((bp = alloc_mbuf(cnt)) == NULLBUF) return NULLBUF; bcopy(data,bp->data,cnt); bp->cnt = cnt; return bp; } /* Copy mbuf data into user buffer */ int16 dqdata(bp,buf,cnt) struct mbuf *bp; char *buf; unsigned cnt; { unsigned n,tot; struct mbuf *bp1; if(buf == NULLCHAR) return 0; tot = 0; for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){ n = min(bp1->cnt,cnt); bcopy(bp1->data,buf,n); cnt -= n; buf += n; tot += n; } free_p(bp); return tot; } #ifdef TRACE /* Comment this out if your library already has this function */ #define isprint(c) ((c) >= ' ' && (c) < 0x7f) /* Assumes ASCII */ /* Dump an mbuf in hex */ void hexdump(bp) struct mbuf *bp; { register struct mbuf *tbp; int16 n; int16 address; void fmtline(); if(bp == NULLBUF) return; tbp = copy_p(bp,len_mbuf(bp)); address = 0; while(tbp->cnt != 0){ n = min(tbp->cnt,16); fmtline(address,tbp->data,n); address += n; tbp->data += n; tbp->cnt -= n; } free_p(tbp); fflush(stdout); } /* Dump an mbuf in ascii */ void asciidump(bp) struct mbuf *bp; { struct mbuf *tbp; char c; int16 tot; if(bp == NULLBUF) return; tbp = copy_p(bp,len_mbuf(bp)); tot = 0; while(pullup(&tbp,&c,1) == 1){ if((tot % 64) == 0) printf("%04x ",tot); if(isprint(c)) putchar(c); else putchar('.'); tot++; if((tot % 64) == 0) printf("\r\n"); } if((tot % 64) != 0) printf("\r\n"); free_p(tbp); fflush(stdout); } /* Print a buffer up to 16 bytes long in formatted hex with ascii * translation, e.g., * 0000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>? */ void fmtline(addr,buf,len) int16 addr; char *buf; int16 len; { char line[80]; char *aptr,*cptr; int16 c; void ctohex(); for(cptr = line;cptr < &line[80];cptr++) *cptr = ' '; ctohex(line,(int16)hibyte(addr)); ctohex(line+2,(int16)lobyte(addr)); aptr = &line[6]; cptr = &line[55]; while(len-- != 0){ c = *buf++ & 0xff; ctohex(aptr,c); aptr += 3; c &= 0x7f; if(isprint(c)){ *cptr++ = c; } else { *cptr++ = '.'; } } *cptr++ = '\r'; *cptr++ = '\n'; #ifdef AMIGA *cptr = '\0'; printf(line); #else fwrite(line,1,(unsigned)(cptr-line),stdout); #endif } /* Convert byte to two ascii-hex characters */ static void ctohex(buf,c) char *buf; int16 c; { static char hex[] = "0123456789abcdef"; buf[0] = hex[hinibble(c)]; buf[1] = hex[lonibble(c)]; } #endif SHAR_EOF cat << \SHAR_EOF > slip.c /* Send and receive IP datagrams on serial lines. Compatible with SLIP * under Berkeley Unix. */ #ifdef TRACE #include <stdio.h> #endif #include "machdep.h" #ifdef TRACE #include "trace.h" #endif #include "mbuf.h" #include "iface.h" #include "ax25.h" #include "slip.h" #ifdef AMIGA #include "amiga.h" #else #include "pc.h" #endif int slip_send(); int doslip(); int asy_output(); /* Slip level control structure */ struct slip slip[ASY_MAX]; unsigned nasy; /* Send routine for point-to-point slip * This is a trivial function since there is no slip link-level header */ int slip_send(bp,interface,gateway,precedence,delay,throughput,reliability) struct mbuf *bp; /* Buffer to send */ struct interface *interface; /* Pointer to interface control block */ int32 gateway; /* Ignored (SLIP is point-to-point) */ char precedence; char delay; char throughput; char reliability; { /* Queue a frame on the slip output queue and start transmitter */ if(interface == NULLIF){ free_p(bp); return; } #ifdef TRACE if(trace & TRACE_SLIP){ printf("%s sent:\r\n",interface->name); if((trace & TRACE_HDR) > 2) ip_dump(bp); if(trace & TRACE_DUMP) hexdump(bp); if(trace & TRACE_ASCII) asciidump(bp); fflush(stdout); } #endif slipq(interface->dev,bp); } /* Encode a packet in slip framing, put on link output queue, and kick * transmitter */ slipq(dev,bp) int16 dev; /* Serial line number */ struct mbuf *bp; /* Buffer to be sent */ { register struct slip *sp; struct mbuf *slip_encode(); if((bp = slip_encode(bp)) == NULLBUF) return; sp = &slip[dev]; enqueue(&sp->sndq,bp); sp->sndcnt++; if(sp->tbp == NULLBUF) asy_start(dev); } /* Start output, if possible, on asynch device dev */ static asy_start(dev) int16 dev; { register struct slip *sp; if(!stxrdy(dev)) return; /* Transmitter not ready */ sp = &slip[dev]; if(sp->tbp != NULLBUF){ /* transmission just completed */ free_p(sp->tbp); sp->tbp = NULLBUF; } if(sp->sndq == NULLBUF) return; /* No work */ sp->tbp = dequeue(&sp->sndq); sp->sndcnt--; asy_output(dev,sp->tbp->data,sp->tbp->cnt); } /* Encode a packet in SLIP format */ static struct mbuf * slip_encode(bp) struct mbuf *bp; { struct mbuf *lbp; /* Mbuf containing line-ready packet */ register char *cp; register int cnt; char c; /* Allocate output mbuf that's twice as long as the packet. * This is a worst-case guess (consider a packet full of FR_ENDs!) */ lbp = alloc_mbuf(2*len_mbuf(bp) + 2); if(lbp == NULLBUF){ /* No space; drop */ free_p(bp); return NULLBUF; } cp = lbp->data; cnt = 0; /* Flush out any line garbage */ *cp++ = FR_END; cnt++; /* Copy input to output, escaping special characters */ while(pullup(&bp,&c,1) == 1){ switch(c & 0xff){ case FR_ESC: *cp++ = FR_ESC; *cp++ = T_FR_ESC; cnt += 2; break; case FR_END: *cp++ = FR_ESC; *cp++ = T_FR_END; cnt += 2; break; default: *cp++ = c; cnt++; } } *cp++ = FR_END; cnt++; lbp->cnt = cnt; return lbp; } /* Process incoming bytes in SLIP format * When a buffer is complete, return it; otherwise NULLBUF */ static struct mbuf * slip_decode(dev,c) int dev; /* Slip unit number */ char c; /* Incoming character */ { struct mbuf *bp,*nbp; register struct slip *sp; sp = &slip[dev]; switch(c & 0xff){ case FR_END: if(sp->rbp != NULLBUF){ /* Kick upstairs */ bp = sp->rbp; sp->rbp = NULLBUF; sp->rcnt = 0; /* Copy into contiguous buffer, if necessary */ if(bp->next != NULLBUF){ nbp = copy_p(bp,len_mbuf(bp)); free_p(bp); bp = nbp; } return bp; } return NULLBUF; case FR_ESC: sp->escaped = 1; return NULLBUF; } if(sp->escaped){ sp->escaped = 0; switch(c & 0xff){ case T_FR_ESC: c = FR_ESC; break; case T_FR_END: c = FR_END; break; default: sp->errors++; } } if(sp->rcnt == SLIP_MTU){ /* Packet is too large, drop it and start over */ free_p(sp->rbp); sp->rbp = NULLBUF; sp->rcnt = 0; return NULLBUF; } /* We reach here with a character for the buffer; * make sure there's space for it */ if(sp->rbp == NULLBUF){ /* Allocate first mbuf for new packet */ if((sp->rbp1 = sp->rbp = alloc_mbuf(SLIP_ALLOC)) == NULLBUF) return NULLBUF; /* No memory, drop */ sp->rcp = sp->rbp->data; } else if(sp->rbp1->cnt == SLIP_ALLOC){ /* Current mbuf is full; link in another */ if((sp->rbp1->next = alloc_mbuf(SLIP_ALLOC)) == NULLBUF){ /* No memory, drop whole thing */ free_p(sp->rbp); sp->rbp = NULLBUF; sp->rcnt = 0; return NULLBUF; } sp->rbp1 = sp->rbp1->next; sp->rcp = sp->rbp1->data; } /* Store the character, increment fragment and total * byte counts */ *sp->rcp++ = c; sp->rbp1->cnt++; sp->rcnt++; return NULLBUF; } /* Process SLIP line I/O */ int doslip(interface) struct interface *interface; { char c; struct mbuf *bp; int16 dev; dev = interface->dev; /* Process any pending input */ while(asy_recv(dev,&c,1) != 0) if((bp = slip_decode(dev,c)) != NULLBUF) (*slip[dev].recv)(interface,bp); /* Kick the transmitter if it's idle */ if(stxrdy(dev)) asy_start(dev); } /* Unwrap incoming SLIP packets -- trivial operation since there's no * link level header */ slip_recv(interface,bp) struct interface *interface; struct mbuf *bp; { #ifdef TRACE if(trace & TRACE_SLIP){ printf("%s recv:\r\n",interface->name); if((trace & TRACE_HDR) > 2) ip_dump(bp); if(trace & TRACE_DUMP) hexdump(bp); if(trace & TRACE_ASCII) asciidump(bp); fflush(stdout); } #endif ip_route(bp,0); } /* Attach a serial interface to the system * argv[0]: hardware type, must be "asy" * argv[1]: I/O address, e.g., "0x3f8" * argv[2]: vector, e.g., "4" * argv[3]: mode, may be: * "slip" (point-to-point SLIP) * "ax25" (AX.25 UI frame format in SLIP for raw TNC) * argv[4]: interface label, e.g., "sl0" * argv[5]: receiver ring buffer size in bytes * argv[6]: maximum transmission unit, bytes * argv[7]: interface speed, e.g, "9600" */ asy_attach(argc,argv) int argc; char *argv[]; { register struct interface *if_asy; extern struct interface *ifaces; int dev; char *malloc(), *calloc(); int asy_init(); int asy_send(); int doslip(); int asy_stop(); int ax_send(); int kiss_recv(); int kiss_output(); if(nasy >= ASY_MAX){ printf("Too many asynch controllers\r\n"); return -1; } dev = nasy++; /* Initialize hardware-level control structure */ asy[dev].addr = htoi(argv[1]); asy[dev].vec = htoi(argv[2]); /* Create interface structure and fill in details */ if_asy = (struct interface *)calloc(1, sizeof(struct interface)); if_asy->name = malloc(strlen(argv[4])+1); strcpy(if_asy->name,argv[4]); if_asy->mtu = atoi(argv[6]); if_asy->dev = dev; if_asy->recv = doslip; if_asy->stop = asy_stop; if(strcmp(argv[3],"slip") == 0){ if_asy->send = slip_send; if_asy->output = NULLFP; if_asy->flags = 0; slip[dev].recv = slip_recv; } else if(strcmp(argv[3],"ax25") == 0){ if_asy->send = ax_send; if_asy->output = kiss_output; if_asy->flags = IF_BROADCAST; if(if_asy->hwaddr == NULLCHAR) if_asy->hwaddr = malloc(AXALEN); bcopy((char *)&mycall,if_asy->hwaddr,AXALEN); slip[dev].recv = kiss_recv; } else { printf("Mode %s unknown for interface %s\r\n", argv[3],argv[4]); free((char *)if_asy); return -1; } if_asy->next = ifaces; ifaces = if_asy; asy_init(dev,(unsigned)atoi(argv[5])); asy_speed(dev,atoi(argv[7])); } SHAR_EOF cat << \SHAR_EOF > smtpcli.c /* smtpcli.c * Client routines for Simple Mail Transfer Protocol ala RFC821 * A.D. Barksdale Garbee II, aka Bdale, N3EUA * Copyright 1986 Bdale Garbee, All Rights Reserved. * Permission granted for non-commercial copying and use, provided * this notice is retained. */ #include <stdio.h> #include "machdep.h" #include "netuser.h" #include "mbuf.h" #include "timer.h" #include "tcp.h" #include "smtp.h" extern int16 lport; /* local port placeholder */ int32 aton(); static void sendit(); static struct timer smtpcli_t; char *index(),*rindex(); /* init routine called when program fired up */ smtpclinit() { int dosmtptick(); smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */ smtpcli_t.arg = 0; /* dummy value */ smtpcli_t.start = SMTPCLITIME; /* set timer duration */ start_timer(&smtpcli_t); /* and fire it up */ } /* this is the routine that gets called every so often to do outgoing mail processing */ int dosmtptick() { char lfilename[LINELEN], tmpstring[LINELEN], wfilename[13], *ptr; FILE *lfile; struct smtp_msg *mp; struct socket lsocket, fsocket; char *calloc(),*malloc(); void smtp_rec(), smtp_cts(), smtp_state(); /* printf("DOSMTPTICK() entered\n"); */ lsocket.address = ip_addr; /* our ip address */ fsocket.port = SMTP_PORT; /* if lock file exists in mqueue dir, return */ sprintf(lfilename,"%s%s",MAILQDIR,"lockfile"); if ((lfile = fopen(lfilename,"x")) == NULL) return; /* get next work filename from mqueue directory */ sprintf(tmpstring,"%s%s",MAILQDIR,"*.wrk"); #ifndef AMIGA filedir(tmpstring,0,wfilename); #endif if (wfilename[0] == '\0') return; /* no work to be done */ /* if we have work, rebuild the exact (non-wild) filename */ mp = (struct smtp_msg *)calloc(1,sizeof (struct smtp_msg)); sprintf(tmpstring,"%s%s",MAILQDIR,wfilename); ptr = &tmpstring[0]; mp->filename = malloc((unsigned)strlen(ptr)+1); strcpy(mp->filename,ptr); /* printf("work file name: %s\n",mp->filename); /* debug only */ mp->wfile = fopen(mp->filename,"r"); /* get ip address, from stuff, to stuff */ fgets(tmpstring,LINELEN,mp->wfile); /* read target ip addr */ /* printf("target ip addr: %s\n",tmpstring); */ fgets(mp->toaddr,LINELEN,mp->wfile); /* who to */ rip(mp->toaddr); /* printf("addressee: %s\n",mp->toaddr); */ fgets(mp->fromaddr,LINELEN,mp->wfile); /* who from */ rip(mp->fromaddr); /* printf("sender: %s\n",mp->fromaddr); */ fclose(mp->wfile); /* set up the rest of the socket info from what we got */ fsocket.address = aton(tmpstring); /* destination ip address */ lsocket.port = lport++; /* next unused port */ /* open smtp connection */ mp->state = CLI_OPEN_STATE; /* init state placeholder */ /* printf("Opening TCP connection for SMTP client\n"); */ mp->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,1024, smtp_rec,smtp_cts,smtp_state,0,(int *)mp); mp->tcb->user = (int *)mp; /* Upward pointer */ /* printf("releasing lock\n"); */ if (lfile != NULL) { /* release lock */ fclose(lfile); unlink(lfilename); } } /* replace terminating end of line marker(s) with null */ rip(s) char *s; { char *c; c=s; while (*c != '\0') { switch (*c) { case '\r': case '\n': *c='\0'; break; default: c++; break; } } } /* this is the master state machine that handles a single SMTP transaction */ smtp_transaction(mp) struct smtp_msg *mp; { char tmpstring[LINELEN]; /* where we build command lines */ /* printf("SMTP_TRANSACTION() called, state=%u\n",mp->state); */ if (affirmative(mp)) { switch(mp->state) { case CLI_OPEN_STATE: mp->state = CLI_MAIL_STATE; /* issue MAIL command */ /* printf("FROMADDR = %s\n",mp->fromaddr); */ sprintf(tmpstring,"mail from:<%s>\r\n",mp->fromaddr); sendit(mp,tmpstring); break; case CLI_MAIL_STATE: mp->state = CLI_RCPT_STATE; /* issue RCPT command */ sprintf(tmpstring,"rcpt to:<%s>\r\n",mp->toaddr); sendit(mp,tmpstring); break; case CLI_RCPT_STATE: mp->state = CLI_SEND_STATE; /* open text file */ strcpy(tmpstring,mp->filename); strcpy(index(tmpstring,'.'),".txt"); /* printf("text filename: %s",tmpstring); */ mp->tfile = fopen(tmpstring,"r"); /* issue DATA command */ sprintf(tmpstring,"data\r\n"); sendit(mp,tmpstring); break; case CLI_SEND_STATE: /* the transmitter upcall routine will advance the state pointer on end of file, so we do nada... */ break; case CLI_UNLK_STATE: unlink(mp->filename); /* unlink workfile */ /* close and unlink the textfile */ fclose(mp->tfile); strcpy(tmpstring,mp->filename); strcpy(index(tmpstring,'.'),".txt"); unlink(tmpstring); mp->state = CLI_QUIT_STATE; /* issue a quit command */ sprintf(tmpstring,"quit\r\n"); sendit(mp,tmpstring); break; case CLI_QUIT_STATE: /* either start next transaction, or quit */ close_tcp(mp->tcb); /* close up connection */ break; } } else { /* if we get here, means we got a negative reply */ /* for the moment, just let that hose us... */ mp->state = CLI_QUIT_STATE; /* issue a quit command */ sprintf(tmpstring,"quit\r\n"); sendit(mp,tmpstring); } } /* return true if the passed string contains a positive response code */ affirmative(mp) struct smtp_msg *mp; { /* 2 is always good, 3 is ok if we've just sent 'data' command */ if ((*mp->buf = '2') || ((*mp->buf = '3') && (mp->state = CLI_DATA_STATE))) return 1; else return 0; } /* smtp receiver upcall routine. fires up the state machine to parse input */ static void smtp_rec(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct smtp_msg *mp; char *inet_ntoa(), c; struct mbuf *bp; /* may want a void line here for procedures used */ /* printf("SMTP_REC called\n"); */ mp = (struct smtp_msg *)tcb->user; /* point to our struct */ recv_tcp(tcb,&bp,cnt); /* suck up chars from low level routine */ /* Assemble input line in buffer, return if incomplete */ while(pullup(&bp,&c,1) == 1) { switch(c) { case '\r': /* strip cr's */ continue; case '\n': /* line is finished, go do it! */ mp->buf[mp->cnt] = '\0'; smtp_transaction(mp); break; default: /* other chars get added to buffer */ mp->buf[mp->cnt++] = c; break; } } } /* smtp transmitter ready upcall routine. twiddles cts flag */ static void smtp_cts(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct smtp_msg *mp; struct mbuf *bp; char tmpstring[LINELEN]; char *cp; int c; /* printf("SMTP_CTS called\n"); */ mp = (struct smtp_msg *)tcb->user; /* point to our struct */ /* don't do anything until/unless we're supposed to be sending */ if(mp->state != CLI_SEND_STATE) return; if((bp = alloc_mbuf(cnt)) == NULLBUF){ /* Hard to know what to do here */ return; } cp = bp->data; while(cnt > 1 && (c = getc(mp->tfile)) != EOF){ *cp++ = c; bp->cnt++; cnt--; } if(bp->cnt != 0) send_tcp(tcb,bp); else free_p(bp); if(cnt > 1){ /* EOF seen */ sprintf(tmpstring,"\r\n.\r\n"); sendit(mp,tmpstring); mp->state = CLI_UNLK_STATE; } } /* smtp state change upcall routine. cans connection on error */ static void smtp_state(tcb,old,new) struct tcb *tcb; char old,new; { struct smtp_msg *mp; /* printf("SMTP_STATE called, state=%u\n",new); */ mp = (struct smtp_msg *)tcb->user; switch(new) { case ESTABLISHED: mp->state = CLI_OPEN_STATE; /* shouldn't be needed */ break; case CLOSE_WAIT: close_tcp(tcb); /* shut things down */ /* may want to do something here to shut down the rest of the transaction? */ break; case CLOSED: del_tcp(tcb); /* hosed for good */ if(mp->filename != NULLCHAR) free(mp->filename); free((char *)mp); break; } } /* Send message back to server */ static void sendit(mp,message) struct smtp_msg *mp; char *message; { struct mbuf *bp,*qdata(); /* printf("SENDIT called: %s",message); */ bp = qdata(message,(int16)strlen(message)); send_tcp(mp->tcb,bp); } SHAR_EOF cat << \SHAR_EOF > tcpout.c #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "tcp.h" int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */ /* Send a segment on the specified connection. One gets sent only * if there is data to be sent or if "force" is non zero */ void tcp_output(tcb) register struct tcb *tcb; { struct pseudo_header ph; struct mbuf *hbp; int16 hsize; /* Size of header */ struct tcp_header *tcph; struct mss *mssp; int16 ssize; /* Size of current segment being sent, * including SYN and FIN flags */ int16 dsize; /* Size of segment less SYN and FIN */ int16 usable; /* Usable window */ int16 sent; /* Sequence count (incl SYN/FIN) already in the pipe */ if(tcb == NULLTCB) return; switch(tcb->state){ case LISTEN: case CLOSED: return; /* Don't send anything */ } for(;;){ sent = tcb->snd.ptr - tcb->snd.una; /* If this is a retransmission, send only the oldest segment * (first-only retransmission policy) */ if(tcb->retry != 0 && sent != 0) break; /* There can only be one outstanding segment in this state * since the other end would reject any segment without SYN */ if(tcb->state == SYN_SENT && sent != 0) break; if(tcb->snd.wnd == 0){ /* Allow only one closed-window probe at a time */ if(sent != 0) break; /* Force a closed-window probe */ usable = 1; } else { /* usable window = offered window - unacked bytes in transit */ usable = tcb->snd.wnd - sent; /* John Nagle's "single outstanding segment" rule. * Allow only one segment in the pipeline unless there is enough * unsent data to form at least one maximum-sized segment. */ if(sent != 0 && tcb->sndcnt - sent < tcb->mss){ usable = 0; } /* Silly window avoidance. Don't send anything if the usable window * is less than a quarter of the offered window. * This test comes into play only when the offered window is at * least 4 times the MSS; otherwise Nagle's test is sufficient * to prevent SWS. */ else if(usable < tcb->snd.wnd/4){ usable = 0; } } /* Compute size of segment to send. This is either the usable * window, the mss, or the amount we have on hand, whichever is less. * (I don't like optimistic windows) */ ssize = min(tcb->sndcnt - sent,usable); ssize = min(ssize,tcb->mss); dsize = ssize; if(ssize == 0 && tcb->force == 0) break; /* No need to send anything */ /* Determine size of TCP header and allocate mbuf. * If sending SYN, allow space for the MSS option */ switch(tcb->state){ case SYN_SENT: case SYN_RECEIVED: hsize = sizeof(struct tcp_header) + sizeof(struct mss); break; default: hsize = sizeof(struct tcp_header); break; } if((hbp = alloc_mbuf(hsize)) == NULLBUF) break; /* No room to form a packet */ tcb->force = 0; /* Only one forced segment! */ hbp->cnt = hsize; tcph = (struct tcp_header *)hbp->data; tcph->source = htons(tcb->conn.local.port); tcph->dest = htons(tcb->conn.remote.port); tcph->offset = hsize/sizeof(int32) << DSHIFT; tcph->flags = 0; /* Set the SYN and ACK flags according to the state we're in. It is * assumed that if this segment is associated with a state transition, * then the state change will already have been made. This allows * this routine to be called from a retransmission timeout with * force=1. * If SYN is being sent, adjust the dsize counter so we'll * try to get the right amount of data off the send queue. */ switch(tcb->state){ case SYN_SENT: case SYN_RECEIVED: if(tcb->snd.ptr == tcb->iss){ tcph->flags = SYN; dsize--; } /* Also send MSS */ mssp = (struct mss *)(tcph + 1); mssp->kind = MSS_KIND; mssp->length = MSS_LENGTH; mssp->mss = htons(tcp_mss); } switch(tcb->state){ case SYN_RECEIVED: case ESTABLISHED: case CLOSE_WAIT: case FINWAIT2: case TIME_WAIT: case CLOSING: case FINWAIT1: case LAST_ACK: tcph->flags |= ACK; break; } tcph->seq = htonl(tcb->snd.ptr); tcph->ack = htonl(tcb->rcv.nxt); tcph->wnd = htons(tcb->rcv.wnd); tcph->checksum = 0; tcph->up = 0; /* Now try to extract some data from the send queue. * Since SYN and FIN occupy sequence space and are reflected * in sndcnt but don't actually sit in the send queue, * dup_p will return one less than dsize if a FIN needs to be sent. */ if(dsize != 0){ if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){ /* We ran past the end of the send queue; send a FIN */ tcph->flags |= FIN; dsize--; } } /* If the entire send queue will now be in the pipe, set the * push flag */ if(dsize != 0 && sent + ssize == tcb->sndcnt) tcph->flags |= PSH; tcb->snd.ptr += ssize; /* If this is the first transmission of a range of sequence * numbers, record it so we'll accept acknowledgments * for it later */ if(seq_gt(tcb->snd.ptr,tcb->snd.nxt)) tcb->snd.nxt = tcb->snd.ptr; /* Fill in fields of pseudo IP header */ ph.source = tcb->conn.local.address; ph.dest = tcb->conn.remote.address; ph.protocol = TCP_PTCL; ph.zero = 0; ph.length = hsize + dsize; /* Compute checksum over pseudo-header, TCP header and data, * and pass it off to IP */ tcph->checksum = cksum(&ph,hbp,ph.length); /* If we're sending some data or flags, start retransmission * timer if it isn't already running. */ if(ssize != 0){ if(tcb->timer.state != TIMER_RUN){ /* Never initialize the timer with zero; it won't run! */ tcb->timer.start = max(tcb->timer.start,1); start_timer(&tcb->timer); } /* If round trip timer isn't running, start it */ if(seq_ge(tcb->snd.una,tcb->rttseq)) tcb->rttseq = tcb->snd.ptr; } ip_send(tcb->conn.local.address,tcb->conn.remote.address, TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0); } } SHAR_EOF cat << \SHAR_EOF > tcpuser.c /* User calls to TCP */ #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "ip.h" #include "tcp.h" int16 tcp_window = DEF_WND; struct tcb * open_tcp(lsocket,fsocket,active,window,r_upcall,t_upcall,s_upcall,tos,user) struct socket *lsocket; /* Local socket */ struct socket *fsocket; /* Remote socket */ int active; /* Active/passive */ int16 window; /* Receive window (and send buffer) sizes */ void (*r_upcall)(); /* Function to call when data arrives */ void (*t_upcall)(); /* Function to call when ok to send more data */ void (*s_upcall)(); /* Function to call when connection state changes */ char tos; int *user; /* User linkage area */ { struct connection conn; register struct tcb *tcb; void send_syn(); if(lsocket == NULLSOCK){ net_error = INVALID; return NULLTCB; } conn.local.address = lsocket->address; conn.local.port = lsocket->port; if(fsocket != NULLSOCK){ conn.remote.address = fsocket->address; conn.remote.port = fsocket->port; } else { conn.remote.address = 0; conn.remote.port = 0; } if((tcb = lookup_tcb(&conn)) == NULLTCB){ if((tcb = create_tcb(&conn)) == NULLTCB){ net_error = NO_SPACE; return NULLTCB; } } else if(tcb->state != LISTEN){ net_error = CON_EXISTS; return NULLTCB; } tcb->user = user; if(window != 0) tcb->window = tcb->rcv.wnd = window; else tcb->window = tcb->rcv.wnd = tcp_window; tcb->r_upcall = r_upcall; tcb->t_upcall = t_upcall; tcb->s_upcall = s_upcall; tcb->tos = tos; if(!active){ setstate(tcb,LISTEN); return tcb; } /* Send SYN, go into SYN_SENT state */ send_syn(tcb); setstate(tcb,SYN_SENT); tcp_output(tcb); tcp_stat.conout++; return tcb; } /* User send routine */ int send_tcp(tcb,bp) register struct tcb *tcb; struct mbuf *bp; { int16 cnt; if(tcb == NULLTCB || bp == NULLBUF){ free_p(bp); net_error = INVALID; return -1; } cnt = len_mbuf(bp); #ifdef TIGHT /* If this would overfill our send queue, reject it entirely */ if(tcb->sndcnt + cnt > tcb->window){ free_p(bp); net_error = WOULDBLK; return -1; } #endif switch(tcb->state){ case CLOSED: free_p(bp); net_error = NO_CONN; return -1; case LISTEN: /* Change state from passive to active */ send_syn(tcb); setstate(tcb,SYN_SENT); /* Note fall-thru */ case SYN_SENT: case SYN_RECEIVED: case ESTABLISHED: case CLOSE_WAIT: append(&tcb->sndq,bp); tcb->sndcnt += cnt; tcp_output(tcb); break; case FINWAIT1: case FINWAIT2: case CLOSING: case LAST_ACK: case TIME_WAIT: free_p(bp); net_error = CON_CLOS; return -1; } return cnt; } /* User receive routine */ int recv_tcp(tcb,bp,cnt) register struct tcb *tcb; struct mbuf **bp; int16 cnt; { if(tcb == NULLTCB || bp == (struct mbuf **)NULL){ net_error = INVALID; return -1; } /* cnt == 0 means "I want it all" */ if(cnt == 0) cnt = tcb->rcvcnt; /* If there's something on the queue, just return it regardless * of the state we're in. */ if(tcb->rcvcnt != 0){ /* See if the user can take all of it */ if(tcb->rcvcnt <= cnt){ cnt = tcb->rcvcnt; *bp = tcb->rcvq; tcb->rcvq = NULLBUF; } else { if((*bp = alloc_mbuf(cnt)) == NULLBUF){ net_error = NO_SPACE; return -1; } pullup(&tcb->rcvq,(*bp)->data,cnt); (*bp)->cnt = cnt; } tcb->rcvcnt -= cnt; tcb->rcv.wnd += cnt; /* Do a window update if it was closed */ if(cnt == tcb->rcv.wnd){ tcb->force = 1; tcp_output(tcb); } return cnt; } else { /* If there's nothing on the queue, our action depends on what state * we're in (i.e., whether or not we're expecting any more data). * If no more data is expected, then simply return 0; this is * interpreted as "end of file". */ switch(tcb->state){ case LISTEN: case SYN_SENT: case SYN_RECEIVED: case ESTABLISHED: case FINWAIT1: case FINWAIT2: *bp = NULLBUF; net_error = WOULDBLK; return -1; case CLOSED: case CLOSE_WAIT: case CLOSING: case LAST_ACK: case TIME_WAIT: *bp = NULLBUF; return 0; } } return 0; /* Not reached, but lint doesn't know that */ } /* This really means "I have no more data to send". It only closes the * connection in one direction, and we can continue to receive data * indefinitely. */ int close_tcp(tcb) register struct tcb *tcb; { if(tcb == NULLTCB){ net_error = INVALID; return -1; } switch(tcb->state){ case LISTEN: case SYN_SENT: close_self(tcb,NORMAL); return 0; case SYN_RECEIVED: case ESTABLISHED: tcb->sndcnt++; tcb->snd.nxt++; setstate(tcb,FINWAIT1); tcp_output(tcb); return 0; case CLOSE_WAIT: tcb->sndcnt++; tcb->snd.nxt++; setstate(tcb,LAST_ACK); tcp_output(tcb); return 0; case FINWAIT1: case FINWAIT2: case CLOSING: case LAST_ACK: case TIME_WAIT: net_error = CON_CLOS; return -1; } return -1; /* "Can't happen" */ } /* Delete TCB, free resources. The user is not notified, even if the TCB is * not in the CLOSED state. This function should normally be called by the * user only in response to a state change upcall to CLOSED state. */ int del_tcp(tcb) register struct tcb *tcb; { void unlink_tcb(); struct reseq *rp,*rp1; if(tcb == NULLTCB){ net_error = INVALID; return -1; } unlink_tcb(tcb); stop_timer(&tcb->timer); for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){ rp1 = rp->next; free_p(rp->bp); free((char *)rp); } tcb->reseq = NULLRESEQ; free_p(tcb->rcvq); free_p(tcb->sndq); free((char *)tcb); return 0; } /* Do printf on a tcp connection */ /*VARARGS*/ tprintf(tcb,message,arg1,arg2) struct tcb *tcb; char *message,*arg1,*arg2; { struct mbuf *bp; int16 len; char *cp,*index(); if(tcb == NULLTCB) return 0; len = strlen(message) + 10; /* fudge factor */ if((cp = index(message,'%')) != NULLCHAR){ /* What a gross hack! */ len += strlen(arg1); if((cp = index(cp+1,'%')) != NULLCHAR){ /* I don't believe I'm writing this */ len += strlen(arg2); } } bp = alloc_mbuf(len); len = sprintf(bp->data,message,arg1,arg2); bp->cnt = strlen(bp->data); send_tcp(tcb,bp); return len; } SHAR_EOF cat << \SHAR_EOF > telnet.c #include <stdio.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" 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 /* 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; (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; { struct mbuf *bp,*qdata(); if(current == NULLSESSION || current->cb.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); } 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.