V8/usr/sys/chncp/chuser.c

#include "../chunix/chsys.h"
#include "../chunix/chconf.h"
#include "../chaos/chaos.h"

/*
 * User (top level) interface routines. Mostly assumed called from low
 * priority unless otherwise mentioned.
 */
static struct packet *rfcseen;	/* used by ch_rnext and ch_listen */
/*
 * Open a connection (send a RFC) given a destination host a RFC
 * packet, and a default receive window size.
 * Return the connection, on which the RFC has been sent.
 * The connection is not necessarily open at this point.
 */
struct connection *
ch_open(destaddr, rwsize, pkt)
struct packet *pkt;
{
	register struct connection *conn;

	if ((conn = allconn()) == NOCONN) {
		ch_free((char *)pkt);
		return(NOCONN);
	}
	conn->cn_faddr = destaddr;
	conn->cn_state = CSRFCSENT;
	conn->cn_fidx = 0;
	conn->cn_rwsize = rwsize;
	conn->cn_rsts = rwsize / 2;
	conn->cn_active = Chclock;
	pkt->pk_op = RFCOP;
	pkt->pk_fc = 0;
	pkt->pk_ackn = 0;
	debug(DCONN,printf("Conn #%x: RFCS state\n", conn->cn_lidx));
	/*
	 * By making the RFC packet written like a data packet,
	 * it will be freed by the normal receipting mechanism, enabling
	 * to easily be kept around for automatic retransmission.
	 * xmitdone (first half) and rcvpkt (handling OPEN pkts) help here.
	 * Since allconn clears the connection (including cn_tlast) the
	 * packet number of the RFC will be 1 (ch_write does pkn = ++tlast)
	 */
	LOCK;
	(void)ch_write(conn, pkt);	/* No errors possible */
	UNLOCK;
	return(conn);
}
/*
 * Start a listener, given a packet with the contact name in it.
 * In all cases packet is consumed.
 * Connection returned is in the listen state.
 */
struct connection *
ch_listen(pkt)
struct packet *pkt;
{
	register struct connection *conn;
	register struct packet *pktl, *opkt;

	if ((conn = allconn()) == NOCONN) {
		ch_free((char *)pkt);
		return(NOCONN);
	}
	conn->cn_state = CSLISTEN;
	pkt->pk_op = LSNOP;
	setpkt(conn, pkt);
	LOCK;
	opkt = NOPKT;
	for (pktl = Chrfclist; pktl != NOPKT; pktl = (opkt = pktl)->pk_next)
		if (concmp(pktl, pkt->pk_cdata, (int)pkt->pk_len)) {
			if(opkt == NOPKT)
				Chrfclist = pktl->pk_next;
			else
				opkt->pk_next = pktl->pk_next;
			if (pktl == Chrfctail)
				Chrfctail = opkt;
			if (pktl == rfcseen)
				rfcseen = NOPKT;
			ch_free((char *)pkt);
			lsnmatch(pktl, conn);
			UNLOCK;
			return(conn);
		}
	/*
	 * Should we check for duplicate listeners??
	 * Or is it better to allow more than one?
	 */
	pkt->pk_next = Chlsnlist;
	Chlsnlist = pkt;
	debug(DCONN,printf("Conn #%x: LISTEN state\n", conn->cn_lidx));
	UNLOCK;
	return(conn);
}
/*
 * Send a new data packet on a connection.
 * Called at high priority since window size check is elsewhere.
 */
ch_write(conn, pkt)
register struct connection *conn;
register struct packet *pkt;
{
	setpkt(conn, pkt);
	pkt->pk_next = NOPKT;	/* just to be sure! */
	switch (pkt->pk_op) {
	case ANSOP:
	case FWDOP:
		if (conn->cn_state != CSRFCRCVD ||
		    (conn->cn_flags & CHANSWER) == 0)
			goto err;
		ch_close(conn, pkt, 0);
		return 0;
	case RFCOP:
		if (conn->cn_state != CSRFCSENT)
			goto err;
		break;
	case UNCOP:
		pkt->pk_pkn = 0;
		senddata(pkt);
		return 0;
	default:
		if (!ISDATOP(pkt))
			goto err;
	case OPNOP:
	case EOFOP:
		if (conn->cn_state != CSOPEN)
			goto err;
		break;
	}
	pkt->pk_pkn = ++conn->cn_tlast;
	senddata(pkt);
	return 0;
err:
	ch_free((char *)pkt);
	return CHERROR;
}
/*
 * Consume the packet at the head of the received packet queue (rhead).
 * Assumes high priority because check for available is elsewhere
 */
