#include "ckcsym.h"
char *cknetv = "Stratus VOS Network Support, 7.0.010,  18 Oct 1999";

/*  C K L N E T  --  Network support  */
/*
  Authors:

  Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
    Columbia University Center for Computing Activities.
  netopen() routine for TCP/IP originally by Ken Yap, Rochester University
    (ken@cs.rochester.edu) (no longer at that address).
  Missing pieces for Excelan sockets library from William Bader, Moravian
    College <bader@moravian.edu>.
  TELNET protocol by Frank da Cruz.
  TGV MultiNet code by Frank da Cruz.
  MultiNet code adapted to WIN/TCP by Ray Hunter of TWG.
  MultiNet code adapted to DEC TCP/IP by Lee Tibbert of DEC and Frank da Cruz.
  SunLink X.25 support by Marcello Frutig, Catholic University,
    Rio de Janeiro, Brazil (frutig@rnp.impa.br) with fixes from
    Stefaan Eeckels, Eurokom, Luxembourg.

  Most non-BSD sockets code removed, converted for Stratus VOS, renamed to
  cklnet.c by David R. Lane, SoftCom Systems, Inc.

  Other contributions as indicated below.

  Copyright (C) 1985, 1999,
    Trustees of Columbia University in the City of New York.
    All rights reserved.  See the C-Kermit COPYING.TXT file or the 
    copyright text in the ckcmai.c module for disclaimer and permissions.
*/

#include "ckcdeb.h"
#include "ckcker.h"
#include "ckcnet.h"

#ifdef NETCONN
/* Don't need these if there is no network support. */

#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <error_codes.h>

/* only need these includes if using OS TCP/IP */
#ifdef TCPSOCKET
#include <tcp_socket.h>
#include <ioctl.h>

#ifdef CK_SOCKS				/* SOCKS Internet relay package */
#define accept  Raccept
#define bind    Rbind
#define connect Rconnect
#define getsockname Rgetsockname
#define listen Rlisten
#endif /* CK_SOCKS */

#endif /* TCPSOCKET */

/* only need these for X.25 support */
#ifdef STRATUSX25
_PROTOTYP( extern VOID s$read_event, (long *event_id, long *event_count,
	short *event_status, short *status) );

_PROTOTYP( extern VOID s$vc_call_full, (CV(32) *gateway, CV(15) *address, 
	CV(63) *facilities, CV(128) *call_data, void *opts, long *timeout,
	short *vcid, long *event_id, short *status) );

_PROTOTYP( extern VOID s$vc_call_full_sts, (CV(66) *destination, 
	short *extension, CV(63) *facilities, CV(128) *call_data, void *opts,
	long *timeout, short *vcid, long *event_id, short *status) );

_PROTOTYP( extern VOID s$vc_clear, (short *vcid, short *diag, short *status) );

_PROTOTYP( extern VOID s$vc_recv_packet, (short *vcid, short *buff_len,
	void *buff, short *packet_size, short *level, short *more,
	short *status) );

_PROTOTYP( extern VOID s$vc_recv_interrupt, (short *vcid, char *data,
	short *status) );

_PROTOTYP( extern VOID s$vc_reset, (short *vcid, short *diag, short *status) );

_PROTOTYP( extern VOID s$vc_send_interrupt, (short *vcid, char *data,
	short *status) );

_PROTOTYP( extern VOID s$vc_send_packet, (short *vcid, short *buff_len,
	void *buff, short *level, short *more, short *status) );

_PROTOTYP( extern VOID s$vc_set_no_wait_mode, (void) );

_PROTOTYP( extern VOID s$vc_set_wait_mode, (void) );

_PROTOTYP( extern VOID s$vc_status, (short *vcid, short *diag_code,
	short *status) );

extern int linkid, lcn, x25ver;
extern int revcall, closgr, cudata;
extern CHAR udata[MAXCUDATA];
extern CHAR padparms[MAXPADPARMS+1];

static long vcid_event;	/* have to save this from open */
static short x25qbit;    /* qbit out flag */
#endif /* STRATUSX25 */

static int netnowait;

extern SIGTYP (*saval)();		/* For saving alarm handler */

_PROTOTYP( VOID bgchk, (void) );
int tcp_rdns = SET_OFF;			/* Not working yet for VOS  */

/* These only go with TCP/IP */
#ifdef TCPSOCKET
#ifdef RLOGCODE
#ifdef TCPIPLIB
_PROTOTYP( static VOID rlog_oob, (CHAR *, int) );
#else /* TCPIPLIB */
_PROTOTYP( static SIGTYP rlogoobh, ( int ) );
#endif /* TCPIPLIB */
_PROTOTYP( static int rlog_ini, (VOID) );
int rlog_mode = RL_COOKED;
int rlog_stopped = 0;
#endif /* RLOGCODE */
#endif /* TCPSOCKET */

_PROTOTYP( long congspd, (void) ); /* returns speed of console device */

extern int				/* External variables */
  duplex, debses, seslog, sessft, wasclosed,
  ttyfd, quiet, msgflg, what, nettype, ttmdm;

#define NAMECPYL 100			/* Local copy of hostname */
char namecopy[NAMECPYL];                /* Referenced by ckctel.c */

char ipaddr[20] = { '\0' };		/* Global copy of IP address */
char myipaddr[20] = { '\0' };		/* Global copy of my IP address */

#endif /* NETCONN */

int ttnet = NET_NONE;			/* Network type */
int ttnproto = NP_NONE;			/* Network virtual terminal protocol */

/* 0 = don't lowercase username for Rlogin/Telnet protocol */
/* nonzero = do lowercase it.  Add a SET command if necessary... */
int ck_lcname = 0;

int tcp_incoming = 0;                   /* Incoming connection? */
extern int sstelnet;                    /* Server side telnet? */

#ifndef NOTCPOPTS

/* Skip all this if NOTCPOPTS specified. */

#ifdef SOL_SOCKET
#ifdef TCP_NODELAY
int tcp_nodelay = 0;			/* Nagle algorithm TCP_NODELAY */
#endif /* TCP_NODELAY */

#ifdef SO_DONTROUTE
int tcp_dontroute = 0;
#endif /* SO_DONTROUTE */

#ifdef SO_LINGER
int tcp_linger  = 0;			/* SO_LINGER */
int tcp_linger_tmo = 0;			/* SO_LINGER timeout */
#endif /* SO_LINGER */

#ifdef SO_SNDBUF 
int tcp_sendbuf = -1;
#endif /* SO_SNDBUF */
#ifdef SO_RCVBUF
int tcp_recvbuf = -1;
#endif /* SO_RCVBUF */
#ifdef SO_KEEPALIVE 
int tcp_keepalive = 1;
#endif /* SO_KEEPALIVE */
#endif /* SOL_SOCKET */

#endif /* NOTCPOPTS */


#ifndef NETCONN
/*
  Network support not defined.
  Dummy functions here in case #ifdef's forgotten elsewhere.
*/
int					/* Open network connection */
netopen(name, lcl, nett) char *name; int *lcl, nett; {
    return(-1);
}
int					/* Close network connection */
netclos() {
    return(-1);
}
int					/* Check network input buffer */
nettchk() {
    return(-1);
}
int					/* Flush network input buffer */
netflui() {
    return(-1);
}
int					/* Send network BREAK */
netbreak() {
    return(-1);
}
int					/* Input character from network */
netinc(timo) int timo; {
    return(-1);
}
int					/* Output character to network */
#ifdef CK_ANSIC
nettoc(char c)
#else
nettoc(c) char c;
#endif /* CK_ANSIC */
/* nettoc */ {
    return(-1);
}
int
nettol(s,n) char *s; int n; {
    return(-1);
}

#else /* NETCONN is defined (rest of this module...) */

#ifdef TCPSOCKET
int tcpsrfd = -1;
#ifndef NOLISTEN			/* For incoming connections */

#ifndef INADDR_ANY
#define INADDR_ANY 0           
#endif /* INADDR_ANY */

_PROTOTYP( int ttbufr, ( VOID ) );
_PROTOTYP( int tcpsrv_open, (char *, int *, int, int ) );

static unsigned short tcpsrv_port = 0;

#endif /* NOLISTEN */

static char svcbuf[80];			/* TCP service string */
static int svcnum = 0;			/* TCP port number */

#endif /* TCPSOCKET */

/* For buffered network reads... */
#define TTXBUFL 1024			/* Maybe 8K?... */

