DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦0720e736f⟧ TextFile

    Length: 23830 (0x5d16)
    Types: TextFile
    Names: »ts2sunlink.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦e83f91978⟧ »EurOpenD22/isode/osimis-2.0.tar.Z« 
        └─⟦d846658bd⟧ 
            └─⟦this⟧ »osimis/misode/tsap/ts2sunlink.c« 
└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦35176feda⟧ »EurOpenD22/isode/isode-6.tar.Z« 
        └─⟦de7628f85⟧ 
            └─⟦this⟧ »isode-6.0/tsap/ts2sunlink.c« 

TextFile

/* ts2sunlink.c - TPM: SunLink OSI TP4 interface */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/tsap/RCS/ts2sunlink.c,v 7.4 89/12/19 10:18:38 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/tsap/RCS/ts2sunlink.c,v 7.4 89/12/19 10:18:38 mrose Exp $
 *
 * Contributed by John A. Scott, The MITRE Corporation
 *
 *
 * $Log:	ts2sunlink.c,v $
 * Revision 7.4  89/12/19  10:18:38  mrose
 * DLOG
 * 
 * Revision 7.3  89/12/13  07:05:45  mrose
 * touch-up
 * 
 * Revision 7.2  89/12/08  09:41:35  mrose
 * touch-up
 * 
 * Revision 7.1  89/12/07  22:15:30  mrose
 * queued writes
 * 
 * Revision 7.0  89/11/23  22:30:38  mrose
 * Release 6.0
 * 
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


/* LINTLIBRARY */

#include <stdio.h>
#include "tpkt.h"
#include "mpkt.h"

#ifdef	TP4
#include "tp4.h"
#endif

#ifdef	SUN_TP4
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include "tailor.h"


#define	XXX			/* SunLink OSI release 5.0 bug... */

#define MAXTP4 (1 << SIZE_8K)

#ifndef	SUNLINK_6_0
#define	OSI_AF_GENERIC	AF_GENERIC
#endif
#ifndef	MSG_OSI_OOB
#define	MSG_OSI_OOB	MSG_OOB
#endif


/* A driver for SunLink OSI's TP4!
 *
 *	SunLink OSI TP4 user interface is very much like a ``datagram''
 *	interface.  Some of the hair in this port involves converting
 *	ISODE TP packets into SUN TP4 packets.  My gut feeling is that the
 *	port should be redone to make better use of existing ISODE
 *	structures (but this works and I hesitate to change something works).
 *			-- John
 *
 *	Actually, I think is fairly close to optimal now.
 *			-- /mtr
 *
 * TODO:
 *
 * 1. Figure out how to implement tsaplisten.c$tp4unique()
 *
 * 2. On failure of sendto, sendmsg, or recvfrom, try to figure out if a
 *    disconnect happened and return the right DR_ reason.
 *
 * 3. SunLink OSI should support TSELs of length greater than two.  In fact,
 *    a transport address really should be:
 *	nsap - 64 octets, tsel - 44 octets (would prefer 64, but mbufs limit)
 *
 * 4. Should do QOS mappings for error recovery (not class 0 or 2) and cost
 *    (class 0).
 *
 */

/* \f

   DATA */

extern int  errno;

/* \f

   UPPER HALF */

static int  TConnect (tb, expedited, data, cc, td)
register struct tsapblk *tb;
char    *data;
int	expedited,
	cc;
struct TSAPdisconnect *td;
{
    int	    result;
    register struct tp4pkt *t;

    if ((t = newtp4pkt (TP_CONNECT_REQ)) == NULL)
	return tsaplose (td, DR_CONGEST, NULLCP, NULLCP);

    if (gen2tp4 (&tb -> tb_responding, &t -> tp4_called,
		 tb -> tb_initiating.ta_naddr > 0
		     ? tb -> tb_initiating.ta_addrs : NULLNA) == NOTOK) {
	result = tsaplose (td, DR_ADDRESS, NULLCP,
			   "unable to parse remote address");
	goto out;
    }

