/*
 *	Copyright (c) 1995, 1996, Sun Microsystems, Inc.
 *
 * This software is provided by Sun ``AS IS'' and any express or implied
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose are disclaimed.
 * In no event shall Sun Microsystems be liable for any direct, indirect,
 * incidental, special, exemplary, or consequential damages.
 *
 * This software is not a product, and is provided for evaluation purposes
 * only.
 *
 * This software may not be resold without the express permission of
 * Sun Microsystems.
 *
 * Feedback on this tool may be sent to packet-shell-owner@sunroof.eng.sun.com.
 *
 * Packet Shell
 *
 */

#pragma ident	"@(#)tli.c	1.33	00/04/21 SMI"

#include <sys/types.h>
#include "mib2.h"	/* for compilation from Solaris 2.4 */

#include <inet/common.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <strings.h>

#include <tiuser.h>
#include <netinet/in.h>
#include <inet/led.h>
#include <inet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/systeminfo.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stropts.h>
#include <poll.h>
#include <sys/tihdr.h>
#include <netdir.h>
#include <netconfig.h>

#include <tcl.h>
#include "psh.h"
#include "name2int.h"

extern int t_getname(int, struct netbuf *, int);

static int tli_bind(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_state(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_event(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_error(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_strerror(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_listen(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_accept(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_connect(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcvconn(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcv(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_snd(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_unbind(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_snddis(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_sndrel(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcvdis(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcvrel(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_sync(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_info(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_mib(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_getopt(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_setopt(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_setroute(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_getname(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_sndudata(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcvudata(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_rcvuderr(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);
static int tli_atmark(ClientData clientData, Tcl_Interp *interp, int argc,
    char *argv[]);

static
struct nameint tlilevelnames[] = {
	{ "SOL_SOCKET",		SOL_SOCKET },
	{ "IPPROTO_TCP",	IPPROTO_TCP }
};

static
struct nameint ioctlargs[] = {
	{ "ANYMARK",	ANYMARK		},
	{ "LASTMARK",	LASTMARK	}
};

static
struct nameint tlierrornames[] = {
	{ "TNOERR",		0		},
	{ "TBADADDR",		TBADADDR	},
	{ "TBADOPT",		TBADOPT		},
	{ "TACCES",		TACCES		},
	{ "TBADF",		TBADF		},
	{ "TNOADDR",		TNOADDR		},
	{ "TOUTSTATE",		TOUTSTATE	},
	{ "TBADSEQ",		TBADSEQ		},
	{ "TLOOK",		TLOOK		},
	{ "TBADDATA",		TBADDATA	},
	{ "TBUFOVFLW",		TBUFOVFLW	},
	{ "TFLOW",		TFLOW		},
	{ "TNODATA",		TNODATA		},
	{ "TNODIS",		TNODIS		},
	{ "TNOUDERR",		TNOUDERR	},
	{ "TBADFLAG",		TBADFLAG	},
	{ "TNOREL",		TNOREL		},
	{ "TNOTSUPPORT",	TNOTSUPPORT	},
	{ "TSTATECHNG",		TSTATECHNG	}
};

/*
 * Structure for checking parameter of "getname" operation.
 * The operation uses t_getname() - undocumented TLI function.
 */
static
struct nameint tli_getnames[] = {
	{ "LOCALNAME",		LOCALNAME },
	{ "REMOTENAME",		REMOTENAME }
};

static
struct nameint tlioptnames[] = {

	/* SOL_SOCKET */
	{ "SO_BROADCAST",		SO_BROADCAST },
	{ "SO_DEBUG",			SO_DEBUG },
	{ "SO_DONTROUTE",		SO_DONTROUTE },
	{ "SO_ERROR",			SO_ERROR },
	{ "SO_KEEPALIVE",		SO_KEEPALIVE },
	{ "SO_LINGER",			SO_LINGER },
	{ "SO_OOBINLINE",		SO_OOBINLINE },
	{ "SO_RCVBUF",			SO_RCVBUF },
	{ "SO_SNDBUF",			SO_SNDBUF },
	{ "SO_RCVLOWAT",		SO_RCVLOWAT },
	{ "SO_SNDLOWAT",		SO_SNDLOWAT },
	{ "SO_RCVTIMEO",		SO_RCVTIMEO },
	{ "SO_SNDTIMEO",		SO_SNDTIMEO },
	{ "SO_REUSEADDR",		SO_REUSEADDR },
	{ "SO_TYPE",			SO_TYPE },
	{ "SO_USELOOPBACK",		SO_USELOOPBACK },

	/* IPPROTO_TCP */
	{ "TCP_NODELAY",		TCP_NODELAY },
	{ "TCP_MAXSEG",			TCP_MAXSEG },
	{ "TCP_NOTIFY_THRESHOLD",	TCP_NOTIFY_THRESHOLD },
	{ "TCP_ABORT_THRESHOLD",	TCP_ABORT_THRESHOLD },
	{ "TCP_CONN_NOTIFY_THRESHOLD",	TCP_CONN_NOTIFY_THRESHOLD },
	{ "TCP_CONN_ABORT_THRESHOLD",	TCP_CONN_ABORT_THRESHOLD }
};

#ifndef	T_CURRENT
#define	T_CURRENT	MI_T_CURRENT
#endif


static char *proto_name;

/*
 * End points for TLI communication.
 */
struct tli_ep {
	ep_t		te_ep;
	int		te_tliep;
	int		te_nonblock;
	struct t_call	*te_servcall;
	char		*te_netid;
};

/*
 * Initialization
 */
/*ARGSUSED0*/
int
init(proto_tbl_t *prototbl, Tcl_Interp *interp)
{
	/* set the size of a TLI end-point */
	prototbl->proto_epsize = sizeof (struct tli_ep);

	if ((proto_name = strdup(prototbl->proto_name)) == NULL) {
		Tcl_AppendResult(interp, "Internal Error: malloc: no space",
		    NULL);
		return (0);
	}
	return (1);
}

/*
 * Supported protocols for TLI end points:
 */

static char *supported_proto[] = {
	"tcp",
	"udp",
	""
};

static int
is_supported(char *pr_name)
{
	int i = 0;

	while (strlen(supported_proto[i]) != 0) {
		if (strcmp(supported_proto[i], pr_name) == 0) {
			return (1);
		}
		i++;
	}
	return (0);
}

static void
del_tli_ep(struct tli_ep *epsp)
{
	free(epsp->te_ep.ep_name);
	if (epsp->te_servcall != NULL) {
		(void) t_free((char *)epsp->te_servcall, T_CALL);
	}
	free(epsp->te_netid);
	free(epsp);
}

static char *
strt_error()
{

	switch (t_errno) {
	case 0: /* some hack */
		return ("TNOERR");
	case TBADADDR:
		return ("TBADADDR");
	case TBADOPT:
		return ("TBADOPT");
	case TACCES:
		return ("TACCES");
	case TBADF:
		return ("TBADF");
	case TNOADDR:
		return ("TNOADDR");
	case TOUTSTATE:
		return ("TOUTSTATE");
	case TBADSEQ:
		return ("TBADSEQ");
	case TSYSERR:
		return ("TSYSERR");
	case TLOOK:
		return ("TLOOK");
	case TBADDATA:
		return ("TBADDATA");
	case TBUFOVFLW:
		return ("TBUFOVFLW");
	case TFLOW:
		return ("TFLOW");
	case TNODATA:
		return ("TNODATA");
	case TNODIS:
		return ("TNODIS");
	case TNOUDERR:
		return ("TNOUDERR");
	case TBADFLAG:
		return ("TBADFLAG");
	case TNOREL:
		return ("TNOREL");
	case TNOTSUPPORT:
		return ("TNOTSUPPORT");
	case TSTATECHNG:
		return ("TSTATECHNG");
	default:
		(void) printf("unknown t_errno=%d\n", t_errno);
		return ("UNKNOWN_T_ERRNO");
	}

}

static char *
get_state(int state)
{
	static char *literal_res;

	switch (state) {
	case T_UNINIT:
		literal_res = "T_UNINIT";
		break;
	case T_UNBND:
		literal_res = "T_UNBND";
		break;
	case T_IDLE:
		literal_res = "T_IDLE";
		break;
	case T_OUTCON:
		literal_res = "T_OUTCON";
		break;
	case T_INCON:
		literal_res = "T_INCON";
		break;
	case T_DATAXFER:
		literal_res = "T_DATAXFER";
		break;
	case T_OUTREL:
		literal_res = "T_OUTREL";
		break;
	case T_INREL:
		literal_res = "T_INREL";
		break;
#ifdef T_FAKE
	case T_FAKE:
		literal_res = "T_FAKE";
		break;
#endif
#ifdef T_HACK
	case T_HACK:
		literal_res = "T_HACK";
		break;
#endif
#ifdef T_BADSTATE
	case T_BADSTATE:
		literal_res = "T_BADSTATE";
		break;
#endif
	default:
		(void) printf("unknown state=%d\n", state);
		literal_res = "UNKNOWN_STATE";
		break;
	}

	return (literal_res);
}

static int
scan_open_flags(char *s)
{
	int flags = 0;

	while (*s) {
		if (strncmp(s, "|", 1) == 0) {
			s++;
			continue;
		}
		if (strncasecmp(s, "O_RDWR", strlen("O_RDWR")) == 0) {
			s += strlen("O_RDWR");
			flags |= O_RDWR;
			continue;
		}
		if (strncasecmp(s, "O_NONBLOCK", strlen("O_NONBLOCK")) == 0) {
			s += strlen("O_NONBLOCK");
			flags |= O_NONBLOCK;
			continue;
		}
		if (strncasecmp(s, "O_NDELAY", strlen("O_NDELAY")) == 0) {
			s += strlen("O_NDELAY");
			flags |= O_NDELAY;
			continue;
		}
		/*
		 * String doesn't match anything we wanted
		 */
		return (flags);
	}
	return (flags);
}

static int
scan_sndrcv_flags(char *s)
{
	int flags = 0;

	while (*s) {
		if (strncmp(s, "|", 1) == 0) {
			s++;
			continue;
		}
		if (strncasecmp(s, "T_EXPEDITED", strlen("T_EXPEDITED")) == 0) {
			s += strlen("T_EXPEDITED");
			flags |= T_EXPEDITED;
			continue;
		}
		if (strncasecmp(s, "T_MORE", strlen("T_MORE")) == 0) {
			s += strlen("T_MORE");
			flags |= T_MORE;
			continue;
		}
		/*
		 * String doesn't match anything we wanted
		 */
		return (flags);
	}
	return (flags);
}


static int
get_ipaddr(const char *host_name, struct in_addr *ip)
{
	struct hostent *hp;

	hp = (struct hostent *)gethostbyname(host_name);
	if (hp == (struct hostent *)NULL) {
		return (TCL_ERROR);
	}
	*ip = *((struct in_addr *)hp->h_addr_list[0]);
	return (TCL_OK);
}


/*ARGSUSED0*/
int
propen(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep	*ep = (struct tli_ep *)clientData;
	int		flags = O_RDWR;
	int		fd;
	char		tmp[128];
	struct netconfig *nconf;

	if (argc > 3 || argc < 2) {
		Tcl_AppendResult(interp, "Usage: popen ", proto_name,
		    " <id> <provider-id> [<flags>]",
		    NULL);
		return (TCL_ERROR);
	}

	/*
	 * First look at the provider-id, and see if it looks cool.
	 */
	if ((nconf = getnetconfigent(argv[1])) == NULL) {
		Tcl_AppendResult(interp, proto_name, ": bad provider-id: ",
		    argv[1], NULL);
		return (TCL_ERROR);
	}

	if (!is_supported(argv[1])) {
		Tcl_AppendResult(interp, proto_name,
		    ": unsupported provider-id: ", argv[1], NULL);
		freenetconfigent(nconf);
		return (TCL_ERROR);
	}

	if (argc == 3) {
		flags = scan_open_flags(argv[2]);
	}

	fd = t_open(nconf->nc_device, flags, NULL);
	freenetconfigent(nconf);
	ep->te_tliep = fd;
	ep->te_netid = strdup(argv[1]);

	if (fd < 0) {
		Tcl_AppendResult(interp, proto_name, ": t_open failed :",
		    strerror(errno), NULL);
		del_tli_ep(ep);
		return (TCL_ERROR);
	}

	if (flags & O_NONBLOCK) {
		ep->te_nonblock = 1;
	}

	/*
	 * Now the busy part--create commands for all the tli ops
	 */

	(void) sprintf(tmp, "%s.bind", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_bind, ep, NULL);

	(void) sprintf(tmp, "%s.state", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_state, ep, NULL);

	(void) sprintf(tmp, "%s.sync", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_sync, ep, NULL);

	(void) sprintf(tmp, "%s.error", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_error, ep, NULL);

	(void) sprintf(tmp, "%s.strerror", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_strerror, ep, NULL);

	(void) sprintf(tmp, "%s.event", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_event, ep, NULL);

	(void) sprintf(tmp, "%s.unbind", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_unbind, ep, NULL);

	(void) sprintf(tmp, "%s.info", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_info, ep, NULL);

	(void) sprintf(tmp, "%s.mib", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_mib, ep, NULL);

	(void) sprintf(tmp, "%s.getopt", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_getopt, ep, NULL);

	(void) sprintf(tmp, "%s.setopt", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_setopt, ep, NULL);

	(void) sprintf(tmp, "%s.setipsr", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_setroute, ep, NULL);

	(void) sprintf(tmp, "%s.atmark", argv[0]);
	(void) Tcl_CreateCommand(interp, tmp, tli_atmark, ep, NULL);

	return (TCL_OK);
}

/*ARGSUSED0*/
int
prclose(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep = (struct tli_ep *)clientData;
	char tmp[128];

	(void) sprintf(tmp, "%s.bind", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.state", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.sync", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.error", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.event", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.unbind", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.listen", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.accept", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.connect", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.getname", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcvconn", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcv", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.peek", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.snd", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.snddis", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.sndrel", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcvdis", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcvrel", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.info", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.getopt", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.setopt", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.sndudata", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcvudata", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) sprintf(tmp, "%s.rcvuderr", argv[0]);
	(void) Tcl_DeleteCommand(interp, tmp);

	(void) t_close(ep->te_tliep);
	del_tli_ep(ep);
	return (TCL_OK);
}

/*ARGSUSED0*/
int
pinit(ClientData clientData, Tcl_Interp *interp, struct packet *pckt,
    int argc, char *argv[])
{
	if (argc < 3 || argc > 4) {
		Tcl_AppendResult(interp, "Usage: pinit ", proto_name,
		    " <pkt> <max. length> <structure length> [<data>]",
		    NULL);
		return (TCL_ERROR);
	}
	if (argc == 4) {
		if (strlen(argv[3]) > pckt->p_maxlen) {
			Tcl_AppendResult(interp, "Error: Packet length ",
			    "is too small for data", NULL);
			return (TCL_ERROR);
		}
		(void) bcopy(argv[3], pckt->p_buf, strlen(argv[3]));
		pckt->p_len = strlen(argv[3]);
	}

	if (ptbl_update(pckt, 1, NULL, pckt->p_len) == 0) {
		return (TCL_ERROR);
	}
	return (TCL_OK);
}

/*
 * Get protocol fields.
 * There are three fields we process:
 *	buflen
 *	datalen
 *	data
 */
/*ARGSUSED0*/
int
get(int element, ClientData clientData, Tcl_Interp *interp, struct packet *pckt,
    int argc, char *argv[])
{
	char	tmp[128];

	if (argc != 1) {
		Tcl_AppendResult(interp, "Usage: pget ", proto_name,
		    " <field-name>", NULL);
		return (TCL_ERROR);
	}
	/*
	 * Print one field
	 */
	if (strcmp(argv[0], "buflen") == 0) {
		(void) sprintf(tmp, "%d", pckt->p_maxlen);
		Tcl_AppendResult(interp, tmp, NULL);
	} else if (strcmp(argv[0], "datalen") == 0) {
		(void) sprintf(tmp, "%d", pckt->p_len);
		Tcl_AppendResult(interp, tmp, NULL);
	} else if (strcmp(argv[0], "data") == 0) {
		/*
		 * Produce TCL string for "packet shell" user.
		 */
		if (pckt->p_len < pckt->p_maxlen) {
			pckt->p_buf[pckt->p_len] = '\0';
		} else {
			(void) printf("\nWARNING: p_len == p_maxlen\n");
		}
		Tcl_AppendResult(interp, pckt->p_buf, NULL);
	} else {
		Tcl_AppendResult(interp, "Unknown ", proto_name, " field: ",
		    argv[0], NULL);
		return (TCL_ERROR);
	}
	return (TCL_OK);
}

/*
 * TLI specific procedures
 */

static int
tli_bind(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	struct t_bind *bnd_req = NULL;
	struct t_bind *bnd_ret = NULL;
	struct sockaddr_in myaddr;
	char hn[128];
	char tmp[128];
	ushort_t bnd_port;
	int r;
	int is_port = 0;
	int is_local = 0;
	int te;
	int qlen = 5;
	static char *literal_res;
	char *p;

	ep = (struct tli_ep *)clientData;
	if (argc > 4 || argc < 1 || argc == 3) {
		Tcl_AppendResult(interp, proto_name, " bind operation: ",
		    "Usage: ", argv[0], " [[/]<port_number> [qlen <value>]]",
		    NULL);
		return (TCL_ERROR);
	}
	if (argc >= 2) {
		p = argv[1];
		if (p[0] == '/') {
			p++;
			is_local = 1;
		}
		bnd_port = (ushort_t)atoi(p);
		is_port = 1;
	}
	if (argc == 4) {
		if (strcmp(argv[2], "qlen") != 0) {
			Tcl_AppendResult(interp, proto_name,
			    "Expected qlen keyword, got: ", argv[2], NULL);
			return (TCL_ERROR);
		}
		qlen = atoi(argv[3]);
	}
	te = ep->te_tliep;

	if (is_port) {
		bnd_req = (struct t_bind *)t_alloc(te, T_BIND, T_ADDR);
		bnd_ret = (struct t_bind *)t_alloc(te, T_BIND, T_ADDR);
		if (bnd_req == NULL || bnd_ret == NULL) {
			Tcl_AppendResult(interp, proto_name,
			    " t_alloc failed: ", NULL);
			return (TCL_ERROR);
		}

		if (is_local) {
			(void) strcpy(hn, "localhost");
		} else {
			(void) sysinfo(SI_HOSTNAME, hn, sizeof (hn));
		}
		(void) bzero(&myaddr, sizeof (struct sockaddr_in));
		r = get_ipaddr(hn, &myaddr.sin_addr);
		if (r != TCL_OK) {
			Tcl_AppendResult(interp, proto_name, " inv host: ",
			    NULL);
			(void) t_free((char *)bnd_ret, T_BIND);
			(void) t_free((char *)bnd_req, T_BIND);
			return (TCL_ERROR);
		}
		bnd_req->qlen = qlen;
		bnd_req->addr.len = sizeof (struct sockaddr_in);
		myaddr.sin_family = AF_INET;
		myaddr.sin_port = htons(bnd_port);
		(void) memcpy(bnd_req->addr.buf, &myaddr,
		    sizeof (struct sockaddr_in));
	}

	r = t_bind(te, bnd_req, bnd_ret);
	if (r == -1) {
		Tcl_AppendResult(interp, proto_name,
		    " t_bind failed: ", t_strerror(t_errno), NULL);
		if (is_port) {
			(void) t_free((char *)bnd_ret, T_BIND);
			(void) t_free((char *)bnd_req, T_BIND);
		}
		return (TCL_ERROR);
	}

	literal_res = "OK";
	if (is_port) {
		r = memcmp(bnd_req->addr.buf, bnd_ret->addr.buf,
			bnd_req->addr.len);
		if (r != 0) {
			literal_res = "ADDR_IN_USE";
		}
	}
	if (is_port) {
		(void) t_free((char *)bnd_ret, T_BIND);
		(void) t_free((char *)bnd_req, T_BIND);
	}
	Tcl_AppendResult(interp, literal_res, NULL);

	/*
	 * Now the busy part--create commands for new available ops
	 */

	(void) sprintf(tmp, "%s.connect", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_connect, ep, NULL);

	(void) sprintf(tmp, "%s.getname", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_getname, ep, NULL);

	(void) sprintf(tmp, "%s.rcvconnect", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcvconn, ep, NULL);

	(void) sprintf(tmp, "%s.listen", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_listen, ep, NULL);

	(void) sprintf(tmp, "%s.accept", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_accept, ep, NULL);

	(void) sprintf(tmp, "%s.rcv", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcv, ep, NULL);

	(void) sprintf(tmp, "%s.snd", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_snd, ep, NULL);

	(void) sprintf(tmp, "%s.snddis", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_snddis, ep, NULL);

	(void) sprintf(tmp, "%s.sndrel", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_sndrel, ep, NULL);

	(void) sprintf(tmp, "%s.rcvdis", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcvdis, ep, NULL);

	(void) sprintf(tmp, "%s.rcvrel", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcvrel, ep, NULL);

	(void) sprintf(tmp, "%s.sndudata", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_sndudata, ep, NULL);

	(void) sprintf(tmp, "%s.rcvudata", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcvudata, ep, NULL);

	(void) sprintf(tmp, "%s.rcvuderr", ep->te_ep.ep_name);
	(void) Tcl_CreateCommand(interp, tmp, tli_rcvuderr, ep, NULL);

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_state(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int state;
	static char *literal_res;

	ep = (struct tli_ep *)clientData;
	state = t_getstate(ep->te_tliep);

	if (state == -1) {
		Tcl_AppendResult(interp, proto_name, " t_getstate failed: ",
		    t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	literal_res = get_state(state);
	Tcl_AppendResult(interp, literal_res, NULL);

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_error(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	Tcl_AppendResult(interp, strt_error(), NULL);

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_strerror(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	int err = t_errno;
	char tmp[256];

	if (argc > 2 || argc < 1) {
		Tcl_AppendResult(interp, proto_name, " strerror operation: ",
		    "Usage: ", argv[0], " [<errnum>]", NULL);
		return (TCL_ERROR);
	}
	if (argc == 2) {
		if (Tcl_GetInt(interp, argv[1], &err) != TCL_OK) {
			Tcl_ResetResult(interp);
			if (name2int(argv[1], &err, tlierrornames,
			    sizeof (tlierrornames)/sizeof (struct nameint)) ==
				0) {
				Tcl_AppendResult(interp, proto_name,
					": bad <errnum>: ", argv[1], NULL);
				return (TCL_ERROR);
			}
		}
	}
	(void) strcpy(tmp, t_strerror(err));
	Tcl_AppendResult(interp, tmp, NULL);

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_event(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int event;
	char tmp[128];

	ep = (struct tli_ep *)clientData;
	event = t_look(ep->te_tliep);
	if (event == -1) {
		Tcl_AppendResult(interp, proto_name, " t_look failed: ",
		    t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	if (event == 0) { /* some hack */
		Tcl_AppendResult(interp, "T_NOEVENT", NULL);
		return (TCL_OK);
	}

	if (event == T_LISTEN) {
		(void) strcpy(tmp, "T_LISTEN");
	} else if (event == T_CONNECT) {
		(void) strcpy(tmp, "T_CONNECT");
	} else if (event == T_DATA) {
		(void) strcpy(tmp, "T_DATA");
	} else if (event == T_EXDATA) {
		(void) strcpy(tmp, "T_EXDATA");
	} else if (event == T_DISCONNECT) {
		(void) strcpy(tmp, "T_DISCONNECT");
	} else if (event == T_UDERR) {
		(void) strcpy(tmp, "T_UDERR");
	} else if (event == T_ORDREL) {
		(void) strcpy(tmp, "T_ORDREL");
#ifdef T_ERROR
	} else if (event == T_ERROR) {
		(void) strcpy(tmp, "T_ERROR");
#endif
	} else {
		(void) printf("XXX: Unexpected event = %d\n", event);
		Tcl_AppendResult(interp, proto_name,
		    " Unexpeted event", NULL);
		return (TCL_ERROR);
	}

	Tcl_AppendResult(interp, tmp, NULL);
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_unbind(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;

	r = t_unbind(ep->te_tliep);
	if (r == -1) {
		Tcl_AppendResult(interp, proto_name,
		    " t_unbind failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	t_errno = 0;
	return (TCL_OK);
}


static int
tli_connect(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	int te;
	int is_timeout = 0;
	char *host;
	ushort_t peer_port;
	struct servent *sp;
	int timeout;
	struct sockaddr_in peeraddr;
	struct t_call *call;
	ulong_t ip;
	struct pollfd pfd;

	ep = (struct tli_ep *)clientData;
	if (argc > 4 || argc < 3) {
		Tcl_AppendResult(interp, proto_name, " connect operation: ",
		    "Usage: ", argv[0], " <peer_host> <peer_port> [<timeout>]",
		    NULL);
		return (TCL_ERROR);
	}
	host = argv[1];
	if (Tcl_GetInt(interp, argv[2], &r) == TCL_OK) {
		peer_port = (ushort_t)r;
	} else if ((sp = getservbyname(argv[2], "tcp")) != NULL) {
		peer_port = sp->s_port;
		Tcl_ResetResult(interp);
	} else {
		Tcl_ResetResult(interp);
		Tcl_AppendResult(interp, proto_name, ": bad peer_port: ",
		    argv[2], NULL);
		return (TCL_ERROR);
	}

	if (argc == 4) {
		timeout = atoi(argv[3]);
		is_timeout = 1;
	}

	te = ep->te_tliep;
	if ((call = (struct t_call *)t_alloc(te, T_CALL, T_ADDR)) == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	/* Initialize a call */
	(void) bzero(&peeraddr, sizeof (struct sockaddr_in));
	if (((long)(ip = inet_addr(host))) != -1) {
		peeraddr.sin_addr.s_addr = ip;
		r = TCL_OK;
	} else {
		r = get_ipaddr(host, &peeraddr.sin_addr);
	}
	if (r != TCL_OK) {
		Tcl_AppendResult(interp, proto_name, " invalid host: ", host,
		    NULL);
		(void) t_free((char *)call, T_CALL);
		return (TCL_ERROR);
	}
	call->addr.len = sizeof (struct sockaddr_in);
	peeraddr.sin_family = AF_INET;
	peeraddr.sin_port = htons(peer_port);
	(void) memcpy(call->addr.buf, &peeraddr, sizeof (struct sockaddr_in));

	if (is_timeout && !(ep->te_nonblock)) {
		/*
		 * Setting temporary nonblock flag to execute below
		 * t_connect() in asynchronous mode,
		 * if <timeout> is present.
		 */
		(void) fcntl(te, F_SETFL, O_RDWR|O_NONBLOCK);
	}
	r = t_connect(te, call, (struct t_call *)NULL);
	if (is_timeout) {
		pfd.fd = te;
		pfd.events = POLLIN|POLLRDNORM;
		pfd.revents = 0;
		r = poll(&pfd, 1, timeout);
		if (!(ep->te_nonblock)) { /* Clear temporary nonblock */
			(void) fcntl(te, F_SETFL, O_RDWR);
		}
		if (r <= 0) {
			Tcl_AppendResult(interp, "timeout", NULL);
			(void) t_free((char *)call, T_CALL);
			return (TCL_OK);
		} else if (t_look(te) == T_CONNECT) {
			t_errno = 0;
			r = t_rcvconnect(te, call);
		} else {
			if (errno) {
				Tcl_AppendResult(interp, proto_name,
				    " Unexpected event ", strerror(errno),
				    NULL);
			} else {
				Tcl_AppendResult(interp, proto_name,
				    " Unexpected t_look error ",
				    t_strerror(t_errno), NULL);
			}
			return (TCL_ERROR);
		}
	}

	(void) t_free((char *)call, T_CALL);
	if (r == 0) {
		Tcl_AppendResult(interp, "connection", NULL);
	} else {
		if (t_errno != TNODATA) {
			Tcl_AppendResult(interp, proto_name,
			    " t_connect failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODATA", NULL);
	}

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_rcvconn(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int te;
	int r;
	struct t_call *call;

	ep = (struct tli_ep *)clientData;
	te = ep->te_tliep;
	if ((call = (struct t_call *)t_alloc(te, T_CALL, T_ADDR)) == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	r = t_rcvconnect(te, call);
	(void) t_free((char *)call, T_CALL);

	if (r == 0) {
		Tcl_AppendResult(interp, "connection", NULL);
		t_errno = 0;
	} else {
		if (t_errno != TNODATA) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcvconnect failed: ", t_strerror(t_errno),
			    NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODATA", NULL);
	}
	return (TCL_OK);
}

static int
tli_listen(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	int te;
	int is_timeout = 0;
	int timeout;
	struct t_call *call;
	struct pollfd pfd;

	ep = (struct tli_ep *)clientData;
	if (argc > 2 || argc < 1) {
		Tcl_AppendResult(interp, proto_name, " listen operation: ",
		    "Usage: ", argv[0], " [<timeout>]", NULL);
		return (TCL_ERROR);
	}

	if (argc == 2) {
		timeout = atoi(argv[1]);
		is_timeout = 1;
	}

	te = ep->te_tliep;

	if (is_timeout) {
		pfd.fd = te;
		pfd.events = POLLIN|POLLRDNORM;
		pfd.revents = 0;
		r = poll(&pfd, 1, timeout);
		if (r <= 0) {
			Tcl_AppendResult(interp, "timeout", NULL);
			return (TCL_OK);
		}
	}

	if (ep->te_servcall != NULL) { /* Can reuse */
		call = (struct t_call *)ep->te_servcall;
	} else {
		call = (struct t_call *)t_alloc(te, T_CALL, T_ADDR);
		if (call == NULL) {
			Tcl_AppendResult(interp, proto_name,
			    " t_alloc failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
	}

	r = t_listen(te, call);
	if (r == 0) {
		Tcl_AppendResult(interp, "conn_request", NULL);
		t_errno = 0;
	} else {
		if (t_errno != TNODATA) {
			Tcl_AppendResult(interp, proto_name,
			    " t_listen failed: ", t_strerror(t_errno), NULL);
			(void) t_free((char *)call, T_CALL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODATA", NULL);
	}

	if (ep->te_servcall == NULL) {
		ep->te_servcall = call;
	}
	return (TCL_OK);
}

static int
tli_accept(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep = (struct tli_ep *)clientData;
	struct tli_ep *n_ep;
	int r;
	int te;
	int te1;
	struct t_call *call;

	if (argc > 2 || argc < 1) {
		Tcl_AppendResult(interp, proto_name, " accept operation: ",
		    "Usage: ", argv[0], " [<new_ep>]", NULL);
		return (TCL_ERROR);
	}

	te = ep->te_tliep;
	if (argc == 2) {
		if ((n_ep = (struct tli_ep *)psh_find_ep(argv[1])) == NULL) {
			Tcl_AppendResult(interp, proto_name, argv[0], " ",
			    argv[1], ": bad identifier", NULL);
			return (TCL_ERROR);
		}
		te1 = n_ep->te_tliep;
	} else {
		te1 = te;
	}

	if ((call = (struct t_call *)ep->te_servcall) == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " accept operation requires listen  operation", NULL);
		return (TCL_ERROR);
	}

	r = t_accept(te, te1, call);
	if (r == 0) {
		Tcl_AppendResult(interp, "connection", NULL);
		t_errno = 0;
	} else {
		Tcl_AppendResult(interp, proto_name, " t_accept failed: ",
		    t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	(void) t_free((char *)ep->te_servcall, T_CALL);
	ep->te_servcall = NULL;
	return (TCL_OK);
}

static int
tli_rcv(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	int te;
	int is_timeout = 0;
	int timeout;
	struct packet *pckt;
	int size;
	struct pollfd pfd;
	int rcv_flags = 0;

	ep = (struct tli_ep *)clientData;
	if (argc > 3 || argc < 2) {
		Tcl_AppendResult(interp, proto_name, " rcv operation: ",
		    "Usage: ", argv[0], " <pckt> [<timeout>]", NULL);
		return (TCL_ERROR);
	}

	if ((pckt = pckt_lookup(argv[1])) == NULL) {
		Tcl_AppendResult(interp, proto_name, " rcv operation: ",
		    "packet not found: ", argv[1], NULL);
		return (TCL_ERROR);
	}

	if (argc == 3) {
		timeout = atoi(argv[2]);
		is_timeout = 1;
	}

	te = ep->te_tliep;
	if (is_timeout) {
		pfd.fd = te;
		pfd.events = POLLIN|POLLRDNORM;
		pfd.revents = 0;
		r = poll(&pfd, 1, timeout);
		if (r <= 0) {
			Tcl_AppendResult(interp, "timeout", NULL);
			return (TCL_OK);
		}
	}

	size = t_rcv(te, pckt->p_buf, pckt->p_maxlen, &rcv_flags);
	if (size >= 0) {
		pckt->p_len = size;
		if (rcv_flags & T_EXPEDITED) {
			Tcl_AppendResult(interp, "expedited_packet", NULL);
		} else {
			Tcl_AppendResult(interp, "packet", NULL);
		}
		t_errno = 0;
	} else {
		if (t_errno != TNODATA) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcv failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODATA", NULL);
	}

	return (TCL_OK);
}

static int
tli_snd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	struct packet *pckt;
	int snd_flags = 0;

	ep = (struct tli_ep *)clientData;
	if (argc > 3 || argc < 2) {
		Tcl_AppendResult(interp, proto_name, " snd operation: ",
		    "Usage: ", argv[0], " <pckt> [<snd_flags>]", NULL);
		return (TCL_ERROR);
	}

	if ((pckt = pckt_lookup(argv[1])) == NULL) {
		Tcl_AppendResult(interp, proto_name, " snd operation: ",
		    "packet not found: ", argv[1], NULL);
		return (TCL_ERROR);
	}

	if (argc == 3) {
		snd_flags = scan_sndrcv_flags(argv[2]);
	}

	r = t_snd(ep->te_tliep, pckt->p_buf, pckt->p_len, snd_flags);

	if (r < 0) {
			Tcl_AppendResult(interp, proto_name,
			    " t_snd failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
	}

	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_snddis(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;
	r = t_snddis(ep->te_tliep, ep->te_servcall);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name, " t_snddis failed: ",
		    t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	if (ep->te_servcall != NULL) {
		/*
		 * It is possible after successful "listen"
		 * operation only
		 */
		(void) t_free((char *)ep->te_servcall, T_CALL);
		ep->te_servcall = NULL;
	}
	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_sndrel(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;
	r = t_sndrel(ep->te_tliep);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name, " t_sndrel failed: ",
		    t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_rcvdis(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;
	r = t_rcvdis(ep->te_tliep, NULL);
	if (r < 0) {
		if (t_errno != TNODIS) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcvdis failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODIS", NULL);
	} else {
		Tcl_AppendResult(interp, "DIS", NULL);
	}

	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_rcvrel(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;
	r = t_rcvrel(ep->te_tliep);
	if (r < 0) {
		if (t_errno != TNOREL) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcvrel failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNOREL", NULL);
	} else {
		Tcl_AppendResult(interp, "REL", NULL);
	}

	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_sync(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int state;
	static char *literal_res;

	ep = (struct tli_ep *)clientData;
	state = t_sync(ep->te_tliep);

	if (state == -1) {
		Tcl_AppendResult(interp, proto_name,
		    " t_sync failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	literal_res = get_state(state);
	Tcl_AppendResult(interp, literal_res, NULL);

	return (TCL_OK);
}

/*
 * Info/Options Management
 */

static int
tli_info(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	struct t_info inf;
	struct t_info *info = &inf;
	int r;
	long value;
	char tmp[256];

	ep = (struct tli_ep *)clientData;
	if (argc > 2 || argc < 1) {
		Tcl_AppendResult(interp, proto_name, " info operation: ",
		    "Usage: ", argv[0], " [<field_name>]", NULL);
		return (TCL_ERROR);
	}
	r = t_getinfo(ep->te_tliep, info);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name,
		    " t_getinfo failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}
	if (argc == 2) {
		if (strcmp(argv[1], "addr") == 0) {
			value = info->addr;
		} else if (strcmp(argv[1], "options") == 0) {
			value = info->options;
		} else if (strcmp(argv[1], "tsdu") == 0) {
			value = info->tsdu;
		} else if (strcmp(argv[1], "etsdu") == 0) {
			value = info->etsdu;
		} else if (strcmp(argv[1], "connect") == 0) {
			value = info->connect;
		} else if (strcmp(argv[1], "discon") == 0) {
			value = info->discon;
		} else if (strcmp(argv[1], "servtype") == 0) {
			value = info->servtype;
		} else {
			Tcl_AppendResult(interp, proto_name,
			    ": bad field_name: ", argv[1], NULL);
			return (TCL_ERROR);
		}
		(void) sprintf(tmp, "%li", value);
		Tcl_AppendResult(interp, tmp, NULL);
	} else {
		(void) sprintf(tmp, "%s%li %s%li %s%li %s%li %s%li %s%li %s%li",
		    " addr ", info->addr,
		    " options ", info->options,
		    " tsdu ", info->tsdu,
		    " etsdu ", info->etsdu,
		    " connect ", info->connect,
		    " discon ", info->discon,
		    " servtype ", info->servtype);
		Tcl_AppendResult(interp, tmp, NULL);
	}
	t_errno = 0;
	return (TCL_OK);
}

static int
tli_mib(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep		*ep;
	int			te;
	int			needed_level = MIB2_UDP;
	int			tcp = 0;
	int			r, sz;
	int			rcv_sz = 0;
	int			flags, value;
	struct strbuf		ctl;
	struct strbuf		dat;
	char			proto_buf[512];
	char			*mib_space;
	struct T_optmgmt_req 	*tor;
	struct T_optmgmt_ack	*toa;
	struct T_error_ack	*tea = (struct T_error_ack *)proto_buf;
	struct opthdr		*req;
	mib2_tcp_t		*mib;
	mib2_udp_t		*mibu;
	char			tmp[256];

	ep = (struct tli_ep *)clientData;
	if (argc != 2) {
		Tcl_AppendResult(interp, proto_name, " mib operation: ",
		    "Usage: ", argv[0], " <field_name>", NULL);
		return (TCL_ERROR);
	}
	te = ep->te_tliep;
	if (strcmp(ep->te_netid, "tcp") == 0) {
		tcp = 1;
		needed_level = MIB2_TCP;
	}

	/* Getting TCP/UDP MIB for this endpoint */

	tor = (struct T_optmgmt_req *)proto_buf;
	tor->PRIM_type = T_OPTMGMT_REQ;
	tor->OPT_length = sizeof (struct opthdr);
	tor->OPT_offset = sizeof (struct T_optmgmt_req);
	tor->MGMT_flags = T_CURRENT;
	req = (struct opthdr *)(proto_buf + tor->OPT_offset);
	req->level = needed_level;
	req->name  = 0;
	req->len   = 0;
	ctl.buf = proto_buf;
	ctl.len = tor->OPT_length + tor->OPT_offset;
	if (putmsg(te, &ctl, (struct strbuf *)NULL, 0) == -1) {
		Tcl_AppendResult(interp, proto_name,
		    " putmsg failed: ", strerror(errno), NULL);
		return (TCL_ERROR);
	}

	toa = (struct T_optmgmt_ack *)proto_buf;
	req = (struct opthdr *)(proto_buf + sizeof (struct T_optmgmt_ack));
	ctl.maxlen = sizeof (proto_buf);
	flags = 0;
	r = getmsg(te, &ctl, (struct strbuf *)NULL, &flags);
	if (r == -1) {
		Tcl_AppendResult(interp, proto_name,
		    " getmsg_1 failed: ", strerror(errno), NULL);
		return (TCL_ERROR);
	}
	if (tea->PRIM_type == T_ERROR_ACK) {
		Tcl_AppendResult(interp, proto_name,
		    " getmsg_1 T_ERROR_ACK: ", strerror(errno), NULL);
		return (TCL_ERROR);
	}
	if (r != MOREDATA || toa->PRIM_type != T_OPTMGMT_ACK ||
	    toa->MGMT_flags != T_SUCCESS || req->level != needed_level) {
		(void) printf("\n\tUnexpected return of 1st getmsg():");
		(void) printf("\nEexpected: rv=%d PRIM_type=%d MGMT_flags=%d"
		    " level=%d", MOREDATA, T_OPTMGMT_ACK, T_SUCCESS,
		    needed_level);
		(void) printf("\nReturned : rv=%d PRIM_type=%ld MGMT_flags=%ld"
		    " level=%ld",
		    r, toa->PRIM_type, toa->MGMT_flags, req->level);
		Tcl_AppendResult(interp, proto_name,
		    " Unexpected result getmsg_1: ", strerror(errno), NULL);
		return (TCL_ERROR);
	}
	sz = (int)req->len;

	mib_space = (char *)malloc(sz);
	if (mib_space == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " malloc failed: ", strerror(errno), NULL);
		return (TCL_ERROR);
	}
	for (;;) {
		dat.maxlen	= sz - rcv_sz;
		dat.buf		= mib_space + rcv_sz;
		dat.len		= 0;
		flags		= 0;
		r = getmsg(te, (struct strbuf *)NULL, &dat, &flags);
		if (r == -1) {
			Tcl_AppendResult(interp, proto_name,
			    " getmsg_2 failed: ", strerror(errno), NULL);
			goto err;
		}
		rcv_sz += dat.len;
		if (r == 0) {
			break;
		} else if (r != MOREDATA) {
			(void) printf(
			    "XXX: MIB r=%d, dat.maxlen = %d, dat.len = %d\n",
			    r, dat.maxlen, dat.len);
			Tcl_AppendResult(interp, proto_name,
			    " getmsg_2 failed: wrong return value", NULL);
			goto err;
		}
		if (rcv_sz == sz) {
			/*
			 * The data buffer is full, but return value == MOREDATA
			 * It is possible when a message of higher priority
			 * has come in on the stream head.
			 */
			Tcl_AppendResult(interp, proto_name,
			    " mib operation is corrupted", NULL);
			goto err;
		}
	}
	if (rcv_sz != sz) {
		Tcl_AppendResult(interp, proto_name,
		    " mib operation - unexpected message", NULL);
		goto err;
	}
	if (tcp) {
		mib = (struct mib2_tcp *)mib_space;

		if (strcmp(argv[1], "tcpRtoAlgorithm") == 0) {
			value = mib->tcpRtoAlgorithm;
		} else if (strcmp(argv[1], "tcpRtoMin") == 0) {
			value = mib->tcpRtoMin;
		} else if (strcmp(argv[1], "tcpRtoMax") == 0) {
			value = mib->tcpRtoMax;
		} else if (strcmp(argv[1], "tcpMaxConn") == 0) {
			value = mib->tcpMaxConn;
		} else if (strcmp(argv[1], "tcpActiveOpens") == 0) {
			value = mib->tcpActiveOpens;
		} else if (strcmp(argv[1], "tcpPassiveOpens") == 0) {
			value = mib->tcpPassiveOpens;
		} else if (strcmp(argv[1], "tcpAttemptFails") == 0) {
			value = mib->tcpAttemptFails;
		} else if (strcmp(argv[1], "tcpEstabResets") == 0) {
			value = mib->tcpEstabResets;
		} else if (strcmp(argv[1], "tcpCurrEstab") == 0) {
			value = mib->tcpCurrEstab;
		} else if (strcmp(argv[1], "tcpInSegs") == 0) {
			value = mib->tcpInSegs;
		} else if (strcmp(argv[1], "tcpOutSegs") == 0) {
			value = mib->tcpOutSegs;
		} else if (strcmp(argv[1], "tcpRetransSegs") == 0) {
			value = mib->tcpRetransSegs;
		} else if (strcmp(argv[1], "tcpConnTableSize") == 0) {
			value = mib->tcpConnTableSize;
		} else if (strcmp(argv[1], "tcpOutRsts") == 0) {
			value = mib->tcpOutRsts;
		} else if (strcmp(argv[1], "tcpOutDataSegs") == 0) {
			value = mib->tcpOutDataSegs;
		} else if (strcmp(argv[1], "tcpOutDataBytes") == 0) {
			value = mib->tcpOutDataBytes;
		} else if (strcmp(argv[1], "tcpRetransBytes") == 0) {
			value = mib->tcpRetransBytes;
		} else if (strcmp(argv[1], "tcpOutAck") == 0) {
			value = mib->tcpOutAck;
		} else if (strcmp(argv[1], "tcpOutAckDelayed") == 0) {
			value = mib->tcpOutAckDelayed;
		} else if (strcmp(argv[1], "tcpOutUrg") == 0) {
			value = mib->tcpOutUrg;
		} else if (strcmp(argv[1], "tcpOutWinUpdate") == 0) {
			value = mib->tcpOutWinUpdate;
		} else if (strcmp(argv[1], "tcpOutWinProbe") == 0) {
			value = mib->tcpOutWinProbe;
		} else if (strcmp(argv[1], "tcpOutControl") == 0) {
			value = mib->tcpOutControl;
		} else if (strcmp(argv[1], "tcpOutFastRetrans") == 0) {
			value = mib->tcpOutFastRetrans;
		} else if (strcmp(argv[1], "tcpInAckSegs") == 0) {
			value = mib->tcpInAckSegs;
		} else if (strcmp(argv[1], "tcpInAckBytes") == 0) {
			value = mib->tcpInAckBytes;
		} else if (strcmp(argv[1], "tcpInDupAck") == 0) {
			value = mib->tcpInDupAck;
		} else if (strcmp(argv[1], "tcpInAckUnsent") == 0) {
			value = mib->tcpInAckUnsent;
		} else if (strcmp(argv[1], "tcpInDataInorderSegs") == 0) {
			value = mib->tcpInDataInorderSegs;
		} else if (strcmp(argv[1], "tcpInDataInorderBytes") == 0) {
			value = mib->tcpInDataInorderBytes;
		} else if (strcmp(argv[1], "tcpInDataUnorderSegs") == 0) {
			value = mib->tcpInDataUnorderSegs;
		} else if (strcmp(argv[1], "tcpInDataUnorderBytes") == 0) {
			value = mib->tcpInDataUnorderBytes;
		} else if (strcmp(argv[1], "tcpInDataDupSegs") == 0) {
			value = mib->tcpInDataDupSegs;
		} else if (strcmp(argv[1], "tcpInDataDupBytes") == 0) {
			value = mib->tcpInDataDupBytes;
		} else if (strcmp(argv[1], "tcpInDataPartDupSegs") == 0) {
			value = mib->tcpInDataPartDupSegs;
		} else if (strcmp(argv[1], "tcpInDataPartDupBytes") == 0) {
			value = mib->tcpInDataPartDupBytes;
		} else if (strcmp(argv[1], "tcpInDataPastWinSegs") == 0) {
			value = mib->tcpInDataPastWinSegs;
		} else if (strcmp(argv[1], "tcpInDataPastWinBytes") == 0) {
			value = mib->tcpInDataPastWinBytes;
		} else if (strcmp(argv[1], "tcpInWinProbe") == 0) {
			value = mib->tcpInWinProbe;
		} else if (strcmp(argv[1], "tcpInWinUpdate") == 0) {
			value = mib->tcpInWinUpdate;
		} else if (strcmp(argv[1], "tcpInClosed") == 0) {
			value = mib->tcpInClosed;
		} else if (strcmp(argv[1], "tcpRttNoUpdate") == 0) {
			value = mib->tcpRttNoUpdate;
		} else if (strcmp(argv[1], "tcpRttUpdate") == 0) {
			value = mib->tcpRttUpdate;
		} else if (strcmp(argv[1], "tcpTimRetrans") == 0) {
			value = mib->tcpTimRetrans;
		} else if (strcmp(argv[1], "tcpTimRetransDrop") == 0) {
			value = mib->tcpTimRetransDrop;
		} else if (strcmp(argv[1], "tcpTimKeepalive") == 0) {
			value = mib->tcpTimKeepalive;
		} else if (strcmp(argv[1], "tcpTimKeepaliveProbe") == 0) {
			value = mib->tcpTimKeepaliveProbe;
		} else if (strcmp(argv[1], "tcpTimKeepaliveDrop") == 0) {
			value = mib->tcpTimKeepaliveDrop;
		} else if (sz < sizeof (struct mib2_tcp)) {
			/*
			 * Solaris 2.4
			 */
			Tcl_AppendResult(interp, proto_name,
			    ": bad field_name: ", argv[1], NULL);
			goto err;
		} else if (strcmp(argv[1], "tcpListenDrop") == 0) {
			value = mib->tcpListenDrop;
		} else if (strcmp(argv[1], "tcpListenDropQ0") == 0) {
			value = mib->tcpListenDropQ0;
		} else if (strcmp(argv[1], "tcpHalfOpenDrop") == 0) {
			value = mib->tcpHalfOpenDrop;
		} else {
			Tcl_AppendResult(interp, proto_name,
			    ": bad field_name: ", argv[1], NULL);
			goto err;
		}
	} else {
		mibu = (struct mib2_udp *)mib_space;

		if (strcmp(argv[1], "udpInDatagrams") == 0) {
			value = mibu->udpInDatagrams;
		} else if (strcmp(argv[1], "udpInErrors") == 0) {
			value = mibu->udpInErrors;
		} else if (strcmp(argv[1], "udpOutDatagrams") == 0) {
			value = mibu->udpOutDatagrams;
		} else if (strcmp(argv[1], "udpEntrySize") == 0) {
			value = mibu->udpEntrySize;
		} else {
			Tcl_AppendResult(interp, proto_name,
			    ": bad field_name: ", argv[1], NULL);
			goto err;
		}
	}
	free(mib_space);
	(void) sprintf(tmp, "%i", value);
	Tcl_AppendResult(interp, tmp, NULL);
	t_errno = 0;
	return (TCL_OK);
err:
	free(mib_space);
	return (TCL_ERROR);
}

#define	MIN_OPT_SZ 64

static int
tli_getopt(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep		*ep;
	int			r;
	struct opthdr		hdr;
	int			level;
	int			name;
	int			len = sizeof (int);
	int			opt_value;
	int			opt_value1;
	int			te;
	struct linger		*lngr;
	struct t_optmgmt	*optmgmt_req = NULL;
	struct t_optmgmt	*optmgmt_ret = NULL;
	char			tmp[256];

	ep = (struct tli_ep *)clientData;
	te = ep->te_tliep;
	if (argc != 3) {
		Tcl_AppendResult(interp, proto_name, " getopt operation: ",
		    "Usage: ", argv[0], " <level> <opt-name> ",
		    NULL);
		return (TCL_ERROR);
	}
	if (name2int(argv[1], &level, tlilevelnames,
	    sizeof (tlilevelnames)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": bad <level>: ",
		    argv[1], NULL);
		return (TCL_ERROR);
	}
	if (name2int(argv[2], &name, tlioptnames,
	    sizeof (tlioptnames)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": bad <opt-name>: ",
		    argv[2], NULL);
		return (TCL_ERROR);
	}
	optmgmt_req = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	optmgmt_ret = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	if (optmgmt_req == NULL || optmgmt_ret == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", NULL);
		return (TCL_ERROR);
	}
	if (optmgmt_req->opt.maxlen < MIN_OPT_SZ) {
		free(optmgmt_req->opt.buf);
		free(optmgmt_ret->opt.buf);
		optmgmt_req->opt.maxlen = MIN_OPT_SZ;
		optmgmt_req->opt.buf = (char *)malloc(MIN_OPT_SZ);
		optmgmt_ret->opt.maxlen = MIN_OPT_SZ;
		optmgmt_ret->opt.buf = (char *)malloc(MIN_OPT_SZ);
	}
	/*
	 * All supported options excluding SO_LINGER are sizeof (int)
	 */
	if ((level == SOL_SOCKET) && (name == SO_LINGER)) {
		len = sizeof (struct linger);
	}
	hdr.level = level;
	hdr.name = name;
	hdr.len = len;
	optmgmt_req->flags = T_CHECK;
	optmgmt_req->opt.len = sizeof (struct opthdr) + len;

	(void) memcpy(optmgmt_req->opt.buf, (char *)&hdr,
	    sizeof (struct opthdr));

	r = t_optmgmt(te, optmgmt_req, optmgmt_ret);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name,
		    " t_optmgmt failed: ", t_strerror(t_errno), NULL);
		(void) t_free((char *)optmgmt_req, T_OPTMGMT);
		(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
		return (TCL_ERROR);
	}
	if (name != SO_LINGER) {
		opt_value = (int)*((int *)(optmgmt_ret->opt.buf +
		    sizeof (struct opthdr)));
		(void) sprintf(tmp, "%d", opt_value);
	} else {
		lngr = (struct linger *)(optmgmt_ret->opt.buf +
		    sizeof (struct opthdr));
		opt_value = lngr->l_onoff;	/* option on/off */
		opt_value1 = lngr->l_linger;	/* linger time   */
		(void) sprintf(tmp, "%d %d", opt_value, opt_value1);
	}

	(void) t_free((char *)optmgmt_req, T_OPTMGMT);
	(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
	Tcl_AppendResult(interp, tmp, NULL);
	t_errno = 0;
	return (TCL_OK);
}

static int
tli_setopt(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep		*ep;
	char			tmp[128];
	int			r;
	struct opthdr		hdr;
	int			level;
	int			name;
	int			len = sizeof (int);
	int			opt_value;
	int			opt_value1;
	int			te;
	struct linger		*lngr;
	struct t_optmgmt	*optmgmt_req = NULL;
	struct t_optmgmt	*optmgmt_ret = NULL;

	ep = (struct tli_ep *)clientData;
	te = ep->te_tliep;
	if (argc < 4 || argc > 5) {
		Tcl_AppendResult(interp, proto_name, " setopt operation: ",
		    "Usage: ", argv[0],
		    " <level> <opt-name> <opt-value1> [<opt-value2>]",
		    NULL);
		return (TCL_ERROR);
	}
	if (name2int(argv[1], &level, tlilevelnames,
	    sizeof (tlilevelnames)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": bad <level>: ",
		    argv[1], NULL);
		return (TCL_ERROR);
	}
	if (name2int(argv[2], &name, tlioptnames,
	    sizeof (tlioptnames)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": bad <opt-name>: ",
		    argv[2], NULL);
		return (TCL_ERROR);
	}
	opt_value = atoi(argv[3]);
	if (name == SO_LINGER && argc != 5) {
		Tcl_AppendResult(interp, proto_name,
		    " Option (SO_LINGER) requires <opt-value2>", NULL);
		return (TCL_ERROR);
	}
	if (argc == 5) {
		opt_value1 = atoi(argv[4]);
	}
	optmgmt_req = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	optmgmt_ret = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	if (optmgmt_req == NULL || optmgmt_ret == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", NULL);
		return (TCL_ERROR);
	}
	if (optmgmt_req->opt.maxlen < MIN_OPT_SZ) {
		free(optmgmt_req->opt.buf);
		free(optmgmt_ret->opt.buf);
		optmgmt_req->opt.maxlen = MIN_OPT_SZ;
		optmgmt_req->opt.buf = (char *)malloc(MIN_OPT_SZ);
		optmgmt_ret->opt.maxlen = MIN_OPT_SZ;
		optmgmt_ret->opt.buf = (char *)malloc(MIN_OPT_SZ);
	}
	/*
	 * All supported options excluding SO_LINGER are sizeof (int)
	 */
	if ((level == SOL_SOCKET) && (name == SO_LINGER)) {
		len = sizeof (struct linger);
	}
	hdr.level = level;
	hdr.name = name;
	hdr.len = len;
	optmgmt_req->flags = T_NEGOTIATE;
	optmgmt_req->opt.len = sizeof (struct opthdr) + len;

	(void) memcpy(optmgmt_req->opt.buf, (char *)&hdr,
	    sizeof (struct opthdr));
	if (name != SO_LINGER) {
		*((int *)(optmgmt_req->opt.buf + sizeof (struct opthdr))) =
		opt_value;
	} else {
		lngr = (struct linger *)(optmgmt_req->opt.buf +
		    sizeof (struct opthdr));
		lngr->l_onoff = opt_value;	/* option on/off */
		lngr->l_linger = opt_value1;	/* linger time in secs */
	}

	r = t_optmgmt(te, optmgmt_req, optmgmt_ret);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name,
		    " t_optmgmt failed: ", t_strerror(t_errno), NULL);
		(void) t_free((char *)optmgmt_req, T_OPTMGMT);
		(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
		return (TCL_ERROR);
	}

	/* Extract of T_NEGOTIATE's results */
	if (name != SO_LINGER) {
		opt_value = (int)*((int *)(optmgmt_ret->opt.buf +
		    sizeof (struct opthdr)));
		(void) sprintf(tmp, "%d", opt_value);
	} else {
		lngr = (struct linger *)(optmgmt_ret->opt.buf +
		    sizeof (struct opthdr));
		opt_value = lngr->l_onoff;	/* option on/off */
		opt_value1 = lngr->l_linger;	/* linger time   */
		(void) sprintf(tmp, "%d %d", opt_value, opt_value1);
	}

	(void) t_free((char *)optmgmt_req, T_OPTMGMT);
	(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
	Tcl_AppendResult(interp, tmp, NULL);
	t_errno = 0;
	return (TCL_OK);
}

static int
tli_setroute(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep		*ep;
	char			*t;
	int			r, i;
	struct opthdr		hdr;
	struct sockaddr_in	taddr;
	int			level = IPPROTO_IP;
	int			name = IP_OPTIONS;
	int			route_type = IPOPT_LSRR;
	int			hl_beg = 1;
	int			hl_len = argc - 1;
	int			opt_len;
	int			buf_len;
	int			te;
	ulong_t			ha;
	struct t_optmgmt	*optmgmt_req = NULL;
	struct t_optmgmt	*optmgmt_ret = NULL;

	ep = (struct tli_ep *)clientData;
	te = ep->te_tliep;
	if (argc < 3) {
		Tcl_AppendResult(interp, proto_name,
		    " setipsr operation: ",
		    "Usage: ", argv[0],
		    "  [!] hop1 hop2 ... dst",
		    NULL);
		return (TCL_ERROR);
	}
	if (strcmp(argv[1], "!") == 0) {
		route_type = IPOPT_SSRR;
		hl_beg++;
		hl_len--;
	}
	/*
	 * code	len	ptr	IP#1	IP#2............IP#N	IPOPT_NOP
	 *	|	|	|	|	|	|	|
	 * --------------------------------------------------------------
	 *
	 */
	opt_len = 3 + (hl_len * sizeof (struct in_addr));
	buf_len = sizeof (struct opthdr) + opt_len + 1;
	optmgmt_req = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	optmgmt_ret = (struct t_optmgmt *)t_alloc(te, T_OPTMGMT, T_ALL);
	if (optmgmt_req == NULL || optmgmt_ret == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", NULL);
		return (TCL_ERROR);
	}
	if (optmgmt_req->opt.maxlen < buf_len) {
		free(optmgmt_req->opt.buf);
		free(optmgmt_ret->opt.buf);
		optmgmt_req->opt.maxlen = buf_len;
		optmgmt_req->opt.buf = (char *)malloc(buf_len);
		optmgmt_ret->opt.maxlen = buf_len;
		optmgmt_ret->opt.buf = (char *)malloc(buf_len);
	}
	hdr.level = level;
	hdr.name = name;
	hdr.len = opt_len + 1;
	optmgmt_req->flags = T_NEGOTIATE;
	optmgmt_req->opt.len = buf_len;

	(void) memcpy(optmgmt_req->opt.buf, (char *)&hdr,
	    sizeof (struct opthdr));
	t = optmgmt_req->opt.buf + sizeof (struct opthdr);
	*t++ = route_type;
	*t++ = opt_len;
	*t++ = sizeof (uint_t);
	for (i = hl_beg; i < argc; i++) {
		r = get_ipaddr(argv[i], &taddr.sin_addr);
		if (r != TCL_OK) {
			Tcl_AppendResult(interp, proto_name, " invalid host: ",
			    argv[i], NULL);
			goto err;
		}
		ha = taddr.sin_addr.s_addr;
		bcopy((char *)(&ha), t, sizeof (uint_t));
		t += sizeof (uint_t);
	}
	*t = IPOPT_NOP;  /* pad out to multiple of 4 */

	r = t_optmgmt(te, optmgmt_req, optmgmt_ret);
	if (r < 0) {
		Tcl_AppendResult(interp, proto_name,
		    " t_optmgmt failed: ", t_strerror(t_errno), NULL);
		goto err;
	}

	(void) t_free((char *)optmgmt_req, T_OPTMGMT);
	(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
	t_errno = 0;
	Tcl_AppendResult(interp, proto_name, " setipsr succeeded", NULL);
	return (TCL_OK);
err:
	(void) t_free((char *)optmgmt_req, T_OPTMGMT);
	(void) t_free((char *)optmgmt_ret, T_OPTMGMT);
	return (TCL_ERROR);
}

static int
tli_getname(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	struct netbuf *name;
	int r;
	int type;
	char *buf;
	char tmp[128];
	struct sockaddr_in *sai;
	int	udp_hack = 0;

	ep = (struct tli_ep *)clientData;
	if (argc != 2) {
		Tcl_AppendResult(interp, proto_name, " info operation: ",
		    "Usage: ", argv[0], " <type>", NULL);
		return (TCL_ERROR);
	}
	if (name2int(argv[1], &type, tli_getnames,
	    sizeof (tli_getnames)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": bad <type>: ",
		    argv[1], NULL);
		return (TCL_ERROR);
	}
	if (strcmp(ep->te_netid, "udp") == 0 &&
	    strcmp(argv[1], "REMOTENAME") == 0) {
		udp_hack = 1;
	}
	if (udp_hack) {
		if (ep->te_servcall == NULL) {
			Tcl_AppendResult(interp, proto_name,
			    " Use the operation (", argv[0],
			    ") after rcvudata only", NULL);
			return (TCL_ERROR);
		}
		buf = ep->te_servcall->addr.buf;
	} else {
		buf = (char *)malloc(128);
		name = (struct netbuf *)malloc(sizeof (struct netbuf));
		if (buf == NULL || name == NULL) {
			Tcl_AppendResult(interp, proto_name,
			    " malloc failed: ", strerror(errno), NULL);
			return (TCL_ERROR);
		}
		name->buf = buf;
		name->maxlen = 128;
		name->len = 0;
		r = t_getname(ep->te_tliep, name, type);
		if (r < 0) {
			Tcl_AppendResult(interp, proto_name,
			    " t_getname failed: ", t_strerror(t_errno), NULL);
			free(buf);
			free(name);
			return (TCL_ERROR);
		}
	}
	sai = (struct sockaddr_in *)buf;
	(void) sprintf(tmp, "%s %d", inet_ntoa(sai->sin_addr),
	    ntohs(sai->sin_port));
	Tcl_AppendResult(interp, tmp, NULL);
	if (!udp_hack) {
		free(buf);
		free(name);
	}
	t_errno = 0;
	return (TCL_OK);
}

static int
tli_sndudata(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	int te;
	char *host;
	ushort_t peer_port;
	struct servent *sp;
	struct sockaddr_in peeraddr;
	struct t_unitdata *unitdata;
	struct packet *pckt;
	ulong_t ip;

	ep = (struct tli_ep *)clientData;
	if (argc != 4) {
		Tcl_AppendResult(interp, proto_name, " sndudata operation: ",
		    "Usage: ", argv[0], "  <pkt> <peer_host> <peer_port>",
		    NULL);
		return (TCL_ERROR);
	}
	host = argv[2];
	if (Tcl_GetInt(interp, argv[3], &r) == TCL_OK) {
		peer_port = (ushort_t)r;
	} else if ((sp = getservbyname(argv[3], "udp")) != NULL) {
		peer_port = sp->s_port;
		Tcl_ResetResult(interp);
	} else {
		Tcl_ResetResult(interp);
		Tcl_AppendResult(interp, proto_name, ": bad peer_port: ",
		    argv[2], NULL);
		return (TCL_ERROR);
	}
	if ((pckt = pckt_lookup(argv[1])) == NULL) {
		Tcl_AppendResult(interp, proto_name, " sndudata operation: ",
		    "packet not found: ", argv[1], NULL);
		return (TCL_ERROR);
	}

	te = ep->te_tliep;
	unitdata = (struct t_unitdata *)t_alloc(te, T_UNITDATA, T_ADDR);
	if (unitdata == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	/* Initialize a unitdata */
	(void) bzero(&peeraddr, sizeof (struct sockaddr_in));
	if (((long)(ip = inet_addr(host))) != -1) {
		peeraddr.sin_addr.s_addr = ip;
		r = TCL_OK;
	} else {
		r = get_ipaddr(host, &peeraddr.sin_addr);
	}
	if (r != TCL_OK) {
		Tcl_AppendResult(interp, proto_name, " invalid host: ", host,
		    NULL);
		(void) t_free((char *)unitdata, T_UNITDATA);
		return (TCL_ERROR);
	}
	unitdata->addr.len = sizeof (struct sockaddr_in);
	peeraddr.sin_family = AF_INET;
	peeraddr.sin_port = htons(peer_port);
	(void) memcpy(unitdata->addr.buf, &peeraddr,
	    sizeof (struct sockaddr_in));

	unitdata->udata.len = pckt->p_len;
	unitdata->udata.maxlen = pckt->p_maxlen;
	unitdata->udata.buf = pckt->p_buf;

	r = t_sndudata(te, unitdata);

	unitdata->udata.len = 0;
	unitdata->udata.maxlen = 0;
	unitdata->udata.buf = NULL;

	(void) t_free((char *)unitdata, T_UNITDATA);
	if (r != 0) {
		Tcl_AppendResult(interp, proto_name,
		    " t_unitdata failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	return (TCL_OK);
}

static int
tli_rcvudata(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;
	int te;
	int is_timeout = 0;
	int timeout;
	struct packet *pckt;
	int size;
	struct pollfd pfd;
	int rcv_flags = 0;
	struct t_unitdata *unitdata;

	ep = (struct tli_ep *)clientData;
	if (argc > 3 || argc < 2) {
		Tcl_AppendResult(interp, proto_name, " rcvudata operation: ",
		    "Usage: ", argv[0], " <pckt> [<timeout>]", NULL);
		return (TCL_ERROR);
	}

	if ((pckt = pckt_lookup(argv[1])) == NULL) {
		Tcl_AppendResult(interp, proto_name, " rcvudata operation: ",
		    "packet not found: ", argv[1], NULL);
		return (TCL_ERROR);
	}

	if (argc == 3) {
		timeout = atoi(argv[2]);
		is_timeout = 1;
	}
	te = ep->te_tliep;
	unitdata = (struct t_unitdata *)t_alloc(te, T_UNITDATA, T_ADDR);
	if (unitdata == NULL) {
		Tcl_AppendResult(interp, proto_name,
		    " t_alloc failed: ", t_strerror(t_errno), NULL);
		return (TCL_ERROR);
	}

	te = ep->te_tliep;
	if (is_timeout) {
		pfd.fd = te;
		pfd.events = POLLIN|POLLRDNORM;
		pfd.revents = 0;
		r = poll(&pfd, 1, timeout);
		if (r <= 0) {
			Tcl_AppendResult(interp, "timeout", NULL);
			return (TCL_OK);
		}
	}

	unitdata->udata.len = 0;
	unitdata->udata.maxlen = pckt->p_maxlen;
	unitdata->udata.buf = pckt->p_buf;
	unitdata->addr.len = 0;

	r = t_rcvudata(te, unitdata, &rcv_flags);
	size = unitdata->udata.len;
	unitdata->udata.len = 0;
	unitdata->udata.maxlen = 0;
	unitdata->udata.buf = NULL;
	if (r >= 0) {
		/*
		 * Conservation of remote address for further using
		 * in "<tli_id>.getname REMOTENAME" operation
		 */
		if (ep->te_servcall == NULL) {
			ep->te_servcall =
			    (struct t_call *)t_alloc(te, T_CALL, T_ADDR);
		}
		(ep->te_servcall)->addr.len = unitdata->addr.len;
		(ep->te_servcall)->addr.maxlen = unitdata->addr.maxlen;
		(void) memcpy((ep->te_servcall)->addr.buf, unitdata->addr.buf,
		    unitdata->addr.len);
	}
	(void) t_free((char *)unitdata, T_UNITDATA);

	if (r >= 0) {
		pckt->p_len = size;

		Tcl_AppendResult(interp, "packet", NULL);
		t_errno = 0;
	} else {
		if (t_errno != TNODATA) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcv failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNODATA", NULL);
	}

	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_rcvuderr(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep *ep;
	int r;

	ep = (struct tli_ep *)clientData;

	r = t_rcvuderr(ep->te_tliep, (struct t_uderr *)NULL);
	if (r < 0) {
		if (t_errno != TNOUDERR) {
			Tcl_AppendResult(interp, proto_name,
			    " t_rcvuderr failed: ", t_strerror(t_errno), NULL);
			return (TCL_ERROR);
		}
		Tcl_AppendResult(interp, "TNOUDERR", NULL);
	} else {
		Tcl_AppendResult(interp, "UDERR", NULL);
	}

	t_errno = 0;
	return (TCL_OK);
}

/*ARGSUSED0*/
static int
tli_atmark(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
	struct tli_ep	*ep;
	int		ioctl_arg;
	int		res;

	ep = (struct tli_ep *)clientData;

	if (argc != 2) {
		Tcl_AppendResult(interp, "Usage: ", argv[0], " <arg>", NULL);
		return (TCL_ERROR);
	}

	if (name2int(argv[1], &ioctl_arg, ioctlargs,
	    sizeof (ioctlargs)/sizeof (struct nameint)) == 0) {
		Tcl_AppendResult(interp, proto_name, ": invalid argument: ",
		    argv[1], NULL);
		return (TCL_ERROR);
	}

	res = ioctl(ep->te_tliep, I_ATMARK, ioctl_arg);
	if (res == -1) {
		Tcl_AppendResult(interp, "ioctl failed", NULL);
		return (TCL_ERROR);
	}

	Tcl_AppendResult(interp, (res == 1) ? "" : "!", "mark", NULL);
	return (TCL_OK);

}