extern CHAR 	ttxbuf[TTXBUFL+1];
extern int 	ttxbp, ttxbn = 0;
/*
  Read bytes from network into internal buffer ttxbuf[], the same buffer
  that is used by ttbufr routine in ASYNC terminal io.
  To be called when input buffer is empty, i.e. when ttxbn == 0.

  Other network reading routines, like ttinc, ttinl, ttxin, should check the
  internal buffer first, and call this routine for a refill if necessary.

  Returns -1 on error, 0 if nothing happens.  When data is read successfully,
  returns number of bytes read, and sets global ttxbn to that number and
  ttxbp (the buffer pointer) to zero.
*/
int
netbufr(int timo) {				/* TT Buffer Read */
    int count;
    short status;

    if (ttnet != NET_TCPB &&		/* First make sure current net is */
	ttnet != NET_VX25) {		/* supported (VOS X.25 & OS TCP/IP) */
	return(-1);			/* if not, do nothing. */
    }

    if (ttxbn > 0)			/* Out internal buffer is not empty, */
	return(ttxbn);			/* so keep using it. */

    count = nettchk();			/* Check network input buffer, */
    if (ttxbn > 0) return(ttxbn);	/* which can put a char there! */
    if (count < 0)			/* Read error */
      return(-1);
    else if (count > TTXBUFL)		/* Too many to read */
      count = TTXBUFL;
    else if (count == 0)		/* None, so read */
      count = 1;

    debug(F101,"netbufr count 1","",count);

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	/* This is for nonblocking reads */
	if ((count = net_read(ttyfd,ttxbuf,count,0)) < 1) {
	    if (count == -1 && errno == e$caller_must_wait) {
		debug(F100,"netbufr finds nothing","",0);
		return(0);
	    } else if (count == 0) {
		debug(F100,"netbufr socket eof","",0);		
		return(-2);  /* this is really a disconnect */
	    } else {
		debug(F101,"netbufr net_read error","",errno);
		return(-2); /* say it's disconnected */
	    }
	}
    }
#endif /* TCPSOCKET */

#ifdef STRATUSX25
    if (ttnet == NET_VX25) {
	count = x25xin(sizeof(ttxbuf), ttxbuf);
	if (count <= 0)
	    return count;
    }    
#endif /* STRATUSX25 */

    ttxbp = 0;			/* Reset buffer pointer. */
    ttxbn = count;

#ifdef DEBUG
    debug(F101,"netbufr count 2","",count); /* Got some bytes. */
    if (count > 0) ttxbuf[count] = '\0';
    debug(F111,"netbufr ttxbuf",ttxbuf,ttxbp);
#endif /* DEBUG */

    return(ttxbn);			/* Return buffer count. */
}

/*
  C-Kermit network open/close functions for OS TCP/IP & X.25/X.29 Networking.
*/

/*  N E T O P E N  --  Open a network connection.  */

/*  Returns 0 on success, -1 on failure.  */

#ifdef TCPSOCKET
#define	TELNET_PORT	23	   /* Should do lookup, but it won't change */
#define RLOGIN_PORT 513
#define KERMIT_PORT 1649
#endif /* TCPSOCKET */