    if (gen2tp4 (&tb -> tb_initiating, &t -> tp4_calling,
		 tb -> tb_responding.ta_naddr > 0
		     ? tb -> tb_responding.ta_addrs : NULLNA) == NOTOK) {
	result = tsaplose (td, DR_ADDRESS, NULLCP,
			   "unable to parse local address");
	goto out;
    }

    if (expedited) {
	tb -> tb_flags |= TB_EXPD;
	t -> tp4_expedited = 1;
    }

    if (sendto (tb -> tb_fd, data, cc, 0, (struct sockaddr *) t,
		sizeof (TP_MSG_CONNECT)) == NOTOK)
	result = tsaplose (td, DR_CONGEST, "failed", "sendto");
    else
	result = CONNECTING_2;

out: ;
    freetp4pkt (t);

    return result;
}

/* \f

 */

static int  TRetry (tb, async, tc, td)
register struct tsapblk *tb;
int	async;
struct TSAPconnect *tc;
struct TSAPdisconnect *td;
{
    int	    cc,
	    header_len,
	    onoff;
    char    data[TS_SIZE];
    register struct tp4pkt *t;

    t = NULL;
    if (async)
	switch ((*tb -> tb_retryfnx) (tb, td)) {
	    case NOTOK:
	        goto out;

	    case OK:
		return CONNECTING_2;

	    case DONE:
		break;		    
	}

    if ((t = newtp4pkt ((TP_EVENT) 0)) == NULL) {
	(void) tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
	goto out;
    }

    header_len = sizeof (TP_MSG_CONNECT);
    if ((cc = recvfrom (tb -> tb_fd, data, sizeof data, 0,
			(struct sockaddr *) t, &header_len)) == NOTOK) {
	if (errno == EWOULDBLOCK) {
	    freetp4pkt (t);
	    return CONNECTING_2;
	}

	(void) tsaplose (td, DR_CONGEST, "failed", "recvfrom");
	goto out;
    }

    if (async)
	(void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));

    switch (t -> tp4_event) {
	case TP_CONNECT_CONF:
	    tc -> tc_sd = tb -> tb_fd;
	    tc -> tc_tsdusize = tb -> tb_tsdusize = MAXTP4;
	    (void) tp42gen (&tb -> tb_responding, &t -> tp4_calling);
	    tc -> tc_responding = tb -> tb_responding;
	    if ((tb -> tb_flags & TB_EXPD) && !t -> tp4_expedited)
		tb -> tb_flags &= ~TB_EXPD;
	    tc -> tc_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
	    if ((tc -> tc_cc = cc) > 0)
		bcopy (data, tc -> tc_data, cc);

	    freetp4pkt (t);
	    tb -> tb_flags |= TB_CONN;
#ifdef  MGMT
	    if (tb -> tb_manfnx)
		(*tb -> tb_manfnx) (OPREQOUT, tb);
#endif

	    return DONE;

	case TP_DISCONNECT_IND:
	    if ((td -> td_reason = (int) t -> tp4_reason) == DR_UNKNOWN)
		td -> td_reason = DR_NETWORK;
	    if ((td -> td_cc = cc) > 0)
		bcopy (data, td -> td_data, cc);
	    break;

	default:
	    (void) tsaplose (td, DR_NETWORK, NULLCP,
			     "expecting 0x%x, got 0x%x",
			     TP_CONNECT_CONF, t -> tp4_event);
	    break;
    }

out: ;
    if (t)
	freetp4pkt (t);
    freetblk (tb);

    return NOTOK;
}

/* \f

 */

static int  TStart (tb, cp, ts, td)
register struct tsapblk *tb;
char   *cp;
struct TSAPstart *ts;
struct TSAPdisconnect *td;
{
    int	    cc,
	    i,
    	    result;
    register struct tp4pkt *tp;

    if ((i = strlen (cp)) < (cc = 2 * sizeof (TP_MSG_CONNECT)))
	return tsaplose (td, DR_PARAMETER, NULLCP,
			 "bad initialization vector");

    if ((tp = newtp4pkt ((TP_EVENT) 0)) == NULL) {
	result = tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
	goto out;
    }

    cc = 2 * implode ((u_char *) tp, cp, cc);
    cp += cc, i -= cc;