ch_read(conn)
register struct connection *conn;
{
	register struct packet *pkt;

	if ((pkt = conn->cn_rhead) == NOPKT)
		return;
	conn->cn_rhead = pkt->pk_next;
	if (conn->cn_rtail == pkt)
		conn->cn_rtail = NOPKT;
	if (CONTPKT(pkt)) {
		conn->cn_rread = pkt->pk_pkn;
		if (pkt->pk_op == EOFOP ||
		    3 * (short)(conn->cn_rread - conn->cn_racked) > conn->cn_rwsize) {
			debug(DPKT,
				printf("Conn#%x: rread=%d rackd=%d rsts=%d\n",
				conn->cn_lidx, conn->cn_rread,
				conn->cn_racked, conn->cn_rsts));
			pkt->pk_next = NOPKT;
			makests(conn, pkt);
			reflect(pkt);
			return;
		}
	}
	ch_free((char *)pkt);
}
/*
 * Send an eof packet on a channel.
 */
ch_eof(conn)
struct connection *conn;
{
	register struct packet *pkt;
	register int ret = 0;

	if ((pkt = pkalloc(0, 0)) != NOPKT) {
		pkt->pk_op = EOFOP;
		pkt->pk_len = 0;
		ret = ch_write(conn, pkt);
	}
	return ret;
}
/*
 * Close a connection, giving close pkt to send (CLS or ANS).
 */
ch_close(conn, pkt, release)
register struct connection *conn;
register struct packet *pkt;
{
	int s = spl6();

	switch (conn->cn_state) {
	    case CSOPEN:
	    case CSRFCRCVD:
		if (pkt != NOPKT) {
			pkt->pk_ackn = pkt->pk_pkn = 0;
			setpkt(conn, pkt);
			sendctl(pkt);
			pkt = NOPKT;
		}
		/* Fall into... */
	    case CSRFCSENT:
		clsconn(conn, CSCLOSED, NOPKT);
		break;
	    case CSLISTEN:
		rmlisten(conn);
		break;
	    default:
		break;
	}
	if (pkt)
		ch_free((char *)pkt);
	splx(s);
	if (release)
		rlsconn(conn);
}
/*
 * Top level raw sts sender - at high priority, like ch_write.
 */
ch_sts(conn)
struct connection *conn;
{
	sendsts(conn);		/* This must be locked */
}
/*
 * Accept an RFC, called on a connection in the CSRFCRCVD state.
 */
ch_accept(conn)
struct connection *conn;
{
	conn->cn_state = CSOPEN;
	sendopn(conn);
}
/*
 * Return the next rfc packet on the list, flushing the one previously
 * looked at if it hasn't been consumed (or skipped) yet.
 * Flushed RFC's get a Close packet sent back.
 * LOCK must be in effect when called. - High priority.
 */
struct packet *
ch_rnext()
{
	register struct packet *pkt, *lpkt;

	if ((pkt = Chrfclist) != NOPKT && rfcseen == pkt) {
		if ((Chrfclist = pkt->pk_next) == NOPKT)
			Chrfctail = NOPKT;
		UNLOCK;
		if ((pkt = pktstr(pkt, "Contact name refused", 20)) != NOPKT) {
			pkt->pk_op = CLSOP;
			pkt->pk_fc = 0;
			pkt->pk_ackn = pkt->pk_pkn = 0;
			reflect(pkt);
		}
		LOCK;
		pkt = Chrfclist;
		rfcseen = NOPKT;
	}
	for (lpkt = pkt; pkt != NOPKT && pkt->pk_ackn != 0; pkt = pkt->pk_next)
		lpkt = pkt;
	if (pkt != NOPKT) {
		if (pkt != Chrfclist) {
			Chrfctail->pk_next = Chrfclist;
			Chrfclist = pkt;
			lpkt->pk_next = NOPKT;
			Chrfctail = lpkt;
		}
		rfcseen = pkt;
	}
	return (pkt);
}
/*
 * Skip the RFC at the head of the unmatched-rfc list, and mark it so that
 * ch_rnext never sees it again.  This is to allow an unmatched rfc server
 * to basically say that the RFC should be handled by a specific listener.
 * This would be necessary in the case where two calls for a given
 * specific listener - say login - came so close together that the
 * login server did not get done with the first one, and back inside the
 * listen before the next one came in. Called at low priority from top level.
 * To mark te RFC we set the pk_ackn to non-zero.  It is assured to be zero
 * when first queued.
 */
ch_rskip()
{
	register struct packet *pkt;

	if ((pkt = Chrfclist) != NOPKT && rfcseen == pkt) {
		pkt->pk_ackn = 1;
		rfcseen = NOPKT;
	}
}