#ifndef NOTCPOPTS
int
ck_linger(onoff, timo) int onoff; int timo; {
/*
  The following, from William Bader, turns off the socket linger parameter,
  which makes a close() block until all data is sent.  "I don't think that
  disabling linger can ever cause kermit to lose data, but you telnet to a
  flaky server (or to our modem server when the modem is in use), disabling
  linger prevents kermit from hanging on the close if you try to exit."

  Modified by Jeff Altman to be generally useful.
*/
#ifdef SOL_SOCKET
#ifdef SO_LINGER
    debug(F100,"TCP ck_linger dummy version", "", 0);
#else
    debug(F100,"TCP ck_linger SO_LINGER not defined","",0);
#endif /* SO_LINGER */
#else
    debug(F100,"TCP ck_linger SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return(0);
}

int 
sendbuf(size) int size; {
/*
  The following, from William Bader, allows changing of socket buffer sizes,
  in case that might affect performance.

  Modified by Jeff Altman to be generally useful.
*/
#ifdef SOL_SOCKET
#ifdef SO_SNDBUF
#else
    debug(F100,"TCP sendbuf SO_SNDBUF not defined","",0);
#endif /* SO_SNDBUF */
#else
    debug(F100,"TCP sendbuf SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return(0);
}

int 
recvbuf(size) int size; {
/*
  The following, from William Bader, allows changing of socket buffer sizes,
  in case that might affect performance.

  Modified by Jeff Altman to be generally useful.
*/
#ifdef SOL_SOCKET
#ifdef SO_RCVBUF
    debug(F100,"TCP recvbuf dummy version","",0);
#else
    debug(F100,"TCP recvbuf SO_RCVBUF not defined","",0);
#endif /* SO_RCVBUF */
#else
    debug(F100,"TCP recvbuf SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return 0;
}

int
keepalive(onoff) int onoff; {
#ifdef SOL_SOCKET
#ifdef SO_KEEPALIVE
    debug(F100,"TCP keepalive dummy version","",0);
#else
    debug(F100,"TCP keepalive SO_KEEPALIVE not defined","",0);
#endif /* SO_KEEPALIVE */
#else
    debug(F100,"TCP keepalive SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return(0);
}


int
dontroute(onoff) int onoff; {
#ifdef SOL_SOCKET
#ifdef SO_DONTROUTE
    debug(F100,"TCP dontroute dummy version","",0);
#else
    debug(F100,"TCP dontroute SO_DONTROUTE not defined","",0);
#endif /* SO_DONTROUTE */
#else
    debug(F100,"TCP dontroute SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return(0);
}

int
no_delay(onoff)	 int onoff; {
#ifdef SOL_SOCKET
#ifdef TCP_NODELAY
    debug(F100,"TCP no_delay dummy version","",0);
#else
    debug(F100,"TCP no_delay TCP_NODELAY not defined","",0);
#endif /* TCP_NODELAY */
#else
    debug(F100,"TCP no_delay SO_SOCKET not defined","",0);
#endif /* SOL_SOCKET */
    return 0;
}
#endif /* NOTCPOPTS */

char *
ckgetpeer() {
#ifdef TCPSOCKET
    static struct hostent *host;
    static struct sockaddr_in saddr;
#ifdef PTX
    static size_t saddrlen;
#else
#ifdef AIX42
    /* It's size_t in 4.2 but int in 4.1 and earlier. */
    /* Note: the 4.2 man page lies; believe socket.h. */
    static size_t saddrlen;
#else
#ifdef UNIXWARE
    static size_t saddrlen;
#else  /* UNIXWARE */
#ifdef VMS
    static unsigned int saddrlen;
#else
    static int saddrlen;
#endif /* VMS */
#endif /* UNIXWARE */
#endif /* AIX42 */
#endif /* PTX */
    saddrlen = sizeof(saddr);
    if (getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen) < 0)
      return(NULL);
    host = gethostbyaddr((char *)&saddr.sin_addr,4,AF_INET);
    return((char *)host->h_name);
#else
    return(NULL);
#endif /* TCPSOCKET */
}

/*  N E T O P E N  --  Open a network connection  */
/*
  Calling conventions same as ttopen(), except third argument is network
  type rather than modem type.  Designed to be called from within ttopen.
*/
int
netopen(name, lcl, nett) char *name; int *lcl, nett; {
    char *p = NULL;
#ifdef SO_OOBINLINE
    int on = 1;
#endif /* SO_OOBINLINE */
    int i=0, x=0, dns=0;;
#ifdef TCPSOCKET
    struct servent *service=NULL, servrec;
    struct hostent *host=NULL;
    struct sockaddr_in saddr;
    extern CHAR uidbuf[];
#endif /* TCPSOCKET */

#ifdef STRATUSX25
    CV(66) destination;
    CV(15) address;
    CV(63) facilities;
    CV(128) call_data;
    long x25_timeout;
    short vcid;
    short status;
    struct {
	short version;
	short max_delay;
	short send_size;
	short recv_size;
    } x25_options;
    short sts_extension;
#endif /* STRATUSX25 */

    debug(F101,"netopen nett","",nett);
    *ipaddr = '\0';			/* Initialize IP address string */

#ifdef STRATUSX25
    if (nett == NET_VX25) {		/* If network type is X.25 */
        netclos();			/* Close any previous net connection */
        ttnproto = NP_NONE;		/* No protocol selected yet */
	sts_extension = -1;

	strncpy (namecopy, name, NAMECPYL);
	p = strchr (namecopy, ':');
	if (NULL == p) { /* no address given */
	    strcpy (&destination, namecopy);
	    strcpy (&address, "");
	}
	else { /* address in part of name */
	    *p++ = '\0';
	    strcpy (&destination, namecopy);
	    strcpy (&address, p);
	}

	if (namecopy[0] == '%') {	/* STS with %system#module */
	     /* remove % from destination if no module name given */
	    if (NULL == strchr (namecopy, '#')) /* no module name */
		strcpy (&destination, &namecopy[1]);

	    sts_extension = (*p) ? atoi (p) : 255;
	    strcpy (&call_data, ""); /* STS includes protocol ID */
	}
	else
	    memcpy (&call_data, "\x00\x04\x01\x00\x00\x00", 6); /* KLUDGE! */

	/* Set call user data if specified */
        if (cudata)
            strcat(&call_data,udata);

	strcpy (&facilities, "");
	
        /* Set reverse charge call and closed user group if requested */
	if (revcall) {		/* reverse charge requested */
	    strcat (&facilities, "\x01\x01");
	}

	/* closed user group not supported */
        if (closgr > -1) {
	    debug(F101,"x25 closed user group not supported","",closgr);
        }

	x25_options.version = 1;
	x25_options.max_delay = 0;
	x25_options.send_size = 1024;
	x25_options.recv_size = 1024;

	x25_timeout = 30 * 1024; /* lost timeout coming to netopen() */

        /* Open X.25 virtual circuit */
	if (sts_extension > -1) {
	    s$vc_call_full_sts (&destination, &sts_extension, &facilities,
		&call_data, &x25_options, &x25_timeout, &vcid,
		&vcid_event, &status);
	    debug(F101,"netopen s$vc_call_full_sts status","",status);
	}
	else {
	    s$vc_call_full ((CV(32) *)&destination, &address, &facilities,
		&call_data, &x25_options, &x25_timeout, &vcid,
		&vcid_event, &status);
	    debug(F101,"netopen s$vc_call_full status","",status);
	}

	if (status) {
            return (-1);
        }

	lcn = vcid;
	ttyfd = vcid;
	linkid = sts_extension;

        ttnet = nett;                   /* Stratus X.25 network */
        ttnproto = NP_X3;               /* PAD X.3, X.28, X.29 protocol */
        if (*lcl < 0) *lcl = 1;         /* Local mode */
        return(0);
    }
#endif /* STRATUSX25 */

#ifdef TCPSOCKET
    if (nett == NET_TCPB) {
	netclos();			/* Close any previous connection. */
	strncpy(namecopy, name, NAMECPYL);	/* Copy the hostname. */
	ttnproto = NP_NONE;		/* No protocol selected yet. */
	debug(F110,"netopen namecopy",namecopy,0);

#ifndef NOLISTEN
        if (name[0] == '*')
            return(tcpsrv_open(name, lcl, nett, 0));
#endif /* NOLISTEN */

    p = namecopy;			/* Was a service requested? */
    while (*p != '\0' && *p != ':') p++; /* Look for colon */
    if (*p == ':') {			/* Have a colon */
	debug(F110,"netopen name has colon",namecopy,0);
	*p++ = '\0';			/* Get service name or number */
#ifdef CK_URL
	/*
	   Here we have to check for various popular syntaxes:
	   host:port (our original syntax)
	   URL such as telnet:host or telnet://host/
	   Or even telnet://user:password@host:port/
	   Or a malformed URL such as generated by Netscape 4.0 like:
	   telnet:telnet or telnet::host.
	*/
	if (*p == ':')			/* a second colon */
	  *p++ = '\0';			/* get rid of that one too */
        while (*p == '/') *p++ = '\0';	/* and slashes */
	x = strlen(p);			/* Length of remainder */
	if (p[x-1] == '/')		/* If there is a trailing slash */
	  p[x-1] = '\0';		/* remove it. */
	debug(F110,"netopen namecopy after stripping",namecopy,0);
	debug(F110,"netopen p after stripping",p,0);
	service = getservbyname(namecopy,"tcp");
	if (service || !xxstrcmp("rlogin",namecopy,NAMECPYL)) {
	    char temphost[80], tempservice[80];
	    char * q = p, *r = p, *w = p;
	    /* Check for userid and possibly password */
	    while (*p != '\0' && *p != '@') p++; /* look for @ */
	    if (*p == '@') {
		/* found username and perhaps password */
		debug(F110,"netopen namecopy found @","",0);
		*p = '\0'; p++;
		while (*w != '\0' && *w != ':')
		  w++;
		if (*w == ':')
		  *w++ = '\0';
		/* r now points to username, save it and discard password */
		debug(F110,"netopen namecopy username",r,0);
		debug(F110,"netopen namecopy password",w,0);
		strncpy(uidbuf,r,64);
		q = p;			/* Host after user and pwd */
	    } else {
		p = q;			/* No username or password */
	    }
	    /* Now we must look for the optional port. */
	    debug(F110,"netopen x p",p,0);
	    debug(F110,"netopen x q",q,0);
	    while (*p != '\0' && *p != ':') /* Look for another colon */
	      p++;
	    if (*p == ':') {
		debug(F110,"netopen found port",q,0);
		*p++ = '\0';		/* Found a port name or number */
		debug(F110,"netopen port",p,0);
		if (ttnproto == NP_NONE) {
		    /* namecopy[] identifies a protocol, so use it */
		    if (!xxstrcmp("telnet",namecopy,NAMECPYL)) 
		    ttnproto = NP_TELNET;
#ifdef RLOGCODE
		    else if (!xxstrcmp("login",namecopy,NAMECPYL) ||
			     !xxstrcmp("rlogin",namecopy,NAMECPYL)) {
			ttnproto = NP_RLOGIN;
		    }
#endif /* RLOGCODE */
		}
		strncpy(tempservice,p,79);
		strncpy(temphost,q,79);
		strncpy(namecopy,temphost,NAMECPYL);
		debug(F110,"netopen tempservice",tempservice,0);
		debug(F110,"netopen temphost",temphost,0);
		x = strlen(namecopy);
		p = namecopy + x + 1;
		strncpy(p, tempservice, NAMECPYL - x - 1);
	    } else {
		/* We didn't find another port, but if q is a service */
		/* then assume that namecopy is actually a host.      */
		if (getservbyname(q,"tcp")) {
		    p = q;
		} else {
#ifdef RLOGCODE
		    /* rlogin is not a valid service */
		    if (!xxstrcmp("rlogin",namecopy,6)) {
			strncpy(namecopy,"login",NAMECPYL);
		    }
#endif /* RLOGCODE */
		    /* Reconstruct namecopy */
		    strncpy(tempservice,namecopy,79);
		    strncpy(temphost,q,79);
		    strncpy(namecopy,temphost,NAMECPYL);
		    debug(F110,"netopen tempservice",tempservice,0);
		    debug(F110,"netopen temphost",temphost,0);
		    x = strlen(namecopy);
		    p = namecopy + x + 1;
		    strncpy(p, tempservice, NAMECPYL - x - 1);
		}
	    }
	    debug(F110,"netopen URL result",namecopy,0);
	}
#endif /* CK_URL */
    } else {				/* Otherwise use telnet */
#ifdef COMMENT
	p = "telnet";
#else
	p = "23";
#endif /* COMMENT */
    }
/*
  By the time we get here, namecopy[] should hold the null-terminated
  hostname or address, and p should point to the service name or number.
*/
    debug(F110,"netopen host",namecopy,0);
    debug(F110,"netopen service requested",p,0);
    if (isdigit(*p)) {			/* Use socket number without lookup */
	service = &servrec;
	service->s_port = htons((unsigned short)atoi(p));
    } else {				/* Otherwise lookup the service name */
	service = getservbyname(p, "tcp");
    }
    if (!service) {
	fprintf(stderr, "Cannot find port for service %s\n", p);
#ifdef TGVORWIN
	debug(F101,"netopen can't get service","",socket_errno);
#else
	debug(F101,"netopen can't get service","",errno);
#endif /* TGVORWIN */
	errno = 0;			/* rather than mislead */
	return(-1);
    }
    strncpy(svcbuf,p,79);
    svcbuf[79] = '\0';
    debug(F110,"netopen service ok",svcbuf,0);

#ifdef RLOGCODE
    if (service && !strcmp("login",p) && service->s_port != htons(513)) {
	fprintf(stderr,
		"  Warning: login service on port %d instead of port 513\n", 
		 ntohs(service->s_port));
	fprintf(stderr, "  Edit SERVICES file if RLOGIN fails to connect.\n");
	debug(F101,"tcpsrv_open login on port","",ntohs(service->s_port));
    }
#endif /* RLOGCODE */


	/* Set up socket structure and get host address */

	memset((char *)&saddr, 0, sizeof(saddr));
	if ((host = gethostbyname(namecopy)) != NULL) {
            dns = 1;
	    saddr.sin_family = host->h_addrtype;
	    memcpy((caddr_t)&saddr.sin_addr, host->h_addr, host->h_length);
	} else {
#ifdef INADDRX
/* inet_addr() is of type struct in_addr */
	    struct in_addr ina;
	    unsigned long uu;

	    ina = inet_addr(namecopy);
	    uu = *(unsigned long *)&ina;

#else /* Not INADDRX */
/* inet_addr() is unsigned long */
	    unsigned long uu;
	    uu = inet_addr(namecopy);
#endif /* INADDRX */

	    if ((saddr.sin_addr.s_addr = uu) != ((unsigned long)-1))
	      saddr.sin_family = AF_INET;
	    else {
	      fprintf(stderr, "Can't get address for %s\n", namecopy);

	      debug(F101,"netopen can't get address","",errno);

	      errno = 0;			/* rather than mislead */
	      return(-1);
	  }
	}

	/* Get a file descriptor for the connection. */

	saddr.sin_port = service->s_port;
	sprintf(ipaddr,"%s", inet_ntoa(saddr.sin_addr));
	if (!quiet && *ipaddr) printf(" Trying %s...\n", ipaddr);

	if ((ttyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	    perror("TCP socket error");
	    debug(F101,"netopen socket error","",errno);
	    return (-1);
	}
	errno = 0;

#ifdef RLOGCODE							   
       /* Not part of the RLOGIN RFC, but the BSD implementation     */
       /* requires that the client port be a priviliged port (<1024) */
       /* on a Unix system this would require SuperUser permissions  */
       /* thereby saying that the root of the Unix system has given  */
       /* permission for this connection to be created		     */
       if (service->s_port == htons((unsigned short)RLOGIN_PORT)) {
	   struct sockaddr_in sin;
	   static unsigned short lport = 1024;	/* max reserved port */
	   int s_errno;

	   lport--;			/* Make sure we do not reuse a port */
	   if (lport == 512)
	     lport = 1023;

	   sin.sin_family = AF_INET;
	   sin.sin_addr.s_addr = INADDR_ANY;
	   while (1) {
	       sin.sin_port = htons(lport);
	       if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
		 break;

		 if (errno != EADDRINUSE)
		   {
		       printf("\nBind failed with errno %d  for port %d.\n",
			      errno, lport);
		       debug(F101,"rlogin bind failed","",errno);
		       perror("rlogin bind");
		       netclos();
		       return -1;
		   }
	       lport--;
	       if (lport == 512 /* lowest reserved port to use */ ) {
		   printf("\nNo reserved ports available.\n");
		   netclos();
		   return -1;
	       }
	   }
	   debug(F101,"rlogin lport","",lport);
	   ttnproto = NP_RLOGIN;
       }
#endif /* RLOGCODE  */

	/* Now connect to the socket on the other end. */

	if (connect(ttyfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
	    i = errno;			/* save error code */
#ifdef RLOGCODE
	      if (errno == EADDRINUSE && ttnproto == NP_RLOGIN) {
#ifdef TCPIPLIB
		   socket_close(ttyfd); /* Close it. */
#else
		   close(ttyfd);
#endif /* TCPIPLIB */
		   continue;		/* Try a different lport */
	       }
#endif /* RLOGCODE */
	    close(ttyfd);
	    ttyfd = -1;
	    wasclosed = 1;
	    errno = i;			/* and report this error */

	    debug(F101,"netopen connect errno","",errno);
	    return(-1);
	}
#ifdef SO_OOBINLINE
    /*
      The symbol SO_OOBINLINE is not known to Ultrix 2.0.
      It means "leave out of band data inline".  The normal value is 0x0100,
      but don't try this on systems where the symbol is undefined.
    */
	setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#endif /* SO_OOBINLINE */

	/* See if the service is TELNET. */
	if ((x = ntohs((unsigned short)service->s_port)) == TELNET_PORT)
	    ttnproto = NP_TELNET;	/* Yes, set global flag. */
	debug(F101,"netopen service","",x);
	ttnet = nett;			/* TCP/IP (sockets) network */
	if (*lcl < 0) *lcl = 1;		/* Local mode. */
	return(0);				/* Done. */
    }

#ifndef NOTCPOPTS
#ifdef SOL_SOCKET
#ifdef TCP_NODELAY
    no_delay(tcp_nodelay);
#endif /* TCP_NODELAY */
#ifdef SO_KEEPALIVE
    keepalive(tcp_keepalive);
#endif /* SO_KEEPALIVE */
#ifdef SO_LINGER 
    ck_linger(tcp_linger, tcp_linger_tmo);
#endif /* SO_LINGER */
#ifdef SO_SNDBUF
    sendbuf(tcp_sendbuf);
#endif /* SO_SNDBUF */
#ifdef SO_RCVBUF
    recvbuf(tcp_recvbuf);
#endif /* SO_RCVBUF */
#endif /* SOL_SOCKET */
#endif /* NOTCPOPTS */

    ttnet = nett;			/* TCP/IP (sockets) network */

    x = ntohs((unsigned short)service->s_port);
    svcnum = x;
    /* See if the service is TELNET. */
    if (x == TELNET_PORT) {
	if (ttnproto != NP_TCPRAW)	/* Yes, so if raw port not requested */
	  ttnproto = NP_TELNET;		/* select TELNET protocol. */
    }
#ifdef RLOGCODE
    else if (x == RLOGIN_PORT) {
	ttnproto = NP_RLOGIN;
    }
#endif /* RLOGCODE */
#ifdef COMMENT /* not yet */
    else if (x == KERMIT_PORT) {
	ttnproto = NP_KERMIT;
    }
#endif /* COMMENT */

#ifndef datageneral
/* Find out our own IP address */
    {
	struct sockaddr_in sa;

#ifndef GSOCKNAME_T
#define GSOCKNAME_T int
#ifdef UNIXWARE
#undef GSOCKNAME_T
#define GSOCKNAME_T size_t
#else
#ifdef VMS
#ifdef DEC_TCPIP
#ifdef __DECC_VER
#undef GSOCKNAME_T
#define GSOCKNAME_T size_t
#endif /* __DECC_VER */
#endif /* DEC_TCPIP */
#endif /* VMS */
#endif /* UNIXWARE */
#endif /* GSOCKNAME_T */

	GSOCKNAME_T slen;

	slen = sizeof(sa);
        /* memset is not portable, but it exists on VOS */
	memset(&sa, 0, slen);

#ifndef EXCELAN
	if (!getsockname(ttyfd, (struct sockaddr *)&sa, &slen) ) {
	    sprintf(myipaddr,"%s", (char *)inet_ntoa(sa.sin_addr));
	    debug(F110,"getsockname",myipaddr,0);
	}
#endif /* EXCELAN */
    }
#endif /* datageneral */

    if (tcp_rdns && dns || tcp_rdns == SET_ON) {
        if (!quiet) {
	    printf(" Reverse DNS Lookup... ");
	    fflush(stdout);
	}
        if (host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET)) {
	    debug(F100,"netopen gethostbyname != NULL","",0);
	    if (!quiet) {
		printf("(OK)\n");
		fflush(stdout);
	    }
	    strncpy(name,host->h_name,79);
	    strncat(name,":",80-strlen(name));
	    strncat(name,p,80-strlen(name));
	    if (!quiet) {
		printf(" %s connected on port %s\n",host->h_name,p);
	    }
	} else {
	    if (!quiet) printf("Failed.\n");
	}
    } else if (!quiet) printf("(OK)\n");
    if (!quiet) fflush(stdout);

    /* This should already have been done but just in case */
    sprintf(ipaddr,"%s", (char *)inet_ntoa(saddr.sin_addr));

#ifdef CK_AUTHENTICATION
    /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
    ck_auth_init( namecopy, uidbuf, ttyfd );
#endif /* CK_AUTHENTICATION */
    tn_ini();                           /* Reset/Start Telnet negotiations */
    if (ttchk() < 0)
	return(-1);
#ifdef RLOGCODE
    if (ttnproto == NP_RLOGIN) {
	if (rlog_ini() < 0) {
	    debug(F100,"rlogin initialization failed","",0);
	    netclos();
	    return -1;
	}
    }
#endif /* RLOGCODE */

    debug(F101,"netopen service","",svcnum);
    if (*lcl < 0) *lcl = 1;		/* Local mode. */
    return(0);
#endif /* TCPSOCKET */

/*
  Add support for other networks here.
*/
    return(-1);				/* Unsupported Network type */
}

/*  N E T C L O S  --  Close current network connection.  */

int
netclos() {
    int x = 0;
    short vcid;
    short status;
    short diag;

    if (ttyfd < 0)			/* Was open? */
      return(0);			/* Wasn't. */

#ifdef STRATUSX25
    if (ttnet == NET_VX25) {
	if (ttyfd > -1) {
	    vcid = ttyfd;
	    diag = 0;
	    s$vc_clear (&vcid, &diag, &status);
	    debug(F101,"s$vc_clear status","",status);

	    ttyfd = -1;
	    wasclosed = 1;
	    x = (status == 0) ? 0 : -1;
	}
    }
#endif /* STRATUSX25 */
#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	if (ttyfd > -1)			/* Was. */
#ifdef TNCODE
            tn_sopt(DO,TELOPT_LOGOUT);    /* Send LOGOUT option before close */
            TELOPT_UNANSWERED_DO(TELOPT_LOGOUT) = 1;
#endif /* TNCODE */
	    x = net_close(ttyfd);
    }
#endif /* TCPSOCKET */
    ttyfd = -1;				/* Mark it as closed. */
    wasclosed = 1;
    tn_init = 0;			/* Remember about telnet protocol... */
    *ipaddr = '\0';			/* Zero the IP address string */
    tcp_incoming = 0;                   /* No longer incoming */
    sstelnet = 0;			/* Client-side Telnet */

#ifdef TCPIPLIB
/*
  Empty the internal buffers so they won't be used as invalid input on
  the next connect attempt (rlogin).
*/
    ttibp = 0;
    ttibn = 0;
#endif /* TCPIPLIB */
    return(x);
}

/*  N E T T C H K  --  Check if network up, and how many bytes can be read */
/*
  Returns number of bytes waiting, or -1 if connection has been dropped.
*/
int					/* Check how many bytes are ready */
nettchk() {				/* for reading from network */
    unsigned int count = 0;
    int x, y;
    char c;

    x = y = 0;

#ifdef COMMENT
    debug(F101,"nettchk entry ttxbn","",ttxbn);
    debug(F101,"nettchk entry ttxbp","",ttxbp);
#endif
    errno = 0;

    /* for X.25, just return count of buffered characters */

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	if (net_ioctl(ttyfd,FIONREAD,(char *)&count) < 0) {
	    debug(F101,"nettchk net_ioctl error","",errno);
	    if (ttxbn < 1) return(-1);
	    else return(ttxbn);
	}
	debug(F101,"nettchk count","",count);
    }
#endif /* TCPSOCKET */

    /* count what's in network and what we have buffered */
    debug(F101,"nettchk returns","",count+ttxbn);
    return(count + ttxbn);
}

/*  N E T I N C --  Input character from network */

int			
netinc(timo) int timo; {
    debug(F101,"WARNING: netinc called, timo","",timo);
    return(-1);
}

/*  N E T T O L  --  Output a string of bytes to the network  */
/*
  Call with s = pointer to string, n = length.
  Returns number of bytes actually written on success, or
  -1 on i/o error, -2 if called improperly.
*/
int
nettol(s,n) char *s; int n; {
    int count = 0;
    short buflen = n;

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	if ((count = net_write(ttyfd,s,n,0)) < 1) {
	    debug(F101,"nettol net_write error","",errno);
	    return(-1);
	}
	debug(F111,"nettol net_write",s,count);
	return(count);
    }
#endif /* TCPSOCKET */

#ifdef STRATUSX25
    if (ttnet == NET_VX25) {
	short mbit = 0;
	short vcid = ttyfd;
	short status;

	if (0 == x25qbit && padparms[PAD_DATA_FORWARD_TIMEOUT])
	    mbit = 2; /* stream data, may combine packets */

        if (buflen > 1024)			/* only try to send 1K */
	    buflen = 1024;

	s$vc_send_packet (&vcid, &buflen, s, &x25qbit, &mbit, &status);
	s[n] = '\0';
	debug(F111,"nettol s$vc_send_packet status",s,status);

	if (status) return (-1);

	count = buflen;
	return (count);
    }
#endif /* STRATUSX25 */

   return(-2);
}

/*  N E T T O C  --   Output character to network */
/*
  Call with character to be transmitted.
  Returns 0 if transmission was successful, or
  -1 upon i/o error, or -2 if called improperly.
*/
int			
#ifdef CK_ANSIC
nettoc(char c)
#else
nettoc(c) char c;
#endif /* CK_ANSIC */
/* nettoc */ {
    unsigned char cc;

    cc = c;

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	debug(F101,"nettoc cc","",cc);
	if (net_write(ttyfd,&cc,1,0) < 1) {
	    debug(F101,"nettoc net_write error","",errno);
	    return(-1);
	}
	debug(F101,"nettoc net_write","", cc);
	return(0);
    }
#endif /* TCPSOCKET */

#ifdef STRATUSX25
    if (ttnet == NET_VX25) {
	short mbit = 2;	/* may be joined with other packets before send */
	short vcid = ttyfd;
	short buflen = 1;
	short status;

	s$vc_send_packet (&vcid, &buflen, &cc, &x25qbit, &mbit, &status);
	if (status) {
	    debug(F101,"nettoc s$vc_send_packet status",0,status);
	}
	return (status ? -2 : 0);
    }
#endif /* STRATUSX25 */

    return(-2);
}

/*  N E T F L U I  --  Flush network input buffer  */

int
netflui() {
    int n;
    CHAR ch;

    ttxbuf[ttxbp+1] = '\0';
    debug(F111,"netflui 1",ttxbuf,ttxbn);

#ifdef TNCODE
    if (ttnproto == NP_TELNET) {
        /* Netflui must process Telnet negotiations or get out of sync */
        if ((n = ttchk()) <= 0) return(0);
        while (n-- > 0) {
            ch = ttinc(1);
            if (ch == IAC) {
                extern int duplex;  /* this really shouldn't be here but ... */
                int tx = tn_doop((CHAR)(ch & 0xff),duplex,ttinc);
                if (tx == 1) duplex = 1;
                else if (tx == 2) duplex = 0;
                n = ttchk();
            }
        }
        return(0);
    } else 
#endif /* TNCODE */
    ttxbn = ttxbp = 0;			/* Flush internal buffer *FIRST* */

    if ((n = nettchk()) > 0) {		/* Now see what's waiting on the net */
	if (n > TTXBUFL) n = TTXBUFL;	/* and sponge it up */
	debug(F101,"netflui 2","",n);	/* ... */

#ifdef TCPSOCKET
	if (ttnet == NET_TCPB)
	    n = net_read(ttyfd,ttxbuf,n,0) ; /* into our buffer */
        else
	    n = 0;
#endif /* TCPSOCKET */

#ifdef STRATUSX25
	if (ttnet == NET_VX25)
	    n = 0;	/* x25 don't read to flush */
#endif /* STRATUSX25 */

	if (n >= 0) ttxbuf[n] = '\0';
	debug(F111,"netflui 3",ttxbuf,n);
	ttxbuf[0] = '\0';
    }

    return(0);
}


/* N E T W A I T -- Set a network connection back and forth
                    between wait mode and nowait mode, used by
                    contti during network connections.  Assumes
                    the caller logs the statuses.

*/
int
#ifdef CK_ANSIC
netwait(int no_wait, int sd, long *evid, long *evcnt)
#else
netwait(no_wait, sd, evid, evcnt) int no_wait; int sd; long *evid; long *evcnt;
#endif
/* netwait(int no_wait, int sd, long *evid, long *evcnt) */ {

    short status;
    int y;
    int x;
    short event_stat;

    status = 0;
    debug(F101,"netwait no_wait","",no_wait);
    debug(F101,"        sd","",sd);

    netnowait = no_wait;

#ifdef STRATUSX25
    /* X.25 we don't actually change the mode.  We stay in wait mode for
	everything except receiving packets.
    */
    if (ttnet == NET_VX25) {
	if (no_wait) { /* pass back the saved event information */
	    *evid = vcid_event;
	    s$read_event (evid, evcnt, &event_stat, &status);
	}
    }
#endif /* STRATUSX25 */

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	if (no_wait) {
	    if (0 > get_socket_event (sd, evid, evcnt)) {
		status = errno;
		return status;
	    }
	    y = 0;				/* Turn on nonblocking reads */
	    x = net_ioctl(ttyfd,FIONBIO,(char*)&y);
	    debug(F101,"netwait FIONBIO","",x);
	    if (x < 0) status = errno;
	}
	else {
	    y = 1;				/* Turn on blocking reads */
	    x = net_ioctl(ttyfd,FIONBIO,(char*)&y);
	    debug(F101,"netwait FIONBIO","",x);
	    if (x < 0) status = errno;
	}
    }
#endif /* TCPSOCKET */

    return (status);
}