    if (tp -> tp4_expedited)
	tb -> tb_flags |= TB_EXPD;

    (void) tp42gen (&tb -> tb_initiating, &tp -> tp4_calling);
    (void) tp42gen (&tb -> tb_responding, &tp -> tp4_called);

    ts -> ts_sd = tb -> tb_fd;
    ts -> ts_calling = tb -> tb_initiating;	/* struct copy */
    ts -> ts_called = tb -> tb_responding;	/* struct copy */
    ts -> ts_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
    ts -> ts_tsdusize = tb -> tb_tsdusize;

    if (i > 0) {
	if (i > 2 * TS_SIZE) {
	    result = tsaplose (td, DR_CONNECT, NULLCP,
			     "too much initial user data");
	    goto out;
	}

	ts -> ts_cc = implode ((u_char *) ts -> ts_data, cp, i);
    }
    else
	ts -> ts_cc = 0;

    result = OK;

out: ;
    if (tp)
	freetp4pkt (tp);

    return result;
}

/* \f

 */

/* ARGSUSED */

static int  TAccept (tb, responding, data, cc, qos, td)
register struct tsapblk *tb;
char   *data;
int	responding,
	cc;
struct QOStype *qos;
struct TSAPdisconnect *td;
{
    int	    result;
    register struct tp4pkt *tp;

    if ((tp = newtp4pkt (TP_CONNECT_RESP)) == NULL)
	return tsaplose (td, DR_CONGEST, NULLCP, NULLCP);

    if (responding)
	(void) gen2tp4 (&tb -> tb_responding, &tp -> tp4_called,
			tb -> tb_initiating.ta_naddr > 0
			    ? tb -> tb_initiating.ta_addrs : NULLNA);

    if (tb -> tb_flags & TB_EXPD)
	tp -> tp4_expedited = 1;

    if (sendto (tb -> tb_fd, data, cc, 0, (struct sockaddr *) tp,
		sizeof (TP_MSG_CONNECT)) == NOTOK)
	result = tsaplose (td, DR_CONGEST, "failed", "sendto");
    else {
	result = OK;

	tb -> tb_flags |= TB_CONN;
#ifdef  MGMT
	if (tb -> tb_manfnx)
	    (*tb -> tb_manfnx) (OPREQIN, tb);
#endif
    }

    freetp4pkt (tp);

    return result;
}

/* \f

 */

/* life would be nice if we didn't have to worry about the maximum number of
   bytes that can be written in a single syscall() */

#ifndef	MSG_MAXIOVLEN
#define	MSG_MAXIOVLEN	NTPUV
#endif


static int  TWrite (tb, uv, expedited, td)
register struct tsapblk *tb;
register struct udvec *uv;
int	expedited;
struct TSAPdisconnect *td;
{
    int	    cc,
	    flags,
	    j,
	    len,
	    size;
#ifdef	MGMT
    int	    dlen;
#endif
    register char *bp,
		  *ep;
#ifdef	XXX
    register char *dp;
    char    data[MAXTP4];
#endif
    struct msghdr msgs;
    register struct msghdr *msg = &msgs;
    register struct tp4pkt *tp;
    struct iovec iovs[MSG_MAXIOVLEN];
    register struct iovec *vv,
			  *wv;

    if ((tp = newtp4pkt (expedited ? TP_X_DATA_REQ : TP_DATA_REQ)) == NULL) {
	(void) tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
	goto out;
    }

    if (expedited)
	size = sizeof (TP_MSG_X_DATA), flags = MSG_OOB;
    else
	size = sizeof (TP_MSG_DATA), flags = 0;
    
#ifdef	MGMT
    dlen = 0;
#endif

