Metropoli BBS
VIEWER: user.c MODE: TEXT (ASCII)
/*
*  USER.C
*  Network library interface routines
*  Generally called by the session layer
*
*	User interface routines 
*
*****************************************************************************
*																			*
*	  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													*
*																			*
*     This program is in the public domain.                                 *
*                                                                           *
****************************************************************************
*  Revisions:
***************************************************************************
*
*	Revision history:
*
*	10/87  Initial source release, Tim Krauskopf
*	2/88  typedef support for other compilers (TK)
*	5/88	clean up for 2.3 release, JKM	
*
*/

#define MASTERDEF 1								/* add user variables */

/*#define DEBUG*/
#include "debug.h"

/*
 *	INCLUDES
 */

#include <stdio.h>
#include <stdarg.h>
#include "protocol.h"
#include "data.h"
#include "externs.h"
#include "defines.h"

#define LOWWATER 600

/***************************************************************************/
/*
*	netread ( pnum, buffer, n )
*
*	Read data from the connection buffer (specified by *pnum*) into the
*	user buffer for up to *n* bytes.
*
*	Returns number of bytes read, or<0 if error.  Does not block.
*
*/
int netread(int pnum,char *buffer,int n)
{
	int howmany,i,lowwater;
	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);
      } /* end if */
    /* else, still data to be read */
    } /* end if */
		else
			return(-1);
  } /* end if */

	howmany=dequeue(&p->in,buffer,n);			/* read from tcp buffer */
  i=p->in.size;                         /* how much before? */
  p->in.size+=howmany;                  /* increment leftover room  */

	lowwater=p->credit>>1;
  if((i<lowwater) && ((p->in.size)>=(uint)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);
}

#ifndef NET14
/**********************************************************************
*  Function :   netprintf
*  Purpose  :   Write printf()-like formatted data into an output queue.
*                   uses netwrite to actually get the data out.
*  Parameters	:
*           pnum - the port to write the data out to.
*           fmt - character string specifying the format
*  Returns  :   the number of bytes sent, or <0 if an error
*  Calls    :   netwrite()
*  Called by    :   all over...
**********************************************************************/
int netprintf(int pnum,char *fmt,...)
{
    char temp_str[256];         /* this may be a problem, if the string is too long */
	va_list arg_ptr;
    int str_len;

	va_start(arg_ptr,fmt);		/* get a pointer to the variable arguement list */
    str_len=vsprintf(temp_str,fmt,arg_ptr); /* print the formatted string into a buffer */
	va_end(arg_ptr);
    if(str_len>0)      /* good string to transmit */
        return(netwrite(pnum,temp_str,str_len));
    else
        return(-3);
}   /* end netprintf() */
#endif

/************************************************************************/
/*
*	netwrite ( pnum, buffer, n )
*	Write data into the output queue (specified by *pnum*).  netsleep will
* come around and distribute the data onto the wire later.
*
*	Returns number of bytes sent or<0 if an error.
*
*/
int
#if defined(NET14)
CDECL
#endif
netwrite(int pnum,char *buffer,int n)
{
	int nsent,before;
	struct port *p;

	if(pnum<0)
		return(-2);

	if((p=portlist[pnum])==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 */
		p->out.push=1;			/* set the push flag */
  } /* end if */

	return(nsent);
}

/**************************************************************************/
/*
*	netpush ( pnum )
*
*	Sets the push bit on the specified port data, and returns whether the
* queue is empty or how many bytes in the queue. <0 if an error.
*
*/
int netpush(int pnum)
{
	struct port *p;

	if(pnum<0)
		return(-2);

	if(NULL==(p=portlist[pnum]))
		return(-2);

	p->out.push=1;
	return((int)p->out.contain);
}	

#ifndef NET14
/**************************************************************************/
/*
*	netqlen ( pnum )
*
*	Returns the number of bytes waiting to be read from the incoming queue
* or<0 if error.
*
*/
int netqlen(int pnum)
{
	if(portlist[pnum]==NULL)
		return(-2);

	return((int)portlist[pnum]->in.contain);
}

/**************************************************************************/
/*
*	netroom ( pnum )
*
*	Returns how many bytes left in the output buffer for port *pnum*. <0 if
* error.
*
*/
int netroom(int pnum)
{
	if(portlist[pnum]==NULL || portlist[pnum]->state!=SEST)
		return(-2);

	return((int)(WINDOWSIZE-portlist[pnum]->out.contain));
}

/**************************************************************************/
/*
*	netsegsize ( newsize )
*	Set the segment size.
*
*	Returns the old size.
*
*/
int netsegsize(int newsize)
{
	int i;

	i=nnsegsize;
	nnsegsize=newsize;
	return(i);
}
#endif

/*
*	netarptime ( t )
*
*	Set the dlayer timeout ( in seconds )
*
*/
void netarptime(int t)                  /* dlayer timeout in secs */
{
	nndto=t;
}

/*
*	netsetip ( st )
*
*	Set a new ip number.  This goes through and changes all sorts of ip
* numbers.
*
*	This routine assumes that there are currently NO open tcp connections.
*
*/
void netsetip(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);						/* main ip number */
	movebytes(arp.spa,nnipnum,4);					/* arp */
	movebytes(blankip.i.ipsource,nnipnum,4);		/* ip source */
    movebytes(udplist[DNRINDEX]->tcps.source,nnipnum,4);         /* udp source - eej */
    movebytes(udplist[DNRINDEX]->udpout.i.ipsource,nnipnum,4);   /* more udp source - eej */
}

