# #include "util.h" #include "pkts.h" #include "buf.h" #include "telnet.h" #include struct tcb tcb; struct sndcbuf sndcbuf; struct rcvpkt buff[NBUF]; struct stats stats; long curtime; int prflag; FILE *log; int udataf; extern usrint(); #define SIGTINT 18 #define OUTQINT 02 #define CANQINT 04 main(argc, argv) int argc; char *argv[]; { register struct tcb *ptcb; register struct scb *pscb; int awaitim; ptcb = &tcb; pscb = &scb; if (!main_init(argc, argv)) exit(); if (!tcp_passive_open(TELNETICP)) exit(); tel_init(); logerr("Connection to %X open\n", ptcb->t_inet.ip_dest); while (!(ptcb->t_state & CLOSED2)) { time(&curtime); netf = 0; udataf = 0; pkt_rcv(ptcb, pscb); tel_snd(); ch_tcpsnd(ptcb); if ((awaitim = ch_work(ptcb)) > 0) { if ( (netf == 0) && (udataf == 0) ) sysleep(awaitim); disabl_uints(ptcb); } } logerr("Connection to %X closed\n", ptcb->t_inet.ip_dest); dmp_sts(); } /* pkt_rcv manage pkts read from net - do inet and tcp checking, put data on sequenced queue (rseqq) or on out of order queue (rwaitq), and then invoke user rcv routine to consume chars. Process pkts on rwaitq after rcv new, ok pkt because 1) did lots of work on rwaitq pkts, may be able to avoid some work if pkts in kernel net buffer are dupes of what is on rwaitq or 2) may be able to drop some pkts on rwaitq Finally, invoke user rcv routine if it was never invoked; it may now be able to handle new data. */ pkt_rcv(ptcb, pscb) register struct tcb *ptcb; register struct scb *pscb; { register struct rcvpkt *ppkt; int npkts; if (ptcb->t_state & CLOSED2) return; npkts = 0; for (ppkt = rd_pkt(); ppkt != NULL; ppkt = rd_pkt()) { if (!ch_inet(ptcb, ppkt) || !ch_tcp(ptcb, ppkt)) { enque(&frq, ppkt); continue; } ch_later(ptcb); if (pscb->s_state & DRCV) tel_rcv(ptcb); npkts++; } if ((npkts == 0) && (pscb->s_state & DRCV)) tel_rcv(ptcb); } /* ch_tcpsnd chk whether should send a packet. First chk whether should retransmit or quit due to timeout. Three variables manage retransmission - t_sndtime, t_retrans, and t_ntrans. T_retrans is no. seconds to wait before retransmit - this time is computed from record of send times and acktimes (see tt_calc). T_sndtime is filled w. current time and t_ntrans is cleared when t_suna is updated or when data is sent starting at t_suna. T_sndtime is filled w. current time and t_ntrans is incremented when retransmit. If don't retransmit, chk whether should send packet - should send if t_acktime != 0, and/or have some new data or flags to send and have approp. amount of useable send window. T_acktime is set when first decide to send an ACK; it is cleared when a packet is sent (tcp_snd). Decide to send ACK when rcv SYN, FIN, and/or data in sequence (tcp_fin, orig_syn, seq_data). Also decide to send ACK in some error conditions: when rcv ACK for seqno not sent (ch_ack) or when rcv sequence no's. not expecting - i.e. not between t_rnxt and t_rlast (ch_seq). If send new data, enter seqno and sndtime into transmit time record - which is used to compute t_retrans. */ ch_tcpsnd(ptcb) register struct tcb *ptcb; { register int nseq; long seqno; if (ptcb->t_state & CLOSED2) return; if (ptcb->t_suna != ptcb->t_snxt) { if ((curtime - ptcb->t_sndtime) > (ptcb->t_retrans * (ptcb->t_ntrans + 1)) ) { if (ptcb->t_ntrans++ > NTRANS) { logerr("ch_tcpsnd timeout\n"); ptcb->t_state = CLOSED2; return; } ptcb->t_sndtime = curtime; stats.s_resnd++; nseq = sb_noseq(ptcb, ptcb->t_suna); tcp_snd(ptcb, ptcb->t_suna, nseq); return; } } if ( (nseq = sb_noseq(ptcb, ptcb->t_snxt)) || ptcb->t_acktime) { seqno = ptcb->t_snxt; tcp_snd(ptcb, ptcb->t_snxt, nseq); if (seqno != ptcb->t_snxt) { /* some new data was sent */ if (seqno == ptcb->t_suna) { ptcb->t_ntrans = 0; ptcb->t_sndtime = curtime; } if (ptcb->t_ttime.tt_next < ptcb->t_ttime.tt_end) { ptcb->t_ttime.tt_next->tt_seqno = seqno; ptcb->t_ttime.tt_next->tt_sndtime = curtime; ptcb->t_ttime.tt_next++; } } } return; } ch_work(ptcb) register struct tcb *ptcb; { register struct scb *pscb; short mode, nch; if (ptcb->t_state == CLOSED2) return(0); /* Chk whether to close */ pscb = &scb; if ( ((ptcb->t_state & CLOSED) == CLOSED) && ((pscb->s_state & (DRCV|DSND)) == 0) ) { if ((curtime - ptcb->t_fintime) > (4 * ptcb->t_retrans)) { ptcb->t_state = CLOSED2; return(0); } return(5 * ptcb->t_retrans); } /* Chk whether new net pkt to read */ netf = 0; ioctl(net, FIONREAD, &nch); if (nch > 0) netf++; if (netf) return(0); /* Chk whether user level wants to timeout */ if (tel_timeout()) return(0); /* Chk whether space and data to read and/or write to user If not, enable interrupts when data comes from user or space to write to user */ udataf = 0; mode = 0; if ((pscb->s_state & DRCV) && (rseqq.q_items > 0)) mode |= CANQINT; if ((pscb->s_state & DSND) && (tc_nput() > 0)) mode |= OUTQINT; ioctl(pty, PTIOCSETMB, &mode); if (mode & CANQINT) { ioctl(pty, FIONWRITE, &nch); if (nch > 0) udataf++; } if (mode & OUTQINT) { ioctl(pty, FIONREAD, &nch); if (nch > 0) udataf++; } if (udataf) { mode = CANQINT|OUTQINT; ioctl(pty, PTIOCCLRMB, &mode); return(0); } /* No work ready to do See whether to calculate new retransmit time, then calculate sleep time */ tt_calc(ptcb); if (ptcb->t_suna != ptcb->t_snxt) return((ptcb->t_retrans * (ptcb->t_ntrans + 1))); else return(LONGSLEEP); } /* tt_calc when round trip timer vector is full calculate retransmit time - 1) if any times exceed current retransmit time, increase retransmit time by 1 second (retrans time too short or connection dreadful with lots of lost pkts) 2) otherwise retransmit time = avg. round trip time (truncated to nearest second) + 1 second */ tt_calc(ptcb) register struct tcb *ptcb; { register struct ttseq *ttp; register int diff, total, nt; if (ptcb->t_ttime.tt_first < ptcb->t_ttime.tt_end) return; total = 0; nt = 0; for (ttp = ptcb->t_ttime.tt_begin; ttp < ptcb->t_ttime.tt_end; ttp++) { diff = ttp->tt_acktime - ttp->tt_sndtime; if (diff < ptcb->t_retrans) { total += diff; nt++; } } if ((NTT - nt) > 0) ptcb->t_retrans++; else ptcb->t_retrans = total/nt + 1; ptcb->t_ttime.tt_first = ptcb->t_ttime.tt_begin; ptcb->t_ttime.tt_next = ptcb->t_ttime.tt_begin; } /* disabl_uints disable pty interrupts when data space available or when data available */ disabl_uints(ptcb) struct tcb *ptcb; { short mode; mode = CANQINT|OUTQINT; ioctl(pty, PTIOCCLRMB, &mode); } main_init(argc, argv) int argc; char *argv[]; { udataf = 0; signal(SIGTINT, &usrint); net = atoi(argv[1]); log_init(0); if (!que_init()) return(FALSE); return(TRUE); } log_init(flags) int flags; { prflag = PF_ERR|flags; if ((log = fopen(server_log, "a")) == NULL) { log = fopen("/dev/cty","w"); logerr("main_init cannot open %s error %d\n", server_log, errno); } } /* logerr write msg to log file _doprnt is routine invoked by fprintf et al. Call it directly so logerr can accept variable no. of args */ logerr(fmt, args) char *fmt; { if (prflag & PF_ERR) { seek(fileno(log), 0, 2); _doprnt(fmt, &args, log); fflush(log); } } usrint() { udataf++; signal(SIGTINT, &usrint); }