    if (!expedited && (tb -> tb_flags & TB_QWRITES)) {
	int	onoff,
		nc;
	register struct qbuf *qb;
	struct udvec *xv;

	cc = 0;
	for (xv = uv; xv -> uv_base; xv++)
	    cc += xv -> uv_len;
#ifdef	MGMT
	dlen = cc;
#endif

	if ((qb = (struct qbuf *) malloc (sizeof *qb + (unsigned) cc))
		== NULL) {
	    (void) tsaplose (td, DR_CONGEST, NULLCP,
			     "unable to malloc %d octets for pseudo-writev, failing...",
			     cc);
	    freetp4pkt (tp);
	    freetblk (tb);

	    return NOTOK;
	}
	qb -> qb_forw = qb -> qb_back = qb;
	qb -> qb_data = qb -> qb_base, qb -> qb_len = cc;

	bp = qb -> qb_data;
	for (xv = uv; xv -> uv_base; xv++) {
	    bcopy (xv -> uv_base, bp, xv -> uv_len);
	    bp += xv -> uv_len;
	}

	if (tb -> tb_qwrites.qb_forw != &tb -> tb_qwrites) {
	    nc = 0;
	    goto insert;
	}

	tp -> tp4_eot = 1;

	vv = iovs;
	vv -> iov_base = qb -> qb_data, vv -> iov_len = qb -> qb_len;
	vv++;

	msg -> msg_name = (caddr_t) tp;
	msg -> msg_namelen = size;
	msg -> msg_iov = iovs;
	msg -> msg_iovlen = vv - iovs;
	msg -> msg_accrights = (caddr_t) NULL;
	msg -> msg_accrightslen = 0;

	(void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));

	nc = sendmsg (tb -> tb_fd, msg, flags);

	(void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));

	if (nc != cc) {
	    if (nc == NOTOK) {
		if (errno != EWOULDBLOCK) {
		    (void) tsaplose (td, DR_CONGEST, "failed", "sendmsg");
		    goto losing;
		}

		nc = 0;
	    }
	    if ((*tb -> tb_queuePfnx) (tb, 1, td) == NOTOK)
		goto losing;

	    qb -> qb_data += nc, qb -> qb_len -= nc;
insert: ;
	    insque (qb, tb -> tb_qwrites.qb_back);
	    DLOG (tsap_log, LLOG_TRACE,
		  ("queueing blocked write of %d of %d octets", nc, cc));
	}
	else
	    free ((char *) qb);
	goto done;

losing: ;
	free ((char *) qb);
	freetp4pkt (tp);
	freetblk (tb);

	return NOTOK;
    }

    ep = (bp = uv -> uv_base) + (cc = uv -> uv_len);
    while (uv -> uv_base) {
	wv = (vv = iovs) + MSG_MAXIOVLEN;
	for (len = tb -> tb_tsdusize; len > 0 && vv < wv; len -= j) {
	    j = min (cc, len);
#ifdef	MGMT
	    dlen += j;
#endif
	    vv -> iov_base = bp, vv -> iov_len = j, vv++;
	    bp += j, cc -= j;

	    if (bp >= ep) {
		if ((bp = (++uv) -> uv_base) == NULL)
		    break;
		ep = bp + (cc = uv -> uv_len);
	    }
	}

	if (!expedited)
	    tp -> tp4_eot = uv -> uv_base == NULL;

#ifdef	XXX
	dp = data, len = 0;
	for (wv = iovs; wv < vv; wv++) {
	    bcopy (wv -> iov_base, dp, wv -> iov_len);
	    dp += wv -> iov_len, len += wv -> iov_len;
	}
	vv = iovs;
	vv -> iov_base = data, vv -> iov_len = len, vv++;
#endif

	msg -> msg_name = (caddr_t) tp;
	msg -> msg_namelen = size;
	msg -> msg_iov = iovs;
	msg -> msg_iovlen = vv - iovs;
	msg -> msg_accrights = (caddr_t) NULL;
	msg -> msg_accrightslen = 0;

	if (sendmsg (tb -> tb_fd, msg, flags) == NOTOK) {
	    (void) tsaplose (td, DR_CONGEST, "failed", "sendmsg");
	    goto out;
	}
    }

done: ;
    freetp4pkt (tp);

#ifdef  MGMT
    if (tb -> tb_manfnx)
	(*tb -> tb_manfnx) (USERDT, tb, dlen);
#endif

    return OK;

out: ;
    if (tp)
	freetp4pkt (tp);
    freetblk (tb);

    return NOTOK;
}

/* \f

 */