/* getlocalipaddr() attempts to resolve an IP Address for the local machine.
 *   If the host is multi-homed it returns only one address.
 * 
 * Two techniques are used.  
 * (1) get the local host name and perform a reverse DNS lookup, then take
 *     the first entry;
 * (2) open a UDP socket, use it to connect to a fictitious host (it's OK,
 *    no data is sent), then retrieve the local address from the socket.
 * Note: the second technique won't work on Microsoft systems.  See 
 * Article ID: Q129065 PRB: Getsockname() Returns IP Address 0.0.0.0 for UDP
 */

int
getlocalipaddr() {
#ifndef datageneral
    struct sockaddr_in l_sa;
    struct sockaddr_in r_sa;
    GSOCKNAME_T slen = sizeof(struct sockaddr_in);
    int sock;
    int rc;
    struct in_addr laddr;

    /* if still not resolved, then try second strategy */
    /* This second strategy does not work on Windows */

    memset(&l_sa,0,slen);
    memset(&r_sa,0,slen);

    /* get a UDP socket */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock != -1) {
	/* connect to arbirary port and address (NOT loopback) */
	r_sa.sin_family = AF_INET;
	r_sa.sin_port = htons(IPPORT_ECHO);

	/* The following is an "illegal conversion" in AOS/VS */
	/* (and who knows where else) */

#ifdef INADDRX
	inaddrx = inet_addr("128.127.50.1");
	r_sa.sin_addr.s_addr = *(unsigned long *)&inaddrx;
#else
	r_sa.sin_addr.s_addr = inet_addr("128.127.50.1");
#endif /* INADDRX */
	rc = connect(sock, (struct sockaddr *) &r_sa, sizeof(struct sockaddr));
	if (!rc) {			/* get local address */
	    getsockname(sock,(struct sockaddr *)&l_sa,&slen);
#ifdef TCPIPLIB
	    socket_close(sock);		/* We're done with the socket */
#else
	    close(sock);
#endif /* TCPIPLIB */
	    if (l_sa.sin_addr.s_addr != INADDR_ANY) {
		sprintf(myipaddr,"%s", (char *)inet_ntoa(l_sa.sin_addr));
		debug(F110,"getlocalipaddr setting buf to",myipaddr,0);
		return(0);
	    }
	}
    }
    return getlocalipaddrs(myipaddr,sizeof(myipaddr),0);
