home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GRIPS 2: Government Rast…rocessing Software & Data
/
GRIPS_2.cdr
/
dos
/
ncsa_tel
/
tel_2_2_
/
source
/
user.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-07-15
|
14KB
|
625 lines
/*
* USER.C
* Network library interface routines
* Generally called by the session layer
*
****************************************************************************
* *
* part of: *
* TCP/IP kernel for NCSA Telnet *
* by Tim Krauskopf *
* *
* National Center for Supercomputing Applications *
* 152 Computing Applications Building *
* 605 E. Springfield Ave. *
* Champaign, IL 61820 *
* *
****************************************************************************
* Revisions:
* 10/87 Initial source release, Tim Krauskopf
* 2/88 typedef support for other compilers (TK)
*
*/
#define MASTERDEF 1
#include "stdio.h"
#include "protocol.h"
#include "data.h"
#define LOWWATER 600
/***************************************************************************/
/* netread
* Read from a connection buffer into a user buffer.
* Returns number of bytes read, < 0 on error
* Never blocks, returns 0 when there are no bytes available.
*/
netread(pnum,buffer,n)
int pnum,n;
char *buffer;
{
int howmany,i;
struct port *p;
if (pnum < 0) /* check validity */
return(-2);
if (NULL == (p = portlist[pnum]))
return(-2);
if (p->state != SEST) { /* foreign or netclose */
if (p->state == SCWAIT) { /* ready for me to close my side? */
if (!p->in.contain) {
p->tcpout.t.flags = TFIN | TACK;
tcpsend(p,0);
p->state = SLAST;
return(-1);
}
/* else, still data to be read */
}
else
return(-1);
}
howmany = dequeue(&p->in,buffer,n); /* read from tcp buffer */
i = p->in.size; /* how much before? */
p->in.size += howmany; /* increment leftover room */
if (i < LOWWATER && p->in.size >= LOWWATER) /* we passed mark */
p->out.lasttime = 0L;
if (p->in.contain) /* if still data to be read */
netputuev(CONCLASS,CONDATA,pnum); /* don't forget it */
return(howmany);
}
/************************************************************************/
/* netwrite
* write something into the output queue, netsleep routine will come
* around and send the data, etc.
*
*/
netwrite(pnum,buffer,n)
int pnum,n;
char *buffer;
{
int nsent,before;
struct port *p;
if (pnum < 0)
return(-2);
p = portlist[pnum];
if (p == NULL)
return(-2);
if (p->state != SEST) /* must be established connection */
return(-1);
before = p->out.contain;
nsent = enqueue(&p->out,buffer,n);
if (!before) /* if this is something new, */
p->out.lasttime = 0L; /* cause timeout to be true */
return(nsent);
}
/**************************************************************************/
/* netpush
* attempt to push the rest of the data from the queue
* and then return whether the queue is empty or not (0 = empty)
* returns the number of bytes in the queue.
*/
netpush(pnum)
int pnum;
{
struct port *p;
if (pnum < 0)
return(-2);
if (NULL == (p = portlist[pnum]))
return(-2);
p->out.push = 1;
return(p->out.contain);
}
/**************************************************************************/
/* netqlen
* return the number of bytes waiting to be read from the incoming queue.
*/
netqlen(pnum)
int pnum;
{
if (portlist[pnum] == NULL)
return(-2);
return(portlist[pnum]->in.contain);
}
/**************************************************************************/
/* netroom()
* return how much room is available in output buffer for a connection
*/
netroom(pnum)
int pnum;
{
if (portlist[pnum] == NULL || portlist[pnum]->state != SEST)
return(-2);
return(WINDOWSIZE - portlist[pnum]->out.contain);
}
/**************************************************************************/
/* netsegsize and neterrchange and netsetip and netgetip
*
* set operating parameters to change them from the default values used.
*/
netsegsize(newsize)
int newsize;
{
int i;
i = nnsegsize;
nnsegsize = newsize;
return(i);
}
netquench(newcredit)
int newcredit;
{
int i;
i = nncredit;
nncredit = newcredit;
return(i);
}
netarptime(t) /* dlayer timeout in secs */
int t;
{
nndto = t;
}
netsetip(st)
unsigned char *st;
{
/*
* change all dependent locations relating to the IP number
* don't worry about open connections, they must be closed by higher layer
*/
movebytes(nnipnum,st,4);
movebytes(&arp.d.me,nnipnum,4);
movebytes(&arp.spa,nnipnum,4);
movebytes(&blankip.i.ipsource,nnipnum,4);
movebytes(ulist.tcps.source,nnipnum,4);
movebytes(ulist.udpout.i.ipsource,nnipnum,4);
}
netgetip(st)
unsigned char *st;
{
movebytes(st,nnipnum,4);
}
netsetmask(st)
unsigned char *st;
{
movebytes(nnmask,st,4);
}
netgetmask(st)
unsigned char *st;
{
movebytes(st,nnmask,4);
}
netfromport(port) /* next "open" will use this port */
int16 port;
{
nnfromport = port;
}
/**************************************************************************/
/* netest?
* is a particular session established yet?
* Returns 0 if the connection is in the established state.
*/
netest(pn)
int pn;
{
struct port *p;
if (pn < 0 || pn > NPORTS)
return(-2);
if (NULL == (p = portlist[pn]))
return(-2);
if (p->state == SEST)
return(0);
else if (p->state == SCWAIT) {
if (!p->in.contain) {
p->tcpout.t.flags = TFIN | TACK;
tcpsend(p,0);
p->state = SLAST;
return(-1);
}
else
return(0); /* still more data to be read */
}
return(-1);
}
/**************************************************************************/
/* netlisten
* Listen to a TCP port number and make the connection automatically when
* the SYN packet comes in. The TCP layer will notify the higher layers
* with a CONOPEN event. Save the port number returned to refer to this
* connection.
*
* usage: portnum = netlisten(service);
* int service;
*
*/
netlisten(serv)
uint serv;
{
int pnum;
struct port *prt;
uint16 nn;
pnum = makeport();
if (pnum < 0)
return(-2);
if (NULL == (prt = portlist[pnum]))
return(-2);
prt->in.port = serv;
prt->out.port = 0; /* accept any outside port #*/
prt->in.lasttime = time(NULL); /* set time we started */
prt->state = SLISTEN;
prt->credit = 512; /* default value until changed */
prt->tcpout.i.protocol = PROTTCP;
prt->tcpout.t.source = intswap(serv); /* set service here too */
/*
* install maximum segment size which will be sent out in the first
* ACK-SYN packet
*/
prt->tcpout.x.options[0] = 2;
prt->tcpout.x.options[1] = 4;
/* install maximum segment size */
nn = intswap(nnsegsize);
movebytes(&prt->tcpout.x.options[2],&nn,2);
return(pnum);
}
/***********************************************************************/
/* netgetftp
* Provides the information that ftp needs to open a stream back to the
* originator of the command connection. The other side's IP number
* and the port numbers to be used to calculate the default data connection
* number. Returns values in an integer array for convenient use in
* PORT commands.
*/
netgetftp(a,pnum)
int a[];
int pnum;
{
struct port *p;
uint i;
p = portlist[pnum];
a[0] = p->tcpout.i.ipdest[0];
a[1] = p->tcpout.i.ipdest[1];
a[2] = p->tcpout.i.ipdest[2];
a[3] = p->tcpout.i.ipdest[3];
i = intswap(p->tcpout.t.source);
a[4] = i >> 8;
a[5] = i & 255;
i = intswap(p->tcpout.t.dest);
a[6] = i >> 8;
a[7] = i & 255;
}
/**************************************************************************/
/* netopen
* Netopen is a cheap way to open a connection without looking up any
* machine information. Uses suitable default values for everything.
*/
netopen(s,tport)
unsigned char *s;
uint tport;
{
return(netxopen(s,tport,MINRTO,TSENDSIZE,DEFSEG,DEFWINDOW));
}
/**************************************************************************/
/* netxopen
* Open a network socket for the user.
*
*/
netxopen(machine,service,rto,mtu,mseg,mwin)
uint8 *machine;
uint service,rto,mtu,mseg,mwin; /* unix service port number */
{
struct port *p;
int pnum,ret,i;
uint8 *pc,*hiset;
/*
* check the IP number and don't allow broadcast addresses
*/
if (machine[3] == 255 || !machine[3]) {
nnerror(506);
return(-4);
}
netsleep(0); /* make sure no waiting packets */
pnum = makeport(); /* set up port structure and packets */
if (pnum < 0)
return(-3);
p = portlist[pnum]; /* create a new port */
/*
* make a copy of the ip number that we are trying for
*/
movebytes(p->tcpout.i.ipdest,machine,4);
movebytes(p->tcps.dest,machine,4); /* pseudo header needs it */
/*
* get the hardware address for that host, or use the one for the gateway
* all handled by 'netdlayer' by ARPs.
*/
pc = netdlayer(machine); /* we have ether? */
if (pc == NULL) { /* cannot connect to local machine */
nnerror(504);
return(-2);
}
movebytes(p->tcpout.d.dest,pc,DADDLEN); /* load it up */
/*
* Add in machine specific settings for performance tuning
*/
if (rto > MINRTO)
p->rto = rto; /* starting retrans timeout */
if (mtu < TMAXSIZE) /* largest packet space we have */
p->sendsize = mtu; /* maximum transmit size for that computer */
if (mwin < WINDOWSIZE) /* buffer size is the limit */
p->credit = mwin; /* most data that we can receive at once */
#ifdef MAC
if (nnemac) {
#endif
/*
* quick check to see if someone else is using your IP number
* Some boards receive their own broadcasts and cannot use this check.
* The Mac will only use this check if it is using EtherTalk.
*/
i = cachelook(nnipnum,0,0); /* don't bother to ARP */
if (i >= 0) { /* if it is not -1, we are in trouble */
hiset = (uint8 *)&arpc[i].hrd;
pc = neterrstring(-1);
sprintf(pc,"Conflict with Ethernet hardware address: %2x:%2x:%2x:%2x:%2x:%2x",
hiset[0],hiset[1],hiset[2],hiset[3],hiset[4],hiset[5]);
nnerror(-1);
nnerror(102);
netclose(pnum);
return(-3);
}
#ifdef MAC
}
#endif
/*
* make the connection, if you can, we will get an event notification later
* if it connects. Timeouts must be done at a higher layer.
*/
ret = doconnect(pnum,service,mseg);
return(ret);
}
/**********************************************************************/
doconnect(pnum,service,mseg)
int service,pnum,mseg;
{
uint16 seg;
struct port *p;
p = portlist[pnum];
p->tcpout.i.protocol = PROTTCP; /* this will be TCP socket */
p->tcpout.t.dest = intswap(service); /* for example, telnet=23 */
p->out.port = service; /* service is same as port num*/
p->tcpout.t.flags = TSYN; /* want to start up sequence */
p->tcpout.t.ack = 0; /* beginning has no ACK */
p->state = SSYNS; /* syn sent */
/*
* install maximum segment size which will be sent out in the first
* ACK-SYN packet
*/
p->tcpout.x.options[0] = 2;
p->tcpout.x.options[1] = 4;
/* install maximum segment size */
seg = intswap(mseg);
movebytes(&p->tcpout.x.options[2],&seg,2);
p->tcpout.t.hlen=96; /* include one more word in hdr */
tcpsend(p,4); /* send opening volley */
p->tcpout.t.hlen=80; /* normal hdr len */
/* savenxt = p->out.nxt; */
p->out.nxt += 1; /* ack should be for next byte */
return(pnum); /* do not wait for connect */
}
/*************************************************************************/
/* netopen2
* Send out repeat SYN on a connection which is not open yet
* Checks, and only sends one if needed.
* Returns 1 if the state is still SYNS and 0 if the connection has proceeded.
* The timing is all handled at a higher layer.
*/
netopen2(pnum)
int pnum;
{
struct port *p;
if (pnum < 0 || pnum > NPORTS)
return(-1);
if (NULL == (p = portlist[pnum]))
return(-2);
if (p->state != SSYNS)
return(0); /* done our job */
/*
* The connection has not proceeded to further states, try retransmission
*/
p->out.nxt--;
p->tcpout.t.hlen=96; /* include one more word in hdr */
tcpsend(p,4); /* try sending again */
p->tcpout.t.hlen=80; /* normal hdr len */
p->out.nxt++;
return(1);
}
/**************************************************************************/
/* netclose
* Do appropriate actions to return connection state to SCLOSED which
* enables the memory for that port to be reused.
*/
netclose(pnum)
int pnum;
{
struct port *p;
if (pnum < 0 || pnum > NPORTS) /* is a valid port? */
return(-1);
if ((p = portlist[pnum]) != NULL) { /* something there */
switch (p->state) {
case SLISTEN: /* we don't care anymore */
case SSYNS:
p->state = SCLOSED;
break;
case SEST: /* must initiate close */
/* send FIN */
p->tcpout.t.flags = TACK | TFIN;
tcpsend(p,0);
p->state = SFW1; /* wait for ACK of FIN */
break; /* do nothing for now ?*/
case SCWAIT: /* other side already closed */
p->tcpout.t.flags = TFIN | TACK;
tcpsend(p,0);
p->state = SLAST;
break;
case STWAIT: /* time out yet? */
if (portlist[pnum]->out.lasttime + WAITTIME < time(NULL))
p->state = SCLOSED;
break;
case SLAST: /* five minute time out */
if (portlist[pnum]->out.lasttime + LASTTIME < time(NULL))
p->state = SCLOSED;
break;
default:
break;
}
}
else
return(1);
return(0);
}
/**************************************************************************/
/* netinit
* Calls all of the various initialization routines that set up queueing
* variables, static values, reads configuration files, etc.
*/
netinit()
{
int ret;
/*
* Initializes all buffers and hardware for data link layer.
* Machine/board dependent.
*/
ret = dlayerinit();
if (ret) {
nnerror(101);
return(ret);
}
/*
* initialize the template packets needed for transmission
*/
ret = protinit();
return(ret); /* set up empty packets */
}
/*************************************************************************/
/* netshut
* Close all the connections and turn off the hardware.
*/
netshut()
{
int i;
for (i=0; i < NPORTS ; i++)
if (portlist[i] != NULL)
netclose(i);
netsleep(1);
dlayershut();
}