static int  TDrain (tb, td)
register struct tsapblk *tb;
struct TSAPdisconnect *td;
{
    int	    nc,
	    onoff,
	    result;
    register struct qbuf *qb;
    struct msghdr msgs;
    register struct msghdr *msg = &msgs;
    register struct tp4pkt *tp;
    struct iovec vvs;
    register struct iovec *vv = &vvs;

    if ((tp = newtp4pkt (TP_DATA_REQ)) == NULL)
	return tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
    tp -> tp4_eot = 1;

    msg -> msg_name = (caddr_t) tp;
    msg -> msg_namelen = sizeof (TP_MSG_DATA);
    msg -> msg_iov = vv, msg -> msg_iovlen = 1;
    msg -> msg_accrights = (caddr_t) NULL;
    msg -> msg_accrightslen = 0;

    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));

    while ((qb = tb -> tb_qwrites.qb_forw) != &tb -> tb_qwrites) {
	vv -> iov_base = qb -> qb_data, vv -> iov_len = qb -> qb_len;

	if (nc = sendmsg (tb -> tb_fd, msg, 0) != qb -> qb_len) {
	    if (nc == NOTOK) {
		if (errno != EWOULDBLOCK) {
		    result = tsaplose (td, DR_NETWORK, "failed",
				      "write to network");
		    goto out;
		}

		nc = 0;
	    }

	    DLOG (tsap_log, LLOG_TRACE,
		  ("wrote %d of %d octets from blocked write", nc,
		   qb -> qb_len));
	    qb -> qb_data += nc, qb -> qb_len -= nc;

	    result = OK;
	    goto out;
	}

	DLOG (tsap_log, LLOG_TRACE,
	      ("finished blocked write of %d octets", qb -> qb_len));
	remque (qb);
	free ((char *) qb);
    }
    result = DONE;

out: ;
    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));

    freetp4pkt (tp);
    return result;
}

/* \f

 */

static int  TRead (tb, tx, td, async, oob)
register struct tsapblk *tb;
register struct TSAPdata *tx;
struct TSAPdisconnect *td;
int	async,
	oob;
{
    int	    cc,
	    header_len;
    register struct qbuf *qb;
    register struct tp4pkt *tp;

    bzero ((char *) tx, sizeof *tx);
    tx -> tx_qbuf.qb_forw = tx -> tx_qbuf.qb_back = &tx -> tx_qbuf;

    for (;;) {
	if ((qb = (struct qbuf *) malloc ((unsigned) (sizeof *qb + sizeof *tp
					  	     + tb -> tb_tsdusize)))
	        == NULL) {
	    (void) tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
	    break;
	}
	tp = (struct tp4pkt *) qb -> qb_base;
	qb -> qb_data = qb -> qb_base + sizeof *tp;

	header_len = sizeof (struct tp4pkt);	
	if ((cc = recvfrom (tb -> tb_fd, qb -> qb_data, tb -> tb_tsdusize,
			    oob ? MSG_OSI_OOB : 0,
			    (struct sockaddr *) tp, &header_len)) == NOTOK) {
	    (void) tsaplose (td, DR_CONGEST, "failed", "recvfrom");
	    break;
	}

	switch (tp -> tp4_event) {
	    case TP_DATA_IND:
		if (cc > 0) {
		    insque (qb, tb -> tb_qbuf.qb_back);
		    tb -> tb_len += (qb -> qb_len = cc);
		}
		else
		    free ((char *) qb);
#ifdef	MGMT
		if (tb -> tb_manfnx)
		    (*tb -> tb_manfnx) (USERDR, tb, tb -> tb_len);
#endif
		if (!tp -> tp4_eot) {
		    if (async)
			return DONE;

		    continue;
		}
		tx -> tx_expedited = 0;
		if (tb -> tb_qbuf.qb_forw != &tb -> tb_qbuf) {
		    tx -> tx_qbuf = tb -> tb_qbuf;	/* struct copy */
		    tx -> tx_qbuf.qb_forw -> qb_back =
			    tx -> tx_qbuf.qb_back -> qb_forw = &tx -> tx_qbuf;
		    tx -> tx_cc = tb -> tb_len;
		    tb -> tb_qbuf.qb_forw =
			    tb -> tb_qbuf.qb_back = &tb -> tb_qbuf;
		    tb -> tb_len = 0;
		}

		return OK;

	    case TP_X_DATA_IND:
		if (cc > 0) {
		    insque (qb, tx -> tx_qbuf.qb_back);
		    tx -> tx_cc = (qb -> qb_len = cc);
		}
		else
		    free ((char *) qb);
		tx -> tx_expedited = 1;

		return OK;

	    case TP_DISCONNECT_IND:
		td -> td_reason = (int) tp -> tp4_reason;
		if ((td -> td_cc = cc) > 0)
		    bcopy (qb -> qb_data, td -> td_data, cc);
		break;

	    default:
		(void) tsaplose (td, DR_NETWORK, NULLCP,
				 "unexpected response 0x%x",
				 (int) (tp -> tp4_event));
		break;
	}

	break;
    }
    if (qb)
	free ((char *) qb);

    freetblk (tb);

    return NOTOK;
}