#else /* datageneral */
    return(-1);
#endif /* datageneral */
}

int
getlocalipaddrs(buf,bufsz,index)
    char * buf;
    int    bufsz;
    int    index;
/* getlocalipaddrs */ {
#ifndef datageneral
    char localhost[256];
    struct hostent * host=NULL;
    struct sockaddr_in l_sa;
    struct sockaddr_in r_sa;
    GSOCKNAME_T slen = sizeof(struct sockaddr_in);
    int sock;
    int rc;
    char messageBuf[60];
    struct in_addr laddr;

    memset(&l_sa,0,slen);
    memset(&r_sa,0,slen);

    /* init local address (to zero) */
    l_sa.sin_addr.s_addr = INADDR_ANY;

#ifdef CKGHNLHOST
    rc = gethostname(localhost, 256);
    debug(F110,"getlocalipaddrs localhost",localhost,0);
#else
    /* This doesn't work on some platforms, e.g. Solaris */
    rc = 0;
    localhost[0] = '\0';
#endif /* CKGHNLHOST */
    if (!rc) {
        /* resolve host name for local address */
        host = gethostbyname(localhost);
        if (host) {
#ifdef HADDRLIST
	    if ( index < 0 || index > 63 || !host->h_addr_list[index] ) {
		buf[0] = '\0';
		return(-1);
	    }
	    l_sa.sin_addr.s_addr =
	      *((unsigned long *) (host->h_addr_list[index]));
	    sprintf(buf,"%s", (char *)inet_ntoa(l_sa.sin_addr));
	    debug(F110,"getlocalipaddrs setting buf to",buf,0);
#else	/* HADDRLIST */
	    if (index != 0) {
		buf[0] = '\0';
		return(-1);
	    }
	    l_sa.sin_addr.s_addr = *((unsigned long *) (host->h_addr));
	    sprintf(buf,"%s", (char *)inet_ntoa(l_sa.sin_addr));
	    debug(F110,"getlocalipaddrs setting buf to",buf,0);
#endif	/* HADDRLIST */
            return(0);
        } else debug(F110,
		     "getlocalipaddrs: gethostbyname() failed",
		     localhost,
		     0
		     );
    }
#endif /* datageneral */
    return(-1);
}


