#include "globdefs.h" #include "cbuf.h" #include "tcp.h" #include "netconn.h" #define entries(f) (sizeof(f)/sizeof(f[0])) /* * TCP - specific routines */ #define SERVER 1 /* Default port of server */ /* -------------------------- N E T O P E N ------------------------ */ /* * netopen(arg, argc, argv) does a TcpInit if none has been done yet, * then a TcpOpen to the specified host. It then grabs a handy connection * block and copies the connection block it has built up into it. * We do NOT yet allocate a state block for the connection because doing so * also captures the original state of the terminal; until the connection * becomes established, it is not known whether the terminal will be the * controlling terminal of this process or a pty. * At any rate, netopen then enters the connection's * file descriptors in the io queue, providing the NetConn pointer as * the argument. The actual establishing of the connection is done by * the CmdNet handler. */ netopen(arg, argc, argv) int arg; int argc; char *argv[]; { int error; int lport, fport, netnum; int servsw; int sprec, rprec, msecur, asecur; int ntcc, tccnum; int opentype; int i, j; int pre; int host_arg_index; long lnum; char *p, *endp; struct CAB cab; struct UserTcb *utcbp; struct NetConn *np; int useptysw; int exitonclose; char *init[entries(np->Init)]; struct { char l_hoi; char l_network; int l_imp; }; static int DidInit; /* Set when an Init has been done */ extern int errno; #ifdef LOGGING extern int logging; #endif extern struct NetConn *NetConP; extern int NoUsrEOF; extern int localnet; extern CmdNet(), netintr(); extern ToUser(); extern char *atoiv(); extern FromNet(), ToNet(); extern NetCmd(); extern long gethost(); #define LEAVE { if (arg) exit(1); return; } /* Help */ if (argc == 0) { fdprintf(2, "Open a network connection\r\n"); return; } else if (argc == 1) { fdprintf(2, "Usage: %s [host] [options]\r\n", argv[0]); fdprintf(2, "-lp #\r\n-fp #\r\n-ps #\r\n-pr #\r\n-sa #\r\n"); fdprintf(2, "-sm #\r\n-tcc #[,#] ...\r\n"); fdprintf(2, "-open\r\n-listen\r\n-accept\r\n"); fdprintf(2, "-p\r\n-server\r\n-init command ...\r\n"); return; } /* Suspend current connection, if any */ ChgConn('s'); /* Set defaults */ opentype = 0; /* Open */ cab.HostHigh = 0; cab.HostLow = 0; localnet = 10; /* Arpanet (should use sgnet) */ netnum = localnet; lport = 0; fport = SERVER; init[0] = 0; useptysw = 0; exitonclose = 0; rprec = sprec = asecur = msecur = 0; ntcc = 0; servsw = 0; pre = 0; /* Preemption is normally off */ /* Process arguments */ host_arg_index = 0; argc--; for (i = 1; i <= argc; i++) { if (seq(argv[i], "-server")) { lport = SERVER; opentype = 1; fport = 0; netnum = 0; servsw++; } #ifdef LOGGING else if (seq(argv[i], "-log")) { if (i == argc) LogInit("thp.log"); else LogInit(argv[++i]); logging++; } #endif else if (seq(argv[i], "-p")) pre++; else if (seq(argv[i], "-lp")) { if (i == argc || *atoiv(argv[++i], &lport)) { fdprintf(2, "%s: no source port number\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-fp")) { if (i == argc || *atoiv(argv[++i], &fport)) { fdprintf(2, "%s: no destination port number\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-ps")) { if (i == argc || *atoiv(argv[++i], &sprec)) { fdprintf(2, "%s: No send precedence specified\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-pr")) { if (i == argc || *atoiv(argv[++i], &rprec)) { fdprintf(2, "%s: No receive precedence specified\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-sa")) { if (i == argc || *atoiv(argv[++i], &asecur)) { fdprintf(2, "%s: No absolute security specified\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-sm")) { if (i == argc || *atoiv(argv[++i], &msecur)) { fdprintf(2, "%s: No maximum security specified\r\n", argv[0]); LEAVE; } } else if (seq(argv[i], "-tcc")) { if (i == argc) { fdprintf(2, "%s: No TCC list\r\n", argv[0]); LEAVE; } i++; for (p = argv[i], ntcc = 0; ; p = endp + 1, ntcc++) { if (ntcc >= entries(cab.TCC)) { fdprintf(2, "%s: Too many TCCs. Maximum is %d\r\n", argv[0], entries(cab.TCC)); LEAVE; } endp = atoiv(p, &tccnum); if (*endp != '\0' && *endp != ',') { fdprintf(2, "%s: Bad TCC list\r\n", argv[0]); LEAVE; } cab.TCC[ntcc] = tccnum; if (*endp == '\0') break; } ntcc++; } else if (seq(argv[i], "-open")) opentype = 0; /* Open */ else if (seq(argv[i], "-listen")) { opentype = 1; /* Listen */ fport = 0; netnum = 0; } else if (seq(argv[i], "-accept")) opentype = 2; /* Accept */ else if (seq(argv[i], "-init")) { i++; j = 0; init[0] = 0; if (i > argc) fdprintf(2, "%s: no args after -init\r\n", argv[0]); else if (argc - i + 1 > entries(init)) fdprintf(2, "%s: too many args after -init. %d max\r\n", argv[0], entries(init)); else for (; i <= argc && j < entries(init); i++, j++) scopy(argv[i], init[j] = alloc(slength(argv[i])+1), -1); init[j] = 0; /* Terminate command list with 0 */ if (j == 0) LEAVE; } else if (argv[i][0] == '-') { fdprintf(2, "%s: unknown flag '%s'\r\n", argv[0], argv[i]); LEAVE; } else if (host_arg_index == 0) host_arg_index = i; else { fdprintf(2, "%s: extraneous argument '%s'\r\n", argv[0], argv[i]); LEAVE; } } if (host_arg_index != 0) { lnum = gethost(argv[host_arg_index]); if (lnum <= 0) { fdprintf(2, "Bad host specifier: %s\n", argv[host_arg_index]); LEAVE; } netnum = lnum.l_network; cab.HostHigh = lnum.l_hoi; cab.HostLow = lnum.l_imp; } /* Call up TCP */ if (DidInit == 0) if (error = TcpInit(40)) fatal(0, "TcpInit failed. %s", TcpErr(error)); /* Initialize the CAB */ cab.Network = netnum; #ifdef NeverDefined cab.HostLow = hostnum & 077; /* Low 6 bits are imp */ cab.HostHigh = (hostnum & 0300) >> 6; /* High 2 bits are host */ #endif cab.SrcPort = lport; cab.DstPort = fport; cab.PrecSend = 0; if (sprec) cab.PrecSend = sprec|0200; cab.PrecRecv = 0; if (rprec) cab.PrecRecv = rprec|0200; cab.SecurAbs = 0; if (asecur) cab.SecurAbs = asecur|0200; cab.SecurMax = 0; if (msecur) cab.SecurMax = msecur|0200; cab.TccCnt = ntcc; /* Ready to open */ if ((error = TcpOpen(&cab, &utcbp, opentype, 40))) { com_err(0, "TcpOpen failed. %s", TcpErr(error)); LEAVE; } /* It seems to have worked */ NetConP = AllocConn(); if (NetConP == -1) { com_err(0, "No more room for connections"); LEAVE; } NetConP->UsePtySw = useptysw; NetConP->ExitOnClose = exitonclose; for (i = 0; i < entries(init); i++) NetConP->Init[i] = init[i]; NetConP->SendSecur = asecur? asecur : msecur; if (host_arg_index != 0) ChgConn('e', NetConP, argv[host_arg_index]); else ChgConn('e', NetConP, 0); NetConP->UtcbP = utcbp; /* Chain the UTCB back to our connection block (for CmdNet) */ utcbp->UInfo1 = NetConP; if (DidInit == 0) { IoxEnter(&CmdNet, 0); /* One serves for all connections */ awtenb(utcbp->CmdFds); /* Be sure to awaken on that fd */ } DidInit = 1; if (pre && (cab.PrecSend&0177) < CAT_I && (cab.PrecRecv&0177) < CAT_I) { cab.Network = 0; cab.HostHigh = 0; cab.HostLow = 0; cab.SrcPort = 1000 + (getuid() & 0377); cab.DstPort = 0; #define IncPrec(prec) (((prec & 0177) + 1) | 0200) cab.PrecSend = IncPrec(cab.PrecSend); cab.PrecRecv = IncPrec(cab.PrecRecv); if (error = TcpOpen(&cab, &utcbp, 1 /*LISTEN*/, 20)) com_err(0, "Preemption open failed. %s", TcpErr(error)); else { fdprintf(2, "Listening on %d\r\n", cab.SrcPort); np = AllocConn(); np->UsePtySw = useptysw; np->ExitOnClose = exitonclose; for (i = 0; i < entries(init); i++) np->Init[i] = init[i]; np->SendSecur = asecur? asecur : msecur; ChgConn('e', np, "preempt"); np->UtcbP = utcbp; np->StateP = AllocSB(); IoxEnter(&NetCmd, np); /* Chain the UTCB back to our connection block (for CmdNet) */ utcbp->UInfo1 = np; IoEnter(utcbp->SendFds, 0, &ToNet, np); IoEnter(utcbp->RecvFds, &FromNet, 0, np); } } fdprintf(2, "Trying...\r\n"); if (servsw) { signal(1, 1); signal(2, 1); signal(3, 1); NetConP->ExitOnClose = 1; NetConP->UsePtySw = 1; NoUsrEOF = 1; } else { signal(2, &netintr); IoEnter(1, 0, &ToUser, 0); } } /* -------------------------- C M D N E T --------------------------- */ /* * CmdNet(arg) is called when there is activity on th read side * of the TCP command port. It calls TcpCmd to find out what happened. * If the connection is just starting up (ESTAB or SYNRECD) perform * initialization sequence, including allocating a state block for it * if necessary. */ CmdNet(junk) int junk; /* Since CmdNet services all connections, this is meaningless */ { int error; int num, i; int c; struct UserTcb *utcbp; struct NetConn *connp; struct CmdBlk Cmdbuf; extern int verbose; /* For urgent notice */ extern FromNet(), ToNet(); extern NetCmd(); extern struct NetConn *NetConP; extern struct NetConn *PreP; /* Get the command (if any) */ Cmdbuf.CNFlags = 0; /* Clear flags first -- tcplib bug */ error = TcpCmd(&Cmdbuf); if (error) { if (error != ENOCHNG) com_err(0, "TcpCmd failed. %s", TcpErr(error)); if (error == ECMDCLS) fatal(0, "TCP died. Exiting."); return(1); } utcbp = Cmdbuf.XPtr; connp = utcbp->UInfo1; num = utcbp->ConnIndex; if (Cmdbuf.CFlags & FSTATECHANGE) { switch(Cmdbuf.NewState) { case ESTAB: case SYNRECD: /* Connection is for real now */ if (connp->Type == PREEMPT) { if (Cmdbuf.NewState == ESTAB) fdprintf(2, "Preempting connection established\r\n"); PreP = connp; } else if (Cmdbuf.NewState == ESTAB) fdprintf(2, "Established\r\n"); else /* SYNRECD */ if (verbose) fdprintf(2, "Syn Received\r\n"); if (connp->UsePtySw) usepty(connp); connp->UsePtySw = 0; if (connp->StateP == 0) { connp->StateP = AllocSB(); if (connp == NetConP) ChgSB(connp->StateP); IoEnter(utcbp->SendFds, 0, &ToNet, connp); IoEnter(utcbp->RecvFds, &FromNet, 0, connp); IoxEnter(&NetCmd, connp); } if (connp->Init[0]) /* Execute initial command sequence */ for (i = 0; connp->Init[i] != 0; i++) ExecCmd(connp->Init[i], slength(connp->Init[i])); connp->Init[0] = 0; /* Don't execute it any more... */ break; case CLOSED: /* We have both agreed it's closed */ fdprintf(2, "Closed\r\n"); netabort(connp, 1, 0); /* Abort connection */ break; case CLOSEWAIT: /* Other side did TcpClose */ fdprintf(2, "Foreign process closed\r\n"); llclose(connp); break; default: com_err(0, "%d: new state %d", num, Cmdbuf.NewState); break; } Cmdbuf.CFlags =& ~FSTATECHANGE; } if (Cmdbuf.CFlags & FURGENT) /* Urgent information */ { if (verbose) fdprintf(2, "Urgent count %s\r\n", locv(utcbp->UrgCount)); BeginSynch(connp); /* Take appropriate action */ Cmdbuf.CFlags =& ~FURGENT; } if (Cmdbuf.CFlags & FDEAD) /* Foreign process deaf */ { if (verbose) fdprintf(2, "Foreign process not responding...\r\n"); c = utcbp->CNState; if (c != ESTAB && c != CLOSEWAIT) { fdprintf(2, "Foreign process not responding, aborting\r\n"); netabort(connp, 1, 0); } Cmdbuf.CFlags =& ~FDEAD; } if (Cmdbuf.CFlags & FALIVE) /* Foreign process regained hearing */ { if (verbose) fdprintf(2, "Foreign process responding again\r\n"); Cmdbuf.CFlags =& ~FALIVE; } if (Cmdbuf.CFlags & FNETMSG) /* Net information message */ { if (Cmdbuf.NetMsg & NNOHOST) fdprintf(2, "Host not responding\r\n"); if (Cmdbuf.NetMsg & 0177776) fdprintf(2, "Net Message = 0%o\r\n", Cmdbuf.NetMsg & 0177776); if (Cmdbuf.NetMsg == 0) fdprintf(2, "Empty net message\r\n"); Cmdbuf.CFlags =& ~FNETMSG; } if (Cmdbuf.CFlags & FRESET) /* Reset */ { fdprintf(2, "Connection reset\r\n"); Cmdbuf.CFlags =& ~FRESET; } if (Cmdbuf.CFlags & FREJECT) /* Rejecting */ { fdprintf(2, "Foreign TCP rejecting...\r\n"); Cmdbuf.CFlags =& ~FREJECT; } if (Cmdbuf.CFlags & FSECTOOHIGH) { fdprintf(2, "Security level out of range\r\n"); Cmdbuf.CFlags =& ~FSECTOOHIGH; } if (Cmdbuf.CFlags & FPRECCHNG) { fdprintf(2, "Send precedence raised to %d\r\n", Cmdbuf.NewPrec); Cmdbuf.CFlags =& ~FPRECCHNG; } if (Cmdbuf.CFlags) { fdprintf(2, "Flags 0%o ignored", Cmdbuf.CFlags); Cmdbuf.CFlags = 0; } return(0); } /* -------------------------- T O N E T ----------------------------- */ /* * ToNet(fd, cap, connp) is called whenever there is capacity on the write side * of the SendFds. It calls TcpSend to do the actual work. * If there is no data to send, it checks for EOF, and does an llclose if it * is set. */ ToNet(fd, cap, connp) int fd; int cap; struct NetConn *connp; { int error; struct UserTcb *utcbp; char buffer[CBUFSIZE]; /* So it can't overflow */ register char *bp; register char *ubp; #ifdef LOGGING extern int logging; #endif if (Cempty(connp->ToNetBuf)) if (connp->ToNetEOF == 1) { llclose(connp); connp->ToNetEOF = 2; /* Dead! */ return(0); } else return(1); /* Nothing to send */ if (connp->ToNetEOF == 2) /* Connection dead, don't send */ return(1); bp = buffer; while (!Cempty(connp->ToNetBuf)) *bp++ = Cgetc(connp->ToNetBuf); #ifdef LOGGING if (logging) Log(1, "r", &buffer, bp - &buffer, -1); #endif utcbp = connp->UtcbP; if (connp->OutSynCnt) /* Urgent data */ if (error = TcpSend(utcbp, buffer, connp->OutSynCnt, connp->SendSecur, 1/*EOL*/, 1/*URG*/)) { if (error != ENOSPC) com_err(0, "%d TcpSend failed. %s", utcbp->ConnIndex, TcpErr(error)); for (ubp = buffer; ubp < bp; ubp++) Cputc(*ubp, connp->ToNetBuf); return(1); /* Try again some other time */ } ubp = buffer + connp->OutSynCnt; connp->OutSynCnt = 0; /* All urgent stuff delivered */ if (ubp < bp) /* Anything not so urgent? */ if (error = TcpSend(utcbp, ubp, bp - ubp, connp->SendSecur, 1/*EOL*/, 0/*Not URG*/)) { if (error != ENOSPC) com_err(0, "%d TcpSend failed. %s", utcbp->ConnIndex, TcpErr(error)); for (; ubp < bp; ubp++) Cputc(*ubp, connp->ToNetBuf); return(1); } return(0); } /* -------------------------- F R O M N E T ------------------------- */ /* * FromNet(fd, cap, connp) is called when there is capacity on the read side of * the RcvFds. Data obtained from TcpReceive is put into FmNetBuf. */ FromNet(fd, cap, aconnp) int fd; int cap; struct NetConn *aconnp; { register char *p; register struct NetConn *connp; int i; int error; int capbuf[2]; /* for The Last Capacity */ int nread, nreq; int UrgFlag; char fnbuffer[CBUFSIZE]; #ifdef LOGGING extern int logging; #endif connp = aconnp; /* Compare eyes to stomach and adjust accordingly */ if (connp->InSynCnt <= 0) nreq = CBUFSIZE - Clen(connp->FmNetBuf); else nreq = CBUFSIZE; if (nreq == 0) return(1); error = TcpReceive(connp->UtcbP, fnbuffer, nreq, &nread, &UrgFlag); #ifdef LOGGING if (logging) Log(2, "r", &fnbuffer, nread, -1); #endif if (error) { com_err(0, "%d TcpReceive failed. %s", connp->UtcbP->ConnIndex, TcpErr(error)); return; } if (UrgFlag) BeginSynch(connp); for (p = fnbuffer; nread > 0; nread--, p++) Cputc(*p & 0377, connp->FmNetBuf); /* * If we are in the CLOSED state, all that was left to do was read in * any data left in the receive port. So if the receive port is empty, * it is safe to abort the connection. * * The library really ought to save up the CLOSEWAIT state change until * the port has been emptied. */ if (connp->CNState == CLOSED) if (capac(fd, capbuf, sizeof(capbuf)) == -1 || capbuf[0] == 0) netabort(connp, 1, 0); return(0); } /* -------------------------- S E N D U R G ------------------------- */ /* * SendUrg(connp) marks the outgoing data as urgent. It should be called AFTER * the appropriate op (e.g. IP) has been sent. All the data in the ToNetBuf * is marked as urgent. */ SendUrg(connp) struct NetConn *connp; { connp->OutSynCnt = Clen(connp->ToNetBuf); } /* -------------------------- B E G I N S Y N C H ------------------- */ /* * BeginSynch() is called at the start of urgent data. It flushes pending * user data and notes that a datamark will be * along. Until it comes along, user-directed data should be flushed. * It also prints a bell on the error output. */ BeginSynch(connp) struct NetConn *connp; { connp->InSynCnt = 1; Cinit(connp->ToUsrBuf); write(2, "\007", 1); } /* -------------------------- L L C L O S E ----------------------- */ /* * Close a TCP connection. All we do here is call TcpClose; eventually the * change in status will be seen by the CmdNet handler, which will do the actual * cleanup. */ llclose(connp) struct NetConn *connp; { struct UserTcb *utcbp; int error; utcbp = connp->UtcbP; if (error = TcpClose(utcbp, 10)) com_err(0, "%d TcpClose failed. %s", utcbp->ConnIndex, TcpErr(error)); else fdprintf(2, "Closing...\r\n"); } /* -------------------------- N E T A B O R T ----------------------- */ /* * Abort a TCP connection. Call TcpAbort, free up allocated resources. * If ExitOnClose, exit. */ netabort(arg, argc, argv) int arg; int argc; char *argv[]; { int error, num; int exsw; struct NetConn *connp; struct UserTcb *utcbp; extern struct NetConn *NetConP; #ifdef LOGGING extern int logging; #endif if (arg != 0) connp = arg; else connp = NetConP; utcbp = connp->UtcbP; if (argc == 0) { fdprintf(2, "Abort the current network connection\r\n"); return; } if (argc != 1) { fdprintf(2, "netabort takes no arguments\r\n"); return; } if (connp == 0) /* Connection we're supposed to close doesn't exist */ { fdprintf(2, "No connection\r\n"); return; } num = utcbp->ConnIndex; if (error = TcpAbort(utcbp, 10)) com_err(0, "TcpAbort(%d) failed. %s", num, TcpErr(error)); else if (arg == 0) /* User command, print message */ fdprintf(2, "Aborted\r\n"); connp->UtcbP = 0; /* So no one tries to use it (esp. CmdNet) */ if (connp->StateP) { IoDelete(utcbp->SendFds); IoDelete(utcbp->RecvFds); if (connp == NetConP) ChgMode(OrigMode()); FreeSB(connp->StateP); connp->StateP = 0; } if (connp == NetConP) /* Current connection -- zero it */ NetConP = 0; exsw = connp->ExitOnClose; ChgConn('d', connp); free(connp); signal(2, 0); /* Interrupts kill once again */ if (exsw) { #ifdef LOGGING if (logging) LogEnd(); #endif exit(0); } return; } /* -------------------------- Q U I T ------------------------------- */ /* * Quit. For TCP, this means closing the current connection and remembering * to exit when the red tape is done. If the first argument is nonzero, abort * instead. */ quit(arg, argc, argv) int arg; int argc; char *argv[]; { extern struct NetConn *NetConP; extern int logging; if (argc == 0) fdprintf(2, "Close connection and return to command level\r\n"); else if (argc != 1) fdprintf(2, "I take no arguments\r\n"); else if (NetConP) { NetConP->ExitOnClose = 1; if (arg) netabort(0, 1, 0); else netclose(0, 1, 0); } else { #ifdef LOGGING if (logging) LogEnd(); #endif exit(0); } } /* -------------------------- S E T S E C U R ----------------------- */ /* * SetSecur() is called from the command processor to process the "security" * command. It just sets SendSecur to the specified value. */ SetSecur(arg, argc, argv) int arg; int argc; char *argv[]; { int temp; extern struct NetConn *NetConP; extern char *atoiv(); if (argc == 0) { fdprintf(2, "Set security level to the specified numeric value\r\n"); return; } if (argc != 2) { fdprintf(2, "Usage: %s security-level\r\n", argv[0]); return; } if (*atoiv(argv[1], &temp)) { fdprintf(2, "%s: security level non-numeric\r\n", argv[0]); return; } NetConP->SendSecur = temp; return; } /* ----------------------- M O V E ----------------------------- */ /* * move() implements the ^move command. It just calls TcpMove. */ move(arg, argc, argv) int arg; int argc; char *argv[]; { int pid; int error; extern struct NetConn *NetConP; char *atoiv(); if (argc == 0) /* Help */ { fdprintf(2, "Move current connection to specified process\r\n"); return; } if (argc != 2) /* Usage */ { fdprintf(2, "Usage: %s process-id\r\n", argv[0]); return; } if (*atoiv(argv[1], &pid)) { fdprintf(2, "%s: Non-numeric process-id\r\n", argv[0]); return; } if (NetConP == 0) { fdprintf(2, "%s: No connection to move.\r\n", argv[0]); return; } if (error = TcpMove(NetConP->UtcbP, pid, 10)) com_err(0, "TcpMove(%d) failed. %s", NetConP->UtcbP->ConnIndex, TcpErr(error)); return; } /* -------------------------- S T A T U S --------------------------- */ /* * Call TcpStatus on the current connection. Print out the info in a nice * format. */ Status(junk, argc, argv) int junk; int argc; char *argv[]; { int i; int error; struct ConnStat cs; extern struct NetConn *NetConP; if (argc == 0) fdprintf(2, "Print status of current connection\r\n"); else if (argc != 1) fdprintf(2, "%s: No arguments expected.\r\n", argv[0]); else if (NetConP == 0) fdprintf(2, "%s: No current connection.\r\n", argv[0]); else if (error = TcpStatus(NetConP->UtcbP, &cs, 10)) com_err(0, "%s: TcpStatus failed. %s.", argv[0], TcpErr(error)); else { fdprintf(2, "State of connection: %d\r\n", cs.CState); fdprintf(2, "Foreign net: 0%o\r\n", cs.CNet&0377); fdprintf(2, "Foreign host: 0%o\r\n", cs.CHost&0377); fdprintf(2, "Foreign imp: 0%o\r\n", cs.CImp); fdprintf(2, "Local port: %d\r\n", cs.CLocPrt); fdprintf(2, "Foreign port: %d\r\n", cs.CForPrt); fdprintf(2, "Max security to net: %d\r\n", cs.CScMxOt&0377); fdprintf(2, "Min security to net: %d\r\n", cs.CScMnOt&0377); fdprintf(2, "Max security from net: %d\r\n", cs.CScMxIn&0377); fdprintf(2, "Min security from net: %d\r\n", cs.CScMnIn&0377); fdprintf(2, "Send precedence: %d\r\n", cs.CSndPrec&0377); fdprintf(2, "Recv precedence: %d\r\n", cs.CRcvPrec&0377); for (i = 0; i < cs.CNTcc; i++) fdprintf(2, "TCC[%d]: %d\r\n", i, cs.CTcc[i]); fdprintf(2, "Send window: %d\r\n", cs.CSndWind); fdprintf(2, "Receive window: %d\r\n", cs.CRcvWind); fdprintf(2, "# bytes awaiting ack: %d\r\n", cs.CAckData); fdprintf(2, "Data in send buffer: %d\r\n", cs.CSndBuff); fdprintf(2, "Data in receive buffer: %d\r\n", cs.CRecBuff); fdprintf(2, "# of segment received: %d\r\n", cs.CSegRecv); fdprintf(2, "# segments received with dup data: %d\r\n", cs.CDupSeg); fdprintf(2, "# discarded segments: %d\r\n", cs.CBusyRecv); fdprintf(2, "# retransmitted segments: %d\r\n", cs.CRetran); } } /* -------------------------- S T O P G O ------------------------------- */ /* * StopGo() expects either a 0 (stop) or 1 (resume) as its arg. The supplied * text arguments should be either -tcc <tccnum> or a host/net specifier * as in the netopen call. */ StopGo(arg, argc, argv) int arg; int argc; char *argv[]; { int i; int tcc; int error; int type; struct { int l_high; int l_low; }; long host; long id; extern char *(atoiv()); extern long gethost(); if (argc == 0) { if (arg == 0) fdprintf(2, "Stop communications with specified host or tcc\r\n"); else fdprintf(2, "Resume communications with specified host or tcc\r\n"); return; } if (argc == 1) { fdprintf(2, "Usage:\t%s -tcc <tcc>\r\n\t%s host\r\n", argv[0], argv[0]); return; } argc--; tcc = -1; host = 0; for (i = 1; i <= argc; i++) { if (seq(argv[i], "-tcc")) { if (i == argc || *atoiv(argv[++i], &tcc) || tcc < 0) { fdprintf(2, "%s: Bad or missing TCC.\r\n", argv[0]); return; } id.l_high = 0; id.l_low = tcc; type = 2; } else { host = gethost(argv[++i]); if (host <= 0) { fdprintf(2, "%s: Bad host specifier: \"%s\"\r\n", argv[0], argv[i]); return; } id = host; type = 1; } } if (tcc != -1 && host != 0) { fdprintf(2, "%s: Cannot specify both host and TCC.\r\n", argv[0]); return; } if (arg == 0) error = TcpStop(type, &id, 10); else error = TcpResume(type, &id, 10); if (error) fdprintf(2, "%s: %s failed. %s.\r\n", argv[0], arg==0? "TcpStop" : "TcpResume", TcpErr(error)); } /* -------------------------- G L O B A L S ------------------------- */ /* * Globals used within this file */ #ifdef LOGGING int logging 0; /* Flag set by -log argument to netopen. */ #endif