/* \f

 */

static int  TDisconnect (tb, data, cc, td)
register struct tsapblk *tb;
char   *data;
int	cc;
struct TSAPdisconnect *td;
{
    int	    result;
    register struct tp4pkt *tp;

    if (tp = newtp4pkt (TP_DISCONNECT_REQ)) {
	tp -> tp4_reason = (TP_DR_REASON) DR_NORMAL;


	if (sendto (tb -> tb_fd, data, cc, 0, (struct sockaddr *) tp,
		    sizeof (TP_MSG_DISCONNECT)) == NOTOK)
	    result = tsaplose (td, DR_CONGEST, "failed", "sendto");
	else
	    result = OK;

	freetp4pkt (tp);
    }
    else
	result = tsaplose (td, DR_CONGEST, NULLCP, NULLCP);

    freetblk (tb);

    return result;
}

/* \f

 */

/* ARGSUSED */

static int  TLose (tb, reason, td)
register struct tsapblk *tb;
int	reason;
struct TSAPdisconnect *td;
{
    register struct tp4pkt *tp;

    SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP, ("TPM error %d", reason));

    if (tp = newtp4pkt (TP_DISCONNECT_REQ)) {
	tp -> tp4_reason = (TP_DR_REASON) reason;

	(void) sendto (tb -> tb_fd, NULLCP, 0, 0, (struct sockaddr *) tp,
		       sizeof (TP_MSG_DISCONNECT));

	freetp4pkt (tp);
    }
}

/* \f

   LOWER HALF */

/* ARGSUSED */

int	tp4open (tb, local_ta, local_na, remote_ta, remote_na, td, async)
register struct tsapblk *tb;
struct TSAPaddr *local_ta,
		*remote_ta;
struct NSAPaddr *local_na,
		*remote_na;
struct TSAPdisconnect *td;
int	async;
{
    int	    fd,
	    onoff;
    struct TSAPaddr tzs;
    register struct TSAPaddr *tz = &tzs;
    register struct NSAPaddr *nz = tz -> ta_addrs;
    OSI_ADDR	ifaddr;

    bzero ((char *) tz, sizeof *tz);
    if (local_ta)
	*tz = *local_ta;	/* struct copy */
    if (local_na) {
	*nz = *local_na;	/* struct copy */
	tz -> ta_naddr = 1;
    }

    (void) gen2tp4 (tz, &ifaddr, remote_na);

    if ((fd = socket (AF_OSI, SOCK_EVENT, OSIPROTO_TP_EVENT)) == NOTOK)
	return tsaplose (td, DR_CONGEST, "socket", "unable to start");

    if (bind (fd, (struct sockaddr *) &ifaddr, sizeof ifaddr) == NOTOK) {
	(void) tsaplose (td, DR_ADDRESS, "socket", "unable to bind");
	(void) close (fd);
	return NOTOK;
    }

    tb -> tb_fd = fd;
    (void) tp4init (tb);

    if (async)
	(void) ioctl (fd, FIONBIO, (onoff = 1, (char *) &onoff));

    return (async ? OK : DONE);
}

/* \f

 */