#ifdef TCPSOCKET
#ifdef RLOGCODE			/* TCP/IP RLOGIN protocol support code */
static int
rlog_naws() {
    struct rlog_naws {
	char id[4];
	unsigned short rows, cols, ypix, xpix;
    } nawsbuf;

    if (ttnet != NET_TCPB)
      return 0;
    if (ttnproto != NP_RLOGIN)
      return 0;
    if (!nawsflg)
      return 0;

    debug(F100,"rlogin Window Size sent","",0);

    nawsbuf.id[0] = nawsbuf.id[1] = 0xFF;
    nawsbuf.id[2] = nawsbuf.id[3] = 's';
    nawsbuf.rows = htons((unsigned short) tt_rows);
    nawsbuf.cols = htons((unsigned short) tt_cols);
    nawsbuf.ypix = htons(0);		/* y pixels */

    nawsbuf.xpix = htons(0);		/* x pixels */
    if (ttol((CHAR *) &nawsbuf, 12) < 0)
      return -1;
    return 0;
}

static int
rlog_ini() {
#ifdef RLOGOUTBUF
    char outbuf[512];
    int  outbytes=0;
#endif /* RLOGOUTBUF */
    int flag = 0;
#define TERMLEN 16
    extern char uidbuf[];
    char localuser[255];
    int userlen = 0;
    char terminal[TERMLEN+1];
#ifdef CONGSPD
#define CONSPDLEN 16
    char conspeed[CONSPDLEN+1];
    long conspd = -1L;
#endif /* CONGSPD */
    int i, n;

    int rc = 0;
    tn_ini();                   /* This call will reset all of the Telnet */
                                /* options and then quit.  We need to do  */
                                /* this since we use the Telnet options   */
                                /* to hold various state information      */
    duplex = 0;			/* Rlogin is always remote echo */

#ifdef CK_TTGWSIZ
/*
  But compute the values anyway before the first read since the out-
  of-band NAWS request would arrive before the first data byte (NULL).
*/

    debug(F101,"rlog_ini tt_rows 1","",tt_rows);
    debug(F101,"rlog_ini tt_cols 1","",tt_cols);
    if (tt_rows < 0 || tt_cols < 0) {	/* Not known yet */
	ttgwsiz();			/* Try to find out */
    }
    debug(F101,"rlog_ini tt_rows 2","",tt_rows);
    debug(F101,"rlog_ini tt_cols 2","",tt_cols);
#endif /* CK_TTGWSIZ */
    ttflui();                           /* Start by flushing the buffers */

    rlog_mode = RL_COOKED;
#ifdef RLOGOUTBUF
    outbuf[outbytes++] = 0;
#else 
    ttoc(0);				/* Send an initial NUL as wake-up */
#endif /* RLOGOUTBUF */

    /* Followed by client username ... */

    localuser[0] = '\0';
    {
	char *s; char *p;
	char * user = getenv("USER");
	debug(F110,"rlogin getenv(USER)",user,0);
	if (!user)
	  user = "";
	userlen = strlen(user);
	s = user;
	p = (char *)localuser;
	while (*p++ = *s++) 
	  ;
	if (ck_lcname) {
	    cklower((char *)localuser);
	    debug(F110,"rlog_ini localuser 2",localuser,0);
	}
#ifdef RLOGOUTBUF
        strcpy(&outbuf[outbytes],localuser);
        outbytes += userlen+1;
#else
	rc = ttol((CHAR *)localuser,userlen+1); /* strlen + 1 */
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttol local user",rc);
    }

    /* Then the server userid... */

    if (uidbuf[0]) {
#ifdef RLOGOUTBUF
        strcpy(&outbuf[outbytes],uidbuf);
        outbytes += strlen(uidbuf) + 1;
#else
        rc = ttol((CHAR *) uidbuf,strlen(uidbuf)+1); /* strlen + 1 */
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttol server user",rc);
    } else if (localuser[0]) {
#ifdef RLOGOUTBUF
        strcpy(&outbuf[outbytes],localuser);
        outbytes += strlen(localuser) + 1;
#else
	rc = ttol((CHAR *) localuser,userlen+1);
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttol server user",rc);
    } else {
#ifdef RLOGOUTBUF
        outbuf[outbytes++]=0;
#else
	rc = ttoc(0);
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttoc NUL server user",rc);
    }
    /* Finally the terminal type and speed */

    terminal[0] = '\0';
    if (tn_term) {			/* SET TELNET TERMINAL-TYPE value */
	if (*tn_term) {			/* (if any) takes precedence. */
	    strncpy(terminal, tn_term, TERMLEN);
	    terminal[TERMLEN] = '\0';
	    flag = 1;
	}
    } else {				/* Otherwise the local terminal type */
      /* we just look at the TERM environment variable */
      /* That's one that we understand in our getenv() replacement */
      char *p = getenv("TERM");
      if (p)
	  strncpy(terminal,p,TERMLEN);
      else
	  terminal[0] = '\0';
    }
    n = strlen(terminal);
    if (n > 0) {			/* We have a terminal type */
	if (!flag) {			/* If not user-specified */
	    for (i = 0; i < n; i++)	/* then lowercase it.	 */
	      if (isupper(terminal[i]))
		terminal[i] = tolower(terminal[i]);
	}
#ifdef RLOGOUTBUF
        strcpy(&outbuf[outbytes],terminal);
        outbytes += n;
#else   
	rc = ttol((CHAR *)terminal,n);
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttol terminal",rc);
#ifdef CONGSPD
	/* conspd() is not yet defined in all ck*tio.c modules */
	/* And it will be defined at some point for us, but not now */
	conspd = congspd();
	if (conspd > 0L) {
	    sprintf(conspeed,"/%ld",conspd);
	    n = strlen(conspeed);
#ifdef RLOGOUTBUF
            strcpy(&outbuf[outbytes],conspeed);
            outbytes += n+1;
#else
	    rc = ttol((CHAR *)conspeed,n+1);
#endif /* RLOGOUTBUF */
	} else 
#endif /* CONGSPD */
        {
#ifdef RLOGOUTBUF
            strcpy(&outbuf[outbytes],"/19200");
            outbytes += 7;
#else
            rc = ttol((CHAR *)"/19200",7); /* strlen + 1 */
#endif /* RLOGOUTBUF */
        }
	debug(F111,"rlog_ini","ttol speed",rc);
    } else {
#ifdef RLOGOUTBUF
        outbuf[outbytes++]=0;
#else
	rc = ttoc(0); 
#endif /* RLOGOUTBUF */
	debug(F111,"rlog_ini","ttoc NUL terminal/speed",rc);
    }

#ifdef RLOGOUTBUF
    ttol(outbuf,outbytes);
#endif /* RLOGOUTBUF */

    /* Now we are supposed to get back a single zero byte as confirmation */
    errno = 0;
    rc = ttinc(60);
    debug(F101,"rlogin first ttinc","",rc);
    if (rc > 0) {
	debug(F101,"rlogin ttinc 1","",rc);
	printf("Rlogin protocol error - 0x%x received instead of 0x00\n", rc);
	return(-1);
    } else if (rc < 0) {
	debug(F101,"rlogin ttinc errno","",errno);
	/* printf("Network error: %d\n", errno); */
	return(-1);
    }	
    return(0);
}

