BBN-V6/telnet/tcpfns.c
#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