/* ARGSUSED */

static int  retry_tp4_socket (tb, td)
register struct tsapblk *tb;
struct TSAPdisconnect *td;
{
    fd_set  mask;

    FD_ZERO (&mask);
    FD_SET (tb -> tb_fd, &mask);
    if (xselect (tb -> tb_fd + 1, &mask, NULLFD, NULLFD, 0) < 1)
	return OK;

    return DONE;
}

/* \f

 */

/* ARGSUSED */

char   *tp4save (fd, td)
int fd;
struct TSAPdisconnect *td;
{
    static char buffer[BUFSIZ];

    (void) sprintf (buffer, "%c%d", NT_SUN, fd);
    return buffer;
}

/* \f

 */

int	tp4restore (tb, buffer, td)
register struct tsapblk *tb;
char   *buffer;
struct TSAPdisconnect *td;
{
    int	    fd;

    if (sscanf (buffer, "%d", &fd) != 1 || fd < 0)
	return tsaplose (td, DR_PARAMETER, NULLCP,
			"bad initialization vector \"%s\"", buffer);

    tb -> tb_fd = fd;
    (void) tp4init (tb);

    return OK;
}

/* \f

 */

int	tp4init (tb)
register struct tsapblk *tb;
{

    tb -> tb_connPfnx = TConnect;
    tb -> tb_retryPfnx = TRetry;

    tb -> tb_startPfnx = TStart;
    tb -> tb_acceptPfnx = TAccept;

    tb -> tb_writePfnx = TWrite;
    tb -> tb_readPfnx = TRead;
    tb -> tb_discPfnx = TDisconnect;
    tb -> tb_losePfnx = TLose;

    tb -> tb_drainPfnx = TDrain;

#ifdef  MGMT
    tb -> tb_manfnx = TManGen;
#endif

    tb -> tb_flags |= TB_TP4;

    tb -> tb_tsdusize = MAXTP4 - (tb -> tb_tpduslop = 0);

    tb -> tb_retryfnx = retry_tp4_socket;

    tb -> tb_closefnx = close_tp4_socket;
    tb -> tb_selectfnx = select_tp4_socket;
}

/* \f

 */

/* ARGSUSED */

int	start_tp4_server (sock, backlog, opt1, opt2, td)
struct TSAPaddr *sock;
int	backlog,
	opt1,
	opt2;
struct TSAPdisconnect *td;
{
    int	    sd;
    OSI_ADDR	ifaddr;

    (void) gen2tp4 (sock, &ifaddr, NULLNA);

    if ((sd = socket (AF_OSI, SOCK_EVENT, OSIPROTO_TP_EVENT)) == NOTOK)
	return tsaplose (td, DR_CONGEST, "socket", "unable to start");

    if (bind (sd, (struct sockaddr *) &ifaddr, sizeof ifaddr) == NOTOK) {
	(void) tsaplose (td, DR_ADDRESS, "socket", "unable to bind");
	(void) close (sd);
	return NOTOK;
    }

    (void) listen (sd, backlog);

    return sd;
}

/* \f

 */

#ifndef	notdef
/* ARGSUSED */
#endif

int	join_tp4_client (fd, sock, td)
int	fd;
struct TSAPaddr *sock;
struct TSAPdisconnect *td;
{
    int	    len,
	    sd;
    OSI_ADDR	ifaddr;

    len = sizeof (OSI_ADDR);
    if ((sd = accept (fd, (struct sockaddr *) &ifaddr, &len)) == NOTOK)
	return tsaplose (td, DR_NETWORK, "socket", "unable to accept");
	
#ifdef	notdef
    /* Ouch!!  Trying to get the remote address off the socket
     * only works for local connections.  Non-local connections
     * causes a core dump when I try to convert.
     */
    (void) tp42gen (sock, &ifaddr);
#endif
    
    return sd;
}

/* \f

 */

/* SunLink OSI address encoding/decoding */

#ifdef	SUNLINK_5_2
/* ARGSUSED */
#endif