static VOID
rlog_oob(oobdata, count) CHAR * oobdata; int count; {
    int i;

    for (i = 0; i<count; i++)	{
	debug(F101,"rlogin out_of_band","",oobdata[i]);
	if (oobdata[i] == 0x02) { /* Flush Buffered Data not yet displayed */
	    debug(F101,"rlogin Flush Buffered Data command","",oobdata[i]);
	    ttflui();
	}
	if (oobdata[i] & 0x10) {	/* Switch to RAW mode */
	    debug(F101,"rlogin Raw Mode command","",oobdata[i]);
	    rlog_mode = RL_RAW;
	}

	if (oobdata[i] & 0x20) {	/* Switch to COOKED mode */
	    debug(F101,"rlogin Cooked Mode command","",oobdata[i]);
	    rlog_mode = RL_COOKED;
	}
	if (oobdata[i] & 0x80) {	/* Send Window Size Info */
	    debug(F101,"rlogin Window Size command","",oobdata[i]);
	    /* Remember to send WS Info when Window Size changes */
	    nawsflg = 1;
	    rlog_naws();
	}
    }
}
#endif /* RLOGCODE */
#endif /* TCPSOCKET */

/* Send network BREAK */
/*
  Returns -1 on error, 0 if nothing happens, 1 if BREAK sent successfully.
*/
int
netbreak() {
    CHAR buf[3];

#ifdef TCPSOCKET
    if (ttnet == NET_TCPB) {
	if (ttnproto == NP_TELNET) {
#ifdef TNCODE
	    buf[0] = (CHAR) IAC; buf[1] = (CHAR) BREAK;
	    if (ttol(buf,2) < 2) return(-1);
	    debug(F101,"telnet BREAK ok","",BREAK);
	    return(1);
#else
	    debug(F100,"netbreak no TNCODE","",0);
	    return(0);
#endif /* TNCODE */
	}
	/* Insert other TCP/IP protocols here */
    }
#endif /* TCPSOCKET */

#ifdef STRATUSX25
    if (ttnet == NET_VX25) {
	breakact ();
    }
#endif /* STRATUSX25 */

    /* Insert other networks here */
    return(0);
}

#ifdef STRATUSX25

/* PAD X.3, X.28 and X.29 support */

static CHAR x29err [MAXPADPARMS+3] = { X29_ERROR, INVALID_PAD_PARM, '\0' };

/* Initialize PAD */

extern CHAR padparms[MAXPADPARMS+1];
static long ccittspeeds[] =
{
    110, 134, 300, 1200, 600, 75, 150, 1800, 200, 100,
    50, 1200, 2400, 4800, 9600, 19200, 48000, 56000, 64000
};

VOID
initpad() {
  int i;
  long speed;
  int match;	/* may have to take closest match */
  long diff;

  padparms[PAD_BREAK_CHARACTER]        = 3;  /* Break character */
  padparms[PAD_ESCAPE]                 = 1;  /* Escape permitted */
  padparms[PAD_ECHO]                   = 1;  /* Kermit PAD does echo */
  padparms[PAD_DATA_FORWARD_CHAR]      = 2;  /* forward character CR */
  padparms[PAD_DATA_FORWARD_TIMEOUT]   = 0;  /* no timeout forward condition */
  padparms[PAD_FLOW_CONTROL_BY_PAD]    = 0;  /* not used */
  padparms[PAD_SUPPRESSION_OF_SIGNALS] = 1;  /* allow PAD service signals */
  padparms[PAD_BREAK_ACTION]           = 21; /* brk action: INT pk + brk ind*/
  padparms[PAD_SUPPRESSION_OF_DATA]    = 0;  /* no supression of user data */
  padparms[PAD_PADDING_AFTER_CR]       = 0;  /* no padding after CR */
  padparms[PAD_LINE_FOLDING]           = 0;  /* no line fold */
  padparms[PAD_LINE_SPEED]             = 0;  /* line speed - don't care */
  padparms[PAD_FLOW_CONTROL_BY_USER]   = 0;  /* flow cont of PAD - not used */
  padparms[PAD_LF_AFTER_CR]            = 0;  /* no LF insertion after CR */
  padparms[PAD_PADDING_AFTER_LF]       = 0;  /* no padding after LF */
  padparms[PAD_EDITING]                = 1;  /* can edit */
  padparms[PAD_CHAR_DELETE_CHAR]       = 8;  /* character delete character */
  padparms[PAD_BUFFER_DELETE_CHAR]     = 21; /* buffer delete character */
  padparms[PAD_BUFFER_DISPLAY_CHAR]    = 18; /* buffer display character */

    speed = congspd();
    match = 0;
    for (i = 0; i < (sizeof ccittspeeds) / (sizeof ccittspeeds[0]); i++) {
	diff = speed - ccittspeeds[i];
	if (diff == 0) {
	    match = i;
	    break;
	}
	if (diff > 0 && diff < (speed - ccittspeeds[match]))
	    match = i;
    }

    padparms[PAD_LINE_SPEED] = match;  /* line speed (as good as we can get) */
}

/* Set PAD parameters */

VOID
setpad(s,n) CHAR *s; int n; {
    int i;
    CHAR *ps = s;
    char buff[30];

    if (n) {
	for (i = 0; i < n; i++) {
	    if(deblog) {
		sprintf(buff,"   set pad parm %d=%d",*ps,*(ps+1));
		debug(F100,buff,"",0);
	    }

	    /* out of range or read only */
	    if (*ps > MAXPADPARMS || *ps == PAD_LINE_SPEED)
	      x29err[i+2] = *ps;
	    else
	      padparms[*ps] = *(ps+1);
	    ps += 2;
	}
    }
    else {
	debug(F100,"  setpad calling initpad","",0);
	initpad();
    }
}

/* Read PAD parameters */

VOID
readpad(s,n,r) CHAR *s; int n; CHAR *r; {
    int i;
    CHAR *ps = s;
    CHAR *pr = r;
    char buff[30];

    *pr++ = X29_PARAMETER_INDICATION;
    if (n == 0) {			/* All supported parameters */
	for (i = 1; i <= MAXPADPARMS; i++, ps++) {
	    *pr++ = i;
	    *pr++ = padparms[i];
	    if(deblog) {
		sprintf(buff,"   read pad parm %d=%d",i,padparms[i]);
		debug(F100,buff,"",0);
	    }
	}
    }
    else {				/* list of specific parameters */
	for (i = 0; i < n; i++, ps++) {
	    if(deblog) {
		sprintf(buff,"   read pad parm %d=%d",
		    *ps, (*ps <= MAXPADPARMS) ? padparms[i] : -1);
		debug(F100,buff,"",0);
	    }
	     if (*ps > MAXPADPARMS) {
		 x29err[i+2] = *ps++;
	     } else {
		 *pr++ = *ps;
		 *pr++ = padparms[*ps++];
	     }
	}
    }
}