/*
*	netgetip ( st )
*
*	Sets *st* to the current up number 
*
*/
void netgetip(unsigned char *st)
{
	movebytes(st,nnipnum,4);
}

/*
*	netsetbroad ( st )
*
*	Set the network broadcast IP address.
*
*/
void netsetbroad(unsigned char *st)
{
	movebytes(broadip,st,4);
}

/*
*	netsetmask ( st )
*
*	Set the network mask.
*
*/
void netsetmask(unsigned char *st)
{
	movebytes(nnmask,st,4);
}

/*
*	netgetmask ( st )
*
*	Get the network mask.
*
*/
void netgetmask(unsigned char *st)
{
	movebytes(st,nnmask,4);
}


#if !defined NET14 || defined NETSPACE || defined WIN   /* rmg  hahaha! */
/*
*	netfromport ( port )
*
*	This sets the port that the next open will use to be *port*
*
*/
void netfromport(int16 port)
{
	nnfromport=port;
}

/**************************************************************************/
/*
*	netest ( pn ) 
*
*	Checks to see if a particular session has been established yet.
*
*	Returns 0 if the connection is in "established" state.
*
*/
int netest(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);
			  }	/* end if */
			else 
				return(0);				/* still more data to be read */
		  }	/* end if */
	return(-1);
}

/**************************************************************************/
/*
*	netlisten ( serv )
*
*   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.
*
*	example usage : portnum=netlisten ( service )
*
*	Returns<0 if error 
*
*/
int netlisten(uint serv)
{
	int	pnum;
	struct port *prt;
	uint16 nn;

	if((pnum=makeport())<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=n_clicks();            /* 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((char *)&prt->tcpout.x.options[2],(char *)&nn,2);
	return(pnum);
}

/***********************************************************************/
/*
*	netgetftp ( a, pnum )
*
*	This routine provides the information needed to open an ftp connection
* back to the originatior of the command connection.  The other side's IP
* number and the port numbers are returned in an INTEGER array ( convenient
* for use in PORT commands ).
*
*/
void netgetftp(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;
}
#endif

/**************************************************************************/
/*
*	netxopen ( machine, service, rto, mtu, mseg, mwin )
*
*	Open a network socket for the user to *machine* using port *service*.
* The rest of the parameters are self-explanatory.
*
*/
int netxopen(uint8 *machine,uint service,uint rto,uint mtu,uint mseg,uint mwin)
{
	struct port *p;
	int pnum,ret,i;
	uint8 *pc,*hiset;

/*
*  check the IP number and don't allow broadcast addresses
*/
#ifdef OLD_WAY
	if(machine[3]==255 || !machine[3]) {
		nnerror(506);
		return(-4);
	  }
#else
    if(machine[3]==255) {
		nnerror(506);
		return(-4);
	  }
#endif

	netsleep(0);					/* make sure no waiting packets */

	if((pnum=makeport())<0)			/* set up port structure and packets */
		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 OLD_WAY
	if(nnemac) {
/*
*   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);
		  }
	  }
#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 )
*
*	This routine sends the actual packet out to try and establish a
* connection.
*
*/
doconnect(int pnum,int service,int 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((char *)&p->tcpout.x.options[2],(char *)&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 ( pnum )
*
*	Send out repeat SYN on a connection which is not open yet.  This checks
* to make sure if it is actually needed.  Timing is handled at a higher
* layer.
*
*	Returns 1 if the state is still at SYNS other wise 0 if connection is
* proceeding.
* 
*/
int netopen2(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 ( pnum )
*
*	Start the closing process on port pnum.
*
*/
int
#if defined(NET14)
CDECL
#endif
netclose(int pnum)
{
	struct port *p;

	if(pnum<0 || pnum>NPORTS)			/* is a valid port? */
		return(-1);

	if((p=portlist[pnum])!=NULL) {		/* something there */

/*
*printf("p->state=%d\n",p->state);
*/
		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<n_clicks())
					p->state=SCLOSED;
				break;

			case SLAST:					/* five minute time out */
                if(portlist[pnum]->out.lasttime + LASTTIME<n_clicks())
					p->state=SCLOSED;
				break;

			default:
				break;
		  }
	  }
	else
		return(1);
	return(0);
}

/**************************************************************************/
/*
*	netinit ()
*
*	Handles all the initialization to bring up the network connection.
*	Assumes that the configuration file has already been read up.
* (called from Snetinit () )
*
*	Returns 0 on successful initialization.
*
*/
int netinit(void )
{
	int ret;

	BUG("in netinit");
/*
*   Initializes all buffers and hardware for data link layer.
*   Machine/board dependent.
*/
    ret=dlayerinit();
    BUG("after dlayerinit");
	if(ret) {
		switch (ret) {
			case -10:
				printf("Need a function for opening board!!\n");
				break;

			default:
				printf("Board initialization failed!.  Error code=%d\n",ret);
				break;
		  }	/* end switch */
		nnerror(101);
		return(ret);
	  }	/* end if */
/*
*  initialize the template packets needed for transmission
*/
    protinit();             /* set up empty packets */
    return(0);
}

/*************************************************************************/
/*
*	netshut ()
*
*	Shut down the hardware.  This tries to close any active connections and
* then turn off the hardware ( via dlayershut )
*
*/
void netshut()
{
	int i;

	for(i=0; i<NPORTS ; i++) 
		if(portlist[i]!=NULL)
			netclose(i);
	netsleep(1);
	dlayershut();
}

[ RETURN TO DIRECTORY ]