static int  gen2tp4 (generic, specific, template)
struct TSAPaddr *generic;
OSI_ADDR	*specific;
struct NSAPaddr	*template;
{
#ifndef	SUNLINK_6_0
    int	    len,
	    paddr_type;
#endif
#ifndef	SUNLINK_5_2
    char    buffer[BUFSIZ];
#endif
    struct NSAPaddr *na;

    OSI_ADDR_INIT (specific);

    if (generic -> ta_naddr > 0) {
	na = generic -> ta_addrs;
#ifndef	SUNLINK_6_0
	paddr_type = AF_OSI, len = 0;
	if (na -> na_addrlen > 0)
	    switch (na -> na_address[0]) {
		case 0x49:
		    paddr_type = AF_NBS;
		    len = 1;
		    break;

		case 0x47:
		    if (na -> na_addrlen < 3
			    || na -> na_address[1] != 0x00
			    || na -> na_address[2] != 0x04)
			break;
		    paddr_type = AF_OSINET;
		    len = 3;
		    break;
	    }

 	osi_set_sap (na -> na_address + len, na -> na_addrlen - len, specific,
		     OSI_NSAP, paddr_type);
#else
 	osi_set_sap (na -> na_address, na -> na_addrlen, specific,
		     OSI_NSAP, AF_USER_DEFINED);
#endif
    }
    else {
#ifndef	SUNLINK_5_2
	/* The SunLink OSI I'm using seems to require something
	 * although the ``manual'' says I don't.  Hmmmm, I wonder
         * if I still need the OSI_ADDR_INIT? BTW, osi_set_sap is one
	 * of the two functions I link from the -losi library.  When I
	 * get the source for SunLink (maybe before I retire) I'll
	 * write my own set/get sap function and punt libosi.a
	 */

	paddr_type = AF_OSI;
	if (template != NULLNA && template -> na_addrlen > 0)
	    switch (template -> na_address[0]) {
		case 0x49:
		    paddr_type = AF_NBS;
		    break;

		case 0x47:
		    if (template -> na_addrlen < 3
			    || template -> na_address[1] != 0x00
			    || template -> na_address[2] != 0x04)
			break;
		    paddr_type = AF_OSINET;
		    break;
	    }

	buffer[0] = 0x00, len = 1;
	osi_set_sap (buffer, len, specific, OSI_NSAP, paddr_type);
#else
	mds_lookup ("localhost", "CLIENT", specific);;
#endif
    }

    osi_set_sap (generic -> ta_selector, generic -> ta_selectlen, specific,
		 OSI_TSAP, OSI_AF_GENERIC);

    return OK;
}

/* \f

 */

int	tp42gen (generic, specific)
struct TSAPaddr *generic;
OSI_ADDR	*specific;
{
    int	    len,
	    paddr_type;
    char    buffer[NASIZE];
    struct NSAPaddr *na;

    paddr_type = 0;
    if ((len = osi_get_sap (specific, buffer, sizeof buffer, OSI_NSAP,
			    &paddr_type)) <= 0)
	return NOTOK;

    na = generic -> ta_addrs;
    na -> na_type = NA_NSAP;
    na -> na_subnet = ts_comm_nsap_default;
    switch (paddr_type) {
	case AF_NBS:
	    na -> na_address[0] = 0x49;
	    na -> na_addrlen = 1;
	    break;

	case AF_OSINET:
	    na -> na_address[0] = 0x47;
	    na -> na_address[1] = 0x00;
	    na -> na_address[2] = 0x04;
	    na -> na_addrlen = 3;
	    break;

	default:
	    na -> na_addrlen = 0;
	    break;
    }
    bcopy (buffer, na -> na_address + na -> na_addrlen, len);
    na -> na_addrlen += len;

    generic -> ta_naddr = 1;
    generic -> ta_selectlen = osi_get_sap (specific, generic -> ta_selector,
					   TSSIZE, OSI_TSAP, &paddr_type);

    return OK;
}

/* \f

 */

struct tp4pkt *newtp4pkt (code)
TP_EVENT code;
{
    struct tp4pkt *tp;

    tp = (struct tp4pkt *) calloc (1, sizeof *tp);
    if (tp != NULL)
	tp -> tp4_event = code;
    
    return tp;
}
#endif