int
qbitpkt(s,n) CHAR *s; int n; {
    CHAR *ps = s;
    int x29cmd = *ps;
    CHAR *psa = s+1;
    CHAR x29resp[(MAXPADPARMS*2)+1];

    debug (F101,"qbitpkt, len","",n);
    switch (x29cmd) {
        case X29_SET_PARMS:
	    debug(F100,"X29 SET PAD","",0);
            setpad (psa,n/2);
            if ((int)strlen(x29err) > 2) {
                ttol (x29err,(int)strlen(x29err));
                x29err[2] = '\0';
            }
	    break;
        case X29_READ_PARMS:
	    debug(F100,"X29 READ PAD","",0);
            readpad (psa,n/2,x29resp);
            setqbit ();
            ttol (x29resp, (n > 1) ? n : (MAXPADPARMS * 2 + 1));
            if ((int)strlen(x29err) > 2) {
                ttol (x29err,(int)strlen(x29err));
                x29err[2] = '\0';
            }
            resetqbit();
            break;
        case X29_SET_AND_READ_PARMS:
	    debug(F100,"X29 SET AND READ PAD","",0);
            setpad (psa,n/2);
            readpad (psa,n/2,x29resp);
            setqbit();
            ttol (x29resp, (n > 1) ? n : (MAXPADPARMS * 2 + 1));
            if ((int)strlen(x29err) > 2) {
                ttol (x29err,(int)strlen(x29err));
                x29err [2] = '\0';
            }
            resetqbit();
	    break;
        case X29_INVITATION_TO_CLEAR:
	    debug(F100,"X29 INVITATION TO CLEAR","",0);
            (VOID) x25clear();
            return (-2) ; /* hang it up */
        case X29_INDICATION_OF_BREAK:
	    debug(F100,"X29 INDICATION OF BREAK","",0);
	    break;
    }
    return (0);
}

/* PAD break action processor */

VOID
breakact() {
    extern char x25obuf[];
    extern int obufl;
    extern int active;
    extern unsigned char tosend;
    static CHAR indbrk[3] = {
	X29_INDICATION_OF_BREAK,
	PAD_SUPPRESSION_OF_DATA,
	1
    };
    CHAR intudat, cause, diag;

    if (x25stat() < 0) return;  /* Ignore if no virtual call established */
    if (padparms[PAD_BREAK_ACTION] != 0) /* Forward condition */
        if (ttol(x25obuf,obufl) < 0) {
            perror ("\r\nCan't send characters");
            active = 0;
        } else {
            /* bzero (x25obuf,sizeof(x25obuf)); */ /**/
            obufl = 0;
            tosend = 0;
        };

    switch (padparms[PAD_BREAK_ACTION]) {

       case 0 : break;			/* do nothing */
       case 1 : /* send interrupt packet with interrupt user data field = 1 */
	        intudat = 1;
                x25intr (intudat);
                break;
       case 2 : /* send reset packet with cause and diag = 0 */
		cause = diag = 0;
                x25reset (cause,diag);
                break;
       case 5 : /* send interrupt packet with interrupt user data field = 0 */
		intudat = 0;
                x25intr (intudat) ;
                setqbit ();
	        /* send indication of break without a parameter field */
                ttoc(X29_INDICATION_OF_BREAK);
                resetqbit ();
                break;
       case 8 : active = 0;		/* leave data transfer */
                conol ("\r\n");
                break;
       case 21: /* send interrupt packet with interrupt user data field = 0 */
		intudat = 0;
                x25intr (intudat);
                setpad (indbrk+1,1);	/* set pad to discard input */
                setqbit ();
		/* send indication of break with parameter field */
                ttol (indbrk,sizeof(indbrk));
                resetqbit ();
                break;
     }
}

/* X.25 support functions */

/*
  Convert a null-terminated string representing an X.121 address
  to a packed BCD form.
*/
#ifdef SUNX25 /* not used for Stratus X.25 */
int
pkx121(str,bcd) char *str; CHAR *bcd; {
    int i, j;
    u_char c;

    i = j = 0;
    while (str[i]) {
        if ( i >= 15 || str [i] < '0' || str [i] > '9' )
	  return (-1);
        c = str [i] - '0';
        if ( i & 1 )
	  bcd [j++] |= c;
        else
	  bcd [j] = c << 4;
        i++;
    }
    return (i);
}
#endif /* SUNX25 */

/* Reads and prints X.25 diagnostic */

int
x25diag () {
    int i;
#ifdef SUNX25
    bzero ((char *)&diag,sizeof(diag));
    if (ioctl(ttyfd,X25_RD_CAUSE_DIAG,&diag)) {
        perror ("Reading X.25 diagnostic");
        return(-1);
    }
    if (diag.datalen > 0) {
        printf ("X.25 Diagnostic :");
        for (i = 0; i < diag.datalen; i++) printf (" %02x",diag.data[i]);
        printf ("\r\n");
    }
#endif /*  SUNX25 */

    return(0);
}

/* X.25 Out-of-Band Signal Handler */
#ifdef SUNX25
VOID
x25oobh() {
    int oobtype;
    u_char oobdata;

    (VOID) signal(SIGURG,x25oobh);
    do {
        if (ioctl(ttyfd,X25_OOB_TYPE,&oobtype)) {
            perror ("Getting signal type");
            return;
        }
        switch (oobtype) {
	  case INT_DATA:
	    if (recv(ttyfd,oobdata,1,MSG_OOB) < 0) {
		perror ("Receiving X.25 interrupt data");
		return;
	    }
	    printf ("\r\nInterrupt received, data = %d\r\n", oobdata);
	    break;
	  case VC_RESET:
	    printf ("\r\nVirtual circuit reset\r\n");
	    x25diag ();
	    break;
	  case N_RESETS:
	    printf ("\r\nReset timeout\r\n");
	    break;
	  case N_CLEARS:
	    printf ("\r\nClear timeout\r\n");
	    break;
	  case MSG_TOO_LONG:
	    printf ("\r\nMessage discarded, too long\r\n");
	    break;
	  default:
	    if (oobtype) printf("\r\nUnknown oob type %d\r\n",oobtype);
	    break;
	}
    } while (oobtype);
}
#endif /* SUNX25 */

/* Send a X.25 interrupt packet */

int
#ifdef CK_ANSIC
x25intr(char intr)
#else
x25intr(intr) char intr;
#endif /* CK_ANSIC */
/* x25intr */ {
    short vcid = ttyfd;
    short status;
    short diag;

    debug(F101,"X.25 intr, char","",(long) intr);
    s$vc_send_interrupt (&vcid, &intr, &status);
    debug(F101,"s$vc_send_interrupt status",0,status);

    s$vc_status (&vcid, &diag, &status);
    debug(F101,"s$vc_status status",0,status);

    return(0);
}

/* Reset X.25 virtual circuit */
int
#ifdef CK_ANSIC
x25reset(char cause, char diagn)
#else
x25reset(cause, diagn) char cause; char diagn;
#endif /* CK_ANSIC */
/* x25reset */ {
    short vcid = ttyfd;
    short status;
    short vc_diag;
    char  buff[50];

    sprintf (buff, "cause=%d[IGNORED], diag=%d", cause, diagn);
    vc_diag = (CHAR) diagn;
    s$vc_reset (&vcid, &vc_diag, &status);
    debug(F111,"X.25 reset, status",buff,status);
    return(0);
}

/* Clear X.25 virtual circuit */
int
x25clear() {
    short vcid = ttyfd;
    short diag = 0;
    short status;

    s$vc_clear (&vcid, &diag, &status);
    debug(F101,"X.25 clear status","",status);
    return(ttclos(0));			/* Close socket */
}

/* X.25 status */
int
x25stat() {
    if (ttyfd < 0) return (-1);
    return(0);
}

/* Set Q_BIT on */
VOID
setqbit() {
    x25qbit = 1;
}

/* Set Q_BIT off */
VOID
resetqbit() {
    x25qbit = 0;
}

/* Read n characters from X.25 circuit into buf */
int
x25xin(n,buf) int n; CHAR *buf; {
    register int x, c;
    int qpkt;
    short vcid = ttyfd;
    short buffsize = n;
    short packsize;
    short mbit;
    short qbit;
    char  intchar;
    short diag;
    short status;

    do {
	if (netnowait)	/* do this in no_wait mode */
	    s$vc_set_no_wait_mode ();

	s$vc_recv_packet (&vcid, &buffsize, buf, &packsize,
	    &qbit, &mbit, &status);
        debug(F101,"s$vc_recv_packet status","",status);

	if (netnowait) /* change it back for everything else */
	    s$vc_set_wait_mode ();

	x = (status)? (-1) : packsize;
	switch (status) {
	    case 0:
		break;

	    case e$vc_interrupt_pending:
		s$vc_recv_interrupt (&vcid, &intchar, &status);
		debug(F101,"s$vc_recv_interrupt status","",status);
		break;

	    case e$vc_reset_pending:
		s$vc_reset (&vcid, &diag, &status);
		debug(F101,"s$vc_reset status","",status);
		break;

	    case e$caller_must_wait:
		x = 0;
		break;

	    default:
		return (-2); /* I dunno, but it's broke */
	}

	if (x > 0 && qbit) { /* If Q_BIT packet, process it */
	    if ((c=qbitpkt(buf,x)) < 0)
		return(c);
	    qpkt = 1;
	} else qpkt = 0;
    } while (qpkt);

    if (x >= 0)
	buf[x] = '\0';
    else
	x = -1;

    debug(F101,"x25xin x","",x);

    return(x);
}
#endif /* STRATUSX25 */
#endif /* NETCONN */
