char *cknetv = "Network support, 11.0.500, 22 Jul 2026";

/*  C K C N E T  --  Network support  */

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

/*
  REMINDER: Any changes made to this file that other modules depend must
  also be made to cklnet.c (for VOS) until such time as cklnet.c and this
  module are merged back together.  (Update 2022-12-06: VOS C-Kermit has
  not been released since 7.0.197 08 Feb 2000, so if the VOS version is
  ever to be resurrected, it should start with the current ckcnet.c and
  then merge back any VOS dependencies.)

  NOTE TO CONTRIBUTORS: This file, and all the other shared (ckc and cku)
  C-Kermit source files, must be compatible with C preprocessors that support
  only #ifdef, #else, #endif, #define, and #undef.  Please do not use #if,
  logical operators, or other preprocessor features in this module.  Also,
  don't use any ANSI C constructs except within #ifdef CK_ANSIC..#endif.

  Authors:

  Frank da Cruz (fdc@columbia.edu),
    Columbia University Academic Information Systems, New York City.
  Jeffrey E Altman (jaltman@secure-endpoints.com) -- Primary
    maintainer/developer since about 1996.
  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.
  Telnet protocol by Frank da Cruz and Jeffrey Altman.
  Rlogin protocol by Jeffrey E Altman.
  SSL support adapted by Jeffrey E Altman from work done by
    Tim Hudson <tjh@cryptosoft.com> +61 7 32781581
  TLS support by Jeffrey E Altman.
  HTTP support by Jeffrey E Altman.
  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.
  TCP/IP support adapted to IBM TCP/IP 1.2.1,2.0 for OS/2 by Kai Uwe Rommel.
  CMU-OpenVMS/IP modifications by Mike O'Malley, Digital (DEC),
    with subsequent improvements by Steven M Schweda (SMS).
  X.25 support by Marcello Frutig, Catholic University,
    Rio de Janeiro, Brazil (frutig@rnp.impa.br) with fixes from
    Stefaan Eeckels, Eurokom, Luxembourg.
    David Lane added support for Stratus VOS X.25 1996 (in cklnet.c).
    Stephen Riehm added support for IBM AIX X.25 in April 1998.
  IPv6 support by John Goerzen, Open Kermit project.
  Other contributions as indicated in the code.
*/
#ifdef NORLOGIN
#ifdef RLOGCODE
#undef RLOGCODE
#endif  /* RLOGCODE */
#endif  /* NORLOGIN */

#define CKCNET_C
#include "ckcsym.h"
#include "ckcdeb.h"
#include "ckcker.h"
#include "ckcasc.h"
#ifdef I386IX                           /* Has to come before ckcnet.h in */
#include <errno.h>                      /* this version, but after in others */
#endif /* I386IX */
#include "ckcnet.h"                     /* which includes ckctel.h */
#include "ckuusr.h"
#ifdef CK_SSL
#include "ck_ssl.h"
#endif /* CK_SSL */

#ifdef CK_DNS_SRV
#ifdef OS2
#ifdef NT
#include <wshelper.h>
#else /* NT */
/* !Error OS/2 does not support DNS Service Records. */
#endif /* NT */
#else /* OS2 */
#include <arpa/inet.h>
#ifdef USE_NAMESER_COMPAT
#include <arpa/nameser_compat.h>
#endif	/* USE_NAMESER_COMPAT */

#ifdef MINIX3
#include <net/gen/resolv.h>
#include <net/gen/nameser.h>
#else
#include <arpa/nameser.h>
#include <resolv.h>
#endif /* MINIX 3 */

#ifndef PS2AIX10
#ifndef BSD4
#ifndef I386IX
#ifndef RTAIX
#include <netdb.h>
#endif /* RTAIX */
#endif /* I386IX */
#endif /* BSD4 */
#endif /* PS2AIX10 */
#endif /* OS2 */
#ifndef T_SRV
#define T_SRV 33
#endif /* T_SRV */
#ifndef T_TXT
#define T_TXT 16
#endif /* T_TXT */

/* for old Unixes and friends ... */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */

#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
#endif /* CK_DNS_SRV */

#ifdef NONET
#ifdef TCPIPLIB
#undef TCPIPLIB
#endif /* TCPIPLIB */
#endif /* NONET */

#ifndef NOMHHOST
#ifdef datageneral
#define NOMHHOST
#else
#ifdef HPUX5WINTCP
#define NOMHHOST
#endif /* HPUX5WINTCP */
#endif /* datageneral */
#endif /* NOMHHOST */

#ifdef INADDRX
  struct in_addr inaddrx;
#endif /* INADDRX */

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

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

extern int                              /* External variables */
  duplex, debses, seslog, sessft, wasclosed,
  quiet, msgflg, what, nettype, ttmdm;
extern CK_TTYFD_T ttyfd;
#ifdef IKSD
extern int inserver;
#endif /* IKSD */

char myipaddr[CK_IPADDRLEN] = { '\0' }; /* Global copy of my IP address */
char hostipaddr[CK_IPADDRLEN] = { '\0' }; /* Global remote IP address */

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

#ifndef OS2
/* Current fd-swapping hack is not thread-safe */
#define HTTP_BUFFERING
#endif	/* OS2 */

#ifdef HTTP_BUFFERING
#define HTTP_INBUFLEN 8192
static char http_inbuf[HTTP_INBUFLEN];
static int http_bufp = 0, http_count;
#endif	/* HTTP_BUFFERING */

/*
  NETLEBUF is (must be) defined for those platforms that call this
  module to do network i/o (e.g. netinc(), nettchk(), etc) rather
  than doing it themselves (ttinc(), ttchk(), etc).  In this case
  the Telnet local-echo buffers and routines are defined and referenced
  here, rather than in the ck?tio.c module.
*/
#ifdef NETLEBUF
#define LEBUFSIZ 4096
int ttpush = -1, le_data = 0;           /* These are seen from outside */
static CHAR le_buf[LEBUFSIZ];           /* These are used internally */
static int le_start = 0, le_end = 0;
int tt_push_inited = 0;
#endif /* NETLEBUF */

#ifdef CK_SOCKS                         /* SOCKS Internet relay package */
#ifdef CK_SOCKS5                        /* SOCKS 5 */
#define accept  SOCKSaccept
#define bind    SOCKSbind
#define connect SOCKSconnect
#define getsockname SOCKSgetsockname
#define listen SOCKSlisten
#else  /* Not SOCKS 5 */
#define accept  Raccept
#define bind    Rbind
#define connect Rconnect
#define getsockname Rgetsockname
#define listen Rlisten
#endif /* CK_SOCKS5 */
#endif /* CK_SOCKS */

#ifdef DEC_TCPIP
#include <time.h>
#include <inet.h>
#endif /* DEC_TCPIP */

/* Also see ckcnet.h -- hmmm, why don't we always include inet.h? */

#ifdef HPUX
#ifndef HPUX7                           /* HPUX 7.00 doesn't have it */
#ifndef HPUX6                           /* HPUX 6.00 doesn't have it */
#include <arpa/inet.h>                  /* For inet_ntoa() prototype */
#endif /* HPUX6 */
#endif /* HPUX7 */
#endif /* HPUX */

#ifdef CMU_TCPIP
#include <time.h>
#endif /* CMU_TCPIP */

#ifndef NODCLTIMEVAL
#ifdef DCLTIMEVAL                       /* UnixWare 7 */
struct timeval {                        /* And define these ourselves. */
    long tv_sec;                        /* (see comments in ckutio.c) */
    long tv_usec;
};
struct timezone {
    int tz_minuteswest;
    int tz_dsttime;
};
#endif /* DCLTIMEVAL */
#endif /* NODCLTIMEVAL */

#ifdef WINTCP

#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
/*
  The WIN/TCP code path is the same as that for MultiNet.
  Only the routine names have changed ...
*/
#define socket_read     netread
#define socket_ioctl    ioctl
#define socket_write    netwrite
#define socket_close    netclose

#ifdef OLD_TWG                         /* some routines have evolved */
        extern int vmserrno, uerrno;
#define socket_errno    uerrno
#define socket_perror   perror         /* which uses errno, not uerrno! */
#else
#define socket_errno    errno
#define socket_perror   win$perror
#endif /* OLD_TWG */

#else /* Not WINTCP */

#ifdef OSF13
#ifdef CK_ANSIC
#ifdef _NO_PROTO
#undef _NO_PROTO
#endif /* _NO_PROTO */
#endif /* CK_ANSIC */
#endif /* OSF13 */

#ifndef OS2
#ifndef I386IX
#ifndef HPUXPRE65
#include <errno.h>			/* Error number symbols */
#else
#ifndef ERRNO_INCLUDED
#include <errno.h>			/* Error number symbols */
#endif	/* ERRNO_INCLUDED */
#endif	/* HPUXPRE65 */
#endif /* I386IX */
#endif /* OS2 */

#ifdef OS2
#ifdef NT
#include <errno.h>			/* Error number symbols */
#else /* OS/2 */
#ifdef __WATCOMC__
/*
  WatcomC doesn't need errno.h
  (definitions conflict with some previous definition)
#include <errno.h>
*/
#else
#include <errno.h>			/* Error number symbols */
#endif
#endif /* NT */
#endif /* OS2 */

#include <signal.h>                     /* Everybody needs this */

#ifdef ZILOG                            /* Zilog has different name for this */
#include <setret.h>
#else
#include <setjmp.h>
#endif /* ZILOG */

#endif /* WINTCP */

#ifdef datageneral                      /* Data General AOS/VS */
#include <:usr:include:vs_tcp_errno.h>
#include <:usr:include:sys:vs_tcp_types.h>
#ifdef SELECT
/*
  NOTE: This can be compiled and linked OK with SELECT defined
  but it doesn't work at all.  Anybody who cares and knows how
  to fix it, feel free.
*/
#include <:usr:include:sys:vs_tcp_time.h>
#endif /* SELECT */
#include <:usr:include:sys:socket.h>
#include <:usr:include:netinet:in.h>
#include <:usr:include:netdb.h>
#endif /* datageneral */

#ifndef socket_errno
#define socket_errno errno
#endif /* socket_errno */

#ifdef TNCODE
extern int tn_deb;
#endif /* TNCODE */

int tcp_rdns =                          /* Reverse DNS lookup */
#ifdef DEC_TCPIP_OLD
    SET_OFF                             /* Doesn't seem to work in UCX */
#else
     SET_AUTO
#endif /* DEC_TCPIP */
      ;
#ifdef CK_DNS_SRV
int tcp_dns_srv = SET_OFF;
#endif /* CK_DNS_SRV */

#ifdef CK_IPV6
int tcp_af = TCP_AF_AUTO;               /* TCP address family (v4, v6, auto) */
#endif /* CK_IPV6 */

_PROTOTYP( char * cmcvtdate, (char *, int) );

#ifdef RLOGCODE
_PROTOTYP( int rlog_ctrl, (CHAR *, int) );
_PROTOTYP( static int rlog_oob, (CHAR *, int) );
#ifndef TCPIPLIB
_PROTOTYP( static SIGTYP rlogoobh, ( int ) );
#endif /* TCPIPLIB */
_PROTOTYP( static int rlog_ini, (CHAR *, int,
                                 struct sockaddr_in *,
                                 struct sockaddr_in *) );
int rlog_mode = RL_COOKED;
int rlog_stopped = 0;
int rlog_inband = 0;
#endif /* RLOGCODE */

#ifndef NOICP
extern int doconx;                      /* CONNECT-class command active */
#endif /* NOICP */

#ifdef IBMX25
/* This variable should probably be generalised for true client/server
 * support - ie: the fd of the listening server, accepted calls should
 * be forked or at least handled via a second fd (for IBM's X.25 -
 * ttyfd always holds the active fd - ie the server becomes inactive
 * as long as a client is connected, and becomes active again when the
 * connection is closed)
 */
int x25serverfd = 0;            /* extern in ckcnet.h */
int x25seqno = 0;               /* Connection sequence number */
int x25lastmsg = -1;            /* A cheapskate's state table */

#define X25_CLOSED      0       /* Default state: no connection, no STREAM */
#define X25_SETUP       1       /* X.25 has been set up (no connection) */
#define X25_CONNECTED   2       /* X.25 connection has been established */
int x25_state = X25_CLOSED;     /* Default state */
#endif /* IBMX25 */

#ifndef DEBUG
#define deblog 0
#endif /* DEBUG */

#ifdef CK_NAWS                          /* Negotiate About Window Size */
#ifdef RLOGCODE
_PROTOTYP( int rlog_naws, (void) );
#endif /* RLOGCODE */
#endif /* CK_NAWS */

#ifdef OS2                              /* For terminal type name string */
#include "ckuusr.h"
#ifndef NT
#define INCL_DOSSEMAPHORES
#include <os2.h>
#undef COMMENT
#endif /* NT */
#include "ckocon.h"
int os2socketerror(int);
extern int tt_type, max_tt;
extern struct tt_info_rec tt_info[];
extern char ttname[];
#else
#ifdef CK_AUTHENTICATION
#include "ckuusr.h"
#endif /* CK_AUTHENTICATION */
#endif /* OS2 */

#ifdef NT
#include <limits.h>
extern int winsock_version;
char * GetLocalUser(); /* defined in ckotio.c */
#ifdef CK_LOGIN
VOID setntcreds();
#endif /* CK_LOGIN */
/* The NT 3.50 SDK defines try as __try */
#ifdef try
#undef try
#endif
#endif /* NT */

#ifdef CK_AUTHENTICATION
#include "ckuath.h"
#endif /* CK_AUTHENTICATION */

#include "ckcsig.h"

#ifndef OS2                             /* For timeout longjumps */
static ckjmpbuf njbuf;
#endif /* OS2 */

#define NAMECPYL 1024                   /* Local copy of hostname */
char namecopy[NAMECPYL];                /* Referenced by ckctel.c */
char namecopy2[NAMECPYL];		/* Referenced by ckctel.c */
#ifndef NOHTTP
char http_host_port[NAMECPYL];          /* orig host/port necessary for http */
char http_ip[CK_IPADDRLEN] = { '\0' };            /* ip address of host */
char http_port = 0;
int  http_ssl = 0;
char * http_agent = 0;
int  httpfd = -1;                       /* socket for http connections */
int  http_code = 0;
#define HTTPBUFLEN  1024
char http_reply_str[HTTPBUFLEN] = "";
#endif /* NOHTTP */

char ipaddr[CK_IPADDRLEN] = { '\0' };   /* Global copy of IP address */
unsigned long myxipaddr = 0L;           /* Ditto as a number */
#endif /* NETCONN */

char *tcp_address = NULL;               /* Preferred IPv4 Address */
#ifdef CK_IPV6
char *tcp_address6 = NULL;              /* Preferred IPv6 Address */
#endif /* CK_IPV6 */
extern char uidbuf[];                   /* User ID buffer */
extern char pwbuf[];                    /* Password buffer */

#ifndef NOHTTP
char * tcp_http_proxy = NULL;           /* Name[:port] of http proxy server */
int    tcp_http_proxy_errno = 0;
char * tcp_http_proxy_user = NULL;
char * tcp_http_proxy_pwd  = NULL;
char * tcp_http_proxy_agent = NULL;
#define HTTPCPYL 1024
static char proxycopy[HTTPCPYL];
#endif /* NOHTTP */

#ifdef OS2
extern int tt_rows[], tt_cols[];
extern int tt_status[VNUM];
#else /* OS2 */
extern int tt_rows, tt_cols;            /* Everybody has this */
#endif /* OS2 */

extern int cmd_cols, cmd_rows;

#ifdef STREAMING                        /* Use blocking writes for streaming */
extern int streaming;
#endif /* STREAMING */

#ifdef NT
extern int WSASafeToCancel;
int win95selectbug = 0;                 /* For TCP/IP stacks whose select() */
/* always fails on write requests such as Cisco and Quarterdeck */
#define stricmp _stricmp
#endif /* NT */

#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 HPUX                             /* But the data structures */
#ifndef HPUX8                           /* needed for linger are not */
#ifndef HPUX9                           /* defined in HP-UX versions */
#ifndef HPUX10                          /* prior to 8.00. */
#ifdef SO_LINGER
#undef SO_LINGER
#endif /* SO_LINGER */
#endif /* HPUX10 */
#endif /* HPUX9 */
#endif /* HPUX8 */
#endif /* HPUX */

#ifndef SO_OOBINLINE                    /* Hopefully only HP-UX 7.0 */
#define SO_OOBINLINE 0x0100
#endif /* SO_OOBINLINE */

#ifndef TCPSNDBUFSIZ
#ifdef VMS
#ifdef __alpha
#define TCPSNDBUFSIZ 16384
#endif /* __alpha */
#endif /* VMS */
#endif /* TCPSNDBUFSIZ */

#ifndef TCPSNDBUFSIZ
#define TCPSNDBUFSIZ -1
#endif /* TCPSNDBUFSIZ */

#ifdef SO_SNDBUF
int tcp_sendbuf = TCPSNDBUFSIZ;
#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 (much of this module...) */

#ifdef NETLEBUF
VOID
le_init() {                             /* LocalEchoInit() */
    int i;
    for (i = 0; i < LEBUFSIZ; i++)
      le_buf[i] = '\0';
    le_start = 0;
    le_end = 0;
    le_data = 0;
    tt_push_inited = 1;
}

VOID
le_clean() {                            /* LocalEchoCleanup() */
    le_init();
    return;
}

int
le_inbuf() {
    int rc = 0;
    if (le_start != le_end) {
        rc = (le_end -
              le_start +
              LEBUFSIZ) % LEBUFSIZ;
    }
    return(rc);
}

int
#ifdef CK_ANSIC
le_putchar(CHAR ch)
#else
le_putchar(ch) CHAR ch;
#endif /* CK_ANSIC */
/* le_putchar */ {
    if ((le_start - le_end + LEBUFSIZ)%LEBUFSIZ == 1) {
        debug(F110,"le_putchar","buffer is full",0);
        return(-1);
    }
    le_buf[le_end++] = ch;
    if (le_end == LEBUFSIZ)
      le_end = 0;
    le_data = 1;
    return(0);
}

int
#ifdef CK_ANSIC
le_puts(CHAR * s, int n)
#else
le_puts(s,n) CHAR * s; int n;
#endif /* CK_ANSIC */
/* le_puts */ {
    int rc = 0;
    int i = 0;
    CHAR * p = (CHAR *)"le_puts";
    ckhexdump(p,s,n);
    for (i = 0; i < n; i++)
      rc = le_putchar((char)s[i]);
    debug(F101,"le_puts","",rc);
    return(rc);
}

int
#ifdef CK_ANSIC
le_putstr(CHAR * s)
#else
le_putstr(s) CHAR * s;
#endif /* CK_ANSIC */
/* le_puts */ {
    CHAR * p;
    int rc = 0;
    p = (CHAR *)"le_putstr";
    ckhexdump(p,s,(int)strlen((char *)s));
    for (p = s; *p && !rc; p++)
      rc = le_putchar(*p);
    return(rc);
}

int
#ifdef CK_ANSIC
le_getchar(CHAR * pch)
#else /* CK_ANSIC */
le_getchar(pch) CHAR * pch;
#endif /* CK_ANSIC */
/* le_gatchar */ {
    int rc = 0;
    if (le_start != le_end) {
        *pch = le_buf[le_start];
        le_buf[le_start] = 0;
        le_start++;

        if (le_start == LEBUFSIZ)
          le_start = 0;

        if (le_start == le_end) {
            le_data = 0;
        }
        rc++;
    } else {
        *pch = 0;
    }
    return(rc);
}
#endif /* NETLEBUF */

#ifdef VMS
/*
  In edit 190, we moved tn_ini() to be called from within netopen().
  But tn_ini() calls ttol(), and ttol() checks to see if it's a net
  connection, but the flag for that isn't set until after netopen()
  is finished.  Since, in this module, we are always doing network
  output anyway, we just call nettol() directly, instead of going thru
  ttol().  Only needed for VMS, since UNIX, AOS/VS, and VOS can handle
  net connections just like regular connections in ttol(), and OS/2
  has a special routine for this.
*/
#define ttol nettol
#endif /* VMS */

int tcpsrfd = -1;
#ifdef CK_IPV6
int tcpsrfd6 = -1;                      /* IPv6 listener; see tcpsrv_open() */
#endif /* CK_IPV6 */

#ifdef CK_KERBEROS

char * krb5_d_principal = NULL;         /* Default principal */
char * krb5_d_instance = NULL;          /* Default instance */
char * krb5_d_realm = NULL;             /* Default realm */
char * krb5_d_cc = NULL;                /* Default credentials cache */
char * krb5_d_srv   = NULL;             /* Default Service */
int    krb5_d_lifetime = 600;           /* Default lifetime (10 hours) */
int    krb5_d_forwardable = 0;          /* creds not forwardable */
int    krb5_d_proxiable = 0;            /* creds not proxiable */
int    krb5_d_renewable = 0;            /* creds not renewable (0 min) */
int    krb5_autoget = 1;                /* Autoget TGTs */
int    krb5_autodel = 0;                /* Auto delete TGTs */
int    krb5_d_getk4 = 0;                /* K5 Kinit gets K4 TGTs */
int    krb5_checkaddrs = 1;             /* Check TGT Addrs */
int    krb5_d_no_addresses = 0;         /* Do not include IP Addresses */
char * krb5_d_addrs[KRB5_NUM_OF_ADDRS+1]={NULL,NULL}; /* Addrs to include */
int    krb5_errno = 0;                  /* Last K5 errno */
char * krb5_errmsg = NULL;              /* Last K5 errmsg */
char * k5_keytab = NULL;

char * krb4_d_principal = NULL;         /* Default principal */
char * krb4_d_realm = NULL;             /* Default realm */
char * krb4_d_srv   = NULL;             /* Default Service */
int    krb4_d_lifetime = 600;           /* Default lifetime (10 hours) */
int    krb4_d_preauth = 1;              /* Use preauth requests */
char * krb4_d_instance = NULL;          /* Default instance */
int    krb4_autoget = 1;                /* Autoget TGTs */
int    krb4_autodel = 0;                /* Auto delete TGTs */
int    krb4_checkaddrs = 1;             /* Check TGT Addrs */
char * k4_keytab = NULL;

int    krb4_errno = 0;                  /* Last K4 errno */
char * krb4_errmsg = NULL;              /* Last K4 errmsg */

struct krb_op_data krb_op = {           /* Operational data structure */
    0, NULL                             /* (version, cachefile) */
};

struct krb4_init_data krb4_init = {     /* Kerberos 4 INIT data structure */
    0, NULL, NULL, NULL, NULL
};

struct krb5_init_data krb5_init = {     /* Kerberos 5 INIT data structure */
    0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0,
    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    0
};

struct krb5_list_cred_data krb5_lc = {  /* List Credentials data structure */
    0, 0, 0
};

int krb_action = -1;                    /* Kerberos action to perform */

#ifndef CK_AUTHENTICATION
char *
ck_krb4_getrealm() {
    return("");
}
char *
ck_krb5_getrealm(cc) char * cc; {
    return("");
}
char *
ck_krb4_getprincipal() {
    return("");
}
char *
ck_krb5_getprincipal(cc) char * cc; {
    return("");
}
#endif /* CK_AUTHENTICATION */

/*  I N I _ K E R B  --  Initialize Kerberos data  */

VOID
ini_kerb() {
    int i;

    krb_action = -1;                    /* No action specified */

    krb_op.version = 0;                 /* Kerberos version (none) */
    krb_op.cache = NULL;                /* Cache file (none) */

/* Kerberos 5 */

    krb5_init.forwardable = krb5_d_forwardable; /* Init switch values... */
    krb5_init.proxiable   = krb5_d_proxiable;
    krb5_init.lifetime    = krb5_d_lifetime;
    krb5_init.renew       = 0;
    krb5_init.renewable   = krb5_d_renewable;
    krb5_init.validate    = 0;
    krb5_init.no_addresses = krb5_d_no_addresses;
    krb5_init.getk4       = krb5_d_getk4;
    if (krb5_init.postdate) {
        free(krb5_init.postdate);
        krb5_init.postdate = NULL;
    }
    if (krb5_init.service) {
        free(krb5_init.service);
        krb5_init.service = NULL;
    }
    if (!krb5_d_cc || !krb5_d_cc[0]) {  /* Set default cache */
        char * p;
        p = ck_krb5_get_cc_name();
        makestr(&krb5_d_cc,(p && p[0]) ? p : NULL);
    }
    if (!krb5_d_realm || !krb5_d_realm[0]) { /* Set default realm */
        char * p;
        p = ck_krb5_getrealm(krb5_d_cc);
        makestr(&krb5_d_realm,(p && p[0]) ? p : NULL);
    }
    makestr(&krb5_init.instance,krb5_d_instance);
    makestr(&krb5_init.realm,krb5_d_realm); /* Set realm from default */
    if (krb5_init.password) {
        memset(krb5_init.password,0xFF,strlen(krb5_init.password));
        free(krb5_init.password);
        krb5_init.password = NULL;
    }
    if (!krb5_d_principal) {            /* Default principal */
        /* a Null principal indicates the user should be prompted */
        char * p = ck_krb5_getprincipal(krb5_d_cc);
        if (!p || !(*p))
          p = (char *)uidbuf;           /* Principal = user */
                makestr(&krb5_d_principal,(p && p[0]) ? p : NULL);
    }
    makestr(&krb5_init.principal,krb5_d_principal);
    for (i = 0; i <= KRB5_NUM_OF_ADDRS; i++) {
        if (krb5_init.addrs[i])
          free(krb5_init.addrs[i]);
        krb5_init.addrs[i] = NULL;
    }
    for (i = 0; i <= KRB5_NUM_OF_ADDRS && krb5_d_addrs[i]; i++) {
        makestr(&krb5_init.addrs[i],krb5_d_addrs[i]);
    }

    /* Kerberos 4 */

    krb4_init.lifetime = krb4_d_lifetime;
    krb4_init.preauth  = krb4_d_preauth;
    makestr(&krb4_init.instance,krb4_d_instance);
    if (!krb4_d_realm || !krb4_d_realm[0]) {/* Set default realm */
        char * p;
        p = ck_krb4_getrealm();
                makestr(&krb4_d_realm,(p && p[0]) ? p : NULL);
    }
    makestr(&krb4_init.realm,krb4_d_realm);
    if (krb4_init.password) {
        memset(krb4_init.password,0xFF,strlen(krb4_init.password));
        free(krb4_init.password);
        krb4_init.password = NULL;
    }
    if (!krb4_d_principal) {            /* Default principal */
        /* a Null principal indicates the user should be prompted */
        char * p = ck_krb4_getprincipal();
        if (!p || !(*p))
          p = (char *)uidbuf;           /* Principal = user */
        makestr(&(krb4_d_principal),(p && p[0]) ? p : NULL);
    }
    makestr(&(krb4_init.principal),krb4_d_principal);
}

/*  D O A U T H  --  AUTHENTICATE action routine  */

int
doauth(cx) int cx; {                    /* AUTHENTICATE action routine */
    int rc = 0;                         /* Return code */

#ifdef CK_AUTHENTICATION
#ifdef OS2
    if (!ck_security_loaddll())         /* Load various DLLs */
      return(rc);
#endif /* OS2 */
    if (krb_op.version == 4) {          /* Version = 4 */
        if (!ck_krb4_is_installed()) {
            printf("?Kerberos 4 is not installed\n");
            return(0);
        }
        switch (krb_action) {           /* Perform V4 functions */
          case KRB_A_IN:                /* INIT */
            rc |= !(ck_krb4_initTGT(&krb_op,&krb4_init) < 0);
            break;
          case KRB_A_DE:                /* DESTROY */
            rc |= !(ck_krb4_destroy(&krb_op) < 0);
            break;
          case KRB_A_LC:                /* LIST-CREDENTIALS */
            rc |= !(ck_krb4_list_creds(&krb_op) < 0);
            break;
        }
    }
    if (krb_op.version == 5) {          /* V5 functions */
        if (!ck_krb5_is_installed()) {
            printf("?Kerberos 5 is not installed\n");
            return(0);
        }
        switch (krb_action) {
          case KRB_A_IN:                /* INIT */
            rc |= !(ck_krb5_initTGT(&krb_op,&krb5_init,
                                     krb5_init.getk4 ? &krb4_init : 0) < 0);
            break;
          case KRB_A_DE:                /* DESTROY */
            rc |= !(ck_krb5_destroy(&krb_op) < 0);
            break;
          case KRB_A_LC:                /* LIST-CREDENTIALS */
            if (krb_op.version == 0)
              printf("\n");
            rc |= !(ck_krb5_list_creds(&krb_op,&krb5_lc) < 0);
            break;
        }
    }
#else
#ifndef NOICP
#ifndef NOSHOW
    rc = sho_auth(0);                   /* Show all */
#endif /* NOSHOW */
#endif /* NOICP */
#endif /* CK_AUTHENTICATION */
    return(rc);
}
#endif /* CK_KERBEROS */

#ifdef TCPSOCKET
#ifndef OS2
#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 ) );

static unsigned short tcpsrv_port = 0;

#endif /* NOLISTEN */
#endif /* OS2 */

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

#endif /* TCPSOCKET */

/*
  TCPIPLIB means use separate socket calls for i/o, while on UNIX the
  normal file system calls are used for TCP/IP sockets too.
  Means "DEC_TCPIP or MULTINET or WINTCP or OS2 or BEBOX" (see ckcnet.h),
*/

#ifdef TCPIPLIB

/* For buffered network reads... */
/*
  If the buffering code is written right, it shouldn't matter
  how long this buffer is.
*/
#ifdef OS2
#ifdef NT
#define TTIBUFL 64240                   /* 44 * 1460 (MSS) */
#else
#define TTIBUFL 32120                   /* 22 * 1460 (MSS) */
#endif /* NT */
#else /* OS2 */
#define TTIBUFL 8191                    /* Let's use 8K. */
#endif /* OS2 */

CHAR ttibuf[TTIBUFL+1];

/*
  select() is used in preference to alarm()/signal(), but different systems
  use different forms of select()...
*/
#ifndef NOSELECT         /* Option to override BSDSELECT */
#ifdef BELLV10
/*
  Note: Although BELLV10 does have TCP/IP support, and does use the unique
  form of select() that is evident in this module (and in ckutio.c), it does
  not have a sockets library and so we can't build Kermit TCP/IP support for
  it.  For this, somebody would have to write TCP/IP streams code.
*/
#define BELLSELECT
#ifndef FD_SETSIZE
#define FD_SETSIZE 128
#endif /* FD_SETSIZE */
#else
#ifdef WINTCP                           /* VMS with Wollongong WIN/TCP */
#ifndef OLD_TWG                         /* TWG 3.2 has only select(read) */
#define BSDSELECT
#endif /* OLD_TWG */
#else
#ifdef CMU_TCPIP                        /* LIBCMU can do select */
#define BSDSELECT
#else
#ifdef DEC_TCPIP
#define BSDSELECT
#else
#ifdef OS2                              /* OS/2 with TCP/IP */
#ifdef NT
#define BSDSELECT
#else /* NT */
#define IBMSELECT
#endif /* NT */
#endif /* OS2 */
#endif /* DEC_TCPIP */
#endif /* CMU_TCPIP */
#endif /* WINTCP */
#endif /* BELLV10 */
#endif /* NOSELECT */
/*
  Others (TGV, TCPware, ...) use alarm()/signal().  The BSDSELECT case does not
  compile at all; the IBMSELECT case compiles and links but crashes at runtime.
  NOTE: If any of these can be converted to select(), they should be for two
  reasons: (1) It's a lot faster; (2) certain sockets libraries do not like
  their socket_read() calls to be interrupted; subsequent socket_read()'s tend
  to fail with EBUSY.  This happened in the UCX case before it was converted
  to use select().
*/
#ifndef OS2
#ifndef VMS
static                                  /* These are used in CKVTIO.C */
#endif /* VMS */                        /* And in CKONET.C            */
#endif /* OS2 */
int
  ttibp = 0,
  ttibn = 0;
/*
  Read bytes from network into internal buffer ttibuf[].
  To be called when input buffer is empty, i.e. when ttibn == 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 ttibn to that number and
  ttibp (the buffer pointer) to zero.
*/
_PROTOTYP( int ttbufr, ( VOID ) );
int
ttbufr() {                              /* TT Buffer Read */
    int count;

    if (ttnet != NET_TCPB)              /* First make sure current net is */
      return(-1);                       /* TCP/IP; if not, do nothing. */

#ifdef OS2
    RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#endif /* OS2 */

    if (ttibn > 0) {                    /* Our internal buffer is not empty, */
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(ttibn);                  /* so keep using it. */
    }

    if (ttyfd == -1) {                  /* No connection, error */
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(-2);
    }

    ttibp = 0;                          /* Else reset pointer to beginning */

#ifdef WINTCP
    count = 512;                        /* This works for WIN/TCP */
#else
#ifdef DEC_TCPIP
    count = 512;                        /* UCX */
#else
#ifdef OS2
    count = TTIBUFL;
#else                                   /* Multinet, etc. */
    count = ttchk();                    /* Check network input buffer, */
    if (ttibn > 0) {                    /* which can put a char there! */
        debug(F111,"ttbufr","ttchk() returns",count);
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(ttibn);
    }
    if (count < 0) {                     /* Read error - connection closed */
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(-2);
    }
    else if (count > TTIBUFL)           /* Too many to read */
      count = TTIBUFL;
    else if (count == 0)                /* None, so force blocking read */
      count = 1;
#endif /* OS2 */
#endif /* DEC_TCPIP */
#endif /* WINTCP */
    debug(F101,"ttbufr count 1","",count);

#ifdef CK_SSL
    if (ssl_active_flag || tls_active_flag) {
        int error;
      ssl_read:
        if (ssl_active_flag)
          count = SSL_read(ssl_con, ttibuf, count);
        else
          count = SSL_read(tls_con, ttibuf, count);
        error = SSL_get_error(ssl_active_flag?ssl_con:tls_con,count);
        switch (error) {
          case SSL_ERROR_NONE:
            debug(F111,"ttbufr SSL_ERROR_NONE","count",count);
            if (count > 0) {
                ttibp = 0;              /* Reset buffer pointer. */
                ttibn = count;
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(ttibn);          /* Return buffer count. */
            } else if (count < 0) {
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            } else {
                netclos();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            }
          case SSL_ERROR_WANT_WRITE:
            debug(F100,"ttbufr SSL_ERROR_WANT_WRITE","",0);
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
            return(-1);
          case SSL_ERROR_WANT_READ:
            debug(F100,"ttbufr SSL_ERROR_WANT_READ","",0);
#ifdef OS2
            ReleaseTCPIPMutex();
#endif /* OS2 */
            return(-1);
          case SSL_ERROR_SYSCALL:
              if ( count == 0 ) { /* EOF */
                  netclos();
#ifdef OS2
                  ReleaseTCPIPMutex();
#endif /* OS2 */
                  return(-2);
              } else {
                  int rc = -1;
#ifdef NT
                  int gle = GetLastError();
                  debug(F111,"ttbufr SSL_ERROR_SYSCALL",
                         "GetLastError()",gle);
                  rc = os2socketerror(gle);
                  if (rc == -1)
                      rc = -2;
                  else if ( rc == -2 )
                      rc = -1;
#endif /* NT */
#ifdef OS2
                  ReleaseTCPIPMutex();
#endif /* OS2 */
                  return(rc);
              }
          case SSL_ERROR_WANT_X509_LOOKUP:
            debug(F100,"ttbufr SSL_ERROR_WANT_X509_LOOKUP","",0);
            netclos();
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
            return(-2);
          case SSL_ERROR_SSL:
              if (bio_err!=NULL) {
                  int len;
                  extern char ssl_err[];
                  BIO_printf(bio_err,"ttbufr SSL_ERROR_SSL\n");
                  ERR_print_errors(bio_err);
                  len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
                  ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
                  debug(F110,"ttbufr SSL_ERROR_SSL",ssl_err,0);
                  if (ssl_debug_flag)
                      printf(ssl_err);
              } else if (ssl_debug_flag) {
                  debug(F100,"ttbufr SSL_ERROR_SSL","",0);
                  fflush(stderr);
                  fprintf(stderr,"ttbufr SSL_ERROR_SSL\n");
                  ERR_print_errors_fp(stderr);
              }
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
            return(-2);
          case SSL_ERROR_ZERO_RETURN:
            debug(F100,"ttbufr SSL_ERROR_ZERO_RETURN","",0);
            netclos();
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
            return(-2);
          default:
              debug(F100,"ttbufr SSL_ERROR_?????","",0);
              netclos();
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
              return(-2);
          }
    }
#endif /* CK_SSL */

/* This is for blocking reads */

#ifndef VMS
#ifdef SO_OOBINLINE
    {
        int outofband = 0;
#ifdef BELLSELECT
        if (select(128, NULL, NULL, efds, 0) > 0 && FD_ISSET(ttyfd, efds))
          outofband = 1;
#else
#ifdef BSDSELECT
        fd_set efds;
        struct timeval tv;
        FD_ZERO(&efds);
        FD_SET(ttyfd, &efds);
        tv.tv_sec  = tv.tv_usec = 0L;
        debug(F100,"Out-of-Band BSDSELECT","",0);
#ifdef NT
        WSASafeToCancel = 1;
#endif /* NT */
        if (select(FD_SETSIZE, NULL, NULL, &efds, &tv) > 0 &&
            FD_ISSET(ttyfd, &efds))
          outofband = 1;
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
#else /* !BSDSELECT */
#ifdef IBMSELECT
/* Is used by OS/2 ... */
/* ... and it came in handy!  For our TCP/IP layer, it avoids all the fd_set */
/* and timeval stuff since this is the only place where it is used. */
        CK_TTYFD_T socket = ttyfd;
        debug(F100,"Out-of-Band IBMSELECT","",0);
        if ((select(&socket, 0, 0, 1, 0L) == 1) && (socket == ttyfd))
          outofband = 1;
#else /* !IBMSELECT */
/*
  If we can't use select(), then we use the regular alarm()/signal()
  timeout mechanism.
*/
      debug(F101,"Out-of-Band data not supported","",0);
      outofband = 0;

#endif /* IBMSELECT */
#endif /* BSDSELECT */
#endif /* BELLSELECT */
      if (outofband) {
         /* Get the Urgent Data */
         /* if OOBINLINE is disabled this should be only a single byte      */
         /* MS Winsock has a bug in Windows 95.  Extra bytes are delivered  */
         /* That were never sent.                                           */
#ifdef OS2
          RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#endif /* OS2 */
          count = socket_recv(ttyfd,&ttibuf[ttibp+ttibn],count,MSG_OOB);
#ifdef OS2
          ReleaseTCPIPMutex();
#endif /* OS2 */
          if (count <= 0) {
              int s_errno = socket_errno;
              debug(F101, "ttbufr socket_recv MSG_OOB","",count);
              debug(F101, "ttbufr socket_errno","",s_errno);
#ifdef OS2ONLY
              if (count < 0 && (s_errno == 0 || s_errno == 23)) {
                  /* These appear in OS/2 - don't know why   */
                  /* ignore it and read as normal data       */
                  /* and break, then we will attempt to read */
                  /* the port using normal read() techniques */
                  debug(F100,"ttbufr handing as in-band data","",0);
                  count = 1;
              } else {
                  netclos();                    /* *** *** */
#ifdef OS2
                  ReleaseTCPIPMutex();
#endif /* OS2 */
                  return(-2);
              }
#else /* OS2ONLY */
              netclos();                        /* *** *** */
#ifdef OS2
              ReleaseTCPIPMutex();
#endif /* OS2 */
              return(-2);
#endif /* OS2ONLY */
          } else {                      /* we got out-of-band data */
              ckhexdump("ttbufr out-of-band chars",&ttibuf[ttibp+ttibn],count);
#ifdef BETADEBUG
              bleep(BP_NOTE);
#endif /* BETADEBUG */
#ifdef RLOGCODE                         /* blah */
              if (ttnproto == NP_RLOGIN  ||
                  ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN ||
                  ((ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) &&
                  !rlog_inband)
                   )
              {
                  /*
                    When urgent data is read with MSG_OOB and not OOBINLINE
                    then urgent data and normal data are not mixed.  So
                    treat the entire buffer as urgent data.
                  */
                  rlog_oob(&ttibuf[ttibp+ttibn], count);
#ifdef OS2
                  ReleaseTCPIPMutex();
#endif /* OS2 */
                  return ttbufr();
              } else
#endif /* RLOGCODE */ /* blah */
              {
                  /* For any protocols we don't have a special out-of-band  */
                  /* handler for, just put the bytes in the normal buffer   */
                  /* and return                                             */

                  ttibp += 0;       /* Reset buffer pointer. */
                  ttibn += count;
#ifdef DEBUG
                  /* Got some bytes. */
                  debug(F101,"ttbufr count 2","",count);
                  if (count > 0)
                      ttibuf[ttibp+ttibn] = '\0';
                  debug(F111,"ttbufr ttibuf",ttibuf,ttibp);
#endif /* DEBUG */
#ifdef OS2
                  ReleaseTCPIPMutex();
#endif /* OS2 */
                  return(ttibn);    /* Return buffer count. */
              }
          }
      }
    }
#endif /* SO_OOBINLINE */
#endif /* VMS */

    count = socket_read(ttyfd,&ttibuf[ttibp+ttibn],count);
    if (count <= 0) {
        int s_errno = socket_errno;
        debug(F101,"ttbufr socket_read","",count);
        debug(F101,"ttbufr socket_errno","",s_errno);
#ifdef OS2
        if (count == 0 || os2socketerror(s_errno) < 0) {
            netclos();
            ReleaseTCPIPMutex();
            return(-2);
        }
        ReleaseTCPIPMutex();
        return(-1);
#else /* OS2 */
        netclos();                      /* *** *** */
        return(-2);
#endif /* OS2 */
    }
    else {
        ttibp = 0;                      /* Reset buffer pointer. */
        ttibn += count;
#ifdef DEBUG
        debug(F101,"ttbufr count 2","",count); /* Got some bytes. */
        if (count > 0)
          ttibuf[ttibp+ttibn] = '\0';
        debug(F111,"ttbufr ttibuf",&ttibuf[ttibp],ttibn);
#endif /* DEBUG */

#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(ttibn);                  /* Return buffer count. */
    }
}
#endif /* TCPIPLIB */

#ifndef IBMSELECT
#ifndef BELLSELECT
#ifndef BSDSELECT               /* Non-TCPIPLIB case */
#ifdef SELECT
#define BSDSELECT
#endif /* SELECT */
#endif /* BSDSELECT */
#endif /* BELLSELECT */
#endif /* IBMSELECT */

#define TELNET_PORT 23          /* Should do lookup, but it won't change */
#define RLOGIN_PORT 513
#define KERMIT_PORT 1649
#define KLOGIN_PORT 543
#define EKLOGIN_PORT 2105

#ifndef NONET
/*
  C-Kermit network open/close functions for BSD-sockets.
  Much of this code shared by SunLink X.25, which also uses the socket library.
*/
#ifdef TCPSOCKET
struct hostent *
#ifdef CK_ANSIC
ck_copyhostent(struct hostent * h)
#else /* CK_ANSIC */
ck_copyhostent(h) struct hostent * h;
#endif /* CK_ANSIC */
{
    /*
     *  The hostent structure is dynamic in nature.
     *  struct  hostent {
     *  char    * h_name;
     *  char    * * h_aliases;
     *  short   h_addrtype;
     *  short   h_length;
     *  char    * * h_addr_list;
     *  #define h_addr  h_addr_list[0]
     */
#define HOSTENTCNT 5
    static struct hostent hosts[HOSTENTCNT] = {{NULL,NULL,0,0,NULL},
                                               {NULL,NULL,0,0,NULL},
                                               {NULL,NULL,0,0,NULL},
                                               {NULL,NULL,0,0,NULL},
                                               {NULL,NULL,0,0,NULL}};
    static int    next = 0;
    int    i,cnt;
    char ** pp;

    if ( h == NULL )
        return(NULL);

    if (next == HOSTENTCNT)
        next = 0;

    if ( hosts[next].h_name ) {
        free(hosts[next].h_name);
        hosts[next].h_name = NULL;
    }
    if ( hosts[next].h_aliases ) {
        pp = hosts[next].h_aliases;
        while ( *pp ) {
            free(*pp);
            pp++;
        }
        free(hosts[next].h_aliases);
    }
#ifdef HADDRLIST
    if ( hosts[next].h_addr_list ) {
        pp = hosts[next].h_addr_list;
        while ( *pp ) {
            free(*pp);
            pp++;
        }
        free(hosts[next].h_addr_list);
    }
#endif /* HADDRLIST */

    makestr(&hosts[next].h_name,h->h_name);
    if (h->h_aliases) {
        for ( cnt=0,pp=h->h_aliases; pp && *pp; pp++,cnt++) ;
        /* The following can give warnings in non-ANSI builds */
        hosts[next].h_aliases = (char **) malloc(sizeof(char *) * (cnt+1));
        for ( i=0; i<cnt; i++) {
            hosts[next].h_aliases[i] = NULL;
            makestr(&hosts[next].h_aliases[i],h->h_aliases[i]);
        }
        hosts[next].h_aliases[i] = NULL;
    } else
        hosts[next].h_aliases = NULL;

    hosts[next].h_addrtype = h->h_addrtype;
    hosts[next].h_length = h->h_length;

#ifdef HADDRLIST
#ifdef h_addr
    if (h->h_addr_list) {
        for ( cnt=0,pp=h->h_addr_list; pp && *pp; pp++,cnt++) ;
        /* The following can give warnings non-ANSI builds */
        hosts[next].h_addr_list = (char **) malloc(sizeof(char *) * (cnt+1));
        for ( i=0; i<cnt; i++) {
            hosts[next].h_addr_list[i] = malloc(h->h_length);
            bcopy(h->h_addr_list[i],hosts[next].h_addr_list[i],h->h_length);
        }
        hosts[next].h_addr_list[i] = NULL;
    } else
        hosts[next].h_addr_list = NULL;
#else
    bcopy(h->h_addr, &hosts[next].h_addr, h->h_length);
#endif /* h_addr */
#else /* HADDRLIST */
    bcopy(h->h_addr, &hosts[next].h_addr, h->h_length);
#endif /* HADDRLIST */

    return(&hosts[next++]);
}

/*
  Renders a socket address as text, supporting IPv6 if available.

  Some BSDs hand back link-local and link-local IPv6 addresses from getifaddrs()
  with the interface index in bytes 2-3 of the address itself, in addition to
  (or instead of) sin6_scope_id. This is the KAME "embedded scope ID"
  convention.  See
  https://lists.freebsd.org/pipermail/freebsd-net/2013-September/036756.html .

  If unmodified, it looks bogus, such as as fe80:2::1 instead of fe80::1%lo0. So
  this function moves any embedded index into sin6_scope_id and clears it from
  the address bytes before formatting.
*/
int
#ifdef CK_ANSIC
ck_straddr(struct sockaddr * sa, GSOCKNAME_T salen, char * buf, int buflen)
#else /* CK_ANSIC */
ck_straddr(sa,salen,buf,buflen)
    struct sockaddr * sa; GSOCKNAME_T salen; char * buf; int buflen;
#endif /* CK_ANSIC */
{
    if (!buf || buflen < 1)
      return(-1);
    *buf = '\0';
    if (!sa)
      return(-1);
#ifdef CK_IPV6
    if (sa->sa_family == AF_INET6 &&
        salen >= (GSOCKNAME_T)sizeof(struct sockaddr_in6)) {
        struct sockaddr_in6 sin6;
        struct in6_addr * a6 = &((struct sockaddr_in6 *)sa)->sin6_addr;

        bcopy((char *)sa,(char *)&sin6,sizeof(sin6));
        if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
            (a6->s6_addr[2] || a6->s6_addr[3])) {
            sin6.sin6_scope_id =
              ((unsigned int)a6->s6_addr[2] << 8) | a6->s6_addr[3];
            sin6.sin6_addr.s6_addr[2] = 0;
            sin6.sin6_addr.s6_addr[3] = 0;
        }
        if (getnameinfo((struct sockaddr *)&sin6,
                         (socklen_t)sizeof(sin6),buf,buflen,NULL,0,
                         NI_NUMERICHOST) != 0) {
            *buf = '\0';
            return(-1);
        }
        return(0);
    }
    if (getnameinfo(sa,(socklen_t)salen,buf,buflen,NULL,0,NI_NUMERICHOST)
        != 0) {
        *buf = '\0';
        return(-1);
    }
    return(0);
#else /* CK_IPV6 */
    if (sa->sa_family != AF_INET)
      return(-1);
    ckstrncpy(buf,(char *)inet_ntoa(((struct sockaddr_in *)sa)->sin_addr),
              buflen);
    return(0);
#endif /* CK_IPV6 */
}

/*
  ck_getport() and ck_setport() read and write the port number of a
  socket address regardless of its address family.  Port numbers are
  always passed in host byte order.  The network-byte-order conversion
  happens inside these functions.  An unrecognized family is treated
  as "no port" (ck_getport() returns 0 and ck_setport() is a no-op).
*/
unsigned short
#ifdef CK_ANSIC
ck_getport(struct sockaddr * sa)
#else /* CK_ANSIC */
ck_getport(sa) struct sockaddr * sa;
#endif /* CK_ANSIC */
{
    if (!sa)
      return(0);
#ifdef CK_IPV6
    if (sa->sa_family == AF_INET6)
      return(ntohs(((struct sockaddr_in6 *)sa)->sin6_port));
#endif /* CK_IPV6 */
    if (sa->sa_family == AF_INET)
      return(ntohs(((struct sockaddr_in *)sa)->sin_port));
    return(0);
}

VOID
#ifdef CK_ANSIC
ck_setport(struct sockaddr * sa, unsigned short port)
#else /* CK_ANSIC */
ck_setport(sa,port) struct sockaddr * sa; unsigned short port;
#endif /* CK_ANSIC */
{
    if (!sa)
      return;
#ifdef CK_IPV6
    if (sa->sa_family == AF_INET6) {
        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
        return;
    }
#endif /* CK_IPV6 */
    if (sa->sa_family == AF_INET)
      ((struct sockaddr_in *)sa)->sin_port = htons(port);
}

#ifdef CK_IPV6
/*
  Parse an IPv6 literal with optional %zone suffix (RFC 4007) into a
  binary address and numeric scope ID.

  The zone may be an interface name or decimal index. The scope ID is
  validated against active system interfaces using if_indextoname().

  Returns 1 on success and populates *addr and *scopeid. Sets *scopeid
  to 0 if no zone suffix is present. Returns 0 on parse or lookup
  failure and leaves output parameters unchanged.
*/
int
#ifdef CK_ANSIC
ck_scopeaddr6(char * in, struct in6_addr * addr, unsigned int * scopeid)
#else /* CK_ANSIC */
ck_scopeaddr6(in,addr,scopeid)
    char * in; struct in6_addr * addr; unsigned int * scopeid;
#endif /* CK_ANSIC */
{
    char * pct;
    char * zp;
    size_t alen, zlen;
    char abuf[INET6_ADDRSTRLEN];
    char zbuf[IFNAMSIZ];
    char ifnamebuf[IFNAMSIZ];
    struct in6_addr taddr;
    unsigned int tscope = 0;

    if (!in || !addr || !scopeid)
      return(0);

    pct = strrchr(in,'%');
    alen = pct ? (size_t)(pct - in) : strlen(in);
    if (alen < 1 || alen >= sizeof(abuf))
      return(0);
    bcopy(in,abuf,alen);
    abuf[alen] = '\0';
    if (inet_pton(AF_INET6,abuf,&taddr) != 1)
      return(0);

    if (pct) {
        int alldigits;
        char * p;

        zp = pct + 1;
        zlen = strlen(zp);
        if (zlen < 1 || zlen >= sizeof(zbuf))
          return(0);                    /* bare trailing '%', or too long */
        bcopy(zp,zbuf,zlen);
        zbuf[zlen] = '\0';

        alldigits = 1;
        for (p = zbuf; *p; p++) {
            if (!isdigit((unsigned char)*p)) {
                alldigits = 0;
                break;
            }
        }
        if (alldigits) {
            char * ep = NULL;
            unsigned long v;

            errno = 0;
            v = strtoul(zbuf,&ep,10);
            tscope = (unsigned int)v;
            if (!ep || *ep != '\0' || errno == ERANGE ||
                (unsigned long)tscope != v)
              return(0);                /* trailing junk, or overflow */
        } else {
            tscope = if_nametoindex(zbuf);
            if (tscope == 0)
              return(0);                /* no such interface */
        }
        /* Verify scope ID corresponds to an active interface. */
        if (!if_indextoname(tscope,ifnamebuf))
          return(0);
    }

    *addr = taddr;
    *scopeid = tscope;
    return(1);
}
#endif /* CK_IPV6 */

#ifdef CK_IPV6
/*
  ck_tcp_connect() implements the telnet-style IPv6 DNS resolution and
  connection policy: try to connect to every candidate in resolver order,
  returning at the first successful connection.

  Used by the general SET HOST code (netopen), the FTP control connection
  (ftp_hookup), and the HTTP client (http_open).

  host and svc are passed straight to getaddrinfo(); the family tried is
  whatever SET TCP ADDRESS-FAMILY selects.  SET TCP ADDRESS
  (setting tcp_address) is IPv4-only and is applied as the local bind address
  for IPv4.  SET TCP ADDRESS6 (setting tcp_address6) is IPv6-only
  and is applied the same way for IPv6.

  Unless quiet_f is set, prints " Trying <addr>... " for each
  candidate attempted and "Failed" (via perror()) for one that fails,
  matching existing SET HOST progress-message behavior.

  On success, returns the connected socket, with raddr and raddrlen set to the
  peer address actually connected to.  On failure, returns -1 and raddr is
  untouched.  got_addr distinguishes the two ways that can happen and it's set
  to 0 if getaddrinfo() failed, or 1 if it returned candidates but none of them
  connected.

  rsvd_port, if nonzero, requests RLOGIN's privileged source port
  convention: bind the local end of an AF_INET candidate to a
  decreasing port in 1023..512 before connecting, mirroring the
  legacy reserved-port scan netopen() has always done for RLOGIN. This
  is a BSD IPv4 trust convention with no IPv6 equivalent in actual
  rlogind implementations, so it is only applied to AF_INET
  candidates.  An AF_INET6 candidate is tried normally. Callers other
  than netopen()'s RLOGIN case always pass 0.
*/
int
#ifdef CK_ANSIC
ck_tcp_connect(char * host, char * svc, int quiet_f, int * got_addr,
               struct sockaddr_storage * raddr, GSOCKNAME_T * raddrlen,
               int rsvd_port)
#else /* CK_ANSIC */
ck_tcp_connect(host,svc,quiet_f,got_addr,raddr,raddrlen,rsvd_port)
    char * host; char * svc; int quiet_f; int * got_addr;
    struct sockaddr_storage * raddr; GSOCKNAME_T * raddrlen; int rsvd_port;
#endif /* CK_ANSIC */
{
    /* Persists across calls so successive RLOGIN connections in the same
       process don't immediately reuse the same source port.  Mirrors the
       legacy netopen() reserved port scan's static lport. */
    static unsigned short rsvd_lport = 1024;
    struct addrinfo hints, *results = NULL, *rp;
    int gai, fd = -1;

    *got_addr = 0;
    bzero((char *)&hints, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family =
      (tcp_af == TCP_AF_V4) ? AF_INET :
      (tcp_af == TCP_AF_V6) ? AF_INET6 : AF_UNSPEC;

    gai = getaddrinfo(host, svc, &hints, &results);
    if (gai != 0 || !results) {
        debug(F110,"ck_tcp_connect getaddrinfo error",
              (char *)gai_strerror(gai),0);
        errno = 0;
        return(-1);
    }
    *got_addr = 1;

    for (rp = results; rp; rp = rp->ai_next) {
        char tbuf[CK_IPADDRLEN];
        if (ck_straddr(rp->ai_addr,(GSOCKNAME_T)rp->ai_addrlen,
                        tbuf,sizeof(tbuf)) < 0)
          tbuf[0] = '\0';
        debug(F110,"ck_tcp_connect trying",tbuf,0);
        if (!quiet_f && *tbuf) {
            printf(" Trying %s... ", tbuf);
            fflush(stdout);
        }

        fd = socket(rp->ai_family,SOCK_STREAM,0);
        if (fd < 0) {
            debug(F101,"ck_tcp_connect socket error","",errno);
            continue;
        }
        errno = 0;

        /* RLOGIN's privileged source port convention (see
           comment above) is only meaningful for an AF_INET candidate. */
        if (rsvd_port && rp->ai_family == AF_INET) {
            struct sockaddr_in lsin;
            int bound = 0;

            bzero((char *)&lsin,sizeof(lsin));
            lsin.sin_family = AF_INET;
            if (tcp_address)
              inet_pton(AF_INET,tcp_address,&lsin.sin_addr);
            else
              lsin.sin_addr.s_addr = INADDR_ANY;

            rsvd_lport--;               /* don't reuse the last port tried */
            if (rsvd_lport == 512)
              rsvd_lport = 1023;
            while (1) {
                lsin.sin_port = htons(rsvd_lport);
                if (bind(fd,(struct sockaddr *)&lsin,sizeof(lsin)) >= 0) {
                    bound = 1;
                    break;
                }
                if (errno != EADDRINUSE)
                  break;
                rsvd_lport--;
                if (rsvd_lport == 512)  /* lowest reserved port to use */
                  break;
            }
            if (!bound) {
                debug(F101,"ck_tcp_connect rsvd bind errno","",errno);
                close(fd);
                fd = -1;
                continue;
            }
        } else
        /* SET TCP ADDRESS (IPv4-only) and SET TCP ADDRESS6 (IPv6-only)
           are each validated at SET time, so only the setting matching
           this candidate's own family is ever relevant here. */
        if (tcp_address && rp->ai_family == AF_INET) {
            struct sockaddr_in lsin;
            bzero((char *)&lsin,sizeof(lsin));
            lsin.sin_family = AF_INET;
            inet_pton(AF_INET,tcp_address,&lsin.sin_addr);
            lsin.sin_port = 0;
            if (bind(fd,(struct sockaddr *)&lsin,sizeof(lsin)) < 0) {
                debug(F101,"ck_tcp_connect bind errno","",errno);
                close(fd);
                fd = -1;
                continue;
            }
        } else if (tcp_address6 && rp->ai_family == AF_INET6) {
            struct sockaddr_in6 lsin6;
            bzero((char *)&lsin6,sizeof(lsin6));
            lsin6.sin6_family = AF_INET6;
            /* Parse tcp_address6 and retrieve scope ID for binding. */
            if (!ck_scopeaddr6(tcp_address6,&lsin6.sin6_addr,
                                &lsin6.sin6_scope_id)) {
                debug(F110,"ck_tcp_connect tcp_address6 no longer valid",
                      tcp_address6,0);
                close(fd);
                fd = -1;
                continue;
            }
            lsin6.sin6_port = 0;
            if (bind(fd,(struct sockaddr *)&lsin6,sizeof(lsin6)) < 0) {
                debug(F101,"ck_tcp_connect bind errno","",errno);
                close(fd);
                fd = -1;
                continue;
            }
        }

        if (connect(fd,rp->ai_addr,rp->ai_addrlen) == 0) {
            /* getaddrinfo() never returns an address larger than
               sockaddr_storage for any family the local resolver
               supports, so this is just a self-documenting guard
               against a future change in how results is populated. */
            if (rp->ai_addrlen > sizeof(*raddr)) {
                debug(F101,"ck_tcp_connect ai_addrlen too large","",
                      (int)rp->ai_addrlen);
                close(fd);
                fd = -1;
                continue;
            }
            bzero((char *)raddr,sizeof(*raddr));
            bcopy(rp->ai_addr,(char *)raddr,rp->ai_addrlen);
            *raddrlen = (GSOCKNAME_T)rp->ai_addrlen;
            break;
        }
        debug(F101,"ck_tcp_connect connect errno","",errno);
        if (!quiet_f) {
            perror("Failed");
            /* Print diagnostic when link-local connect lacks a zone. */
            if (rp->ai_family == AF_INET6) {
                struct sockaddr_in6 * rp6 =
                  (struct sockaddr_in6 *)rp->ai_addr;
                if (IN6_IS_ADDR_LINKLOCAL(&rp6->sin6_addr) &&
                    rp6->sin6_scope_id == 0)
                  printf("Link-local addresses require a zone, e.g. fe80::1%%eth0\n");
            }
        }
        close(fd);
        fd = -1;
    }

    freeaddrinfo(results);
    return(fd);
}
#endif /* CK_IPV6 */

/*
  ck_splithostport() splits a "host[:port]" specifier into separate
  host and port strings.  If in[0] is '[', the host is everything up
  to the matching ']', which is needed for IPv6.  If the character
  right after ']' is ':', everything after that is the port.

  Otherwise (no brackets), the number of colons in the whole string
  decides how it's read:
    0 colons: the whole string is the host, no port.
    1 colon:  split there.  This is "host:port", the original syntax this
              function has always supported.
    2+ colons: the whole string is the host, no port. A real IPv6
              literal always has at least two colons, and the plain
              host:port syntax never allowed more than one, so this can't
              be anything else.

  hostbuf is always NULL-terminated on return, and is the empty string on
  failure.

  portbuf, if non-NULL, is always NULL-terminated too, and likewise is
  the empty string if no port was found.

  Returns:
    1  if a port was found (hostbuf and portbuf both set)
    0  if no port was found (hostbuf set, portbuf empty)
   -1  on malformed input (in[0] == '[' with no matching ']', or bad
       arguments); hostbuf and portbuf are cleared but not otherwise set
*/
int
#ifdef CK_ANSIC
ck_splithostport(char * in, char * hostbuf, int hostbuflen,
                  char * portbuf, int portbuflen)
#else /* CK_ANSIC */
ck_splithostport(in,hostbuf,hostbuflen,portbuf,portbuflen)
    char * in; char * hostbuf; int hostbuflen;
    char * portbuf; int portbuflen;
#endif /* CK_ANSIC */
{
    char * p;
    int n;

    if (hostbuf && hostbuflen > 0)
      hostbuf[0] = '\0';
    if (portbuf && portbuflen > 0)
      portbuf[0] = '\0';
    if (!in || !hostbuf || hostbuflen < 1)
      return(-1);

    if (in[0] == '[') {
        p = in + 1;
        while (*p != '\0' && *p != ']') p++;
        if (*p != ']')
          return(-1);                  /* Unterminated bracket */
        n = (int)(p - (in + 1));
        if (n >= hostbuflen)
          n = hostbuflen - 1;
        bcopy(in + 1,hostbuf,n);
        hostbuf[n] = '\0';
        p++;                           /* Past the ']' */
        if (*p == ':') {
            if (portbuf && portbuflen > 0)
              ckstrncpy(portbuf,p + 1,portbuflen);
            return(1);
        }
        return(0);
    }

    {
        int ncolons = 0;
        for (p = in; *p != '\0'; p++)
          if (*p == ':') ncolons++;
        if (ncolons != 1) {           /* 0, or 2+ (a bare IPv6 literal) */
            ckstrncpy(hostbuf,in,hostbuflen);
            return(0);
        }
    }

    p = in;
    while (*p != ':') p++;
    n = (int)(p - in);
    if (n >= hostbuflen)
      n = hostbuflen - 1;
    bcopy(in,hostbuf,n);
    hostbuf[n] = '\0';
    if (portbuf && portbuflen > 0)
      ckstrncpy(portbuf,p + 1,portbuflen);
    return(1);
}

/*
  Enclose unbracketed multi-colon IPv6 address literals in brackets.

  Modifies buf in place before a service colon is appended. Leaves buf
  unmodified if it contains fewer than two colons, already begins with
  '[', or if buflen is insufficient for brackets.
*/
VOID
#ifdef CK_ANSIC
ck_bracketaddr(char * buf, int buflen)
#else /* CK_ANSIC */
ck_bracketaddr(buf,buflen) char * buf; int buflen;
#endif /* CK_ANSIC */
{
    int ncolons = 0;
    char * p;
    char tmp[1024];

    if (!buf || buflen < 1 || !*buf || *buf == '[')
      return;
    for (p = buf; *p; p++)
      if (*p == ':') ncolons++;
    if (ncolons < 2)
      return;
    if ((int)strlen(buf) >= (int)sizeof(tmp) || (int)strlen(buf) > buflen - 3)
      return;                   /* Too long to safely re-bracket */
    ckstrncpy(tmp,buf,sizeof(tmp));
    buf[0] = '\0';
    ckstrncat(buf,"[",buflen);
    ckstrncat(buf,tmp,buflen);
    ckstrncat(buf,"]",buflen);
}

#ifdef EXCELAN
/*
  Most other BSD sockets implementations define these in header files
  and libraries.
*/
struct servent {
    unsigned short s_port;
};

struct hostent {
    short h_addrtype;
    struct in_addr h_addr;
    int h_length;
};

struct servent *
getservbyname(service, connection) char *service,*connection; {
    static struct servent servrec;
    int port;

    port = 0;
    if (strcmp(service, "telnet") == 0) port = 23;
    else if (strcmp(service, "smtp") == 0) port = 25;
    else port = atoi(service);

    debug(F101,"getservbyname return port ","",port);

    if (port > 0) {
        servrec.s_port = htons(port);
        return(&servrec);
    }
    return((struct servent *) NULL);
}

struct hostent *
gethostbyname(hostname) char *hostname; {
    return((struct hostent *) NULL);
}

unsigned long
inet_addr(name) char *name; {
    unsigned long addr;

    addr = rhost(&name);
    debug(F111,"inet_addr ",name,(int)addr);
    return(addr);
}

char *
inet_ntoa(in) struct in_addr in; {
    static char name[80];
    ckmakxmsg(name, ckuitoa(in.s_net),".",ckuitoa(in.s_host),".",
               ckuitoa(in.s_lh),".", ckuitoa(in.s_impno));
    return(name);
}
#else
#ifdef DEC_TCPIP                        /* UCX */

int ucx_port_bug = 0;                   /* Explained below */

#ifdef OLDIP				/* Very old VAXC or GCC */
/*
  Note that my oldest VAX C (V3.1-051) does not need (or want) OLDIP,
  hence the "Very old" in the comment - SMS, 2010/03/15.
*/
#define getservbyname my_getservbyname

#ifdef CK_ANSIC
globalref int (*C$$GA_UCX_GETSERVBYNAME)();
extern void C$$TRANSLATE();
extern void C$$SOCK_TRANSLATE();
#else
globalref int (*C$$GA_UCX_GETSERVBYNAME)();
extern VOID C$$TRANSLATE();
extern VOID C$$SOCK_TRANSLATE();
#endif /* CK_ANSIC */

struct servent *
my_getservbyname (service, proto) char *service, *proto; {
    static struct servent sent;
    struct iosb {
        union {
            unsigned long status;
            unsigned short st[2];
        } sb;
        unsigned long spare;
    } s;
    struct {
        struct iosb *s;
        char *serv;
        char *prot;
    } par;
    unsigned long e;
    char sbuf[30], pbuf[30];
    char *p;

    debug(F111,"UCX getservbyname",service,(int)C$$GA_UCX_GETSERVBYNAME);

    p = sbuf;
    ckstrncpy(p, service, 29);
    while (*p = toupper(*p), *p++) {}
    p = pbuf;
    ckstrncpy(p, proto, 29);
    while (*p = toupper(*p), *p++) {}

    par.s = &s;

    par.serv = "";
    par.prot = "";
    /* reset file pointer or something like that!?!? */
    e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s);
    par.serv = sbuf;
    par.prot = pbuf;            /* that is don't care */
    e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s);
    if ((long)e == -1L)
      return NULL;
    if ((e & 1) == 0L) {
        C$$TRANSLATE(e);
        return NULL;
    }
    if ((s.sb.st[0] & 1) == 0) {
        C$$SOCK_TRANSLATE(&s.sb.st[0]);
        return NULL;
    }
/*
  sent.s_port is supposed to be returned by UCX in network byte order.
  However, UCX 2.0 through 2.0C did not do this; 2.0D and later do it.
  But there is no way of knowing which UCX version, so we have a user-settable
  runtime variable.  Note: UCX 2.0 was only for the VAX.
*/
    debug(F101,"UCX getservbyname port","",sent.s_port);
    debug(F101,"UCX getservbyname ntohs(port)","",ntohs(sent.s_port));
    if (ucx_port_bug) {
        sent.s_port = htons(sent.s_port);
        debug(F100,"UCX-PORT-BUG ON: swapping bytes","",0);
        debug(F101,"UCX swapped port","",sent.s_port);
        debug(F101,"UCX swapped ntohs(port)","",ntohs(sent.s_port));
    }
    return &sent;
}
#endif /* def OLDIP */
#endif /* DEC_TCPIP */
#endif /* EXCELAN */

int
gettcpport() {
    return(svcnum);
}

#endif /* TCPSOCKET */

#ifndef NOTCPOPTS
#ifndef datageneral
int
#ifdef CK_ANSIC
ck_linger( int sock, int onoff, int timo )
#else
ck_linger(sock, onoff, timo) int sock; int onoff; int timo;
#endif /* CK_ANSIC */
{
/*
  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
    struct linger set_linger_opt;
    struct linger get_linger_opt;
    SOCKOPT_T x;

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
        nettype != NET_TCPA && nettype != NET_TCPB &&
        nettype != NET_SSH || ttmdm >= 0) {
        tcp_linger = onoff;
        tcp_linger_tmo = timo;
        return(1);
    }
    x = sizeof(get_linger_opt);
    if (getsockopt(sock, SOL_SOCKET, SO_LINGER,
                    (char *)&get_linger_opt, &x)) {
        debug(F111,"TCP ck_linger can't get SO_LINGER",ck_errstr(),errno);
    } else if (x != sizeof(get_linger_opt)) {
#ifdef OS2
        struct _linger16 {
            short s_linger;
            short s_onoff;
        } get_linger_opt16, set_linger_opt16;
        if ( x == sizeof(get_linger_opt16) ) {
            debug(F111,"TCP setlinger warning: SO_LINGER","len is 16-bit",x);
            if (getsockopt(sock,
                           SOL_SOCKET, SO_LINGER,
                           (char *)&get_linger_opt16, &x)
                ) {
                debug(F111,
                      "TCP ck_linger can't get SO_LINGER",ck_errstr(),errno);
            } else if (get_linger_opt16.s_onoff != onoff ||
                       get_linger_opt16.s_linger != timo)
            {
                set_linger_opt16.s_onoff  = onoff;
                set_linger_opt16.s_linger = timo;
                if (setsockopt(sock,
                               SOL_SOCKET,
                               SO_LINGER,
                               (char *)&set_linger_opt16,
                               sizeof(set_linger_opt16))
                    ) {
                    debug(F111,
                          "TCP ck_linger can't set SO_LINGER",
                          ck_errstr(),
                          errno
                          );
                    tcp_linger = get_linger_opt16.s_onoff;
                    tcp_linger_tmo = get_linger_opt16.s_linger;
                } else {
                    debug(F101,
                          "TCP ck_linger new SO_LINGER","",
                          set_linger_opt16.s_onoff);
                    tcp_linger = set_linger_opt16.s_onoff;
                    tcp_linger_tmo = set_linger_opt16.s_linger;
                    return 1;
                }
            } else {
                debug(F101,"TCP ck_linger SO_LINGER unchanged","",
                       get_linger_opt16.s_onoff);
                tcp_linger = get_linger_opt16.s_onoff;
                tcp_linger_tmo = get_linger_opt16.s_linger;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP ck_linger error: SO_LINGER","len",x);
        debug(F111,"TCP ck_linger SO_LINGER",
              "expected len",sizeof(get_linger_opt));
        debug(F111,"TCP ck_linger SO_LINGER","linger_opt.l_onoff",
              get_linger_opt.l_onoff);
        debug(F111,"TCP linger SO_LINGER","linger_opt.l_linger",
               get_linger_opt.l_linger);
    } else if (get_linger_opt.l_onoff != onoff ||
               get_linger_opt.l_linger != timo) {
        set_linger_opt.l_onoff  = onoff;
        set_linger_opt.l_linger = timo;
        if (setsockopt(sock,
                       SOL_SOCKET,
                       SO_LINGER,
                       (char *)&set_linger_opt,
                       sizeof(set_linger_opt))) {
            debug(F111,"TCP ck_linger can't set SO_LINGER",ck_errstr(),errno);
            tcp_linger = get_linger_opt.l_onoff;
            tcp_linger_tmo = get_linger_opt.l_linger;
         } else {
             debug(F101,
                   "TCP ck_linger new SO_LINGER",
                   "",
                   set_linger_opt.l_onoff
                   );
             tcp_linger = set_linger_opt.l_onoff;
             tcp_linger_tmo = set_linger_opt.l_linger;
             return 1;
         }
    } else {
        debug(F101,"TCP ck_linger SO_LINGER unchanged","",
              get_linger_opt.l_onoff);
        tcp_linger = get_linger_opt.l_onoff;
        tcp_linger_tmo = get_linger_opt.l_linger;
        return 1;
    }
#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
#ifdef CK_ANSIC
sendbuf( int sock, int size )
#else
sendbuf(sock,size) int sock; int size;
#endif /* CK_ANSIC */
{
/*
  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
    int i, j;
    SOCKOPT_T x;

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
        nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
                || ttmdm >= 0) {
        tcp_sendbuf = size;
        return 1;
    }
    x = sizeof(i);
    if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&i, &x)) {
        debug(F111,"TCP sendbuf can't get SO_SNDBUF",ck_errstr(),errno);
    } else if (x != sizeof(i)) {
#ifdef OS2
        short i16,j16;
        if (x == sizeof(i16)) {
            debug(F111,"TCP sendbuf warning: SO_SNDBUF","len is 16-bit",x);
            if (getsockopt(sock,
                           SOL_SOCKET, SO_SNDBUF,
                           (char *)&i16, &x)
                ) {
                debug(F111,"TCP sendbuf can't get SO_SNDBUF",
                      ck_errstr(),errno);
            } else if (size <= 0) {
                tcp_sendbuf = i16;
                debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i16);
                return 1;
            } else if (i16 != size) {
                j16 = size;
                if (setsockopt(sock,
                               SOL_SOCKET,
                               SO_SNDBUF,
                               (char *)&j16,
                               sizeof(j16))
                    ) {
                    debug(F111,"TCP sendbuf can't set SO_SNDBUF",
                          ck_errstr(),errno);
                } else {
                    debug(F101,"TCP sendbuf old SO_SNDBUF","",i16);
                    debug(F101,"TCP sendbuf new SO_SNDBUF","",j16);
                    tcp_sendbuf = size;
                    return 1;
                }
            } else {
                debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i16);
                tcp_sendbuf = size;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP sendbuf error: SO_SNDBUF","len",x);
        debug(F111,"TCP sendbuf SO_SNDBUF","expected len",sizeof(i));
        debug(F111,"TCP sendbuf SO_SNDBUF","i",i);
    } else if (size <= 0) {
        tcp_sendbuf = i;
        debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i);
        return 1;
    } else if (i != size) {
        j = size;
        if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&j, sizeof(j))) {
            debug(F111,"TCP sendbuf can't set SO_SNDBUF",ck_errstr(),errno);
            tcp_sendbuf = i;
        } else {
            debug(F101,"TCP sendbuf old SO_SNDBUF","",i);
            debug(F101,"TCP sendbuf new SO_SNDBUF","",j);
            tcp_sendbuf = size;
            return 1;
        }
    } else {
        debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i);
        tcp_sendbuf = size;
        return 1;
    }
#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
#ifdef CK_ANSIC
recvbuf( int sock, int size )
#else
recvbuf(sock,size) int sock; int size;
#endif /* CK_ANSIC */
{
/*
  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
    int i, j;
    SOCKOPT_T x;

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
	  nettype != NET_TCPA && nettype != NET_TCPB &&
	  nettype != NET_SSH || ttmdm >= 0) {
        tcp_recvbuf = size;
        return(1);
    }
    x = sizeof(i);
    if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&i, &x)) {
        debug(F111,"TCP recvbuf can't get SO_RCVBUF",ck_errstr(),errno);
    } else if (x != sizeof(i)) {
#ifdef OS2
        short i16,j16;
        if ( x == sizeof(i16) ) {
            debug(F111,"TCP recvbuf warning: SO_RCVBUF","len is 16-bit",x);
            if (getsockopt(sock,
                           SOL_SOCKET, SO_RCVBUF,
                           (char *)&i16, &x)
                ) {
                debug(F111,"TCP recvbuf can't get SO_RCVBUF",
                      ck_errstr(),errno);
            } else if (size <= 0) {
                tcp_recvbuf = i16;
                debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i16);
                return 1;
            } else if (i16 != size) {
                j16 = size;
                if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j16,
                               sizeof(j16))) {
                    debug(F111,"TCP recvbuf can' set SO_RCVBUF",
                          ck_errstr(),errno);
                } else {
                    debug(F101,"TCP recvbuf old SO_RCVBUF","",i16);
                    debug(F101,"TCP recvbuf new SO_RCVBUF","",j16);
                    tcp_recvbuf = size;
                    return 1;
                }
            } else {
                debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i16);
                tcp_recvbuf = size;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP recvbuf error: SO_RCVBUF","len",x);
        debug(F111,"TCP recvbuf SO_RCVBUF","expected len",sizeof(i));
        debug(F111,"TCP recvbuf SO_RCVBUF","i",i);
    } else if (size <= 0) {
        tcp_recvbuf = i;
        debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i);
        return 1;
    } else if (i != size) {
        j = size;
        if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j, sizeof(j))) {
            debug(F111,"TCP recvbuf can't set SO_RCVBUF",ck_errstr(),errno);
            tcp_recvbuf = i;
        } else {
            debug(F101,"TCP recvbuf old SO_RCVBUF","",i);
            debug(F101,"TCP recvbuf new SO_RCVBUF","",j);
            tcp_recvbuf = size;
            return 1;
        }
    } else {
        debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i);
        tcp_recvbuf = size;
        return 1;
    }
#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
#ifdef CK_ANSIC
keepalive( int sock, int onoff )
#else
keepalive(sock,onoff) int sock; int onoff;
#endif /* CK_ANSIC */
{
#ifdef SOL_SOCKET
#ifdef SO_KEEPALIVE
    int get_keepalive_opt;
    int set_keepalive_opt;
    SOCKOPT_T x;

    debug(F111,"TCP keepalive","sock",sock);
    debug(F111,"TCP keepalive","nettype",nettype);
    debug(F111,"TCP keepalive","ttmdm",ttmdm);

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
        nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
                || ttmdm >= 0) {
        tcp_keepalive = onoff;
        return 1;
    }
    x = sizeof(get_keepalive_opt);
    if (getsockopt(sock,
                   SOL_SOCKET, SO_KEEPALIVE, (char *)&get_keepalive_opt, &x)) {
        debug(F111,"TCP keepalive can't get SO_KEEPALIVE",ck_errstr(),errno);
    } else if (x != sizeof(get_keepalive_opt)) {
#ifdef OS2
        short get_keepalive_opt16;
        short set_keepalive_opt16;
        if (x == sizeof(get_keepalive_opt16)) {
            debug(F111,"TCP keepalive warning: SO_KEEPALIVE",
                  "len is 16-bit",x);
            if (getsockopt(sock,
                           SOL_SOCKET, SO_KEEPALIVE,
                           (char *)&get_keepalive_opt16, &x)
                ) {
                debug(F111,
                      "TCP keepalive can't get SO_KEEPALIVE",
                      ck_errstr(),
                      errno
                      );
            } else if (get_keepalive_opt16 != onoff) {
                set_keepalive_opt16 = onoff;
                if (setsockopt(sock,
                               SOL_SOCKET,
                               SO_KEEPALIVE,
                               (char *)&set_keepalive_opt16,
                               sizeof(set_keepalive_opt16))
                    ) {
                    debug(F111,
                          "TCP keepalive can't clear SO_KEEPALIVE",
                          ck_errstr(),
                          errno
                          );
                    tcp_keepalive = get_keepalive_opt16;
                } else {
                    debug(F101,
                          "TCP keepalive new SO_KEEPALIVE","",
                          set_keepalive_opt16);
                    tcp_keepalive = set_keepalive_opt16;
                    return 1;
                }
            } else {
                debug(F101,"TCP keepalive SO_KEEPALIVE unchanged","",
                      get_keepalive_opt16);
                tcp_keepalive = onoff;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP keepalive error: SO_KEEPALIVE","len",x);
        debug(F111,
              "TCP keepalive SO_KEEPALIVE",
              "expected len",
              sizeof(get_keepalive_opt)
              );
        debug(F111,
              "TCP keepalive SO_KEEPALIVE",
              "keepalive_opt",
              get_keepalive_opt
              );
    } else if (get_keepalive_opt != onoff) {
            set_keepalive_opt = onoff;
            if (setsockopt(sock,
                            SOL_SOCKET,
                            SO_KEEPALIVE,
                            (char *)&set_keepalive_opt,
                            sizeof(set_keepalive_opt))
                ) {
                debug(F111,
                      "TCP keepalive can't clear SO_KEEPALIVE",
                      ck_errstr(),
                      errno
                      );
                tcp_keepalive = get_keepalive_opt;
            } else {
                debug(F101,
                      "TCP keepalive new SO_KEEPALIVE",
                      "",
                      set_keepalive_opt
                      );
                tcp_keepalive = onoff;
                return 1;
            }
        } else {
            debug(F101,"TCP keepalive SO_KEEPALIVE unchanged",
                  "",
                  get_keepalive_opt
                  );
            tcp_keepalive = onoff;
            return 1;
    }
#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
#ifdef CK_ANSIC
dontroute( int sock, int onoff )
#else
dontroute(sock,onoff) int sock; int onoff;
#endif /* CK_ANSIC */
{
#ifdef SOL_SOCKET
#ifdef SO_DONTROUTE
    int get_dontroute_opt;
    int set_dontroute_opt;
    SOCKOPT_T x;

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
        nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
                || ttmdm >= 0) {
        tcp_dontroute = onoff;
        return 1;
    }
    x = sizeof(get_dontroute_opt);
    if (getsockopt(sock,
                   SOL_SOCKET, SO_DONTROUTE, (char *)&get_dontroute_opt, &x)) {
        debug(F111,"TCP dontroute can't get SO_DONTROUTE",ck_errstr(),errno);
    } else if (x != sizeof(get_dontroute_opt)) {
#ifdef OS2
        short get_dontroute_opt16;
        short set_dontroute_opt16;
        if (x == sizeof(get_dontroute_opt16)) {
            debug(F111,"TCP dontroute warning: SO_DONTROUTE",
                  "len is 16-bit",x);
            if (getsockopt(sock,
                           SOL_SOCKET, SO_DONTROUTE,
                           (char *)&get_dontroute_opt16, &x)
                ) {
                debug(F111,
                      "TCP dontroute can't get SO_DONTROUTE",
                      ck_errstr(),
                      errno
                      );
            } else if (get_dontroute_opt16 != onoff) {
                set_dontroute_opt16 = onoff;
                if (setsockopt(sock,
                               SOL_SOCKET,
                               SO_DONTROUTE,
                               (char *)&set_dontroute_opt16,
                               sizeof(set_dontroute_opt16))
                    ) {
                    debug(F111,
                          "TCP dontroute can't clear SO_DONTROUTE",
                          ck_errstr(),
                          errno
                          );
                    tcp_dontroute = get_dontroute_opt16;
                } else {
                    debug(F101,
                          "TCP dontroute new SO_DONTROUTE","",
                          set_dontroute_opt16);
                    tcp_dontroute = set_dontroute_opt16;
                    return 1;
                }
            } else {
                debug(F101,"TCP dontroute SO_DONTROUTE unchanged","",
                      get_dontroute_opt16);
                tcp_dontroute = onoff;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP dontroute error: SO_DONTROUTE","len",x);
        debug(F111,
              "TCP dontroute SO_DONTROUTE",
              "expected len",
              sizeof(get_dontroute_opt)
              );
        debug(F111,
              "TCP dontroute SO_DONTROUTE",
              "dontroute_opt",
              get_dontroute_opt
              );
    } else if (get_dontroute_opt != onoff) {
            set_dontroute_opt = onoff;
            if (setsockopt(sock,
                            SOL_SOCKET,
                            SO_DONTROUTE,
                            (char *)&set_dontroute_opt,
                            sizeof(set_dontroute_opt))
                ) {
                debug(F111,
                      "TCP dontroute can't clear SO_DONTROUTE",
                      ck_errstr(),
                      errno
                      );
                tcp_dontroute = get_dontroute_opt;
            } else {
                debug(F101,
                      "TCP dontroute new SO_DONTROUTE",
                      "",
                      set_dontroute_opt
                      );
                tcp_dontroute = onoff;
                return 1;
            }
        } else {
            debug(F101,"TCP dontroute SO_DONTROUTE unchanged",
                  "",
                  get_dontroute_opt
                  );
            tcp_dontroute = onoff;
            return 1;
    }
#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
#ifdef CK_ANSIC
no_delay( int sock, int onoff )
#else
no_delay(sock,onoff)  int sock; int onoff;
#endif /* CK_ANSIC */
{
#ifdef SOL_SOCKET
#ifdef TCP_NODELAY
    int get_nodelay_opt;
    int set_nodelay_opt;
    SOCKOPT_T x;

#ifdef IKSD
    if (!inserver)
#endif /* IKSD */
      if (sock == -1 ||
        nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
                || ttmdm >= 0) {
        tcp_nodelay = onoff;
        return(1);
    }
    x = sizeof(get_nodelay_opt);
    if (getsockopt(sock,IPPROTO_TCP,TCP_NODELAY,
                   (char *)&get_nodelay_opt,&x)) {
        debug(F111,
              "TCP no_delay can't get TCP_NODELAY",
              ck_errstr(),
              errno);
    } else if (x != sizeof(get_nodelay_opt)) {
#ifdef OS2
        short get_nodelay_opt16;
        short set_nodelay_opt16;
        if (x == sizeof(get_nodelay_opt16)) {
            debug(F111,"TCP no_delay warning: TCP_NODELAY","len is 16-bit",x);
            if (getsockopt(sock,
                           IPPROTO_TCP, TCP_NODELAY,
                           (char *)&get_nodelay_opt16, &x)
                ) {
                debug(F111,
                      "TCP no_delay can't get TCP_NODELAY",
                      ck_errstr(),
                      errno);
            } else if (get_nodelay_opt16 != onoff) {
                set_nodelay_opt16 = onoff;
                if (setsockopt(sock,
                               IPPROTO_TCP,
                               TCP_NODELAY,
                               (char *)&set_nodelay_opt16,
                               sizeof(set_nodelay_opt16))
                    ) {
                    debug(F111,
                          "TCP no_delay can't clear TCP_NODELAY",
                          ck_errstr(),
                          errno);
                    tcp_nodelay = get_nodelay_opt16;
                } else {
                    debug(F101,
                          "TCP no_delay new TCP_NODELAY",
                          "",
                          set_nodelay_opt16);
                    tcp_nodelay = onoff;
                    return 1;
                }
            } else {
                debug(F101,"TCP no_delay TCP_NODELAY unchanged","",
                      get_nodelay_opt16);
                tcp_nodelay = onoff;
                return 1;
            }
            return(0);
        }
#endif /* OS2 */
        debug(F111,"TCP no_delay error: TCP_NODELAY","len",x);
        debug(F111,"TCP no_delay TCP_NODELAY","expected len",
              sizeof(get_nodelay_opt));
        debug(F111,"TCP no_delay TCP_NODELAY","nodelay_opt",get_nodelay_opt);
    } else if (get_nodelay_opt != onoff) {
        set_nodelay_opt = onoff;
        if (setsockopt(sock,
                       IPPROTO_TCP,
                       TCP_NODELAY,
                       (char *)&set_nodelay_opt,
                       sizeof(set_nodelay_opt))) {
            debug(F111,
                  "TCP no_delay can't clear TCP_NODELAY",
                  ck_errstr(),
                  errno
                  );
            tcp_nodelay = get_nodelay_opt;
        } else {
            debug(F101,"TCP no_delay new TCP_NODELAY","",set_nodelay_opt);
            tcp_nodelay = onoff;
            return 1;
        }
    } else {
        debug(F101,"TCP no_delay TCP_NODELAY unchanged","",get_nodelay_opt);
        tcp_nodelay = onoff;
        return(1);
    }
#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 /* datageneral */
#endif /* NOTCPOPTS */

#ifdef SUNX25
#ifndef X25_WR_FACILITY
/* For Solaris 2.3 / SunLink 8.x - see comments in ckcnet.h */
void
bzero(s,n) char *s; int n; {
    memset(s,0,n);
}
#endif /* X25_WR_FACILITY */
#endif /* SUNX25 */

#ifdef TCPSOCKET
#ifndef OS2
#ifndef NOLISTEN

#ifdef BSDSELECT
#ifndef VMS
#ifndef BELLV10
#ifndef datageneral
#ifdef hp9000s500                       /* HP-9000/500 HP-U 5.21 */
#include <time.h>
#else

/****** THIS SECTION ADDED BY STEVE RANCE - OS9 NETWORK SERVER
*       ------------------------------------------------------
*
*       Due to OS9's Lack of a select() call, the following seems to be
*       enough to fool the rest of the code into compiling. The only
*       effect that I can see is using control L to refresh the status
*       display gets qued up until some network packets arrive.
*
*       This solution is by no means elegant but works enough to be
*       a (the) solution.
*
*       Also with the defines I had specified in my makefile I had to
*       have an #endif right at the end of the file when compiling.
*       I did not bother speding time to find out why.
*
*       COPTS   = -to=osk -d=OSK -d=TCPSOCKET -d=SELECT -d=VOID=void -d=SIG_V \
*          -d=DYNAMIC -d=PARSENSE -d=KANJI -d=MYCURSES -d=ZFCDAT \
*          -d=CK_APC -d=CK_REDIR -d=RENAME -d=CK_TTYFD -d=NOOLDMODEMS \
*          -d=CK_ANSIC -d=CK_XYZ -tp=68040d -l=netdb.l -l=socklib.l \
*          -l=termlib.l -l=math.l -l=sys_clib.l
*
*       stever@ozemail.com.au
*/

#ifdef  OSK
#define BSDSELECT                       /* switch on BSD select code */
#define FD_SETSIZE 32                   /* Max # of paths in OS9 */
#define FD_ZERO(p)                      ((*p)=0)
#define FD_SET(n,b)                     ((*b)|=(1<<(n)))
#define FD_ISSET(n,b)           1       /* always say data is ready */
#define select(a,b,c,d,e)       1       /* always say 1 path has data */
typedef int     fd_set;                 /* keep BSD Code Happy */
struct timeval {int tv_sec,tv_usec;};   /* keep BSD Code Happy */

/****** END OF OS9 MODS FROM STEVE RANCE **************************/
#endif /* OSK */

#include <sys/time.h>
#endif /* hp9000s500 */
#endif /* datageneral */
#endif /* BELLV10 */
#endif /* VMS */
#ifdef SELECT_H
#include <sys/select.h>
#endif /* SELECT_H */
#endif /* BSDSELECT */

#ifdef SELECT
#ifdef CK_SCOV5
#include <sys/select.h>
#endif /* CK_SCOV5 */
#endif /* SELECT */

#ifdef NOTUSED
/* T C P S O C K E T _ O P E N -- Open a preexisting socket number */

int
tcpsocket_open(name,lcl,nett,timo) char * name; int * lcl; int nett; int timo {
    int on = 1;
    static struct servent *service, servrec;
    static struct hostent *host;
    static struct sockaddr_in saddr;
    static
#ifdef UCX50
      unsigned
#endif /* UCX50 */
      int saddrlen;
#ifdef BSDSELECT
    fd_set rfds;
    struct timeval tv;
#else
#ifdef BELLSELECT
    fd_set rfds;
#else
    fd_set rfds;
    fd_set rfds;
    struct timeval {
        long tv_sec;
        long tv_usec;
    } tv;
#endif /* BELLSELECT */
#endif /* BSDSELECT */

    debug(F101,"tcpsocket_open nett","",nett);
    *ipaddr = '\0';

    if (nett != NET_TCPB)
      return(-1);                       /* BSD socket support */

    netclos();                          /* Close any previous connection. */
    ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */
    /* fdc's version from 4 Dec 2005 works ok */
    if (ttnproto != NP_TCPRAW)
      ttnproto = NP_NONE;               /* No protocol selected yet. */
    debug(F110,"tcpsocket_open namecopy",namecopy,0);

    /* Assign the socket number to ttyfd and then fill in tcp structures */
    ttyfd = atoi(&name[1]);
    debug(F111,"tcpsocket_open","ttyfd",ttyfd);

#ifndef NOTCPOPTS
#ifdef SOL_SOCKET
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);

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

#ifdef NT_TCP_OVERLAPPED
    OverlappedWriteInit();
    OverlappedReadInit();
#endif /* NT_TCP_OVERLAPPED */


    /* Get the name of the host we are connected to */

    saddrlen = sizeof(saddr);
    getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen);

    ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),CK_IPADDRLEN);

    if (tcp_rdns == SET_ON
#ifdef CK_KERBEROS
        || tcp_rdns == SET_AUTO &&
         (ck_krb5_is_installed() || ck_krb4_is_installed())
#endif /* CK_KERBEROS */
#ifndef NOHTTP
          && (tcp_http_proxy == NULL)
#endif /* NOHTTP */
#ifdef CK_SSL
          && !(ssl_only_flag || tls_only_flag)
#endif /* CK_SSL */
         ) {                            /* Reverse DNS */
        if (!quiet) {
            printf(" Reverse DNS Lookup... ");
            fflush(stdout);
        }
        host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET);
        debug(F110,"tcpsocket_open gethostbyaddr",host ? "OK" : "FAILED",0);
        if (host) {
            host = ck_copyhostent(host);
            debug(F100,"tcpsocket_open gethostbyaddr != NULL","",0);
            if (!quiet) {
                printf("(OK)\n");
                fflush(stdout);
            }
            ckstrncpy(name, host->h_name, 80);
            ckstrncat(name, ":", 80);
            ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)), 80);
            if (!quiet
#ifndef NOICP
                && !doconx
#endif /* NOICP */
                )
              printf("%s connected on port %d\n",
                   host->h_name,
                   ntohs(saddr.sin_port)
                   );
        } else if (!quiet)
          printf("Failed\n");
    } else if (!quiet)
      printf("(OK)\n");

    if (tcp_rdns != SET_ON || !host) {
        ckstrncpy(name,ipaddr,80);
        ckstrncat(name,":",80);
        ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80);
        if (!quiet
#ifdef NOICP
            && !doconx
#endif /* NOICP */
            )
          printf("%s connected on port %d\n",ipaddr,ntohs(saddr.sin_port));
    }
    if (!quiet) fflush(stdout);
    ttnet = nett;                       /* TCP/IP (sockets) network */

#ifdef RLOGCODE
    if (ntohs(saddr.sin_port) == 513)
        ttnproto = NP_LOGIN;
    else
#endif /* RLOGCODE */
    /* Assume the service is TELNET. */
      /* fdc's code from 2005/12/04 */
      if (ttnproto != NP_TCPRAW)
	ttnproto = NP_TELNET;		/* Yes, set global flag. */
#ifdef CK_SECURITY
    /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
    ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ?
                host->h_name : ipaddr,
                ipaddr,
                uidbuf,
                ttyfd
                );
#endif /* CK_SECURITY */
    if (tn_ini() < 0)                   /* Start/Reset TELNET negotiations */
      if (ttchk() < 0)                  /* Did it fail due to connect loss? */
        return(-1);

    if (*lcl < 0) *lcl = 1;             /* Local mode. */

    return(0);                          /* Done. */
}
#endif /* NOTUSED */

/*  T C P S R V _ O P E N  --  Open a TCP/IP Server connection  */
/*
  Calling conventions same as ttopen(), except third argument is network
  type rather than modem type.  ttopen() also takes a fourth argument,
  a timeout, for backends where polling with a timeout instead of
  blocking makes sense.  tcpsrv_open() has no such argument, since its
  own accept() wait is always a plain, indefinite block and its one
  caller, netopen(), never wanted anything else.
*/
int
#ifdef CK_ANSIC
tcpsrv_open( char * name, int * lcl, int nett )
#else
tcpsrv_open(name,lcl,nett) char * name; int * lcl; int nett;
#endif /* CK_ANSIC */
{
    char *p;
    int i, x;
    SOCKOPT_T on = 1;
    static struct servent *service, *service2, servrec;
    static struct hostent *host;
    static struct sockaddr_in saddr;
    struct sockaddr_in l_addr;
    GSOCKNAME_T l_slen;
#ifdef UCX50
    static u_int saddrlen;
#else
    static SOCKOPT_T saddrlen;
#endif /* UCX50 */

#ifdef CK_IPV6
    fd_set rfds;                     /* For picking between two listeners */
#endif /* CK_IPV6 */
#ifdef CK_SSL
    int ssl_failed = 0;
#endif /* CK_SSL */
#ifdef CK_IPV6
    static struct sockaddr_in6 saddr6;
    struct sockaddr_storage acc_addr;
    GSOCKNAME_T acc_addrlen;
    int tcpsrv_isv6 = 0;             /* Set if the accepted peer is IPv6 */
    int want_v4 = (tcp_af != TCP_AF_V6);
    int want_v6 = (tcp_af != TCP_AF_V4);
    int connport = 0;                /* Peer port, either family */
    int v6_rdns_ok = 0;              /* Set if v6 reverse lookup succeeded */
#endif /* CK_IPV6 */

    debug(F101,"tcpsrv_open nett","",nett);
    *ipaddr = '\0';

    if (nett != NET_TCPB)
      return(-1);                       /* BSD socket support */

    netclos();                          /* Close any previous connection. */
    ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */
    /* Don't do this. */
    debug(F110,"tcpsrv_open namecopy",namecopy,0);

    p = namecopy;                       /* Was a service requested? */
    while (*p != '\0' && *p != ':')
      p++; /* Look for colon */
    if (*p == ':') {                    /* Have a colon */
        *p++ = '\0';                    /* Get service name or number */
    } else {                            /* Otherwise use kermit */
        p = "kermit";
    }
    debug(F110,"tcpsrv_open 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 && !strcmp("kermit",p)) { /* Use Kermit service port */
        service = &servrec;
        service->s_port = htons(1649);
    }
#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 */
    if (!service) {
        fprintf(stderr, "Cannot find port for service: %s\n", p);
        debug(F111,"tcpsrv_open can't get service",p,errno);
        errno = 0;                      /* rather than mislead */
        return(-1);
    }

    /* If we currently have a listen active but port has changed then close */

    debug(F101,"tcpsrv_open checking previous connection","",tcpsrfd);
    debug(F101,"tcpsrv_open previous tcpsrv_port","",tcpsrv_port);
    if (tcpsrfd != -1 &&
        tcpsrv_port != ntohs((unsigned short)service->s_port)) {
        debug(F100,"tcpsrv_open closing previous connection","",0);
#ifdef TCPIPLIB
        socket_close(tcpsrfd);
#else
        close(tcpsrfd);
#endif /* TCPIPLIB */
        tcpsrfd = -1;
    }
#ifdef CK_IPV6
    if (tcpsrfd6 != -1 &&
        tcpsrv_port != ntohs((unsigned short)service->s_port)) {
        debug(F100,"tcpsrv_open closing previous v6 connection","",0);
        close(tcpsrfd6);
        tcpsrfd6 = -1;
    }

    /* SET TCP ADDRESS-FAMILY may have changed since the last listener
       was opened on this same port. Retract any already-open socket
       of a family that's no longer wanted, so a stale v4 or v6
       listener doesn't keep accepting after the setting changes. */
    if (tcpsrfd != -1 && !want_v4) {
        debug(F100,"tcpsrv_open closing v4 socket, family changed","",0);
#ifdef TCPIPLIB
        socket_close(tcpsrfd);
#else /* TCPIPLIB */
        close(tcpsrfd);
#endif /* TCPIPLIB */
        tcpsrfd = -1;
    }
    if (tcpsrfd6 != -1 && !want_v6) {
        debug(F100,"tcpsrv_open closing v6 socket, family changed","",0);
        close(tcpsrfd6);
        tcpsrfd6 = -1;
    }
#endif /* CK_IPV6 */
    debug(F100,"tcpsrv_open tcpsrfd","",tcpsrfd);
#ifdef CK_IPV6
    if (want_v4 && tcpsrfd == -1) {
#else /* CK_IPV6 */
    if (tcpsrfd == -1) {
#endif /* CK_IPV6 */

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

        bzero((char *)&saddr, sizeof(saddr));
        debug(F100,"tcpsrv_open bzero ok","",0);
        saddr.sin_family = AF_INET;
        if (tcp_address) {
#ifdef INADDRX
            inaddrx = inet_addr(tcp_address);
            saddr.sin_addr.s_addr = *(unsigned long *)&inaddrx;
#else
            saddr.sin_addr.s_addr = inet_addr(tcp_address);
#endif /* INADDRX */
        } else
          saddr.sin_addr.s_addr = INADDR_ANY;

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

        saddr.sin_port = service->s_port;
        ipaddr[0] = '\0';

        debug(F100,"tcpsrv_open calling socket","",0);
        if ((tcpsrfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("TCP socket error");
            debug(F101,"tcpsrv_open socket error","",errno);
            return (-1);
        }
        errno = 0;

        /* Specify the Port may be reused */

        debug(F100,"tcpsrv_open calling setsockopt","",0);
        x = setsockopt(tcpsrfd,
                       SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof on);
        debug(F101,"tcpsrv_open setsockopt","",x);

       /* Now bind to the socket */
        printf("\nBinding socket to port %d ...\n",
               ntohs((unsigned short)service->s_port));
        if (bind(tcpsrfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
            i = errno;                  /* Save error code */
#ifdef TCPIPLIB
            socket_close(tcpsrfd);
#else /* TCPIPLIB */
            close(tcpsrfd);
#endif /* TCPIPLIB */
            tcpsrfd = -1;
            tcpsrv_port = 0;
            ttyfd = -1;
            wasclosed = 1;
            errno = i;                  /* and report this error */
            debug(F101,"tcpsrv_open bind errno","",errno);
            printf("?Unable to bind to socket (errno = %d)\n",errno);
            return(-1);
        }
        debug(F100,"tcpsrv_open bind OK","",0);
        printf("Listening ...\n");
        if (listen(tcpsrfd, 15) < 0) {
            i = errno;                  /* Save error code */
#ifdef TCPIPLIB
            socket_close(tcpsrfd);
#else /* TCPIPLIB */
            close(tcpsrfd);
#endif /* TCPIPLIB */
            tcpsrfd = -1;
            tcpsrv_port = 0;
            ttyfd = -1;
            wasclosed = 1;
            errno = i;                  /* And report this error */
            debug(F101,"tcpsrv_open listen errno","",errno);
            return(-1);
        }
        debug(F100,"tcpsrv_open listen OK","",0);
        tcpsrv_port = ntohs((unsigned short)service->s_port);
    }

#ifdef CK_IPV6
    /*
      IPv6 listener, as a separate socket rather than one dual-stack one, since
      IPV6_V6ONLY isn't sufficiently portable.

      Its failure is fatal only when IPv6 is the only wanted family.
      Under AUTO, a v6 setup problem falls back to IPv4-only,
      leaving alone the v4 socket that was already set up above.

      A v4 setup failure is fatal.
    */
    if (want_v6 && tcpsrfd6 == -1) {
        int v6_fatal = !want_v4;

        bzero((char *)&saddr6, sizeof(saddr6));
        saddr6.sin6_family = AF_INET6;
        if (tcp_address6) {
            /* Parse tcp_address6 and retrieve scope ID for binding. */
            if (!ck_scopeaddr6(tcp_address6,&saddr6.sin6_addr,
                                &saddr6.sin6_scope_id)) {
                debug(F110,"tcpsrv_open tcp_address6 invalid",
                      tcp_address6,0);
                if (v6_fatal) {
                    fprintf(stderr,
                            "?SET TCP ADDRESS6 value \"%s\" is no longer "
                            "valid\n",tcp_address6);
                    return(-1);
                }
                fprintf(stderr,
                        "?SET TCP ADDRESS6 value \"%s\" is no longer "
                        "valid; continuing with IPv4 only\n",tcp_address6);
                goto skip_v6_listener;
            }
        } else
          saddr6.sin6_addr = in6addr_any;
        saddr6.sin6_port = service->s_port;

        debug(F100,"tcpsrv_open calling socket (v6)","",0);
        if ((tcpsrfd6 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
            debug(F101,"tcpsrv_open v6 socket error","",errno);
            if (v6_fatal) {
                perror("TCP socket error");
                return(-1);
            }
            fprintf(stderr,
                    "?Unable to create IPv6 listening socket (errno = %d); "
                    "continuing with IPv4 only\n",errno);
        } else {
            x = setsockopt(tcpsrfd6,
                           SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof on);
            debug(F101,"tcpsrv_open v6 setsockopt SO_REUSEADDR","",x);
#ifdef IPV6_V6ONLY
            {
                int v6only = 1;
                x = setsockopt(tcpsrfd6,IPPROTO_IPV6,IPV6_V6ONLY,
                               (char *)&v6only,sizeof v6only);
                debug(F101,"tcpsrv_open v6 setsockopt IPV6_V6ONLY","",x);
                if (x < 0) {
                    if (v6_fatal) {
                        /* An explicit IPv6-only request must not fall
                           through to a dual-stack socket that also
                           accepts IPv4-mapped clients on kernels whose
                           default is to allow that (Linux does). */
                        i = errno;
                        close(tcpsrfd6);
                        tcpsrfd6 = -1;
                        errno = i;
                        debug(F101,"tcpsrv_open v6 V6ONLY errno","",errno);
                        printf(
                          "?Unable to restrict IPv6 socket to IPv6-only "
                          "(errno = %d)\n",errno);
                        ttyfd = -1;
                        wasclosed = 1;
                        return(-1);
                    }
                    fprintf(stderr,
                            "?Warning: unable to restrict IPv6 socket to "
                            "IPv6-only (errno = %d); it may also accept "
                            "IPv4-mapped connections\n",errno);
                }
            }
#endif /* IPV6_V6ONLY */
            printf("\nBinding IPv6 socket to port %d ...\n",
                   ntohs((unsigned short)service->s_port));
            if (bind(tcpsrfd6,(struct sockaddr *)&saddr6,
                     sizeof(saddr6)) < 0) {
                i = errno;
                close(tcpsrfd6);
                tcpsrfd6 = -1;
                errno = i;
                debug(F101,"tcpsrv_open v6 bind errno","",errno);
                if (v6_fatal) {
                    printf("?Unable to bind to IPv6 socket (errno = %d)\n",
                           errno);
                    ttyfd = -1;
                    wasclosed = 1;
                    return(-1);
                }
                fprintf(stderr,
                        "?Unable to bind IPv6 socket (errno = %d); "
                        "continuing with IPv4 only\n",errno);
            } else {
                printf("Listening (IPv6) ...\n");
                if (listen(tcpsrfd6, 15) < 0) {
                    i = errno;
                    close(tcpsrfd6);
                    tcpsrfd6 = -1;
                    errno = i;
                    debug(F101,"tcpsrv_open v6 listen errno","",errno);
                    if (v6_fatal) {
                        ttyfd = -1;
                        wasclosed = 1;
                        return(-1);
                    }
                    fprintf(stderr,
                            "?Unable to listen on IPv6 socket (errno = %d); "
                            "continuing with IPv4 only\n",errno);
                } else {
                    debug(F100,"tcpsrv_open v6 listen OK","",0);
                    tcpsrv_port = ntohs((unsigned short)service->s_port);
                }
            }
        }
      skip_v6_listener:
        ;
    }
#endif /* CK_IPV6 */

#ifdef CK_SSL
    if (ck_ssleay_is_installed()) {
        if (!ssl_tn_init(SSL_SERVER)) {
            ssl_failed = 1;
            if (bio_err!=NULL) {
                BIO_printf(bio_err,"do_ssleay_init() failed\n");
                ERR_print_errors(bio_err);
            } else {
                fflush(stderr);
                fprintf(stderr,"do_ssleay_init() failed\n");
                ERR_print_errors_fp(stderr);
            }
            if (tls_only_flag || ssl_only_flag) {
#ifdef TCPIPLIB
                socket_close(ttyfd);
                socket_close(tcpsrfd);
#else /* TCPIPLIB */
                close(ttyfd);
                close(tcpsrfd);
#endif /* TCPIPLIB */
                ttyfd = -1;
                wasclosed = 1;
                tcpsrfd = -1;
                tcpsrv_port = 0;
                return(-1);
            }
            /* we will continue to accept the connection   */
            /* without SSL or TLS support unless required. */
            if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
        } else {
            /* Without a certificate we cannot complete a START-TLS
             * handshake.  Do not request or offer it in that case,
             * so we never begin a handshake we cannot finish. */
            if (!ssl_rsa_cert_file && !ssl_dsa_cert_file) {
                if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
                    TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
                if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
                    TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
            }
        }
    }
#endif /* CK_SSL */

    printf("\nWaiting to Accept a TCP/IP connection on port %d ...\n",
           ntohs((unsigned short)service->s_port));
    saddrlen = sizeof(saddr);
#ifdef CK_IPV6
    acc_addrlen = sizeof(acc_addr);
#endif /* CK_IPV6 */

    /* Block until a connection arrives, then accept it. */
    {
#ifdef CK_IPV6
        {
            int srvfd;
            if (tcpsrfd != -1 && tcpsrfd6 != -1) {
                /* Both active: a plain accept() can only ever watch one fd, so
                   select() figures out which one to use. A NULL timeval blocks
                   indefinitely, same as accept() would have. Retry on EINTR
                   instead of trusting rfds, whose state POSIX leaves
                   unspecified when select() returns -1.

                   maxfd and FD_ISSET() must skip the same
                   out-of-range descriptor FD_SET() skips.  Passing an fd
                   >= FD_SETSIZE to either is the same out-of-bounds
                   access on the fd_set bitmask that the FD_SET() guard
                   exists to prevent. */
                int maxfd = -1;
                int rc;
                if (tcpsrfd < FD_SETSIZE && tcpsrfd > maxfd)
                  maxfd = tcpsrfd;
                if (tcpsrfd6 < FD_SETSIZE && tcpsrfd6 > maxfd)
                  maxfd = tcpsrfd6;
                if (maxfd < 0) {
                    fprintf(stderr,
                          "?Listener socket descriptor exceeds FD_SETSIZE\n");
                    return(-1);
                }
                do {
                    FD_ZERO(&rfds);
                    if (tcpsrfd < FD_SETSIZE)
                      FD_SET(tcpsrfd,  &rfds);
                    if (tcpsrfd6 < FD_SETSIZE)
                      FD_SET(tcpsrfd6, &rfds);
                    rc = select(maxfd + 1, &rfds, NULL, NULL, NULL);
                } while (rc < 0 && errno == EINTR);
                if (rc < 0) {
                    perror("select");
                    return(-1);
                }
                srvfd = (tcpsrfd < FD_SETSIZE && FD_ISSET(tcpsrfd, &rfds)) ?
                  tcpsrfd : tcpsrfd6;
            } else {
                srvfd = (tcpsrfd != -1) ? tcpsrfd : tcpsrfd6;
            }
            tcpsrv_isv6 = (srvfd == tcpsrfd6);
            if (tcpsrv_isv6)
              ttyfd = accept(srvfd,(struct sockaddr *)&acc_addr,
                              &acc_addrlen);
            else
              ttyfd = accept(srvfd,(struct sockaddr *)&saddr,&saddrlen);
        }
        if (ttyfd < 0) {
            i = errno;                  /* save error code */
            if (tcpsrv_isv6) {
                close(tcpsrfd6);
                tcpsrfd6 = -1;
            } else {
#ifdef TCPIPLIB
                socket_close(tcpsrfd);
#else /* TCPIPLIB */
                close(tcpsrfd);
#endif /* TCPIPLIB */
                tcpsrfd = -1;
            }
            ttyfd = -1;
            wasclosed = 1;
            errno = i;                  /* and report this error */
            debug(F101,"tcpsrv_open accept errno","",errno);
            return(-1);
        }
#else /* CK_IPV6 */
        if ((ttyfd = accept(tcpsrfd,
                            (struct sockaddr *)&saddr,&saddrlen)) < 0) {
            i = errno;                  /* save error code */
#ifdef TCPIPLIB
            socket_close(tcpsrfd);
#else /* TCPIPLIB */
            close(tcpsrfd);
#endif /* TCPIPLIB */
            ttyfd = -1;
            wasclosed = 1;
            tcpsrfd = -1;
            tcpsrv_port = 0;
            errno = i;                  /* and report this error */
            debug(F101,"tcpsrv_open accept errno","",errno);
            return(-1);
        }
#endif /* CK_IPV6 */
        setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);

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

        ttnet = nett;                   /* TCP/IP (sockets) network */
        tcp_incoming = 1;               /* This is an incoming connection */
        sstelnet = 1;                   /* Do server-side Telnet protocol */

        /* See if the service is TELNET. */
        x = (unsigned short)service->s_port;
        service2 = getservbyname("telnet", "tcp");
        if (service2 && x == service2->s_port) {
	    /* fdc 2005/12/04 */
            if (ttnproto != NP_TCPRAW)  /* Yes and if raw port not requested */
              ttnproto = NP_TELNET;	/* set protocol to TELNET. */
        }
#ifdef CK_IPV6
        if (tcpsrv_isv6) {
            if (ck_straddr((struct sockaddr *)&acc_addr,acc_addrlen,
                            ipaddr,CK_IPADDRLEN) < 0)
              ipaddr[0] = '\0';
            connport = ck_getport((struct sockaddr *)&acc_addr);
        } else {
            ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),CK_IPADDRLEN);
            connport = ntohs(saddr.sin_port);
        }
#else /* CK_IPV6 */
        ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),CK_IPADDRLEN);
#endif /* CK_IPV6 */
        if (tcp_rdns) {
            if (!quiet) {
                printf(" Reverse DNS Lookup... ");
                fflush(stdout);
            }
#ifdef CK_IPV6
            if (tcpsrv_isv6) {
                char v6nibuf[256];
                /* Clear any stale hostent left by a previous IPv4
                   client accepted on this same listener.  Otherwise, a
                   failed v6 lookup would leave this connection
                   misattributed to the earlier client's name. */
                host = NULL;
                if (getnameinfo((struct sockaddr *)&acc_addr,acc_addrlen,
                                 v6nibuf,sizeof(v6nibuf),NULL,0,
                                 NI_NAMEREQD) == 0) {
                    v6_rdns_ok = 1;
                    debug(F110,"tcpsrv_open getnameinfo",v6nibuf,0);
                    if (!quiet) {
                        printf("(OK)\n");
                        fflush(stdout);
                    }
                    name[0] = '*';
                    ckstrncpy(&name[1],v6nibuf,78);
                    ckstrncat(name,":",80);
                    ckstrncat(name,p,80);
                    if (!quiet
#ifndef NOICP
                        && !doconx
#endif /* NOICP */
                        )
                      printf("%s connected on port %s\n",v6nibuf,p);
                } else {
                    if (!quiet) printf("Failed.\n");
                }
            } else
#endif /* CK_IPV6 */
            if ((host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET))) {
                host = ck_copyhostent(host);
                debug(F100,"tcpsrv_open gethostbyaddr != NULL","",0);
                if (!quiet) {
                    printf("(OK)\n");
                    fflush(stdout);
                }
                name[0] = '*';
                ckstrncpy(&name[1],host->h_name,78);
                ckstrncat(name,":",80);
                ckstrncat(name,p,80);
                if (!quiet
#ifndef NOICP
                    && !doconx
#endif /* NOICP */
                    )
                  printf("%s connected on port %s\n",host->h_name,p);
            } else {
                if (!quiet) printf("Failed.\n");
            }
        } else if (!quiet) printf("(OK)\n");

        if (!tcp_rdns ||
#ifdef CK_IPV6
            (!host && !v6_rdns_ok)
#else /* CK_IPV6 */
            !host
#endif /* CK_IPV6 */
            ) {
            ckstrncpy(name,ipaddr,80);
            ckstrncat(name,":",80);
#ifdef CK_IPV6
            ckstrncat(name,ckuitoa(tcpsrv_isv6 ?
                                    connport : ntohs(saddr.sin_port)),80);
#else /* CK_IPV6 */
            ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80);
#endif /* CK_IPV6 */
            if (!quiet
#ifndef NOICP
                && !doconx
#endif /* NOICP */
                )
              printf("%s connected on port %d\n",ipaddr,
#ifdef CK_IPV6
                     tcpsrv_isv6 ? connport : ntohs(saddr.sin_port));
#else /* CK_IPV6 */
                     ntohs(saddr.sin_port));
#endif /* CK_IPV6 */
        }
        if (!quiet) fflush(stdout);

#ifdef CK_SECURITY
        /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
        ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ?
                     (char *)host->h_name : ipaddr,
                     ipaddr,
                     uidbuf,
                     ttyfd
                     );
#endif /* CK_SECURITY */

#ifdef CK_SSL
        if (ck_ssleay_is_installed() && !ssl_failed) {
            if (ck_ssl_incoming(ttyfd) < 0) {
#ifdef TCPIPLIB
                    socket_close(ttyfd);
                    socket_close(tcpsrfd);
#else /* TCPIPLIB */
                    close(ttyfd);
                    close(tcpsrfd);
#endif /* TCPIPLIB */
                    ttyfd = -1;
                    wasclosed = 1;
                    tcpsrfd = -1;
                    tcpsrv_port = 0;
#ifdef CK_IPV6
                    if (tcpsrfd6 != -1) {
                        close(tcpsrfd6);
                        tcpsrfd6 = -1;
                    }
#endif /* CK_IPV6 */
                    return(-1);
            }
        }
#endif /* CK_SSL */

#ifndef datageneral
        /* Find out our own IP address. */
#ifdef CK_IPV6
        if (tcpsrv_isv6) {
            struct sockaddr_storage lss;
            GSOCKNAME_T lsslen = sizeof(lss);
            bzero((char *)&lss, sizeof(lss));
            if (!getsockname(ttyfd, (struct sockaddr *)&lss, &lsslen)) {
                char lsbuf[CK_IPADDRLEN];
                if (ck_straddr((struct sockaddr *)&lss,lsslen,
                                lsbuf,sizeof(lsbuf)) == 0)
                  ckstrncpy(myipaddr, lsbuf, CK_IPADDRLEN);
                debug(F110,"getsockname",myipaddr,0);
            }
        } else
#endif /* CK_IPV6 */
        {
        l_slen = sizeof(l_addr);
        bzero((char *)&l_addr, l_slen);
#ifndef EXCELAN
        if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) {
            char * s = (char *)inet_ntoa(l_addr.sin_addr);
            ckstrncpy(myipaddr, s,CK_IPADDRLEN);
            debug(F110,"getsockname",myipaddr,0);
        }
#endif /* EXCELAN */
        }
#endif /* datageneral */

        if (tn_ini() < 0)               /* Start TELNET negotiations. */
          if (ttchk() < 0) {            /* Disconnected? */
              i = errno;                /* save error code */
#ifdef TCPIPLIB
              socket_close(tcpsrfd);
#else /* TCPIPLIB */
              close(tcpsrfd);
#endif /* TCPIPLIB */
              ttyfd = -1;
              wasclosed = 1;
              tcpsrfd = -1;
              tcpsrv_port = 0;
#ifdef CK_IPV6
              if (tcpsrfd6 != -1) {
                  close(tcpsrfd6);
                  tcpsrfd6 = -1;
              }
#endif /* CK_IPV6 */
              errno = i;                /* and report this error */
              debug(F101,"tcpsrv_open accept errno","",errno);
              return(-1);
          }
        debug(F101,"tcpsrv_open service","",x);
        if (*lcl < 0)                   /* Set local mode. */
          *lcl = 1;

#ifdef CK_KERBEROS
#ifdef KRB5_U2U
        if ( ttnproto == NP_K5U2U ) {
            if (k5_user_to_user_server_auth() != 0) {
                i = errno;                /* save error code */
#ifdef TCPIPLIB
                socket_close(tcpsrfd);
#else /* TCPIPLIB */
                close(tcpsrfd);
#endif /* TCPIPLIB */
                ttyfd = -1;
                wasclosed = 1;
                tcpsrfd = -1;
                tcpsrv_port = 0;
#ifdef CK_IPV6
                if (tcpsrfd6 != -1) {
                    close(tcpsrfd6);
                    tcpsrfd6 = -1;
                }
#endif /* CK_IPV6 */
                errno = i;                /* and report this error */
                debug(F101,"tcpsrv_open accept errno","",errno);
                return(-1);
            }
        }
#endif /* KRB5_U2U */
#endif /* CK_KERBEROS */
        return(0);                      /* Done. */
    }
}
#endif /* NOLISTEN */
#endif /* OS2 */
#endif /* TCPSOCKET */
#endif /* NONET */

#ifdef TCPSOCKET
/*
  ckname2addr() and ckaddr2name() are general-purpose DNS lookups and
  support the \fname2addr() and \faddr2name() script functions.
*/
char *
#ifdef CK_ANSIC
ckname2addr( char * name )
#else
ckname2addr(name) char * name;
#endif /* CK_ANSIC */
{
#ifdef HPUX5
    return("");
#else
#ifdef CK_IPV6
    static char buf[CK_IPADDRLEN];
    struct addrinfo hints, *results = NULL;

    if (name == NULL || *name == '\0')
        return("");

    bzero((char *)&hints, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family =
      (tcp_af == TCP_AF_V4) ? AF_INET :
      (tcp_af == TCP_AF_V6) ? AF_INET6 : AF_UNSPEC;

    if (getaddrinfo(name,NULL,&hints,&results) != 0 || !results)
      return("");
    if (ck_straddr(results->ai_addr,(GSOCKNAME_T)results->ai_addrlen,
                    buf,sizeof(buf)) < 0) {
        freeaddrinfo(results);
        return("");
    }
    freeaddrinfo(results);
    return(buf);
#else /* CK_IPV6 */
    struct hostent *host;

    if (name == NULL || *name == '\0')
        return("");

    host = gethostbyname(name);
    if ( host ) {
        host = ck_copyhostent(host);
        return(inet_ntoa(*((struct in_addr *) host->h_addr)));
    }
    return("");
#endif /* CK_IPV6 */
#endif /* HPUX5 */
}

char *
#ifdef CK_ANSIC
ckaddr2name( char * addr )
#else
ckaddr2name(addr) char * addr;
#endif /* CK_ANSIC */
{
#ifdef HPUX5
    return("");
#else
#ifdef CK_IPV6
    static char buf[256];
    struct addrinfo hints, *results = NULL;

    if (addr == NULL || *addr == '\0')
        return("");

    bzero((char *)&hints, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_UNSPEC;
    hints.ai_flags = AI_NUMERICHOST;   /* addr is a literal, not a name */

    if (getaddrinfo(addr,NULL,&hints,&results) != 0 || !results)
      return("");
    if (getnameinfo(results->ai_addr,(socklen_t)results->ai_addrlen,
                     buf,sizeof(buf),NULL,0,NI_NAMEREQD) != 0) {
        freeaddrinfo(results);
        return("");
    }
    freeaddrinfo(results);
    return(buf);
#else /* CK_IPV6 */
    struct hostent *host;
    struct in_addr sin_addr;

    if (addr == NULL || *addr == '\0')
        return("");

    sin_addr.s_addr = inet_addr(addr);
    host = gethostbyaddr((char *)&sin_addr,4,AF_INET);
    if (host) {
        host = ck_copyhostent(host);
        return((char *)host->h_name);
    }
    return("");
#endif /* CK_IPV6 */
#endif /* HPUX5 */
}
#endif /* TCPSOCKET */

/*
  peeripaddr is the peer's IP address as text, set by every successful call to
  ckgetpeer() alongside its usual resolved name return value.  Unlike namebuf
  (the return value), it's never a resolved hostname, so it's usable by callers
  such as the IKSD session database that want a numeric addresses.
*/
char peeripaddr[CK_IPADDRLEN] = { '\0' };

char *
ckgetpeer() {
#ifdef TCPSOCKET
    static char namebuf[256];
#ifdef CK_IPV6
    {
        struct sockaddr_storage pss;
        GSOCKNAME_T pslen = sizeof(pss);

        if (getpeername(ttyfd,(struct sockaddr *)&pss,&pslen) < 0) {
            debug(F111,"ckgetpeer failure",ckitoa(ttyfd),errno);
            peeripaddr[0] = '\0';
            return(NULL);
        }
        if (ck_straddr((struct sockaddr *)&pss,pslen,
                        peeripaddr,CK_IPADDRLEN) < 0)
          peeripaddr[0] = '\0';
        if (pss.ss_family == AF_INET6) {
            char nibuf[256];
            if (getnameinfo((struct sockaddr *)&pss,(socklen_t)pslen,
                             nibuf,sizeof(nibuf),NULL,0,NI_NAMEREQD) == 0) {
                ckstrncpy(namebuf,nibuf,256);
            } else {
                ckstrncpy(namebuf,peeripaddr,256);
            }
        } else {
            struct sockaddr_in * sinp = (struct sockaddr_in *)&pss;
            struct hostent * host;
            host = gethostbyaddr((char *)&sinp->sin_addr,4,AF_INET);
            if (host) {
                host = ck_copyhostent(host);
                ckstrncpy(namebuf,(char *)host->h_name,256);
            } else {
                ckstrncpy(namebuf,(char *)inet_ntoa(sinp->sin_addr),256);
            }
        }
        debug(F110,"ckgetpeer",namebuf,0);
        return(namebuf);
    }
#else /* CK_IPV6 */
    static struct hostent *host;
    static struct sockaddr_in saddr;
#ifdef GPEERNAME_T
    static GPEERNAME_T saddrlen;
#else
#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 DEC_TCPIP
/* 2010-03-08 SMS.
 * Coincidentally, the condition for integer arguments in select(),
 * which is actually "defined( _DECC_V4_SOURCE)", works for an integer
 * argument in getpeername().  Sadly, due to a lack of foresight,
 * "defined( _DECC_V4_SOURCE)" doesn't work with DEC C V4.0, so the
 * user-specified INTSELECT is used instead.  Most likely, "size_t"
 * should be used instead of "unsigned int", but I'm a coward.
 */
#ifdef INTSELECT
    static int saddrlen;
#else /* def INTSELECT */
    static unsigned int saddrlen;
#endif /* def INTSELECT [else] */
#else
#ifdef MACOSX10
    static unsigned int saddrlen;
#else
#ifdef NT
    static int saddrlen;
#else
#ifdef CK_64BIT
    static socklen_t saddrlen;
#else
    static int saddrlen;
#endif	/* CK_64BIT */
#endif /* NT */
#endif /* MACOSX10 */
#endif /* DEC_TCPIP */
#endif /* UNIXWARE */
#endif /* AIX42 */
#endif /* PTX */
#endif	/* GPEERNAME_T */
    saddrlen = sizeof(saddr);
    if (getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen) < 0) {
        debug(F111,"ckgetpeer failure",ckitoa(ttyfd),errno);
        peeripaddr[0] = '\0';
        return(NULL);
    }
    host = gethostbyaddr((char *)&saddr.sin_addr,4,AF_INET);
    if (host) {
        host = ck_copyhostent(host);
        ckstrncpy(namebuf,(char *)host->h_name,80);
    } else {
        ckstrncpy(namebuf,(char *)inet_ntoa(saddr.sin_addr),80);
    }
    ckstrncpy(peeripaddr,(char *)inet_ntoa(saddr.sin_addr),CK_IPADDRLEN);
    debug(F110,"ckgetpeer",namebuf,0);
    return(namebuf);
#endif /* CK_IPV6 */
#else
    return(NULL);
#endif /* TCPSOCKET */
}

#ifndef NOTCPIP
/* Get fully qualified IP hostname */

char *
#ifdef CK_ANSIC
ckgetfqhostname(char * name)
#else
ckgetfqhostname(name) char * name;
#endif /* CK_ANSIC */
{
#ifdef NOCKGETFQHOST

    return(name);

#else /* If the following code dumps core, define NOCKGETFQHOST and rebuild. */

    static char namebuf[256];

    debug(F110,"ckgetfqhn()",name,0);

    ckstrncpy(namebuf,name,256);
    namebuf[255] = '\0';

#ifdef CK_IPV6
    /*
      Split off an optional ":service" suffix with ck_splithostport()
      rather than the legacy branch's plain "find the first
      colon" rule.

      Once the host part is isolated, resolve it, then reverse-resolve
      (PTR lookup) that result like the legacy branch does.
      getaddrinfo() alone isn't enough, since its AI_CANONNAME only follows
      CNAMEs on the forward side and is not a real reverse lookup. Malformed
      bracket syntax is ignored.
    */
    {
        char sphost[256], spport[256];
        int hasport = ck_splithostport(namebuf,sphost,sizeof(sphost),
                                        spport,sizeof(spport));
        if (hasport >= 0) {
            struct addrinfo hints, *results = NULL;
            int gai, gni;

            bzero((char *)&hints, sizeof(hints));
            hints.ai_socktype = SOCK_STREAM;
            hints.ai_family = AF_UNSPEC;
            gai = getaddrinfo(sphost,NULL,&hints,&results);
            if (gai == 0 && results) {
                char nibuf[256];
                debug(F100,"ckgetfqhn() getaddrinfo != NULL","",0);
                gni = getnameinfo(results->ai_addr,
                                   (socklen_t)results->ai_addrlen,
                                   nibuf,sizeof(nibuf),NULL,0,NI_NAMEREQD);
                if (gni == 0) {
                    debug(F100,"ckgetfqhn() getnameinfo != NULL","",0);
                    ckstrncpy(sphost,nibuf,256);
                }
                freeaddrinfo(results);
            }
            ckstrncpy(namebuf,sphost,256);
            if (hasport) {
                ckstrncat(namebuf,":",256);
                ckstrncat(namebuf,spport,256);
            }
        }
    }
#else /* CK_IPV6 */
    {
        struct hostent *host=NULL;
        struct sockaddr_in r_addr;
        int i;

        i = ckindex(":",namebuf,0,0,0);
        if (i)
          namebuf[i-1] = '\0';

        bzero((char *)&r_addr, sizeof(r_addr));

        host = gethostbyname(namebuf);
        if (host) {
            host = ck_copyhostent(host);
            debug(F100,"ckgetfqhn() gethostbyname != NULL","",0);
            r_addr.sin_family = host->h_addrtype;
#ifdef HADDRLIST
#ifdef h_addr
            /* This is for trying multiple IP addresses - see <netdb.h> */
            if (!(host->h_addr_list))
              goto exit_func;
            bcopy(host->h_addr_list[0],
                  (caddr_t)&r_addr.sin_addr,
                  host->h_length
                  );
#else
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif /* h_addr */
#else  /* HADDRLIST */
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif /* HADDRLIST */
            debug(F111,"BCOPY","host->h_length",host->h_length);

#ifdef NT
            /* Windows 95/98 requires a 1 second wait between calls to */
            /* Microsoft provided DNS functions.  Otherwise, the TTL   */
            /* of the DNS response is ignored.                        */
            if (isWin95())
              sleep(1);
#endif /* NT */
            host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET);
            if (host) {
                host = ck_copyhostent(host);
                debug(F100,"ckgetfqhn() gethostbyaddr != NULL","",0);
                ckstrncpy(namebuf, host->h_name, 256);
            }
        }

#ifdef HADDRLIST
#ifdef h_addr
      exit_func:
#endif /* h_addr */
#endif /* HADDRLIST */
        if (i > 0)
          ckstrncat(namebuf,&name[i-1],256);
    }
#endif /* CK_IPV6 */

    debug(F110,"ckgetfqhn()",namebuf,0);
    return(namebuf);
#endif /* NOCKGETFQHOST */
}

VOID
#ifdef CK_ANSIC
setnproto(char * p)
#else
setnproto(p) char * p;
#endif /* CK_ANSIC */
{
    if (!isdigit(*p)) {
        if (!strcmp("kermit",p))
          ttnproto = NP_KERMIT;
        else if (!strcmp("telnet",p))
          ttnproto = NP_TELNET;
        else if (!strcmp("http",p))
          ttnproto = NP_TCPRAW;
#ifdef RLOGCODE
        else if (!strcmp("login",p))
          ttnproto = NP_RLOGIN;
#endif /* RLOGCODE */
#ifdef CK_SSL
        /* Commonly used SSL ports (might not be in services file) */
        else if (!strcmp("https",p)) {
          ttnproto = NP_SSL_RAW;
          ssl_only_flag = 1;
        } else if (!strcmp("ssl-telnet",p)) {
          ttnproto = NP_TELNET;
          ssl_only_flag = 1;
        } else if (!strcmp("telnets",p)) {
          ttnproto = NP_TELNET;
          ssl_only_flag = 1;
        }
#endif /* CK_SSL */
#ifdef CK_KERBEROS
#ifdef RLOGCODE
        else if (!strcmp("klogin",p)) {
            if (ck_krb5_is_installed())
              ttnproto = NP_K5LOGIN;
            else if (ck_krb4_is_installed())
              ttnproto = NP_K4LOGIN;
            else
              ttnproto = NP_RLOGIN;
        } else if (!strcmp("eklogin",p)) {
            if (ck_krb5_is_installed())
              ttnproto = NP_EK5LOGIN;
            else if (ck_krb4_is_installed())
              ttnproto = NP_EK4LOGIN;
            else
              ttnproto = NP_RLOGIN;
        }
#endif /* RLOGCODE */
#endif /* CK_KERBEROS */
        else
          ttnproto = NP_NONE;
    } else {
        switch (atoi(p)) {
          case 23:                      /* Telnet */
            ttnproto = NP_TELNET;
            break;
          case 513:
            ttnproto = NP_RLOGIN;
            break;
          case 1649:
            ttnproto = NP_KERMIT;
            break;
#ifdef CK_SSL
          case 443:
	    /* fdc 2005/12/04 */
            ttnproto = NP_SSL;
            ssl_only_flag = 1;
            break;
          case 151:
          case 992:
            ttnproto = NP_TELNET;
            ssl_only_flag = 1;
            break;
#endif /* CK_SSL */
#ifdef CK_KERBEROS
          case 543:
            if (ck_krb5_is_installed())
              ttnproto = NP_K5LOGIN;
            else if (ck_krb4_is_installed())
              ttnproto = NP_K4LOGIN;
            else
              ttnproto = NP_RLOGIN;
            break;
          case 2105:
            if (ck_krb5_is_installed())
              ttnproto = NP_EK5LOGIN;
            else if (ck_krb4_is_installed())
              ttnproto = NP_EK4LOGIN;
            else
              ttnproto = NP_RLOGIN;
            break;
#endif /* CK_KERBEROS */
          case 80:                      /* HTTP */
            ttnproto = NP_TCPRAW;
            break;
          default:
            ttnproto = NP_NONE;
            break;
        }
    }
}

/* ckgetservice() is used to determine the port number for a given */
/* service taking into account the use of DNS SRV records.         */

static struct servent servrec;

#ifdef CK_ANSIC
/* prototype for static functions - fdc 01 December 2022 */
static struct servent * ckgetservice( char *, char *, char *, int );
#endif /* CK_ANSIC */

static struct servent *
#ifdef CK_ANSIC
ckgetservice( char *hostname, char * servicename, char * ip, int iplen )
#else
ckgetservice(hostname, servicename, ip, iplen)
    char *hostname; char * servicename; char * ip; int iplen;
#endif /* CK_ANSIC */
{
    struct servent * service = NULL;
#ifdef CK_DNS_SRV
    /*
      dns_addrs is IPv4-only (see locate_srv_dns() below): a DNS SRV
      target with only an AAAA record is silently skipped rather than
      used.  Not supported for IPv6 for now.  It is believed this is
      not used in the wild anywhere, making the added code complexity
      not worth it.
    */
    struct sockaddr * dns_addrs = NULL;
    int dns_naddrs = 0;
#endif /* CK_DNS_SRV */

    if (isdigit(*servicename)) {        /* Use socket number without lookup */
        service = &servrec;
        service->s_port = htons((unsigned short)atoi(servicename));
    } else {                            /* Otherwise lookup the service name */
#ifdef CK_DNS_SRV
        if (tcp_dns_srv && !quiet) {
            printf(" DNS SRV Lookup... ");
            fflush(stdout);
        }
        if (tcp_dns_srv &&
            locate_srv_dns(hostname,
                           servicename,
                           "tcp",
                           &dns_addrs,
                           &dns_naddrs
                           )
            ) {
            /* Use the first one.  Eventually we should cycle through all */
            /* the returned IP addresses and port numbers. */
            struct sockaddr_in *sin = NULL;
#ifdef BETADEBUG
            int i;
            printf("\r\n");
            for ( i=0;i<dns_naddrs;i++ ) {
                sin = (struct sockaddr_in *) &dns_addrs[i];
                printf("dns_addrs[%d] = %s %d\r\n", i,
                        (char *)inet_ntoa(sin->sin_addr),
                        ntohs(sin->sin_port));
            }
#endif /* BETADEBUG */
            sin = (struct sockaddr_in *) &dns_addrs[0];
            if ( ip && iplen > 0 )
                ckstrncpy(ip,(char *)inet_ntoa(sin->sin_addr),iplen);
            service = &servrec;
            service->s_port = sin->sin_port;

            free(dns_addrs);
            dns_addrs = NULL;
            dns_naddrs = 0;
        } else
#endif /* CK_DNS_SRV */
            service = getservbyname(servicename, "tcp");
    }
    if (!service) {
        if (!ckstrcmp("kermit",servicename,-1,0)) { /* Kermit service port */
            service = &servrec;
            service->s_port = htons(1649);
        } else if (!ckstrcmp("telnet",servicename,-1,0)) { /* Telnet port */
            service = &servrec;
            service->s_port = htons(23);
        } else if (!ckstrcmp("http",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(80);
        }
#ifdef RLOGCODE
        else if (!ckstrcmp("login",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(513);
        }
#endif /* RLOGCODE */
#ifdef CK_SSL
        /* Commonly used SSL ports (might not be in services file) */
        else if (!ckstrcmp("https",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(443);
        } else if (!ckstrcmp("ssl-telnet",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(151);
        } else if (!ckstrcmp("telnets",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(992);
        }
#endif /* CK_SSL */
#ifdef CK_KERBEROS
#ifdef RLOGCODE
        else if (!ckstrcmp("klogin",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(543);
        } else if (!ckstrcmp("eklogin",servicename,-1,0)) {
            service = &servrec;
            service->s_port = htons(2105);
        }
#endif /* RLOGCODE */
#endif /* CK_KERBEROS */
    }
    return(service);
}
#endif  /* NOTCPIP */

/*  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.
  Call with:
    name - name of host (or host:service),
    lcl  - local-mode flag to be set if this function succeeds,
    nett - network type (value defined in ckcnet.h)
*/

int
#ifdef CK_ANSIC
netopen( char *name, int *lcl, int nett )
#else
netopen(name, lcl, nett) char *name; int *lcl, nett;
#endif /* CK_ANSIC */
{
    char *p;
    int i, x, dns = 0;
#ifdef SOLARIS
    int rc_inet_addr = 0;
#endif /* SOLARIS */
#ifdef TCPSOCKET
    int isconnect = 0;
    /*
      Set when the connection that was actually made is IPv6, so the IPv4-only
      code (local address lookup and picking gethostbyaddr() instead of
      getnameinfo() for reverse DNS) knows which path to take instead of being
      fed a zeroed r_addr. Always declared as 0 on builds without CK_IPV6, so
      those guards need no #ifdef of their own.
    */
    int ckv6_connected = 0;
#ifdef CK_IPV6
    struct sockaddr_storage ckv6_addr;   /* Valid only if ckv6_connected */
#endif /* CK_IPV6 */
#ifdef SO_OOBINLINE
    int on = 1;
#endif /* SO_OOBINLINE */
    struct servent *service=NULL;
    struct hostent *host=NULL;
    struct sockaddr_in r_addr;
    struct sockaddr_in sin;
    struct sockaddr_in l_addr;
    GSOCKNAME_T l_slen;
#ifdef EXCELAN
    struct sockaddr_in send_socket;
#endif /* EXCELAN */

#ifdef INADDRX
/* inet_addr() is of type struct in_addr */
#ifdef datageneral
    extern struct in_addr inet_addr();
#else
#ifdef HPUX5WINTCP
    extern struct in_addr inet_addr();
#endif /* HPUX5WINTCP */
#endif /* datageneral */
    struct in_addr iax;
#else
#ifdef INADDR_NONE
    struct in_addr iax;
#else /* INADDR_NONE */
    long iax;
#endif /* INADDR_NONE */
#endif /* INADDRX */
#endif /* TCPSOCKET */

#ifdef SUNX25                           /* Code for SunLink X.25 support */
#define X29PID 1                        /* X.29 Protocol ID */
_PROTOTYP(SIGTYP x25oobh, (int) );
    CONN_DB x25host;
#ifndef X25_WR_FACILITY
    FACILITY x25facil;
#else
    FACILITY_DB x25facil;
#endif /* X25_WR_FACILITY */
    static int needh = 1;
    PID_T pid;
    extern int linkid, lcn, x25ver;
#endif /* SUNX25 */
#ifdef ANYX25
    extern int revcall, closgr, cudata;
    extern char udata[];
#endif /* ANYX25 */

#ifdef IBMX25                           /* Variables for IBM X25 */
    extern int x25port;                 /* Logical port to use */
    extern x25addr_t local_nua;         /* Local X.25 address */
    extern x25addr_t remote_nua;        /* Remote X.25 address */
    extern char x25name[];              /* X25 device name (sx25a0) */
    extern char x25dev[];               /* X25 device file /dev/x25pkt */
    ulong bind_flags = 0;               /* Flags for binding the X25 stream */
    ulong token = 0;                    /* Temporary return code */
#endif /* IBMX25 */

    debug(F101,"netopen nett","",nett);
    *ipaddr = '\0';                     /* Initialize IP address string */
    /* Not every path below fills these in.  For instance, an IPv6 connection
       fills ckv6_addr and myipaddr instead.  Since rlog_ini() is called
       unconditionally for any RLOGIN variant, both must never be
       left uninitialized. */
    bzero((char *)&r_addr,sizeof(r_addr));
    bzero((char *)&l_addr,sizeof(l_addr));

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

        /* Set up host structure */
        bzero((char *)&x25host,sizeof(x25host));
        if ((x25host.hostlen = pkx121(name,x25host.host)) < 0) {
            fprintf (stderr,"Invalid X.121 host address %s\n",name);
            errno = 0;
            return (-1);
        }
        x25host.datalen = X29PIDLEN;
        x25host.data[0] = X29PID;

        /* Set call user data if specified */
        if (cudata) {
            ckstrncpy((char *)x25host.data+X29PIDLEN,udata,(int)strlen(udata));
            x25host.datalen += (int)strlen(udata);
        }

        /* Open SunLink X.25 socket */
        if (!quiet && *name) {
            printf(" Trying %s... ", name);
            fflush(stdout);
        }
        if ((ttyfd = socket(AF_X25, SOCK_STREAM, 0)) < 0) {
            debug(F101,"netopen socket error","",errno);
            perror ("X.25 socket error");
            return (-1);
        }

        /* Setting X.25 out-of-band data handler */
        pid = getpid();
        if (ioctl(ttyfd,SIOCSPGRP,&pid)) {
            perror("X.25 set process group id error");
            return(-1);
        }
        (VOID) signal(SIGURG,x25oobh);

        /* Set reverse charge call and closed user group if requested */
        bzero ((char *)&x25facil,sizeof(x25facil));

#ifndef X25_WR_FACILITY
/*  New SunLink (7.0 or 8.0, not sure which)... */
        x25facil.type = T_REVERSE_CHARGE; /* Reverse Charge */
        x25facil.f_reverse_charge = revcall ? 1 : 0;
        if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) {
            perror ("Setting X.25 reverse charge");
            return (-1);
        }
        if (closgr > -1) {              /* Closed User Group (Outgoing) */
            bzero ((char *)&x25facil,sizeof(x25facil));
            x25facil.type = T_CUG;
            x25facil.f_cug_req = CUG_REQ_ACS;
            x25facil.f_cug_index = closgr;
            if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) {
                perror ("Setting X.25 closed user group");
                return (-1);
            }
        }
#else
/*  Old SunLink 6.0 (or 7.0?)... */
        if (revcall) x25facil.reverse_charge = revcall;
        if (closgr > -1) {
            x25facil.cug_req = 1;
            x25facil.cug_index = closgr;
        }
        if (ioctl(ttyfd,X25_WR_FACILITY,&x25facil) < 0) {
            perror ("Setting X.25 facilities");
            return (-1);
        }
#endif /* X25_WR_FACILITY */

        /*  Need X.25 header with bits Q and M */
        if (ioctl (ttyfd,X25_HEADER,&needh) < 0) {
            perror ("Setting X.25 header");
            return (-1);
        }

        /* Connects to remote host via SunLink X.25 */
        if (connect(ttyfd,(struct sockaddr *)&x25host,sizeof(x25host)) < 0) {
            i = errno;
            debug(F101,"netopen connect errno","",i);
            if (i) {
                perror("netopen x25 connect");
                x25diag();
            }
            (VOID) netclos();
            ttyfd = -1;
            wasclosed = 1;
            ttnproto = NP_NONE;
            errno = i;
            return (-1);
        }

        /* Get X.25 link identification used for the connection */
        if (ioctl(ttyfd,X25_GET_LINK,&linkid) < 0) {
            perror ("Getting X.25 link id");
            return (-1);
        }

        /* Get X.25 logical channel number used for the connection */
        if (ioctl(ttyfd,X25_RD_LCGN,&lcn) < 0) {
            perror ("Getting X.25 lcn");
            return (-1);
        }

        /* Get SunLink X.25 version */
        if (ioctl(ttyfd,X25_VERSION,&x25ver) < 0) {
            perror ("Getting SunLink X.25 version");
            return (-1);
        }
        ttnet = nett;                   /* Sunlink X.25 network */
        ttnproto = NP_X3;               /* PAD X.3, X.28, X.29 protocol */
        if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */
        return(0);
    } else /* Note that SUNX25 support can coexist with TCP/IP support. */
#endif /* SUNX25 */

#ifdef IBMX25
    /* riehm */
    if (nett == NET_IX25) {             /* IBM AIX X.25 */
        netclos();                      /* Close any previous net connection */
        ttnproto = NP_NONE;             /* No protocol selected yet */

        /* find out who we are - this is not so easy on AIX */
        /* riehm: need to write the code that finds this out
         * automatically, or at least allow it to be configured
         * somehow
         */
        if (!local_nua[0] && !x25local_nua(local_nua)) {
            return(-1);
        }

        /* Initialise the X25 API (once per process? once per connection?) */

        debug(F110, "Opening ", x25dev, 0 );
        /* set O_NDELAY to allow polling? */
        if ((ttyfd = open(x25dev, O_RDWR)) < 0) {
            perror ("X.25 device open error");
            debug(F101,"netopen: device open error","",errno);
            return (-1);
        }

        /* push the NPI onto the STREAM */
        if (ioctl(ttyfd,I_PUSH,"npi") < 0 ) {
            close(ttyfd);
            ttyfd = -1;
            wasclosed = 1;
            perror( "kermit: netopen(): couldn't push npi on the X25 stream" );
            debug(F101,"netopen: can't push npi on the X25 stream","",errno);
            return (-1);
        }

        /* set up server mode - bind the x25 port and wait for
         * incoming connections
         */
        if (name[0] == '*') {           /* Server */
            /* set up a server - see the warning in x25bind() */
            bind_flags |= TOKEN_REQUEST;

            /* bind kermit to the local X25 address */
            token = x25bind(ttyfd,
                            local_nua,
                            udata,
                            (int)strlen( udata ),
                            1,
                            x25port,
                            bind_flags
                            );
            if (token < 0) {
                debug(F100,"netopen: couldn't bind to local X25 address","",0);
                netclos();
                return(-1);
            }
            /* Currently not connected to a remote host */

            remote_nua[0] = '\0';

            /* store the fd so that incoming calls can have their own fd
             * This is almost support for a true server (ie: a'la ftpd)
             * but we're not quite there yet.
             * used in netclos()
             */
            x25serverfd = ttyfd;
            /*
             * wait for an incoming call
             * this should happen in the "server" command and not in
             * the "set host *" command.
             */
            if ((ttyfd = x25getcall(ttyfd)) < 0) {
                netclos();
                return(-1);
            }
        } else {                        /* Client */
            /* Bind kermit to the local X25 address */
            token = x25bind(
                            ttyfd,
                            local_nua,
                            (char *)NULL,
                            0,
                            0,
                            x25port,
                            bind_flags
                            );
            if (token < 0) {
                debug(F100,"netopen: couldn't bind to local X25 address","",0);
                netclos();
                return(-1);
            }
/* riehm: this should be done via the CONNECT command, not HOST! */
            {
                x25serverfd = 0;
                /* call the remote host */
                /* name == address of remote host as char* */
                if (x25call(ttyfd, name, udata) < 0 ) {
                    debug(F100,
                          "netopen: couldn't connect to remote X25 address",
                          "", 0);
                    netclos();
                    return(-1);
                }
                strcpy(remote_nua, name);
            }
        }
        ttnet = nett;                   /* AIX X.25 network */
        if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */
        return(0);

    } else /* Note that IBMX25 support can coexist with TCP/IP support. */
#endif /* IBMX25 */

/*   Add support for other networks here. */

      if (nett != NET_TCPB) return(-1); /* BSD socket support */

#ifdef TCPSOCKET
    netclos();                          /* Close any previous connection. */
    ckstrncpy(namecopy, name, NAMECPYL);        /* Copy the hostname. */
    debug(F110,"netopen namecopy",namecopy,0);

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

    if (namecopy[0] == '[') {           /* Bracketed literal, no URL */
        char hbuf[NAMECPYL], pbuf[NAMECPYL];
        int hasport = ck_splithostport(namecopy,hbuf,sizeof(hbuf),
                                        pbuf,sizeof(pbuf));
        if (hasport < 0) {
            fprintf(stderr,"Malformed address literal: %s\n",namecopy);
            errno = 0;
            return(-1);
        }
        ckstrncpy(namecopy,hbuf,NAMECPYL);
        if (hasport) {
            x = strlen(namecopy);
            p = namecopy + x + 1;
            ckstrncpy(p,pbuf,NAMECPYL - x - 1);
        } else {
            p = "telnet";
        }
    } else {
#ifdef CK_IPV6
        struct in6_addr ck_bare_v6_addr;
        unsigned int ck_bare_v6_scope;

        /* Check for a bare IPv6 literal before legacy colon parsing. */
        if (ck_scopeaddr6(namecopy,&ck_bare_v6_addr,&ck_bare_v6_scope)) {
            p = "telnet";
        } else
#endif /* CK_IPV6 */
        {
        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/path/
            Or a malformed URL such as generated by Netscape 4.0 like:
            telnet:telnet or telnet::host.
            */

            /*
            * REPLACE THIS CODE WITH urlparse() but not on the day of the
            * C-Kermit 8.0 RELEASE.
            */

            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 ||
#ifdef RLOGCODE
                !ckstrcmp("rlogin",namecopy,NAMECPYL,0) ||
#endif /* RLOGCODE */
#ifdef CK_SSL
                !ckstrcmp("telnets",namecopy,NAMECPYL,0) ||
#endif /* CK_SSL */
                !ckstrcmp("iksd",namecopy,NAMECPYL,0)
                ) {
                char temphost[256], tempservice[80], temppath[256];
                char * q = p, *r = p, *w = p;
                int uidfound=0;
                extern char pwbuf[];
                extern int pwflg, pwcrypt;

                if (ttnproto == NP_DEFAULT)
                    setnproto(namecopy);

                /* 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 the password */
                    debug(F110,"netopen namecopy username",r,0);
                    debug(F110,"netopen namecopy password",w,0);
                    uidfound=1;
                    if ( strcmp(uidbuf,r) || *w )
                        ckstrncpy(pwbuf,w,PWBUFL+1);
                    ckstrncpy(uidbuf,r,UIDBUFLEN);
                    pwflg = 1;
                    pwcrypt = 0;
                    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);

                /* If the host is a bracketed literal, skip over it
                   before scanning for a port or a path, so a colon
                   inside the brackets isn't mistaken for the port
                   separator. q (unlike p) is left pointing at the
                   '[' for later temphost extraction. */
                if (*p == '[') {
                    while (*p != '\0' && *p != ']') p++;
                    if (*p != ']') {
                        fprintf(stderr,"Malformed address literal: %s\n",q);
                        errno = 0;
                        return(-1);
                    }
                    p++;                     /* Past the ']' */
                }

                /* Look for the port/service or a file/directory path */
                while (*p != '\0' && *p != ':' && *p != '/')
                    p++;
                if (*p == ':') {
                    debug(F110,"netopen found port",q,0);
                    *p++ = '\0';            /* Found a port name or number */
                    r = p;

                    /* Look for the end of port/service or a file/directory path */
                    while (*p != '\0' && *p != '/')
                        p++;
                    if (*p == '/')
                        *p++ = '\0';

                    debug(F110,"netopen port",r,0);
                    ckstrncpy(tempservice,r,80);
                    {                        /* q may be a bracketed literal */
                        char tpport[16];
                        if (ck_splithostport(q,temphost,256,
                                            tpport,sizeof(tpport)) < 0) {
                            fprintf(stderr,"Malformed address literal: %s\n",q);
                            errno = 0;
                            return(-1);
                        }
                    }
                    ckstrncpy(temppath,p,256);
                    ckstrncpy(namecopy,temphost,NAMECPYL);
                    debug(F110,"netopen tempservice",tempservice,0);
                    debug(F110,"netopen temphost",temphost,0);
                    debug(F110,"netopen temppath",temppath,0);

                    /* move port/service to a buffer that won't go away */
                    x = strlen(namecopy);
                    p = namecopy + x + 1;
                    ckstrncpy(p, tempservice, NAMECPYL - x - 1);
                } else {
                    /* Handle a path if we found one */
                    if (*p == '/')
                        *p++ = '\0';
                    ckstrncpy(temppath,p,256);

                    /* 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 (!ckstrcmp("rlogin",namecopy,6,0)) {
                            ckstrncpy(namecopy,"login",NAMECPYL);
                        }
#endif /* RLOGCODE */
                        /* iksd is not a valid service */
                        if (!ckstrcmp("iksd",namecopy,6,0)) {
                            ckstrncpy(namecopy,"kermit",NAMECPYL);
                        }
                        /* Reconstruct namecopy */
                        ckstrncpy(tempservice,namecopy,80);
                        {                    /* q may be a [bracketed] literal */
                            char tpport[16];
                            if (ck_splithostport(q,temphost,256,
                                                tpport,sizeof(tpport)) < 0) {
                                fprintf(stderr,
                                        "Malformed address literal: %s\n",q);
                                errno = 0;
                                return(-1);
                            }
                        }
                        ckstrncpy(namecopy,temphost,NAMECPYL);
                        debug(F110,"netopen tempservice",tempservice,0);
                        debug(F110,"netopen temphost",temphost,0);
                        debug(F110,"netopen temppath",temppath,0);

                        /* move port/service to a buffer that won't go away */
                        x = strlen(namecopy);
                        p = namecopy + x + 1;
                        ckstrncpy(p, tempservice, NAMECPYL - x - 1);
                    }
                }
                debug(F110,"netopen URL result: host",namecopy,0);
                debug(F110,"netopen URL result: service",p,0);
                debug(F110,"netopen URL result: path",temppath,0);

#ifdef IKS_GET
                /* If we have set a path specified, we need to try to GET it */
                /* But we have another problem, we have to login first.  How */
                /* do we specify that a login must be done before the GET?   */
                /* The user's name if specified is in 'userid' and the       */
                /* password if any is in 'pwbuf'.                            */
                if ( temppath[0] ) {
                    extern int action;
                    extern char * cmarg;

                    if ( !uidfound ) {
                        /* If no userid was specified as part of the URL but
                        * a path was specified, then we
                        * set the user name to anonymous and the password
                        * to the current userid.
                        */
                        ckstrncpy(pwbuf,uidbuf,PWBUFL);
                        ckstrncat(pwbuf,"@",PWBUFL);
                        pwflg = 1;
                        pwcrypt = 0;
                        ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
                    }

                    /*
                    * If a file path was specified we perform the GET
                    * operation and then terminate the connection.
                    *
                    * If a directory was given instead of a file, then
                    * we should REMOTE CD to the directory and list its
                    * contents.  But how do we tell the difference?
                    */
                    makestr(&cmarg,temppath);
                    action = 'r';
                }
#endif /* IKS_GET */
            }
#endif /* CK_URL */
        } else {                            /* Otherwise use telnet */
            p = "telnet";
        }
        }
    }                                    /* End of bracketed-literal check */
/*
  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);

   /* Use the service port to set the default protocol type if necessary */
    if (ttnproto == NP_DEFAULT)
       setnproto(p);

    ckstrncpy(namecopy2,namecopy,NAMECPYL);
    service = ckgetservice(namecopy,p,namecopy,NAMECPYL);
    if (!service) {
        fprintf(stderr, "Can't 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);
    } else {
        if (!ckstrcmp(namecopy,namecopy2,-1,0))
	  namecopy2[0] = '\0';
        ckstrncpy(svcbuf,ckuitoa(ntohs(service->s_port)),sizeof(svcbuf));
        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 */

#ifndef NOHTTP
   /* For HTTP connections we must preserve the original hostname and */
   /* service requested so we can include them in the Host header.    */
    ckmakmsg(http_host_port,sizeof(http_host_port),namecopy,":",
              ckitoa(ntohs(service->s_port)),NULL);

    /* 'namecopy' contains the name of the host to which we want to connect */
    /* 'svcbuf'   contains the service name                                 */
    /* 'service->s_port' contains the port number in network byte order     */

    /* If we are using an http proxy, we need to create a buffer containing */
    /*   hostname:port-number                                               */
    /* to pass to the http_connect() function.  Then we need to replace     */
    /* 'namecopy' with the name of the proxy server and the service->s_port */
    /* with the port number of the proxy (default port 80).                 */

    if ( tcp_http_proxy ) {
        ckmakmsg(proxycopy,sizeof(proxycopy),namecopy,":",
                 ckuitoa(ntohs(service->s_port)),NULL);
        ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);

        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 */
        } else {
            strcpy(++p,"http");
        }

        service = ckgetservice(namecopy,p,namecopy,NAMECPYL);
        if (!service) {
            fprintf(stderr, "Can't find port for service %s\n", p);
#ifdef TGVORWIN
            debug(F101,"netopen can't get service for proxy","",socket_errno);
#else
            debug(F101,"netopen can't get service for proxy","",errno);
#endif /* TGVORWIN */
            errno = 0;                  /* (rather than mislead) */
            return(-1);
        }
        ckstrncpy(p,ckuitoa(ntohs(service->s_port)),NAMECPYL-(p-namecopy));

    }
#endif /* NOHTTP */

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

#ifdef CK_IPV6
    /*
      Resolve and connect using ck_tcp_connect().

      If the connected address is IPv4, r_addr is filled in from it exactly as
      the legacy code would have, so all downstream code that still assumes IPv4
      keeps working unchanged. If it's IPv6, ckv6_connected is set instead, and
      that downstream code is skipped rather than fed a zeroed r_addr.
    */
    {
        struct sockaddr_storage cn_addr;
        GSOCKNAME_T cn_len = sizeof(cn_addr);
        int got_addr = 0;
        int isv4lit, isv6lit;
        struct in_addr t4chk;
        struct in6_addr t6chk;

        /* Don't print the "DNS Lookup" status when we have a literal IP
           address already, since we expect that step to complete
           immediately.  Matches the legacy behavior. */
        isv4lit = (inet_pton(AF_INET,namecopy,&t4chk) == 1);
        isv6lit = (!isv4lit && inet_pton(AF_INET6,namecopy,&t6chk) == 1);
        if (!quiet && !isv4lit && !isv6lit) {
            printf(" DNS Lookup... ");
            fflush(stdout);
        }

        /* getaddrinfo() is supposed to reject a bogus zone, and glibc does, but
           macOS just returns scope 0 instead of failing.  Validate the zone
           here rather than relying on getaddrinfo() to catch it. */
        if (!isv4lit && strchr(namecopy,'%')) {
            struct in6_addr tzchk;
            unsigned int tzscope;
            if (!ck_scopeaddr6(namecopy,&tzchk,&tzscope)) {
                fprintf(stderr,"Can't get address for %s\n",namecopy);
                errno = 0;
                return(-1);
            }
        }
        /* Remember whether a real DNS lookup happened, same meaning as
           the legacy branch's "dns" -- used below to help decide
           whether SET TCP REVERSE-DNS-LOOKUP AUTO should apply. */
        dns = !isv4lit && !isv6lit;

#ifdef RLOGCODE
        ttyfd = ck_tcp_connect(namecopy, svcbuf, quiet, &got_addr,
                               &cn_addr, &cn_len,
                               (service->s_port ==
                                htons((unsigned short)RLOGIN_PORT)));
#else /* RLOGCODE */
        ttyfd = ck_tcp_connect(namecopy, svcbuf, quiet, &got_addr,
                               &cn_addr, &cn_len, 0);
#endif /* RLOGCODE */
        if (ttyfd < 0) {
            if (!got_addr)
              fprintf(stderr, "Can't get address for %s\n", namecopy);
            netclos();
            ttyfd = -1;
            wasclosed = 1;
            ttnproto = NP_NONE;
            errno = 0;
            return(-1);
        }
        isconnect = 1;
        if (cn_addr.ss_family == AF_INET) {
            bzero((char *)&r_addr,sizeof(r_addr));
            bcopy(&cn_addr,(char *)&r_addr,sizeof(r_addr));
        } else {
            ckv6_connected = 1;
            bzero((char *)&ckv6_addr,sizeof(ckv6_addr));
            bcopy(&cn_addr,(char *)&ckv6_addr,cn_len);
        }
        if (ck_straddr((struct sockaddr *)&cn_addr,cn_len,
                        ipaddr,CK_IPADDRLEN) < 0)
          ipaddr[0] = '\0';
    }
#else /* CK_IPV6 */
    bzero((char *)&r_addr, sizeof(r_addr));
    debug(F100,"netopen bzero ok","",0);
/*
   NOTE: Originally the inet_addr() check was #ifdef NT, but is enabled for
   all as of 20 Sep 97, to allow people to "set host" to a specific numeric IP
   address without going through the multihomed host sequence and winding up
   at a different place than the one requested.
*/
#ifdef INADDR_NONE
    debug(F101,"netopen INADDR_NONE defined","",INADDR_NONE);
#else /* INADDR_NONE */
    debug(F100,"netopen INADDR_NONE not defined","",0);
#endif /* INADDR_NONE */
#ifdef INADDRX
    debug(F100,"netopen INADDRX defined","",0);
#else /* INADDRX */
    debug(F100,"netopen INADDRX not defined","",0);
#endif /* INADDRX */

#ifndef NOMHHOST
#ifdef INADDRX
    iax = inet_addr(namecopy);
    debug(F111,"netopen inet_addr",namecopy,iax.s_addr);
#else /* INADDRX */
#ifdef INADDR_NONE
    iax.s_addr = inet_addr(namecopy);
    debug(F111,"netopen inet_addr",namecopy,iax.s_addr);
#else /* INADDR_NONE */
#ifdef SOLARIS
    /* In Solaris inet_addr() is of type in_addr_t which is uint32_t */
    /* (unsigned) yet it returns -1 (signed) on failure. */
    /* It makes a difference in 64-bit builds. */
    rc_inet_addr = inet_addr(namecopy);	/* Assign return code to an int */
    iax = (unsigned) rc_inet_addr;	/* and from there to whatever.. */
    debug(F111,"netopen rc_inet_addr",namecopy,rc_inet_addr);
#else
#ifndef datageneral
    iax = (unsigned int) inet_addr(namecopy);
#else
    iax = -1L;
#endif /* datageneral */
#endif /* SOLARIS */
    debug(F111,"netopen inet_addr",namecopy,iax);
#endif /* INADDR_NONE */
#endif /* INADDRX */

    dns = 0;
    if (
/* This might give warnings on 64-bit platforms but they should be harmless */
/* because INADDR_NONE should be all 1's anyway, thus the OR part is */
/* probably superfluous -- not sure why it's even there, maybe it should be */
/* removed. */
#ifdef SOLARIS
	rc_inet_addr == -1
#else
#ifdef INADDR_NONE
        iax.s_addr == INADDR_NONE /* || iax.s_addr == (unsigned long) -1L */
#else /* INADDR_NONE */
        iax < 0
#endif /* INADDR_NONE */
#endif /* SOLARIS */
        ) {
        if (!quiet) {
            printf(" DNS Lookup... ");
            fflush(stdout);
        }
        if ((host = gethostbyname(namecopy)) != NULL) {
            debug(F110,"netopen gethostbyname != NULL",namecopy,0);
            host = ck_copyhostent(host);
            dns = 1;                    /* Remember we performed dns lookup */
            r_addr.sin_family = host->h_addrtype;
            if (tcp_rdns && host->h_name && host->h_name[0]
#ifndef NOHTTP
                 && (tcp_http_proxy == NULL)
#endif /* NOHTTP */
		) {
                ckstrncpy(name,host->h_name,80);  /* Bad Bad Bad */
                ckstrncat(name,":",80);
                ckstrncat(name,svcbuf,80);
            }
	    debug(F110,"netopen name after lookup",name,0);

#ifdef HADDRLIST
#ifdef h_addr
            /* This is for trying multiple IP addresses - see <netdb.h> */
            if (!(host->h_addr_list))
              return(-1);
            bcopy(host->h_addr_list[0],
                  (caddr_t)&r_addr.sin_addr,
                  host->h_length
                  );
#else
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif /* h_addr */
#else  /* HADDRLIST */
#ifdef HPUX6
	    r_addr.sin_addr.s_addr = (u_long)host->h_addr;
#else  /* HPUX6 */
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif	/* HPUX6 */
#endif /* HADDRLIST */

#ifndef HPUX6
            debug(F111,"BCOPY","host->h_length",host->h_length);
#endif	/* HPUX6 */
        }
    }
#endif /* NOMHHOST */

    debug(F101,"netopen dns","",dns);

    if (!dns) {
#ifdef INADDRX
/* inet_addr() is of type struct in_addr */
        struct in_addr ina;
        unsigned long uu;
        debug(F100,"netopen gethostbyname == NULL: INADDRX","",0);
        ina = inet_addr(namecopy);
        uu = *(unsigned int *)&ina;
#else /* Not INADDRX */
/* inet_addr() is unsigned long */
        unsigned long uu;
        debug(F100,"netopen gethostbyname == NULL: Not INADDRX","",0);
        uu = inet_addr(namecopy);
#endif /* INADDRX */
        debug(F101,"netopen uu","",uu);
        if (
#ifdef INADDR_NONE
            !(uu == INADDR_NONE || uu == (unsigned int) -1L)
#else   /* INADDR_NONE */
            uu != ((unsigned long)-1)
#endif /* INADDR_NONE */
            ) {
            r_addr.sin_addr.s_addr = uu;
            r_addr.sin_family = AF_INET;
        } else {
#ifdef VMS
            fprintf(stdout, "\r\n");    /* complete any previous message */
#endif /* VMS */
            fprintf(stderr, "Can't get address for %s\n", namecopy);
#ifdef TGVORWIN
            debug(F101,"netopen can't get address","",socket_errno);
#else
            debug(F101,"netopen can't get address","",errno);
#endif /* TGVORWIN */
            errno = 0;                  /* Rather than mislead */
            return(-1);
        }
    }

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

    r_addr.sin_port = service->s_port;
    ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);
    debug(F110,"netopen trying",ipaddr,0);
    if (!quiet && *ipaddr) {
        printf(" Trying %s... ", ipaddr);
        fflush(stdout);
    }

    /* Loop to try additional IP addresses, if any. */

    do {
#ifdef EXCELAN
        send_socket.sin_family = AF_INET;
        send_socket.sin_addr.s_addr = 0;
        send_socket.sin_port = 0;
        if ((ttyfd = socket(SOCK_STREAM, (struct sockproto *)0,
                            &send_socket, SO_REUSEADDR)) < 0)
#else  /* EXCELAN */
#ifdef NT
#endif /* NT */

        if ((ttyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
#endif /* EXCELAN */
            {
#ifdef EXCELAN
                experror("TCP socket error");
#else
#ifdef VMS
                fprintf(stdout, "\r\n"); /* complete any previous stdout */
#endif /* VMS */
#ifdef TGVORWIN
#ifdef OLD_TWG
                errno = socket_errno;
#endif /* OLD_TWG */
                socket_perror("TCP socket error");
                debug(F101,"netopen socket error","",socket_errno);
#else
                perror("TCP socket error");
                debug(F101,"netopen socket error","",errno);
#endif /* TGVORWIN */
#endif /* EXCELAN */
                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)) {
           static unsigned short lport = 1024;  /* max reserved port */
#ifdef OS2
           int s_errno;
#endif /* OS2 */

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

           sin.sin_family = AF_INET;
           if (tcp_address) {
#ifdef INADDRX
               inaddrx = inet_addr(tcp_address);
               sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
#else
               sin.sin_addr.s_addr = inet_addr(tcp_address);
#endif /* INADDRX */
           } else
             sin.sin_addr.s_addr = INADDR_ANY;
           while (1) {
               sin.sin_port = htons(lport);
               if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
                 break;
#ifdef OS2
               s_errno = socket_errno;
               if (s_errno && /* OS2 bind fails with 0, if already in use */
#ifdef NT
                   s_errno != WSAEADDRINUSE
#else
                   s_errno != SOCEADDRINUSE &&
                   s_errno != (SOCEADDRINUSE - SOCBASEERR)
#endif /* NT */
                   )
#else /* OS2 */
#ifdef TGVORWIN
                 if (socket_errno != EADDRINUSE)
#else
                 if (errno != EADDRINUSE)
#endif /* TGVORWIN */
#endif /* OS2 */
                   {
#ifdef OS2
                       debug(F101,"rlogin bind s_errno","",s_errno);
                       perror("rlogin bind");
#else
#ifdef VMS
                       printf("\r\n");  /* complete any previous message */
#endif /* VMS */
#ifdef TGVORWIN
                       debug(F101,"rlogin bind socket_errno","",socket_errno);
#ifdef OLD_TWG
                       errno = socket_errno;
#endif /* OLD_TWG */
                       socket_perror("rlogin bind");
#else
                       debug(F101,"rlogin bind errno","",errno);
                       perror("rlogin bind");
#endif /* TGVORWIN */
#endif /* OS2 */
                       debug(F101,"rlogin local port","",lport);
                       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;
       } else
#endif /* RLOGCODE  */

       /* If a specific TCP address on the local host is desired we */
       /* must bind it to the socket.                               */
#ifndef datageneral
         if (tcp_address) {
             int s_errno;

             debug(F110,"netopen binding socket to",tcp_address,0);
             bzero((char *)&sin,sizeof(sin));
             sin.sin_family = AF_INET;
#ifdef INADDRX
             inaddrx = inet_addr(tcp_address);
             sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
#else
             sin.sin_addr.s_addr = inet_addr(tcp_address);
#endif /* INADDRX */
             sin.sin_port = 0;
             if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
                 s_errno = socket_errno; /* Save error code */
#ifdef TCPIPLIB
                 socket_close(ttyfd);
#else /* TCPIPLIB */
                 close(ttyfd);
#endif /* TCPIPLIB */
                 ttyfd = -1;
                 wasclosed = 1;
                 errno = s_errno;       /* and report this error */
                 debug(F101,"netopen bind errno","",errno);
                 return(-1);
             }
         }
#endif /* datageneral */

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

#ifdef EXCELAN
        if (connect(ttyfd, &r_addr) < 0)
#else
#ifdef NT
          WSASafeToCancel = 1;
#endif /* NT */
        if (connect(ttyfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0)
#endif /* EXCELAN */
          {
#ifdef NT
              WSASafeToCancel = 0;
#endif /* NT */
#ifdef OS2
              i = socket_errno;
#else /* OS2 */
#ifdef TGVORWIN
              i = socket_errno;
#else
              i = errno;                /* Save error code */
#endif /* TGVORWIN */
#endif /* OS2 */
#ifdef RLOGCODE
              if (
#ifdef OS2
                 i && /* OS2 bind fails with 0, if already in use */
#ifdef NT
                 i == WSAEADDRINUSE
#else
                 (i == SOCEADDRINUSE ||
                 i == (SOCEADDRINUSE - SOCBASEERR))
#endif /* NT */
#else /* OS2 */
#ifdef TGVORWIN
                  socket_errno == EADDRINUSE
#else
                  errno == EADDRINUSE
#endif /* TGVORWIN */
#endif /* OS2 */
                  && ttnproto == NP_RLOGIN) {
#ifdef TCPIPLIB
                   socket_close(ttyfd); /* Close it. */
#else
                   close(ttyfd);
#endif /* TCPIPLIB */
                   continue;            /* Try a different lport */
               }
#endif /* RLOGCODE */
#ifdef HADDRLIST
#ifdef h_addr
              if (host && host->h_addr_list && host->h_addr_list[1]) {
                  perror("");
                  host->h_addr_list++;
                  bcopy(host->h_addr_list[0],
                        (caddr_t)&r_addr.sin_addr,
                        host->h_length);

                  ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);
                  debug(F110,"netopen h_addr_list",ipaddr,0);
                  if (!quiet && *ipaddr) {
                      printf(" Trying %s... ", ipaddr);
                      fflush(stdout);
                  }
#ifdef TCPIPLIB
                  socket_close(ttyfd); /* Close it. */
#else
                  close(ttyfd);
#endif /* TCPIPLIB */
                  continue;
              }
#endif /* h_addr */
#endif  /* HADDRLIST */
              netclos();
              ttyfd = -1;
              wasclosed = 1;
              ttnproto = NP_NONE;
              errno = i;                /* And report this error */
#ifdef EXCELAN
              if (errno) experror("netopen connect");
#else
#ifdef TGVORWIN
              debug(F101,"netopen connect error","",socket_errno);
              /* if (errno) socket_perror("netopen connect"); */
#ifdef OLD_TWG
              errno = socket_errno;
#endif /* OLD_TWG */
              if (!quiet)
                socket_perror("netopen connect");
#else /* TGVORWIN */
              debug(F101,"netopen connect errno","",errno);
#ifdef VMS
              if (!quiet)
                perror("\r\nFailed");
#else
              if (!quiet)
                perror("Failed");
#endif /* VMS */
#ifdef DEC_TCPIP
              if (!quiet)
                perror("netopen connect");
#endif /* DEC_TCPIP */
#ifdef CMU_TCPIP
              if (!quiet)
                perror("netopen connect");
#endif /* CMU_TCPIP */
#endif /* TGVORWIN */
#endif /* EXCELAN */
              return(-1);
          }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
        isconnect = 1;
    } while (!isconnect);
#endif /* CK_IPV6 */

#ifdef NON_BLOCK_IO
    on = 1;
    x = socket_ioctl(ttyfd,FIONBIO,&on);
    debug(F101,"netopen FIONBIO","",x);
#endif /* NON_BLOCK_IO */

#ifdef NT_TCP_OVERLAPPED
    OverlappedWriteInit();
    OverlappedReadInit();
#endif /* NT_TCP_OVERLAPPED */

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

#ifndef NOHTTP
    /* We have succeeded in connecting to the HTTP PROXY.  So now we   */
    /* need to attempt to connect through the proxy to the actual host */
    /* If that is successful, we have to pretend that we made a direct */
    /* connection to the actual host.                                  */

    if ( tcp_http_proxy ) {
#ifdef OS2
        char * agent = "Kermit 95";             /* Default user agent */
#else
        char * agent = "C-Kermit";
#endif /* OS2 */

        if (http_connect(ttyfd,
			 tcp_http_proxy_agent ? tcp_http_proxy_agent : agent,
			 NULL,
                         tcp_http_proxy_user,
                         tcp_http_proxy_pwd,
                         0,
                         proxycopy
                         ) < 0) {
            netclos();
            return(-1);
        }

        ckstrncpy(namecopy,proxycopy,NAMECPYL);
        p = namecopy;                       /* Was a service requested? */
        while (*p != '\0' && *p != ':') p++; /* Look for colon */
        *p = '\0';
    }
#endif /* NOHTTP */

    /* Jeff - Does this next block of code that set's the protocol */
    /* need to be here anymore?  5/10/2000                         */

    /* There are certain magic port numbers that when used require */
    /* the use of specific protocols.  Check this now before we    */
    /* set the SO_OOBINLINE state or we might get it wrong.        */
    x = ntohs((unsigned short)service->s_port);
    svcnum = x;
    /* See if the service is TELNET. */
    if (x == TELNET_PORT) {
        /* Yes, so if raw port not requested */
	/* fdc 2005/12/04 */
        if (ttnproto != NP_TCPRAW && ttnproto != NP_NONE)
          ttnproto = NP_TELNET;         /* Select TELNET protocol. */
    }
#ifdef RLOGCODE
    else if (x == RLOGIN_PORT) {
        ttnproto = NP_RLOGIN;
    }
#ifdef CK_KERBEROS
    /* There is no good way to do this.  If the user didn't tell    */
    /* which one to use up front.  We may guess wrong if the user   */
    /* has both Kerberos versions installed and valid TGTs for each */
    else if (x == KLOGIN_PORT &&
             ttnproto != NP_K4LOGIN &&
             ttnproto != NP_K5LOGIN) {
        if (ck_krb5_is_installed() &&
            ck_krb5_is_tgt_valid())
          ttnproto = NP_K5LOGIN;
        else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid())
          ttnproto = NP_K4LOGIN;
        else
          ttnproto = NP_K4LOGIN;
    } else if (x == EKLOGIN_PORT &&
               ttnproto != NP_EK4LOGIN &&
               ttnproto != NP_EK5LOGIN) {
        if (ck_krb5_is_installed() && ck_krb5_is_tgt_valid())
          ttnproto = NP_EK5LOGIN;
        else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid())
          ttnproto = NP_EK4LOGIN;
        else
          ttnproto = NP_EK4LOGIN;
    }
#endif /* CK_KERBEROS */
#endif /* RLOGCODE */
#ifdef IKS_OPTION
    else if (x == KERMIT_PORT) {        /* IKS uses Telnet protocol */
        if (ttnproto == NP_NONE)
          ttnproto = NP_KERMIT;
    }
#endif /* IKS_OPTION */

#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.
*/
/*
  Note from Jeff Altman: 12/13/95
  In implementing rlogin protocol I have come to the conclusion that it is
  a really bad idea to read out-of-band data inline.
  At least Windows and OS/2 does not handle this well.
  And if you need to know that data is out-of-band, then it becomes
  absolutely pointless.

  Therefore, at least on OS2 and Windows (NT) I have changed the value of
  on to 0, so that out-of-band data stays out-of-band.

  12/18/95
  Actually, OOB data should be read inline when possible.  Especially with
  protocols that don't care about the Urgent flag.  This is true with Telnet.
  With Rlogin, you need to be able to catch OOB data.  However, the best
  way to do this is to set a signal handler on SIGURG.  This isn't possible
  on OS/2 and Windows.  But it is in UNIX.  We will also need OOB data for
  FTP so better create a general mechanism.

  The reason for making OOB data be inline is that the standard ttinc/ttoc
  calls can be used for reading that data on UNIX systems.  If we didn't
  have the OOBINLINE option set then we would have to use recv(,MSG_OOB)
  to read it.
*/
#ifdef RLOGCODE
#ifdef TCPIPLIB
    if (ttnproto == NP_RLOGIN
#ifdef CK_KERBEROS
        || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
        || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
#endif /* CK_KERBEROS */
      )
      on = 0;
#else /* TCPIPLIB */
    if (ttnproto == NP_RLOGIN
#ifdef CK_KERBEROS
         || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
         || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
#endif /* CK_KERBEROS */
         ) {
        debug(F100,"Installing rlogoobh on SIGURG","",0);
        signal(SIGURG, rlogoobh);
        on = 0;
    } else {
        debug(F100,"Ignoring SIGURG","",0);
        signal(SIGURG, SIG_DFL);
    }
#endif /* TCPIPLIB */
#endif /* RLOGCODE */

#ifdef datageneral
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef BSD43
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OSF1
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef POSIX
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef MOTSV88R4
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef SOLARIS
/*
  Maybe this applies to all SVR4 versions, but the other (else) way has been
  compiling and working fine on all the others, so best not to change it.
*/
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OSK
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OS2
    {
        int rc;
        rc = setsockopt(ttyfd,
                        SOL_SOCKET,
                        SO_OOBINLINE,
                        (char *) &on,
                        sizeof on
                        );
        debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc);
    }
#else
#ifdef VMS /* or, at least, VMS with gcc */
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef CLIX
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
    setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
#endif /* CLIX */
#endif /* VMS */
#endif /* OS2 */
#endif /* OSK */
#endif /* SOLARIS */
#endif /* MOTSV88R4 */
#endif /* POSIX */
#endif /* BSD43 */
#endif /* OSF1 */
#endif /* datageneral */
#endif /* SO_OOBINLINE */

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

#ifndef datageneral
    /* Find out our own IP address. */
    /* We need the l_addr structure for [E]KLOGIN. */
#ifdef CK_IPV6
    if (ckv6_connected) {
        struct sockaddr_storage lss;
        GSOCKNAME_T lsslen = sizeof(lss);
        bzero((char *)&lss, sizeof(lss));
        if (!getsockname(ttyfd, (struct sockaddr *)&lss, &lsslen)) {
            char lsbuf[CK_IPADDRLEN];
            if (ck_straddr((struct sockaddr *)&lss,lsslen,
                            lsbuf,sizeof(lsbuf)) == 0)
              ckstrncpy(myipaddr, lsbuf, CK_IPADDRLEN);
            debug(F110,"getsockname",myipaddr,0);
        }
    } else
#endif /* CK_IPV6 */
    {
    l_slen = sizeof(l_addr);
    bzero((char *)&l_addr, l_slen);
#ifndef EXCELAN
    if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) {
        char * s = (char *)inet_ntoa(l_addr.sin_addr);
        ckstrncpy(myipaddr, s, CK_IPADDRLEN);
        debug(F110,"getsockname",myipaddr,0);
    }
#endif /* EXCELAN */
    }
#endif /* datageneral */

/*
  This is really only needed for Kerberos IV but is useful information in any
  case.  If we connect to a name that is really a pool, we need to get the
  name of the machine we are actually connecting to for K4 to authenticate
  properly.  This way we also update the names properly.

  However, it is a security hole when used with insecure DNS.

  Note: This does not work on Windows 95 or Windows NT 3.5x.  This is because
  of the Microsoft implementation of gethostbyaddr() in both Winsock 1.1
  and Winsock 2.0 on those platforms.  Their algorithm is:

  1. Check the HOSTENT cache.
  2. Check the HOSTS file at %SystemRoot%\System32\DRIVERS\ETC.
  3. Do a DNS query if the DNS server is configured for name resolution.
  4. Do an additional NetBIOS remote adapter status to an IP address for its
     NetBIOS name table. This step is specific only to the Windows NT version
     3.51 implementation.

  The problem is the use of the HOSTENT cache.  It means that gethostbyaddr()
  can not be used to resolve the real name of machine if it was originally
  accessed by an alias used to represent a cluster.
*/
     if ((tcp_rdns && dns || tcp_rdns == SET_ON
#ifdef CK_KERBEROS
         || tcp_rdns == SET_AUTO &&
          (ck_krb5_is_installed() || ck_krb4_is_installed())
#endif /* CK_KERBEROS */
         )
#ifndef NOHTTP
          && (tcp_http_proxy == NULL)
#endif /* NOHTTP */
#ifdef CK_SSL
          && !(ssl_only_flag || tls_only_flag)
#endif /* CK_SSL */
         ) {
#ifdef NT
        if (isWin95())
          sleep(1);
#endif /* NT */
        if (!quiet) {
            printf(" Reverse DNS Lookup... ");
            fflush(stdout);
        }
#ifdef CK_IPV6
        if (ckv6_connected) {
            char v6nibuf[256];
            int v6gni;

            /*
	      The salen argument must be the actual sockaddr_in6 size, not that
	      of sockaddr_storage, which is larger.  OpenBSD's getnameinfo()
	      rejects a salen that does not match the address family's real
	      structure size.

	      So here we give the smaller size, even though the memory region
	      is larger, and that's safe.
            */
            v6gni = getnameinfo((struct sockaddr *)&ckv6_addr,
                                 sizeof(struct sockaddr_in6),
                                 v6nibuf,sizeof(v6nibuf),NULL,0,
                                 NI_NAMEREQD);
            if (v6gni == 0) {
                char * s = v6nibuf;
                debug(F110,"netopen getnameinfo",s,0);
                if (!quiet) {
                    printf("(OK)\n");
                    fflush(stdout);
                }
                ckstrncpy(name,s,80);      /* Bad Bad Bad, but matches */
                ckstrncat(name,":",80);
                ckstrncat(name,svcbuf,80);
                if (!quiet
#ifndef NOICP
                    && !doconx
#endif /* NOICP */
                    )
                  printf(" %s connected on port %s\n",s,p);
            } else {
                debug(F111,"netopen getnameinfo failed","",v6gni);
                if (!quiet) printf("Failed.\n");
            }
        } else
#endif /* CK_IPV6 */
        if ((host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET))) {
            char * s;
            host = ck_copyhostent(host);
            debug(F100,"netopen gethostbyname != NULL","",0);
            if (!quiet) {
                printf("(OK)\n");
                fflush(stdout);
            }
            s = host->h_name;
            if (!s) {                   /* This can happen... */
                debug(F100,"netopen host->h_name is NULL","",0);
                s = "";
            }
            /* Something is wrong with inet_ntoa() on HPUX 10.xx */
            /* The compiler says "Integral value implicitly converted to */
            /* pointer in assignment."  The prototype is right there */
            /* in <arpa/inet.h> so what's the problem? */
            /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */
            if (!*s) {                  /* No name so substitute the address */
                debug(F100,"netopen host->h_name is empty","",0);
                s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */
                if (!s)                 /* Trust No 1 */
                  s = "";
                if (*s) {               /* If it worked, use this string */
                    ckstrncpy(ipaddr,s,CK_IPADDRLEN);
                }
                s = ipaddr;             /* Otherwise stick with the IP */
                if (!*s)                /* or failing that */
                  s = namecopy;         /* the name we were called with. */
            }
            if (*s) {                   /* Copying into our argument? */
                ckstrncpy(name,s,80);   /* Bad Bad Bad */
                ckstrncat(name,":",80);
                ckstrncat(name,svcbuf,80);
            }
            if (!quiet && *s
#ifndef NOICP
                && !doconx
#endif /* NOICP */
                ) {
                printf(" %s connected on port %s\n",s,p);
#ifdef BETADEBUG
                /* This is simply for testing the DNS entries */
                if (host->h_aliases) {
                    char ** a = host->h_aliases;
                    while (*a) {
                        printf(" alias => %s\n",*a);
                        a++;
                    }
                }
#endif /* BETADEBUG */
            }
        } 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.  Skipped
       for an IPv6 connection: r_addr is IPv4-only and was never
       populated in that case, so ipaddr (already set correctly by
       the getaddrinfo() path above) must not be overwritten here. */
    if (!ckv6_connected)
      ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);

#ifdef CK_SECURITY

    /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
#ifndef NOHTTP
    if (tcp_http_proxy) {
        for (i=strlen(proxycopy); i >= 0 ; i--)
            if ( proxycopy[i] == ':' )
                proxycopy[i] = '\0';
    }
#endif /* NOHTTP */
    ck_auth_init(
#ifndef NOHTTP
                 tcp_http_proxy ? proxycopy :
#endif /* NOHTTP */
                 (tcp_rdns && host && host->h_name && host->h_name[0]) ?
                 (char *)host->h_name : (namecopy2[0] ? namecopy2 :
                                        (namecopy[0] ? namecopy : ipaddr)),
                 ipaddr,
                 uidbuf,
                 ttyfd
                 );
#endif /* CK_SECURITY */
#ifdef CK_SSL
    if (ck_ssleay_is_installed()) {
        if (!ssl_tn_init(SSL_CLIENT)) {
            debug(F100,"netopen ssl_tn_init() failed","",0);
            if (bio_err!=NULL) {
                BIO_printf(bio_err,"ssl_tn_init() failed\n");
                ERR_print_errors(bio_err);
            } else {
                fflush(stderr);
                fprintf(stderr,"ssl_tn_init() failed\n");
                ERR_print_errors_fp(stderr);
            }
            if (tls_only_flag || ssl_only_flag) {
                debug(F100,"netopen ssl/tls required","",0);
                netclos();
                return(-1);
            }

            /* we will continue to accept the connection   */
            /* without SSL or TLS support unless required. */
            if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
            if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
                TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
        } else if ( ck_ssl_outgoing(ttyfd) < 0 ) {
            debug(F100,"ck_ssl_outgoing() failed","",0);
            netclos();
            return(-1);
        }
    }
#endif /* CK_SSL */

#ifdef RLOGCODE
    if (ttnproto == NP_RLOGIN
#ifdef CK_KERBEROS
        || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
        || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
#endif /* CK_KERBEROS */
        ) {                             /* Similar deal for rlogin */
#ifdef CK_KERBEROS
        /* Kerberized rlogin's ticket address restriction
           data has no IPv6 representation.
           l_addr/r_addr are never populated for an IPv6 connection, so
           reject cleanly here instead of handing rlog_ini() uninitialized
           stack contents. Plain (non-Kerberized) NP_RLOGIN doesn't use
           l_addr/r_addr and is unaffected. */
        if (ckv6_connected &&
            (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN ||
             ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN)) {
            fprintf(stderr,
                    "?Kerberized rlogin requires an IPv4 connection\n");
            netclos();
            return(-1);
        }
#endif /* CK_KERBEROS */
        if (rlog_ini(((tcp_rdns && host && host->h_name && host->h_name[0]) ?
                      (CHAR *)host->h_name : (CHAR *)ipaddr),
                     service->s_port,
                     &l_addr,&r_addr
                     ) < 0) {
            debug(F100,"rlogin initialization failed","",0);
            netclos();
            return(-1);
        }
    } else
#endif /* RLOGCODE */
    if (tn_ini() < 0) {                 /* Start Telnet negotiations. */
        netclos();
        return(-1);                     /* Gone, so open failed.  */
    }
    if (ttchk() < 0) {
        netclos();
        return(-1);
    }
#ifdef CK_KERBEROS
#ifdef KRB5_U2U
   if ( ttnproto == NP_K5U2U ) {
       if (k5_user_to_user_client_auth()) {
           netclos();
           return(-1);
       }
   }
#endif /* KRB5_U2U */
#endif /* CK_KERBEROS */

    debug(F101,"netopen service","",svcnum);
    debug(F110,"netopen name",name,0);
    debug(F110,"netopen ipaddr",ipaddr,0);
    ckstrncpy(hostipaddr,ipaddr,CK_IPADDRLEN-1);

    if (lcl) if (*lcl < 0)              /* Local mode. */
      *lcl = 1;
#endif /* TCPSOCKET */
    return(0);                          /* Done. */
}

/*  N E T C L O S  --  Close current network connection.  */

#ifndef NOLOCAL
#ifdef CK_SSL
int tls_norestore = 0;
#endif /* CK_SSL */
#endif /* NOLOCAL */

int
netclos() {
    static int close_in_progress = 0;
    int x = 0;
#ifdef VMS
    int y, z;
#endif /* VMS */
    debug(F101,"netclos","",ttyfd);

#ifdef NETLEBUF
    if (!tt_push_inited)
      le_init();
#endif /* NETLEBUF */

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

    if (close_in_progress)
      return(0);
    close_in_progress = 1;              /* Remember */

#ifndef NOLOCAL
    /* This function call should not be here since this is a direct call */
    /* from an I/O routine to a user interface level function.  However, */
    /* the reality is that we do not have pure interfaces.  If we ever   */
    /* decide to clean this up the UI level should assign this function  */
    /* via a pointer assignment.  - Jeff 9/10/1999                       */
#ifdef CK_SSL
    if (!tls_norestore)
#endif /* CK_SSL */
      slrestor();
#endif /* NOLOCAL */
#ifdef OS2
    RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#else /* OS2 */
    if (ttyfd > -1)                     /* Was. */
#endif /* OS2 */
      {
#ifdef VMS
	  y = 1;                        /* Turn on nonblocking reads */
	  z = socket_ioctl(ttyfd,FIONBIO,&y);
	  debug(F111,"netclos FIONBIO","on",z);
#endif /* VMS */
#ifdef TNCODE
          if (ttnproto == NP_TELNET) {
            if (!TELOPT_ME(TELOPT_LOGOUT)
		) {
		/* Send LOGOUT option before close */
		if (tn_sopt(DO,TELOPT_LOGOUT) >= 0) {
		    TELOPT_UNANSWERED_DO(TELOPT_LOGOUT) = 1;
		    /* It would be nice to call tn_wait but we can't */
		}
	    }
            tn_push();			/* Place any waiting data into input*/
          }
#endif /* TNCODE */
#ifdef CK_SSL
          if (ssl_active_flag) {
              if (ssl_debug_flag)
                BIO_printf(bio_err,"calling SSL_shutdown\n");
              SSL_shutdown(ssl_con);
              ssl_active_flag = 0;
          }
          if (tls_active_flag) {
              if (ssl_debug_flag)
                BIO_printf(bio_err,"calling SSL_shutdown\n");
              SSL_shutdown(tls_con);
              tls_active_flag = 0;
          }
#endif /* CK_SSL */
#ifdef VMS
          ck_cancio();                  /* Cancel any outstanding reads. */
#endif /* VMS */
#ifdef TCPIPLIB
          x = socket_close(ttyfd);      /* Close it. */
#else
#ifndef OS2
#ifdef IBMX25
        if (ttnet == NET_IX25) {
            /* riehm: should send a disc_req - but only if link is still OK */
            x = x25clear();
            close(ttyfd);
            if (x25serverfd) {
                  /* we were the passive client of a server, now we
                   * go back to being the normal client.
                   * I hope that kermit can cope with the logic that
                   * there can still be a connection after netclos
                   * has been called.
                   */
                  ttyfd = x25serverfd;
                  x25serverfd = 0;
                  /*
                   * need to close the server connection too - because
                   * all file descriptors connected to the NPI have the
                   * same status.
                   *
                   * The problem is that any waiting connections get
                   * lost, the client doesn't realise, and hangs.
                   */
                  netclos();
              }
            x25_state = X25_CLOSED;     /* riehm: dead code? */
        } else
#endif /* IBMX25 */
          x = close(ttyfd);
#endif /* OS2 */
#endif /* TCPIPLIB */
      }
    ttyfd = -1;                         /* Mark it as closed. */
    wasclosed = 1;
#ifdef OS2
    ReleaseTCPIPMutex();
#endif /* OS2 */
#ifdef TNCODE
#ifdef CK_FORWARD_X
    fwdx_close_all();                   /* Shut down any Forward X sockets */
#endif /* CK_FORWARD_X */
    tn_reset();                   /* The Reset Telnet Option table.  */
    debug(F100,"netclose setting tn_init = 0","",0);
    tn_init = 0;                        /* Remember about telnet protocol... */
    sstelnet = 0;                       /* Client-side Telnet */
#endif /* TNCODE */
    *ipaddr = '\0';                     /* Zero the IP address string */
    tcp_incoming = 0;                   /* No longer incoming */
    /* Don't reset ttnproto so that we can remember which protocol is in use */

#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 */
#ifdef CK_KERBEROS
    /* If we are automatically destroying Kerberos credentials on Close */
    /* do it now. */
#ifdef KRB4
    if (krb4_autodel == KRB_DEL_CL) {
        extern struct krb_op_data krb_op;
        krb_op.version = 4;
        krb_op.cache = NULL;
        ck_krb4_destroy(&krb_op);
    }
#endif /* KRB4 */
#ifdef KRB5
    if (krb5_autodel == KRB_DEL_CL) {
        extern struct krb_op_data krb_op;
        extern char * krb5_d_cc;
        krb_op.version = 5;
        krb_op.cache = krb5_d_cc;
        ck_krb5_destroy(&krb_op);
    }
#endif /* KRB5 */
#endif /* CK_KERBEROS */
    close_in_progress = 0;              /* Remember we are done. */
    return(x);
}
#ifndef NOTCPIP

#ifdef OS2
int
os2socketerror( int s_errno ) {
#ifdef OS2ONLY
    if (s_errno > 0 && s_errno <= SOCBASEERR) {
        /* in OS/2, there is a problem with threading in that
         * the value of errno is not thread safe.  It can be
         * set to a value from a previous library call and if
         * it was not cleared it will appear here.  Only treat
         * valid socket error codes as errors in this function.
         */
        debug(F100,"os2socketerror errno.h","",0);
        socket_errno = 0;
        return(0);
    }
#endif /* OS2ONLY */

    switch (s_errno) {
      case 0:                           /* NO ERROR */
        debug(F100,"os2socketerror NOERROR","",0);
        return(0);
#ifdef NT
      case WSAECONNRESET:
#else /* NT */
      case SOCECONNRESET:
      case SOCECONNRESET - SOCBASEERR:
#endif /* NT */
        debug(F100,"os2socketerror ECONRESET","",0);
        tn_debug("ECONRESET");
        netclos();              /* *** *** */
        return(-1);             /* Connection is broken. */
#ifdef NT
      case WSAECONNABORTED:
#else /* NT */
      case SOCECONNABORTED:
      case SOCECONNABORTED - SOCBASEERR:
#endif /* NT */
        debug(F100,"os2socketerror ECONNABORTED","",0);
        tn_debug("ECONNABORTED");
        netclos();              /* *** *** */
        return(-1);             /* Connection is broken. */
#ifdef NT
      case WSAENETRESET:
#else /* NT */
      case SOCENETRESET:
      case SOCENETRESET - SOCBASEERR:
#endif /* NT */
        debug(F100,"os2socketerror ENETRESET","",0);
        tn_debug("ENETRESET");
        netclos();              /* *** *** */
        return(-1);             /* Connection is broken. */
#ifdef NT
      case WSAENOTCONN:
#else /* NT */
      case SOCENOTCONN:
      case SOCENOTCONN - SOCBASEERR:
#endif /* NT */
        debug(F100,"os2socketerror ENOTCONN","",0);
        tn_debug("ENOTCONN");
        netclos();                      /* *** *** */
        return(-1);                     /* Connection is broken. */
#ifdef NT
      case WSAESHUTDOWN:
        debug(F100,"os2socketerror ESHUTDOWN","",0);
        tn_debug("ESHUTDOWN");
        netclos();                      /* *** *** */
        return(-1);                     /* Connection is broken. */
#endif /* NT */
#ifdef NT
      case WSAEWOULDBLOCK:
#else
      case SOCEWOULDBLOCK:
      case SOCEWOULDBLOCK - SOCBASEERR:
#endif /* NT */
        debug(F100,"os2socketerror EWOULDBLOCK","",0);
        return(0);
#ifdef NT
      case ERROR_IO_INCOMPLETE:
      case ERROR_IO_PENDING:
      case ERROR_OPERATION_ABORTED:
        return(0);
#endif /* NT */
      default:
        return(-2);
    }
    return(0);
}
#endif /* OS2 */

/*  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 */
#ifdef TCPIPLIB
    long count = 0;
    int x = 0, z;
    unsigned long uy;
#ifndef OS2
    char c;
#endif /* OS2 */
    int rc;
#ifdef NT
    unsigned long ucount;
    extern int ionoblock;               /* For Overlapped I/O */
#else
    long y;
#endif /* NT */

    debug(F101,"nettchk entry ttibn","",ttibn);
    debug(F101,"nettchk entry ttibp","",ttibp);

#ifdef NETLEBUF
    {
        int n = 0;
        if (ttpush >= 0)
          n++;
        n += le_inbuf();
        if (n > 0)
          return(n);
    }
#endif /* NETLEBUF */

#ifndef OS2
#ifndef BEBOX
    socket_errno = 0; /* This is a function call in NT, and BeOS */
#endif /* BEBOX */
#endif /* OS2 */

    if (ttyfd == -1) {
        debug(F100,"nettchk socket is closed","",0);
        return(-1);
    }
/*
  Note: this socket_ioctl() call does NOT return an error if the
  connection has been broken.  (At least not in MultiNet.)
*/
#ifdef COMMENT
/*  Another trick that can be tried here is something like this: */

    if (ttnet == NET_TCPB) {
        char dummy;
        x = read(ttyfd,&dummy,0);       /* Try to read nothing */
        if (x < 0) {                    /* "Connection reset by peer" */
            perror("TCP/IP");           /* or somesuch... */
            ttclos(0);                  /* Close our end too. */
            return(-1);
        }
    }
#endif /* COMMENT */


#ifdef CK_SSL
    if (ssl_active_flag) {
#ifndef IKSDONLY
#ifdef OS2
        if ( IsConnectMode() ) {
            debug(F101,"nettchk (ssl_active_flag) returns","",count);
            return(0);
        }
#endif /* OS2 */
#endif /* IKSDONLY */
        count = SSL_pending(ssl_con);
        if (count < 0) {
            debug(F111,"nettchk","SSL_pending error",count);
            netclos();
            return(-1);
        }
        if ( count > 0 )
            return(count);                  /* Don't perform a read */
    } else if (tls_active_flag) {
#ifndef IKSDONLY
#ifdef OS2
        if ( IsConnectMode() ) {
            debug(F101,"nettchk (tls_active_flag) returns","",count);
            return(0);
        }
#endif /* OS2 */
#endif /* IKSDONLY */
        count = SSL_pending(tls_con);
        if (count < 0) {
            debug(F111,"nettchk","TLS_pending error",count);
            netclos();
            return(-1);
        }
        if ( count > 0 )
            return(count);                  /* Don't perform a read */
    } else
#endif /* CK_SSL */

    if (socket_ioctl(ttyfd,FIONREAD,
#ifdef NT
                     &ucount
#else
                     &count
#endif /* NT */
                     ) < 0) {
        debug(F101,"nettchk socket_ioctl error","",socket_errno);
        /* If the connection is gone, the connection is gone. */
        netclos();

#ifdef NT
        /* Handle FIONREAD giving us a number larger than we can handle */
        if (ucount > INT_MAX) ucount -= INT_MAX;
        count = ucount;
#endif /* NT */

#ifdef NT_TCP_OVERLAPPED
        /* Is there anything in the overlapped I/O buffers? */
        count += OverlappedDataWaiting();
#endif /* NT_TCP_OVERLAPPED */
        count += ttibn;
        return(count>0?count:-1);
    } else {
#ifdef NT
        /* Handle FIONREAD giving us a number larger than we can handle */
        if (ucount > INT_MAX) ucount -= INT_MAX;
        count = ucount;
#endif /* NT */
    }
    debug(F101,"nettchk count","",count);
#ifdef NT_TCP_OVERLAPPED
    /* Is there anything in the overlapped I/O buffers? */
    count += OverlappedDataWaiting();
    debug(F101,"nettchk count w/overlapped","",count);
#endif /* NT_TCP_OVERLAPPED */

#ifdef OS2
#ifndef IKSDONLY
    if ( IsConnectMode() ) {
        debug(F101,"nettchk (FIONREAD) returns","",count);
        return(count);
    }
#endif /* IKSDONLY */
#endif /* OS2 */

/* For the sake of efficiency, if there is still data in the ttibuf */
/* do not go to the bother of checking to see of the connection is  */
/* still valid.  The handle is still good, so just return the count */
/* of the bytes that we already have left to process.               */
#ifdef OS2
    if ( count > 0 || ttibn > 0 ) {
        count+=ttibn;
        debug(F101,"nettchk (count+ttibn > 0) returns","",count);
        return(count);
    } else {
        RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
        if ( ttibn == 0 )
            ttibp = 0;      /* reset for next read */
    }
#else /* OS2 */
    if ( count > 0 || ttibn > 0 ) {
        debug(F101,"nettchk returns","",count+ttibn);
        return(count+ttibn);
    }
    ttibn = ttibp = 0;
#endif /* OS2 */

/*
  The following code works well in most settings, but messes things up in
  others, including CMU/Tek TCP/IP and UCX 2.0, where it somehow manages to
  make it impossible to ever make a new connection to the same host again with
  CONNECT, once it has been logged out from the first time.  Not even if you
  HANGUP first, or SET HOST<CR>, or SET LINE<CR>.  Reportedly, however, it
  does work OK in later releases of UCX.  But there is no way we can
  accommodate both old and new -- we might have static linking or dynamic
  linking, etc etc.  If we have static, I only have access to 2.0, where this
  doesn't work, etc etc blah blah.

  In the following lines, we define a symbol NOCOUNT for builds where we want
  to omit this code.  By default, it is omitted for CMU/Tek.  You can force
  omission of it for other combinations by defining NOCOUNT in CFLAGS.  You
  can force inclusion of this code, even for CMU/Tek, by including NONOCOUNT
  in CFLAGS.
*/
#ifdef NONOCOUNT
#ifdef NOCOUNT
#undef NOCOUNT
#endif /* NOCOUNT */
#else
#ifndef NOCOUNT
#ifdef CMU_TCPIP
#define NOCOUNT
#endif /* CMU_TCPIP */
#endif /* NOCOUNT */
#endif /* NONOCOUNT */


    /* From this point forward we have a possible race condition in K95
     * due to its use of multiple threads.  Therefore, we must ensure
     * that only one thread attempt to read/write from the socket at a
     * time.  Otherwise, it is possible for a buffer to be overwritten.
     */
    /* we know now that count >= 0 and that ttibn == 0 */

    if (count == 0
#ifdef RLOGCODE
#ifdef CK_KERBEROS
        && ttnproto != NP_EK4LOGIN && ttnproto != NP_EK5LOGIN
#endif /* CK_KERBEROS */
#endif /* RLOGCODE */
        ) {
        int s_errno = 0;
#ifndef NOCOUNT
/*
  Here we need to tell the difference between a 0 count on an active
  connection, and a 0 count because the remote end of the socket broke the
  connection.  There is no mechanism in TGV MultiNet (or WIN/TCP?) to query
  the status of the connection, so we have to do a read.  -1 means there was
  no data available (socket_errno == EWOULDBLOCK), 0 means the connection is
  down.  But if, by chance, we actually get a character, we have to put it
  where it won't be lost.
*/
#ifndef NON_BLOCK_IO
#ifdef OS2
#ifdef CK_SSL
        RequestSSLMutex(SEM_INDEFINITE_WAIT);
#endif /* CK_SSL */
#endif /* OS2 */
#ifdef NT
        uy = 1;                         /* Turn on nonblocking reads */
        z = socket_ioctl(ttyfd,FIONBIO,&uy);
#else
        y = 1;                          /* Turn on nonblocking reads */
        z = socket_ioctl(ttyfd,FIONBIO,&y);
#endif /* NT */
        debug(F111,"nettchk FIONBIO","on",z);
#ifdef OS2
#ifdef CK_SSL
        ReleaseSSLMutex();
#endif /* CK_SSL */
#endif /* OS2 */
#endif /* NON_BLOCK_IO */
#ifdef NT_TCP_OVERLAPPED
        ionoblock = 1;                  /* For Overlapped I/O */
#endif /* NT_TCP_OVERLAPPED */
#ifdef CK_SSL
        if ( ssl_active_flag || tls_active_flag ) {
#ifdef OS2
	  ssl_read:
            x = SSL_read( ssl_active_flag?ssl_con:tls_con,
                          &ttibuf[ttibp+ttibn],
                          TTIBUFL-ttibp-ttibn );
            switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,x)) {
            case SSL_ERROR_NONE:
                debug(F111,"nettchk SSL_ERROR_NONE","x",x);
                break;
            case SSL_ERROR_WANT_WRITE:
                debug(F100,"nettchk SSL_ERROR_WANT_WRITE","",0);
                x = -1;
                break;
            case SSL_ERROR_WANT_READ:
                debug(F100,"nettchk SSL_ERROR_WANT_READ","",0);
                x = -1;
                break;
            case SSL_ERROR_SYSCALL:
                if ( x == 0 ) { /* EOF */
                    netclos();
                    rc = -1;
                    goto nettchk_return;
              } else {
#ifdef NT
                  int gle = GetLastError();
#endif /* NT */
#ifndef NON_BLOCK_IO
#ifdef OS2
#ifdef CK_SSL
		  RequestSSLMutex(SEM_INDEFINITE_WAIT);
#endif /* CK_SSL */
#endif /* OS2 */
#ifdef NT
         uy = 0;                         /* Turn off nonblocking reads */
         z = socket_ioctl(ttyfd,FIONBIO,&uy);
#else
		  y = 0;                          /* Turn off nonblocking reads */
		  z = socket_ioctl(ttyfd,FIONBIO,&y);
#endif
		  debug(F111,"nettchk FIONBIO","off",z);
#ifdef OS2
#ifdef CK_SSL
		  ReleaseSSLMutex();
#endif /* CK_SSL */
#endif /* OS2 */
#endif /* NON_BLOCK_IO */
#ifdef NT_TCP_OVERLAPPED
		  ionoblock = 0;                  /* For Overlapped I/O */
#endif /* NT_TCP_OVERLAPPED */
#ifdef NT
                  debug(F111,"nettchk SSL_ERROR_SYSCALL",
                         "GetLastError()",gle);
                  rc = os2socketerror(gle);
                  if (rc == -1)
                      rc = -2;
                  else if ( rc == -2 )
                      rc = -1;
		  goto nettchk_return;
#endif /* NT */
                  break;
              }
          case SSL_ERROR_WANT_X509_LOOKUP:
                debug(F100,"nettchk SSL_ERROR_WANT_X509_LOOKUP","",0);
                break;
            case SSL_ERROR_SSL:
                if (bio_err!=NULL) {
                    int len;
                    extern char ssl_err[];
                    BIO_printf(bio_err,"nettchk() SSL_ERROR_SSL\n");
                    ERR_print_errors(bio_err);
                    len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
                    ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
                    debug(F110,"nettchk SSL_ERROR_SSL",ssl_err,0);
                    if (ssl_debug_flag)
                        printf(ssl_err);
                } else if (ssl_debug_flag) {
                    debug(F100,"nettchk SSL_ERROR_SSL","",0);
                    fflush(stderr);
                    fprintf(stderr,"nettchk() SSL_ERROR_SSL\n");
                    ERR_print_errors_fp(stderr);
                }
                x = -1;
		break;
          case SSL_ERROR_ZERO_RETURN:
                debug(F100,"nettchk SSL_ERROR_ZERO_RETURN","",0);
                netclos();
                rc = -1;
                goto nettchk_return;
            default:
                debug(F100,"nettchk SSL_ERROR_?????","",0);
                netclos();
                rc = -1;
                goto nettchk_return;
            }
#else /* OS2 */
	    /* Do not block */
	    x = -1;
#endif /* OS2 */
        } else
#endif /* CK_SSL */
        {
#ifdef OS2
        x = socket_read(ttyfd,&ttibuf[ttibp+ttibn],
                         TTIBUFL-ttibp-ttibn);  /* Returns -1 if no data */
#else /* OS2 */
        x = socket_read(ttyfd,&c,1);    /* Returns -1 if no data */
#endif /* OS2 */
        }
        s_errno = socket_errno;         /* socket_errno may be a function */
        debug(F101,"nettchk socket_read","",x);

#ifndef NON_BLOCK_IO
#ifdef OS2
#ifdef CK_SSL
        RequestSSLMutex(SEM_INDEFINITE_WAIT);
#endif /* CK_SSL */
#endif /* OS2 */
#ifdef NT
        uy = 0;                         /* Turn off nonblocking reads */
        z = socket_ioctl(ttyfd,FIONBIO,&uy);
#else
        y = 0;                          /* Turn off nonblocking reads */
        z = socket_ioctl(ttyfd,FIONBIO,&y);
#endif /* NT */
        debug(F111,"nettchk FIONBIO","off",z);
#ifdef OS2
#ifdef CK_SSL
        ReleaseSSLMutex();
#endif /* CK_SSL */
#endif /* OS2 */
#endif /* NON_BLOCK_IO */
#ifdef NT_TCP_OVERLAPPED
        ionoblock = 0;                  /* For Overlapped I/O */
#endif /* NT_TCP_OVERLAPPED */

        if (x == -1) {
            debug(F101,"nettchk socket_read errno","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0) {
                rc = -1;
                goto nettchk_return;
            }
#endif /* OS2 */
        } else if (x == 0) {
            debug(F100,"nettchk connection closed","",0);
            netclos();                  /* *** *** */
            rc = -1;
            goto nettchk_return;
        }
        if (x >= 1) {                   /* Oops, actually got a byte? */
#ifdef OS2
            /* In OS/2 we read directly into ttibuf[] */
            ckhexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x);
            ttibn += x;
#else /* OS2 */
#ifdef CK_SSL
	    if ( ssl_active_flag || tls_active_flag ) {
		ckhexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x);
		ttibn += x;
	    } else
#endif /* CK_SSL */
	    {
		debug(F101,"nettchk socket_read char","",c);
		debug(F101,"nettchk ttibp","",ttibp);
		debug(F101,"nettchk ttibn","",ttibn);
/*
  In the case of Overlapped I/O the character would have come from
  the beginning of the buffer, so put it back.
*/
		if (ttibp > 0) {
		    ttibp--;
		    ttibuf[ttibp] = c;
		    ttibn++;
		} else {
		    ttibuf[ttibp+ttibn] = c;
		    ttibn++;
		}
	    }
#endif /* OS2 */
        }
#else /* NOCOUNT */
        if (ttnet == NET_TCPB) {
            char dummy;
            x = read(ttyfd,&dummy,0);   /* Try to read nothing */
            if (x < 0) {                /* "Connection reset by peer" */
                perror("TCP/IP");       /* or somesuch... */
                ttclos(0);              /* Close our end too. */
                rc = -1;
                goto nettchk_return;
            }
        }
#endif /* NOCOUNT */
    }
#ifdef CK_KERBEROS
#ifdef KRB4
#ifdef RLOGCODE
    if (ttnproto == NP_EK4LOGIN)
      count += krb4_des_avail(ttyfd);
#endif /* RLOGCODE */
#endif /* KRB4 */
#ifdef KRB5
#ifdef RLOGCODE
    if (ttnproto == NP_EK5LOGIN)
      count += krb5_des_avail(ttyfd);
#endif /* RLOGCODE */
#ifdef KRB5_U2U
    if (ttnproto == NP_K5U2U)
      count += krb5_u2u_avail(ttyfd);
#endif /* KRB5_U2U */
#endif /* KRB5 */
#endif /* CK_KERBEROS */

    debug(F101,"nettchk returns","",count+ttibn);
    rc = count + ttibn;

  nettchk_return:
#ifdef OS2
    ReleaseTCPIPMutex();
#endif /* OS2 */
    return(rc);

#else /* Not TCPIPLIB */
/*
  UNIX just uses ttchk(), in which the ioctl() calls on the file descriptor
  seem to work OK.
*/
    return(ttchk());
#endif /* TCPIPLIB */
/*
  But what about X.25?
*/
}

#ifndef OS2
VOID
#ifdef CK_ANSIC
nettout( int i )                      /* Catch the alarm interrupts */
#else
nettout(i) int i;
#endif /* CK_ANSIC */
{
    debug(F100,"nettout caught timeout","",0);
    ttimoff();
    cklongjmp(njbuf, -1);
}
#endif /* !OS2 */

#ifdef TCPIPLIB

VOID
#ifdef CK_ANSIC
donetinc(void * threadinfo)
#else /* CK_ANSIC */
donetinc(threadinfo) VOID * threadinfo;
#endif /* CK_ANSIC */
/* donetinc */ {
#ifdef NTSIG
    extern int TlsIndex;
    setint();
    if (threadinfo) {                   /* Thread local storage... */
        TlsSetValue(TlsIndex,threadinfo);
    }
#endif /* NTSIG */
#ifdef CK_LOGIN
#ifdef NT
#ifdef IKSD
    if (inserver)
      setntcreds();
#endif /* IKSD */
#endif /* NT */
#endif /* CK_LOGIN */
    while (1) {
        if (ttbufr() < 0)               /* Keep trying to refill it. */
          break;                        /* Till we get an error. */
        if (ttibn > 0)                  /* Or we get a character. */
          break;
    }
}
#endif /* TCPIPLIB */

VOID
#ifdef CK_ANSIC
failnetinc(void * threadinfo)
#else /* CK_ANSIC */
failnetinc(threadinfo) VOID * threadinfo;
#endif /* CK_ANSIC */
/* failnetinc */ {
    ; /* Nothing to do on an error */
}

/* N E T X I N -- Input block of characters from network */

int
#ifdef CK_ANSIC
netxin( int n, CHAR * buf )
#else
netxin(n,buf) int n; CHAR * buf;
#endif /* CK_ANSIC */
{
    int len;
#ifdef TCPIPLIB
    int rc;
#else
    int i, j;
#endif /* TCPIPLIB */

    if (ttyfd == -1) {
        debug(F100,"netxin socket is closed","",0);
        return(-2);
    }
#ifdef CK_KERBEROS
#ifdef KRB4
#ifdef RLOGCODE
    if (ttnproto == NP_EK4LOGIN) {
        if ((len = krb4_des_read(ttyfd,buf,n)) < 0)
          return(-1);
        else
          return(len);
    }
#endif /* RLOGCODE */
#endif /* KRB4 */
#ifdef KRB5
#ifdef RLOGCODE
    if (ttnproto == NP_EK5LOGIN) {
        if ((len = krb5_des_read(ttyfd,(char *)buf,n,0)) < 0)
          return(-1);
        else
          return(len);
    }
#endif /* RLOGCODE */
#ifdef KRB5_U2U
    if (ttnproto == NP_K5U2U) {
        if ((len = krb5_u2u_read(ttyfd,(char *)buf,n)) < 0)
          return(-1);
        else
          return(len);
    }
#endif /* KRB5_U2U */
#endif /* KRB5 */
#endif /* CK_KERBEROS */

#ifdef TCPIPLIB
#ifdef OS2
    RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#endif /* OS2 */
    if (ttibn == 0)
      if ((rc = ttbufr()) <= 0) {
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(rc);
      }

    if (ttibn <= n) {
        len = ttibn;
        memcpy(buf,&ttibuf[ttibp],len);         /* safe */
        ttibp += len;
        ttibn = 0;
    } else {
        memcpy(buf,&ttibuf[ttibp],n);           /* safe */
        ttibp += n;
        ttibn -= n;
        len = n;
    }
#ifdef OS2
    ReleaseTCPIPMutex();
#endif /* OS2 */
#else /* TCPIPLIB */
    for (i = 0; i < n; i++) {
        if ((j = netinc(0)) < 0) {
            if (j < -1)
              return(j);
            else
              break;
        }
        buf[i] = j;
    }
    len = i;
#endif /* TCPIPLIB */

    return(len);
}

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

#ifdef NETLEBUF
#define LEBUF
#endif /* NETLEBUF */
#ifdef TTLEBUF
#define LEBUF
#endif /* TTLEBUF */
#ifndef LEBUF
#ifdef OS2
#define LEBUF
#endif /* OS2 */
#endif /* LEBUF */

int
#ifdef CK_ANSIC
netinc( int timo )
#else
netinc(timo) int timo;
#endif /* CK_ANSIC */
{
#ifdef TCPIPLIB
    int x; unsigned char c;             /* The locals. */

#ifdef NETLEBUF
    if (ttpush >= 0) {
        debug(F111,"netinc","ttpush",ttpush);
        c = ttpush;
        ttpush = -1;
        return(c);
    }
    if (le_data) {
        if (le_getchar((CHAR *)&c) > 0) {
            debug(F111,"netinc le_getchar","c",c);
            return(c);
        }
    }
#endif /* NETLEBUF */

    if (ttyfd == -1) {
        debug(F100,"netinc socket is closed","",0);
        return(-2);
    }

#ifdef CK_KERBEROS
#ifdef KRB4
#ifdef RLOGCODE
    if (ttnproto == NP_EK4LOGIN) {
        if ((x = krb4_des_read(ttyfd,&c,1)) == 0)
          return(-1);
        else if (x < 0)
          return(-2);
        else
          return(c);
    }
#endif /* RLOGCODE */
#endif /* KRB4 */
#ifdef KRB5
#ifdef RLOGCODE
    if (ttnproto == NP_EK5LOGIN) {
        if ((x = krb5_des_read(ttyfd,&c,1,0)) == 0)
          return(-1);
        else if (x < 0)
          return(-2);
        else
          return(c);
    }
#endif /* RLOGCODE */
#ifdef KRB5_U2U
    if (ttnproto == NP_K5U2U) {
        if ((x = krb5_u2u_read(ttyfd,&c,1)) == 0)
          return(-1);
        else if (x < 0)
          return(-2);
        else
          return(c);
    }
#endif /* KRB5_U2U */
#endif /* KRB5 */
#endif /* CK_KERBEROS */

#ifdef OS2
    RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#endif /* OS2 */
    if (ttibn > 0) {                    /* Something in internal buffer? */
        x = 0;                          /* Success. */
    } else {                            /* Else must read from network. */
        x = -1;                         /* Assume failure. */
#ifdef DEBUG
        debug(F101,"netinc goes to net, timo","",timo);
#endif /* DEBUG */
#ifdef CK_SSL
        /*
         * In the case of OpenSSL, it is possible that there is still
         * data waiting in the SSL session buffers that has not yet
         * been read by Kermit.  If this is the case we must process
         * it without calling select() because select() will not return
         * with an indication that there is data to be read from the
         * socket.  If there is no data pending in the SSL session
         * buffers then fall through to the select() code and wait for
         * some data to arrive.
         */
        if (ssl_active_flag) {
            x = SSL_pending(ssl_con);
            if (x < 0) {
                debug(F111,"netinc","SSL_pending error",x);
                netclos();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            } else if ( x > 0 ) {
                if ( ttbufr() >= 0 ) {
                    x = netinc(timo);
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(x);
                }
            }
            x = -1;
        } else if (tls_active_flag) {
            x = SSL_pending(tls_con);
            if (x < 0) {
                debug(F111,"netinc","TLS_pending error",x);
                netclos();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            } else if ( x > 0 ) {
                if ( ttbufr() >= 0 ) {
                    x = netinc(timo);
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(x);
                }
            }
            x = -1;
        }
#endif /* CK_SSL */
#ifndef LEBUF
        if (timo == 0) {                /* Untimed case. */
            while (1) {                 /* Wait forever if necessary. */
                if (ttbufr() < 0)       /* Refill buffer. */
                  break;                /* Error, fail. */
                if (ttibn > 0) {        /* Success. */
                    x = 0;
                    break;
                }
            }
        } else                          /* Timed case... */
#endif /* LEBUF */
          {
#ifdef NT_TCP_OVERLAPPED
            /* This code is for use on NT when we are using */
            /* Overlapped I/O to handle reads.  In the case */
            /* of outstanding reads select() doesn't work   */

            if (WaitForOverlappedReadData(timo)) {
                while (1) {
                    if (ttbufr() < 0)   /* Keep trying to refill it. */
                        break;          /* Till we get an error. */
                    if (ttibn > 0) {    /* Or we get a character. */
                        x = 0;
                        break;
                    }
                }
            }
#else /* NT_TCP_OVERLAPPED */
#ifdef BSDSELECT
            fd_set rfds;
            struct timeval tv;
            int timeout = timo < 0 ? -timo : 1000 * timo;
            debug(F101,"netinc BSDSELECT","",timo);

            for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
                int rc;
                debug(F111,"netinc","timeout",timeout);
                /* Don't move select() initialization out of the loop. */
                FD_ZERO(&rfds);
                FD_SET(ttyfd, &rfds);
                tv.tv_sec  = tv.tv_usec = 0L;
                if (timo)
                  tv.tv_usec = (long) 100000L;
                else
                  tv.tv_sec = 30;
#ifdef NT
                WSASafeToCancel = 1;
#endif /* NT */
                rc = select(FD_SETSIZE,
#ifdef __DECC
#ifdef INTSELECT
                            (int *)
#else /* def INTSELECT */
                            (fd_set *)
#endif /* def INTSELECT [else] */
#else /* def __DECC */
                            (fd_set *)
#endif /* def __DECC [else] */
                            &rfds, NULL, NULL, &tv);
                if (rc < 0) {
                    int s_errno = socket_errno;
                    debug(F111,"netinc","select",rc);
                    debug(F111,"netinc","socket_errno",s_errno);
                    if (s_errno) {
#ifdef OS2
                        ReleaseTCPIPMutex();
#endif /* OS2 */
                        return(-1);
                    }
                }
                debug(F111,"netinc","select",rc);

                /*
                 * For some yet to be determined reason, on NT 3.1 the select()
                 * call is claiming the socket isn't ready for reading -
                 *   !FD_ISSET(ttyfd, &rfds) == true && rc == 0
                 * this occurs even when ttchk() returns some number of bytes
                 * ready for reading which should mean the socket is ready for
                 * reading. And indeed if we go ahead and try to read when
                 * ttchk() > 0 telnet suddenly works!
                 *
                 * So maybe there is some bug in the NT 3.1 Winsock
                 * implementation? Or is K95 doing something that NT 3.1 doesn't
                 * like?
                 *
                 * problem is, the API ttchk() relies on (FIONREAD) is slow and
                 * potentially unreliable so its not really an acceptable
                 * replacement for select() unless we're ok with really bad
                 * performance
                 *
                 * Note: ttchk eventually arrives at  nettchk further up in this file.
                 * Line 5473 is about where the FIONREAD is.
                 *
                 */
#ifdef NT
                WSASafeToCancel = 0;
#endif /* NT */
                if (!FD_ISSET(ttyfd, &rfds)) {
#ifdef LEBUF
                    if (le_inbuf() > 0) {
                        timeout = -1;
                        break;
                    }
#endif /* LEBUF */
                    /* If waiting forever we have no way of knowing if the */
                    /* socket closed so try writing a 0-length TCP packet  */
                    /* which should force an error if the socket is closed */
                    if (!timo) {
                        if ((rc = socket_write(ttyfd,"",0)) < 0) {
                            int s_errno = socket_errno;
                            debug(F101,"netinc socket_write error","",s_errno);
#ifdef OS2
                            if (os2socketerror(s_errno) < 0) {
                              ReleaseTCPIPMutex();
                              return(-2);
                            }
                            ReleaseTCPIPMutex();
#endif /* OS2 */
                            return(-1); /* Call it an i/o error */
                        }
                    }
                    continue;
                }
                while (1) {
                    if (ttbufr() < 0) { /* Keep trying to refill it. */
                        timeout = -1;
                        break;          /* Till we get an error. */
                    }
                    if (ttibn > 0) {    /* Or we get a character. */
                        x = 0;
                        timeout = -1;
                        break;
                    }
                }
            }
#ifdef NT
            WSASafeToCancel = 0;
#endif /* NT */
#else /* !BSDSELECT */
#ifdef IBMSELECT
/*
  Was used by OS/2, currently not used, but might come in handy some day...
  ... and it came in handy!  For our TCP/IP layer, it avoids all the fd_set
  and timeval stuff since this is the only place where it is used.
*/
            int socket = ttyfd;
            int timeout = timo < 0 ? -timo : 1000 * timo;

            debug(F101,"netinc IBMSELECT","",timo);
            for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
                if (select(&socket, 1, 0, 0, 100L) == 1) {
                    while (1) {
                        if (ttbufr() < 0) { /* Keep trying to refill it. */
                            timeout = -1;
                            break;      /* Till we get an error. */
                        }
                        if (ttibn > 0) { /* Or we get a character. */
                            x = 0;
                            timeout = -1;
                            break;
                        }
                    }
                }
#ifdef LEBUF
                else if (le_inbuf() > 0)  {
                    timeout = -1;
                    break;
                }
#endif /* LEBUF */
            }
#else /* !IBMSELECT */
#ifdef WINSOCK
       /* Actually, under WinSock we have a better mechanism than select() */
       /* for setting timeouts (SO_RCVTIMEO, SO_SNDTIMEO) */
            SOCKET socket = ttyfd;
            debug(F101,"netinc NTSELECT","",timo);
            if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timo,
                            sizeof(timo))  == NO_ERROR)
              while (1) {
                  if (ttbufr() < 0)     /* Keep trying to refill it. */
                    break;              /* Till we get an error. */
                  if (ttibn > 0) {      /* Or we get a character. */
                      x = 0;
                      break;
                  }
              }
#else /* WINSOCK */
/*
  If we can't use select(), then we use the regular alarm()/signal()
  timeout mechanism.
*/
            debug(F101,"netinc alarm","",timo);
            x = alrm_execute(ckjaddr(njbuf),timo,nettout,donetinc,failnetinc);
            ttimoff();                  /* Timer off. */
#endif /* WINSOCK */
#endif /* IBMSELECT */
#endif /* BSDSELECT */
#endif /* NT_TCP_OVERLAPPED */
        }
    }

#ifdef LEBUF
    if (le_inbuf() > 0) {               /* If data was inserted into the */
        if (le_getchar((CHAR *)&c) > 0) {/* Local Echo buffer while the   */
#ifdef OS2                               /* was taking place do not mix   */
          ReleaseTCPIPMutex();           /* the le data with the net data */
#endif /* OS2 */
          return(c);
        }
    }
#endif /* LEBUF */
    if (x < 0) {                        /* Return -1 if we failed. */
        debug(F100,"netinc timed out","",0);
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
        return(-1);
    } else {                            /* Otherwise */
        c = ttibuf[ttibp];              /* Return the first char in ttibuf[] */
        if (deblog) {
#ifndef COMMENT
            debug(F101,"netinc returning","",c);
#endif /* COMMENT */
            if (c == 0) {
                debug(F101,"netinc 0 ttibn","",ttibn);
                debug(F101,"netinc 0 ttibp","",ttibp);
#ifdef BETADEBUG
                {
#ifdef OS2
                    extern int tt_type_mode;
                    if ( !ISVTNT(tt_type_mode) )
#endif /* OS2 */
                    ckhexdump("netinc &ttbuf[ttibp]",&ttibuf[ttibp],ttibn);
                }
#endif /* BETADEBUG */
            }
        }
        ttibp++;
        ttibn--;
#ifdef OS2
        ReleaseTCPIPMutex();
#endif /* OS2 */
#ifdef CK_ENCRYPTION
        if (TELOPT_U(TELOPT_ENCRYPTION))
          ck_tn_decrypt(&c,1);
#endif /* CK_ENCRYPTION */
        return(c);
    }
#else /* Not using TCPIPLIB */
    return(-1);
#endif /* TCPIPLIB */
}

/*  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
#ifdef CK_ANSIC
nettol( CHAR *s, int n )
#else
nettol(s,n) CHAR *s; int n;
#endif /* CK_ANSIC */
{
#ifdef TCPIPLIB
    int count = 0;
    int len = n;
    int try = 0;

    if (ttyfd == -1) {
        debug(F100,"nettol socket is closed","",0);
        return -1;
    }
    debug(F101,"nettol TCPIPLIB ttnet","",ttnet);

#ifdef CK_KERBEROS
#ifdef KRB4
#ifdef RLOGCODE
    if (ttnproto == NP_EK4LOGIN) {
        return(krb4_des_write(ttyfd,s,n));
    }
#endif /* RLOGCODE */
#endif /* KRB4 */
#ifdef KRB5
#ifdef RLOGCODE
    if (ttnproto == NP_EK5LOGIN) {
        return(krb5_des_write(ttyfd,s,n,0));
    }
#endif /* RLOGCODE */
#ifdef KRB5_U2U
    if (ttnproto == NP_K5U2U) {
        return(krb5_u2u_write(ttyfd,s,n));
    }
#endif /* KRB5_U2U */
#endif /* KRB5 */
#endif /* CK_KERBEROS */

#ifdef CK_ENCRYPTION
    if (TELOPT_ME(TELOPT_ENCRYPTION))
      ck_tn_encrypt(s,n);
#endif /* CK_ENCRYPTION */

#ifdef CK_SSL
    if (ssl_active_flag || tls_active_flag) {
        int error, r;
        /* Write using SSL */
      ssl_retry:
        if (ssl_active_flag)
          r = SSL_write(ssl_con, s, len /* >1024?1024:len */);
        else
          r = SSL_write(tls_con, s, len /* >1024?1024:len */);
        switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,r)) {
          case SSL_ERROR_NONE:
            debug(F111,"nettol","SSL_write",r);
            if ( r == len )
                return(n);
             s += r;
             len -= r;
             goto ssl_retry;
          case SSL_ERROR_WANT_WRITE:
            debug(F100,"nettol SSL_ERROR_WANT_WRITE","",0);
            return(-1);
          case SSL_ERROR_WANT_READ:
            debug(F100,"nettol SSL_ERROR_WANT_READ","",0);
            return(-1);
          case SSL_ERROR_SYSCALL:
              if ( r == 0 ) { /* EOF */
                  netclos();
                  return(-2);
              } else {
                  int rc = -1;
#ifdef NT
                  int gle = GetLastError();
                  debug(F111,"nettol SSL_ERROR_SYSCALL",
                         "GetLastError()",gle);
                  rc = os2socketerror(gle);
                  if (rc == -1)
                      rc = -2;
                  else if ( rc == -2 )
                      rc = -1;
#endif /* NT */
                  return(rc);
              }
          case SSL_ERROR_WANT_X509_LOOKUP:
            debug(F100,"nettol SSL_ERROR_WANT_X509_LOOKUP","",0);
            netclos();
            return(-2);
          case SSL_ERROR_SSL:
            debug(F100,"nettol SSL_ERROR_SSL","",0);
              if (bio_err!=NULL) {
                  int len;
                  extern char ssl_err[];
                  BIO_printf(bio_err,"nettol() SSL_ERROR_SSL\n");
                  ERR_print_errors(bio_err);
                  len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
                  ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
                  debug(F110,"nettol SSL_ERROR_SSL",ssl_err,0);
                  if (ssl_debug_flag)
                      printf(ssl_err);
              } else if (ssl_debug_flag) {
                  debug(F100,"nettol SSL_ERROR_SSL","",0);
                  fflush(stderr);
                  fprintf(stderr,"nettol() SSL_ERROR_SSL\n");
                  ERR_print_errors_fp(stderr);
              }
              return(-1);
          case SSL_ERROR_ZERO_RETURN:
            debug(F100,"nettol SSL_ERROR_ZERO_RETURN","",0);
            netclos();
            return(-2);
          default:
            debug(F100,"nettol SSL_ERROR_?????","",0);
            netclos();
            return(-2);
        }
    }
#endif /* CK_SSL */

  nettol_retry:
    try++;                              /* Increase the try counter */

    if (ttnet == NET_TCPB) {
#ifdef BSDSELECT
        fd_set wfds;
        struct timeval tv;

        debug(F101,"nettol BSDSELECT","",0);
        tv.tv_usec = 0L;
        tv.tv_sec=30;
#ifdef NT
        WSASafeToCancel = 1;
#endif /* NT */
#ifdef STREAMING
      do_select:
#endif /* STREAMING */
        FD_ZERO(&wfds);
        FD_SET(ttyfd, &wfds);
        if (select(FD_SETSIZE, NULL,
#ifdef __DECC
#ifndef __DECC_VER
                    (int *)
#endif /* __DECC_VER */
#endif /* __DECC */
                   &wfds, NULL, &tv) < 0) {
            int s_errno = socket_errno;
            debug(F101,"nettol select failed","",s_errno);
#ifdef BETADEBUG
            printf("nettol select failed: %d\n", s_errno);
#endif /* BETADEBUG */
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
        if (!FD_ISSET(ttyfd, &wfds)) {
#ifdef STREAMING
            if (streaming)
              goto do_select;
#endif /* STREAMING */
            debug(F111,"nettol","!FD_ISSET",ttyfd);
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
#else /* BSDSELECT */
#ifdef IBMSELECT
        {
            int tries = 0;
            debug(F101,"nettol IBMSELECT","",0);
            while (select(&ttyfd, 0, 1, 0, 1000) != 1) {
                int count;
                if (tries++ >= 60) {
                    /* if after 60 seconds we can't get permission to write */
                    debug(F101,"nettol select failed","",socket_errno);
                    return(-1);
                }
                if ((count = nettchk()) < 0) {
                    debug(F111,"nettol","nettchk()",count);
                    return(count);
                }
            }
        }
#endif /* IBMSELECT */
#endif /* BSDSELECT */
        if ((count = socket_write(ttyfd,s,n)) < 0) {
            int s_errno = socket_errno; /* maybe a function */
            debug(F101,"nettol socket_write error","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0)
              return(-2);
#endif /* OS2 */
            return(-1);                 /* Call it an i/o error */
        }
        if (count < n) {
            debug(F111,"nettol socket_write",s,count);
            if (try > 25) {
                /* don't try more than 25 times */
                debug(F100,"nettol tried more than 25 times","",0);
                return(-1);
            }
            if (count > 0) {
                s += count;
                n -= count;
            }
            debug(F111,"nettol retry",s,n);
            goto nettol_retry;
        } else {
            debug(F111,"nettol socket_write",s,count);
            return(len); /* success - return total length */
        }
    } else
      return(-2);
#else
    debug(F100,"nettol TCPIPLIB not defined","",0);
    return(-2);
#endif /* TCPIPLIB */
}

/*  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 */ {
#ifdef UNIX
    return(ttoc(c));
#else
#ifdef TCPIPLIB
    unsigned char cc;
    if (ttyfd == -1) {
        debug(F100,"nettoc socket is closed","",0);
        return -1;
    }
    cc = c;
    debug(F101,"nettoc cc","",cc);

#ifdef CK_KERBEROS
#ifdef KRB4
#ifdef RLOGCODE
    if (ttnproto == NP_EK4LOGIN) {
        return(krb4_des_write(ttyfd,&cc,1)==1?0:-1);
    }
#endif /* RLOGCODE */
#endif /* KRB4 */
#ifdef KRB5
#ifdef RLOGCODE
    if (ttnproto == NP_EK5LOGIN) {
        return(krb5_des_write(ttyfd,&cc,1,0)==1?0:-1);
    }
#endif /* RLOGCODE */
#ifdef KRB5_U2U
    if (ttnproto == NP_K5U2U) {
        return(krb5_u2u_write(ttyfd,&cc,1)==1?0:-1);
    }
#endif /* KRB5_U2U */
#endif /* KRB5 */
#endif /* CK_KERBEROS */

#ifdef CK_ENCRYPTION
        if ( TELOPT_ME(TELOPT_ENCRYPTION) )
            ck_tn_encrypt(&cc,1);
#endif /* CK_ENCRYPTION */
#ifdef CK_SSL
    if (ssl_active_flag || tls_active_flag) {
        int len, error;
        /* Write using SSL */
      ssl_retry:
        if (ssl_active_flag)
          len = SSL_write(ssl_con, &cc, 1);
        else
          len = SSL_write(tls_con, &cc, 1);
        switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,len)) {
          case SSL_ERROR_NONE:
            debug(F111,"nettoc","SSL_write",len);
            return(len == 1 ? 0 : -1);
          case SSL_ERROR_WANT_WRITE:
  	  case SSL_ERROR_WANT_READ:
            return(-1);
          case SSL_ERROR_SYSCALL:
              if ( len == 0 ) { /* EOF */
                  netclos();
                  return(-2);
              } else {
                  int rc = -1;
#ifdef NT
                  int gle = GetLastError();
                  debug(F111,"nettoc SSL_ERROR_SYSCALL",
                         "GetLastError()",gle);
                  rc = os2socketerror(gle);
                  if (rc == -1)
                      rc = -2;
                  else if ( rc == -2 )
                      rc = -1;
#endif /* NT */
                  return(rc);
              }
        case SSL_ERROR_SSL:
              if (bio_err!=NULL) {
                  int len;
                  extern char ssl_err[];
                  BIO_printf(bio_err,"nettoc() SSL_ERROR_SSL\n");
                  ERR_print_errors(bio_err);
                  len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
                  ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
                  debug(F110,"nettoc SSL_ERROR_SSL",ssl_err,0);
                  if (ssl_debug_flag)
                      printf(ssl_err);
              } else if (ssl_debug_flag) {
                  debug(F100,"nettoc SSL_ERROR_SSL","",0);
                  fflush(stderr);
                  fprintf(stderr,"nettoc() SSL_ERROR_SSL\n");
                  ERR_print_errors_fp(stderr);
              }
              return(-1);
              break;
          case SSL_ERROR_WANT_X509_LOOKUP:
          case SSL_ERROR_ZERO_RETURN:
          default:
            netclos();
            return(-2);
        }
    }
#endif /* CK_SSL */
    if (ttnet == NET_TCPB) {
#ifdef BSDSELECT
        fd_set wfds;
        struct timeval tv;

        debug(F101,"nettoc BSDSELECT","",0);
        tv.tv_usec = 0L;
        tv.tv_sec = 30;

#ifdef STREAMING
      do_select:
#endif /* STREAMING */

        FD_ZERO(&wfds);
        FD_SET(ttyfd, &wfds);
        if (select(FD_SETSIZE, NULL,
#ifdef __DECC
#ifndef __DECC_VER
                   (int *)
#endif /* __DECC_VER */
#endif /* __DECC */
                   &wfds, NULL, &tv) < 0) {
            int s_errno = socket_errno;
            debug(F101,"nettoc select failed","",s_errno);
#ifdef BETADEBUG
            printf("nettoc select failed: %d\n", s_errno);
#endif /* BETADEBUG */
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
        if (!FD_ISSET(ttyfd, &wfds)) {
#ifdef STREAMING
            if (streaming)
              goto do_select;
#endif /* STREAMING */
            debug(F111,"nettoc","!FD_ISSET",ttyfd);
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
#else /* BSDSELECT */
#ifdef IBMSELECT
        {
            int tries = 0;
            while (select(&ttyfd, 0, 1, 0, 1000) != 1) {
                int count;
                if (tries++ >= 60) {
                    /* if after 60 seconds we can't get permission to write */
                    debug(F101,"nettoc select failed","",socket_errno);
                    return(-1);
                }
                if ((count = nettchk()) < 0) {
                    debug(F111,"nettoc","nettchk()",count);
                    return(count);
                }
            }
        }
#endif /* IBMSELECT */
#endif /* BSDSELECT */
        if (socket_write(ttyfd,&cc,1) < 1) {
            int s_errno = socket_errno;         /* maybe a function */
            debug(F101,"nettoc socket_write error","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0)
              return(-2);
#endif /* OS2 */
            return(-1);
        }
        debug(F101,"nettoc socket_write","", cc);
        return(0);
    } else return(-2);
#else
    return(-2);
#endif /* TCPIPLIB */
#endif /* UNIX */
}

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

#ifdef TNCODE
static int
#ifdef CK_ANSIC
netgetct(int timo)                      /* Input function to point to... */
#else  /* CK_ANSIC */
netgetct(timo) int timo;
#endif /* CK_ANSIC */
{                                       /* ...in the tn_doop() call */
#ifdef TCPIPLIB
    return netinc(timo);
#else /* TCPIPLIB */
    return ttinc(timo);
#endif /* TCPIPLIB */
}
#endif /* TNCODE */

int
netflui() {
    int n;
    int ch;
#ifdef NETLEBUF
    ttpush = -1;                        /* Clear the peek-ahead char */
    while (le_data && (le_inbuf() > 0)) {
        CHAR ch = '\0';
        if (le_getchar(&ch) > 0) {
            debug(F101,"ttflui le_inbuf ch","",ch);
        }
    }
#endif /* NETLEBUF */

#ifdef TCPIPLIB
#ifdef OS2
    RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
#endif /* OS2 */
#ifdef TNCODE
    if (ttnproto == NP_TELNET) {
        /* Netflui must process Telnet negotiations or get out of sync */
        if ((n = nettchk()) <= 0)
          goto exit_flui;
        while (n-- > 0) {
            ch = netinc(1);
            if (ch == IAC) {
                extern int duplex;  /* this really shouldn't be here but ... */
                int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetct);
                if (tx == 1) duplex = 1;
                else if (tx == 2) duplex = 0;
                n = nettchk();
            }
        }
    } else
#endif /* TNCODE */
    {
        ttibuf[ttibp+ttibn] = '\0';
        debug(F111,"netflui 1",ttibuf,ttibn);
#ifdef CK_ENCRYPTION
        if (TELOPT_U(TELOPT_ENCRYPTION)) {
            ck_tn_decrypt(&ttibuf[ttibp],ttibn);
        }
#endif /* CK_ENCRYPTION */
        ttibn = ttibp = 0;              /* Flush internal buffer *FIRST* */
        if (ttyfd < 1)
          goto exit_flui;
        if ((n = nettchk()) > 0) {      /* Now see what's waiting on the net */
            if (n > TTIBUFL) n = TTIBUFL;       /* and sponge it up */
            debug(F101,"netflui 2","",n);       /* ... */
            n = socket_read(ttyfd,ttibuf,n); /* into our buffer */
            if (n >= 0) ttibuf[n] = '\0';
            debug(F111,"netflui 3",ttibuf,n);
#ifdef CK_ENCRYPTION
            if (TELOPT_U(TELOPT_ENCRYPTION)) {
                ck_tn_decrypt(&ttibuf[ttibp],n);
            }
#endif /* CK_ENCRYPTION */
            ttibuf[0] = '\0';
        }
    }
#else  /* !TCPIPLIB */
    if (ttyfd < 1)
      goto exit_flui;
#ifdef TNCODE
    if (ttnproto == NP_TELNET) {
        if ((n = ttchk()) <= 0)
          goto exit_flui;
        while (n-- >= 0) {
            /* Netflui must process Telnet negotiations or get out of sync */
            ch = ttinc(1);
            if (ch == IAC) {
                extern int duplex;  /* this really shouldn't be here but ... */
                int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetct);
                if (tx == 1) duplex = 1;
                else if (tx == 2) duplex = 0;
                n = ttchk();
            }
        };
    } else
#endif /* TNCODE */
    if ((n = ttchk()) > 0) {
        debug(F101,"netflui non-TCPIPLIB","",n);
        while ((n--) && ttinc(1) > -1)  /* Don't worry, ttinc() is buffered */
          ;                             /* and it handles the decryption... */
    }
#endif /* TCPIPLIB */
  exit_flui:
#ifdef OS2
    ReleaseTCPIPMutex();
#endif /* OS2 */
    return(0);
}

#ifdef CK_KERBEROS
/* The following two functions are required for encrypted rlogin */
/* They are called with nettoc() or nettol() are transmitting    */
/* encrypted data.  They call a function to encrypt the data     */
/* and that function needs to be able to write to/read from the  */
/* network in an unimpeded manner.  Hence, these two simple fns. */
int
net_write(fd, buf, len)
    int fd;
    register const char *buf;
    int len;
{
    int cc;
    register int wrlen = len;
    do {
#ifdef TCPIPLIB
        cc = socket_write(fd, buf, wrlen);
#else
        cc = write(fd,buf,wrlen);
#endif /* TCPIPLIB */
        if (cc < 0) {
            int s_errno = socket_errno;
            debug(F101,"net_write error","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0)
                return(-1);
            else
                continue;
#else /* OS2 */
            if (errno == EINTR)
                continue;
            return(-1);
#endif /* OS2 */
        }
        else {
            buf += cc;
            wrlen -= cc;
        }
    } while (wrlen > 0);
    return(len);
}
int
net_read(fd, buf, len)
    int fd;
    register char *buf;
    register int len;
{
    int cc, len2 = 0;

    do {
#ifdef TCPIPLIB
        cc = socket_read(fd, buf, len);
#else
        cc = read(fd,buf,len);
#endif
        if (cc < 0) {
            int s_errno = socket_errno;
            debug(F101,"net_read error","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0)
                return(-1);
#endif /* OS2 */
            return(cc);          /* errno is already set */
        }
        else if (cc == 0) {
            netclos();
            return(len2);
        } else {
            buf += cc;
            len2 += cc;
            len -= cc;
        }
    } while (len > 0);
    return(len2);
}
#endif /* CK_KERBEROS */

/* 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 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
 */

/* Technique number one cannot work reliably if the machine is a laptop
 * and the hostname is associated with a physical adapter which is not
 * installed and a PPP connection is being used instead.  This is because
 * the hostname DNS lookup will succeed for the physical adapter even though
 * it would be impossible to use it.  In NT4 SP4, the gethostbyname()
 * when given the result of gethostname() returns not the real DNS entries
 * for that name+domain.  Instead it returns all of the static and dynamic
 * IP addresses assigned to any physical or virtual adapter defined in the
 * system regardless of whether or not it is installed.  The order of the
 * addresses is fixed according to the binding order in the NT registry.
 */

/*
 * It appears that calling gethostbyname(NULL) is more reliable than
 * calling gethostbyname(gethostname()) on Windows.  So on Windows we will
 * only call gethostbyname(NULL).
 */

/*
  getlocalipaddr() and getlocalipaddrs() (below) are IPv4-only and
  unconverted: the UDP-connect trick here and gethostbyname() both
  have no IPv6 equivalent path.  On an IPv6-only host they simply
  fail (leave buf empty) rather than return a wrong result, and every
  caller already handles that as an expected "no address" case.

  Left as a self-contained follow-up: every caller uses its own
  small, IPv4-sized buffer for the result, so widening those buffers
  has to happen alongside converting these.
*/
int
#ifdef CK_ANSIC
getlocalipaddr( void )
#else
getlocalipaddr()
#endif /* CK_ANSIC */
{
#ifndef datageneral
    struct sockaddr_in l_sa;
    struct sockaddr_in r_sa;
    GSOCKNAME_T slen = sizeof(struct sockaddr_in);
    int sock;
    int rc;

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

    debug(F100,"getlocalipaddr","",0);
    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) {
                myxipaddr = ntohl(l_sa.sin_addr.s_addr);
                ckstrncpy(myipaddr,(char *)inet_ntoa(l_sa.sin_addr),CK_IPADDRLEN);
                debug(F110,"getlocalipaddr setting buf to",myipaddr,0);
                return(0);
            }
        }
    }
    return getlocalipaddrs(myipaddr,sizeof(myipaddr),0);
#else /* datageneral */
    return(-1);
#endif /* datageneral */
}

int
#ifdef CK_ANSIC
getlocalipaddrs( char * buf, int  bufsz, int index )
#else
getlocalipaddrs(buf,bufsz,index) char * buf; int bufsz; int index;
#endif /* CK_ANSIC */
{
#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 rc;

    debug(F100,"getlocalipaddrs","",0);
    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';
#ifdef NT
    if ( winsock_version < 20 ) {
        rc = gethostname(localhost, 256);
        debug(F110,"getlocalipaddrs localhost",localhost,0);
    }
#endif /* NT */
#endif /* CKGHNLHOST */
    if (!rc) {
        /* resolve host name for local address */
        debug(F110,"getlocalipaddrs","calling gethostbyname()",0);
        host = gethostbyname(localhost);
        /* debug(F111,"getlocalipaddrs","gethostbyname() returned",host); */
        if (host) {
#ifdef HADDRLIST
            host = ck_copyhostent(host);
            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]));
            ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),bufsz);
            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));
            ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),bufsz);
            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 RLOGCODE                 /* TCP/IP RLOGIN protocol support code */
#ifdef CK_NAWS
int
#ifdef CK_ANSIC
rlog_naws( void )
#else
rlog_naws()
#endif /* CK_ANSIC */
{
    struct rlog_naws {
        unsigned char id[4];
        unsigned short rows, cols, ypix, xpix;
    } nawsbuf;

    if (ttnet != NET_TCPB)
      return 0;
    if (ttnproto != NP_RLOGIN
#ifdef CK_KERBEROS
        && ttnproto != NP_K4LOGIN
        && ttnproto != NP_EK4LOGIN
        && ttnproto != NP_K5LOGIN
        && ttnproto != NP_EK5LOGIN
#endif /* CK_KERBEROS */
         )
      return 0;
    if (!TELOPT_ME(TELOPT_NAWS))
      return 0;

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

    nawsbuf.id[0] = nawsbuf.id[1] = 0377;
    nawsbuf.id[2] = nawsbuf.id[3] = 's';
#ifdef OS2
    nawsbuf.rows = htons((unsigned short) (VscrnGetHeight(VTERM)
                          -(tt_status[VTERM]?1:0)));
    nawsbuf.cols = htons((unsigned short) VscrnGetWidth(VTERM));
#else /* OS2 */
    nawsbuf.rows = htons((unsigned short) tt_rows);
    nawsbuf.cols = htons((unsigned short) tt_cols);
#endif /* OS2 */
    nawsbuf.ypix = htons(0);            /* y pixels */

    nawsbuf.xpix = htons(0);            /* x pixels */
    if (ttol((CHAR *)(&nawsbuf), sizeof(nawsbuf)) < 0)
      return(-1);
    return(0);
}
#endif /* CK_NAWS */
#endif /* NOTCPIP */

#ifndef NORLOGIN
#ifdef OS2ORUNIX
#define RLOGOUTBUF
#endif /* OS2 */
static int
#ifdef CK_ANSIC
rlog_ini(CHAR * hostname, int port,
         struct sockaddr_in * l_addr, struct sockaddr_in * r_addr)
#else /* CK_ANSIC */
rlog_ini(hostname, port, l_addr, r_addr)
    CHAR * hostname;
    int port;
    struct sockaddr_in * l_addr;
    struct sockaddr_in * r_addr;
#endif /* CK_ANSIC */
/* rlog_ini */ {

#ifdef RLOGOUTBUF
    char outbuf[512];
    int  outbytes=0;
#endif /* RLOGOUTBUF */
    int flag = 0;
#define TERMLEN 16
#define CONSPDLEN 16
    CHAR localuser[UIDBUFLEN+1];
    CHAR remoteuser[UIDBUFLEN+1];
    int userlen = 0;
    CHAR term_speed[TERMLEN+CONSPDLEN+1];
#ifdef CONGSPD
    long conspd = -1L;
#endif /* CONGSPD */
#ifdef OS2
    extern int tt_type, max_tt;
    extern struct tt_info_rec tt_info[];
#endif /* OS2 */
    int i, n;

    int rc = 0;
    tn_reset();                 /* 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 */
    rlog_inband = 0;

#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).
*/
#ifdef OS2
    /* Console terminal screen rows and columns */
    debug(F101,"rlog_ini tt_rows 1","",VscrnGetHeight(VTERM)
           -(tt_status[VTERM]?1:0));
    debug(F101,"rlog_ini tt_cols 1","",VscrnGetWidth(VTERM));
    /* Not known yet */
    if (VscrnGetWidth(VTERM) < 0 ||
        VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) < 0) {
        ttgwsiz();                      /* Try to get screen dimensions */
    }
    debug(F101,
          "rlog_ini tt_rows 2",
          "",
          VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0)
          );
    debug(F101,"rlog_ini tt_cols 2","",VscrnGetWidth(VTERM));
#else /* OS2 */
    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 /* OS2 */
#endif /* CK_TTGWSIZ */

    ttflui();                           /* Start by flushing the buffers */

    rlog_mode = RL_COOKED;

    /* Determine the user's local username ... */

    localuser[0] = '\0';
#ifdef NT
    {
        ckstrncpy((char *)localuser,GetLocalUser(),UIDBUFLEN);
    }

    if ( !localuser[0] )
#endif /* NT */
    {
        char * user = getenv("USER");
        if (!user)
          user = "";
        userlen = strlen(user);
        debug(F111,"rlogin getenv(USER)",user,userlen);
        ckstrncpy((char *)localuser,user,UIDBUFLEN);
        debug(F110,"rlog_ini localuser 1",localuser,0);
    }
    if ( !localuser[0] )
        strcpy((char *)localuser,"unknown");
    else if (ck_lcname) {
        cklower((char *)localuser);
        debug(F110,"rlog_ini localuser 2",localuser,0);
    }

    /* And the username to login with */
    if (uidbuf[0]) {
        ckstrncpy((char *)remoteuser,uidbuf,UIDBUFLEN);
        debug(F110,"rlog_ini remoteuser 1",remoteuser,0);
    } else if (localuser[0]) {
        ckstrncpy((char *)remoteuser,(char *)localuser,UIDBUFLEN);
        debug(F110,"rlog_ini remoteuser 2",remoteuser,0);
    } else {
        remoteuser[0] = '\0';
        debug(F110,"rlog_ini remoteuser 3",remoteuser,0);
    }
    if (ck_lcname)
      cklower((char *)remoteuser);
    debug(F110,"rlog_ini remoteuser 4",remoteuser,0);

    /* The command to issue is the terminal type and speed */
    term_speed[0] = '\0';
    if (tn_term) {                      /* SET TELNET TERMINAL-TYPE value */
        if (*tn_term) {                 /* (if any) takes precedence. */
            ckstrncpy((char *)term_speed, tn_term, TERMLEN);
            flag = 1;
        }
    } else {                            /* Otherwise the local terminal type */
#ifdef OS2
        /* In terminal-emulating versions, it's the SET TERM TYPE value */
        ckstrncpy(term_speed, (tt_type >= 0 && tt_type <= max_tt) ?
                tt_info[tt_type].x_name : "network", TERMLEN);
#else
        /* In the others, we just look at the TERM environment variable */
        {
            char *p = getenv("TERM");
            if (p)
              ckstrncpy((char *)term_speed,p,TERMLEN);
            else
              term_speed[0] = '\0';
#ifdef VMS
            for (p = (char *) term_speed; *p; p++) {
                if (*p == '-' && (!strcmp(p,"-80") || !strcmp(p,"-132")))
                  break;
                else if (isupper(*p))
                  *p = tolower(*p);
            }
            *p = '\0';
#endif /* VMS */
        }
#endif /* OS2 */
    }
    n = strlen((char *)term_speed);
    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(term_speed[i]))
                term_speed[i] = tolower(term_speed[i]);
        }
        debug(F110,"rlog_ini term_speed 1",term_speed,0);

#ifdef CONGSPD
        /* conspd() is not yet defined in all ck*tio.c modules */
        conspd = congspd();
        if (conspd > 0L) {
            ckstrncat((char *)term_speed,"/",sizeof(term_speed));
            ckstrncat((char *)term_speed,ckltoa(conspd),sizeof(term_speed));
        } else
#endif /* CONGSPD */
          ckstrncat((char *)term_speed,"/19200",sizeof(term_speed));
        debug(F110,"rlog_ini term_speed 2",term_speed,0);
    } else {
        term_speed[0] = '\0';
        debug(F110,"rlog_ini term_speed 3",term_speed,0);
    }

#ifdef CK_KERBEROS
    if (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN ||
        ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) {
        int kver, encrypt, rc;
        switch (ttnproto) {
          case NP_K4LOGIN:
            kver = 4;
            encrypt = 0;
            break;
          case NP_EK4LOGIN:
            kver = 4;
            encrypt = 1;
            break;
          case NP_K5LOGIN:
            kver = 5;
            encrypt = 0;
            break;
          case NP_EK5LOGIN:
            kver = 5;
            encrypt = 1;
            break;
        default:
            kver = 0;
            encrypt = 0;
        }
        rc = ck_krb_rlogin(hostname, port,
                           localuser, remoteuser, term_speed,
                           l_addr, r_addr, kver, encrypt);
        if (!rc) {                      /* success */
            TELOPT_ME(TELOPT_NAWS) = 1;
            rc = rlog_naws();
        }
        return(rc);
    } else
#endif /* CK_KERBEROS */
    if (ttnproto == NP_RLOGIN) {
#ifdef RLOGOUTBUF
        /*
         *  The rcmds start the connection with a series of init data:
         *
         *    a port number upon which client is listening for stderr data
         *    the user's name on the client machine
         *    the user's name on the server machine
         *    the terminal_type/speed or command to execute
         */
        outbuf[outbytes++] = 0;
        strcpy((char *)outbuf+outbytes,(char *)localuser);
        outbytes += strlen((char *)localuser) + 1;
        strcpy((char *)outbuf+outbytes,(char *)remoteuser);
        outbytes += strlen((char *)remoteuser) + 1;
        strcpy((char *)outbuf+outbytes,(char *)term_speed);
        outbytes += strlen((char *)term_speed) + 1;
        rc = ttol((CHAR *)outbuf,outbytes);
#else /* RLOGOUTBUF */
        ttoc(0);                        /* Send an initial NUL as wake-up */
        /* Send each variable with the trailing NUL */
        rc = ttol(localuser,strlen((char *)localuser)+1);
        if (rc > 0)
          rc = ttol(remoteuser,strlen((char *)remoteuser)+1);
        if (rc > 0)
          rc = ttol(term_speed,strlen((char *)term_speed)+1);
#endif /* RLOGOUTBUF */

        /* Now we are supposed to get back a single NUL 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);
}
/* two control messages are defined:

   a double flag byte of 'o' indicates a one-byte message which is
   identical to what was once carried out of band.

   a double flag byte of 'q' indicates a zero-byte message.  This
   message is interpreted as two \377 data bytes.  This is just a
   quote rule so that binary data from the server does not confuse the
   client.  */

int
#ifdef CK_ANSIC
rlog_ctrl( unsigned char *cp, int n )
#else
rlog_ctrl(cp, n) unsigned char *cp; int n;
#endif /* CK_ANSIC */
{
    if ((n >= 5) && (cp[2] == 'o') && (cp[3] == 'o')) {
        if (rlog_oob(&cp[4],1))
            return(-5);
        return(5);
    } else if ((n >= 4) && (cp[2] == 'q') && (cp[3] == 'q')) {
        /* this is somewhat of a hack */
        cp[2] = '\377';
        cp[3] = '\377';
        return(2);
    }
    return(0);
}

static int
#ifdef CK_ANSIC
rlog_oob( CHAR * oobdata, int count )
#else
rlog_oob(oobdata, count) CHAR * oobdata; int count;
#endif /* CK_ANSIC */
{
    int i;
    int flush = 0;

    debug(F111,"rlogin out_of_band","count",count);

    for (i = 0; i<count; i++)   {
        debug(F101,"rlogin out_of_band","",oobdata[i]);
                if (oobdata[i] & 0x01)
                        continue;

        if (oobdata[i] & 0x02) { /* Flush Buffered Data not yet displayed */
            debug(F101,"rlogin Flush Buffered Data command","",oobdata[i]);

            /* Only flush the data if in fact we are in a mode that won't */
            /* get out of sync.  Ie, not when we are in protocol mode.    */
            switch ( what ) {
            case W_NOTHING:
            case W_CONNECT:
            case W_COMMAND:
                if ( rlog_inband )
                    flush = 1;
                else
                    ttflui();
                break;
            }
        }
        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 */
            if ( !TELOPT_ME(TELOPT_NAWS) ) {
                TELOPT_ME(TELOPT_NAWS) = 1;
                rlog_naws();
            }
        }
    }
    return(flush);
}
#ifndef TCPIPLIB
static SIGTYP
#ifdef CK_ANSIC
rlogoobh( int sig )
#else
rlogoobh(sig) int sig;
#endif /* CK_ANSIC */
{
#ifdef SOLARIS
    char                                /* Or should it be char for all? */
#else
    CHAR
#endif /* SOLARIS */
      oobdata;

    /* int  count = 0; */ /* (not used) */

    while (recv(ttyfd, &oobdata, 1, MSG_OOB) < 0) {
      /*
       * We need to do some special processing here.
       * Just in case the socket is blocked for input
       *
       */
        switch (errno) {
          case EWOULDBLOCK:
            break;
          default:
            return;
        }
    }
    debug(F101,"rlogin out_of_band","",oobdata);
    if (oobdata == 0x02) {      /* Flush Buffered Data not yet displayed */
        debug(F101,"rlogin Flush Buffered Data command","",oobdata);
        netflui();
    }
    if (oobdata & 0x10) {               /* Switch to raw mode */
        debug(F101,"rlogin Raw Mode command","",oobdata);
        rlog_mode = RL_RAW;
    }
    if (oobdata & 0x20) {               /* Switch to cooked mode */
        debug(F101,"rlogin Cooked Mode command","",oobdata);
        rlog_mode = RL_COOKED;
    }
    if (oobdata & 0x80) {                 /* Send Window Size Info */
        debug(F101,"rlogin Window Size command","",oobdata);
        /* Remember to send WS Info when Window Size changes */
        if ( !TELOPT_ME(TELOPT_NAWS) ) {
            TELOPT_ME(TELOPT_NAWS) = 1;
            rlog_naws();
        }
    }
}
#endif /* TCPIPLIB */
#endif /* RLOGCODE */
#endif /* NORLOGIN */

/* Send network BREAK */
/*
  Returns -1 on error, 0 if nothing happens, 1 if BREAK sent successfully.
*/
int
netbreak() {
    CHAR buf[3];
    if (ttnet == NET_TCPB) {
        if (ttnproto == NP_TELNET) {
#ifdef TNCODE
            buf[0] = (CHAR) IAC; buf[1] = (CHAR) BREAK; buf[2] = (CHAR) 0;
            if (
#ifdef OS2
                nettol((char *) buf, 2)
#else
                ttol(buf, 2)
#endif /* OS2 */
                < 2)
              return(-1);
            if (tn_deb || debses || deblog) {
                extern char tn_msg[];
                ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET SENT ",TELCMD(BREAK),
                          NULL,NULL);
                debug(F101,tn_msg,"",BREAK);
                if (debses || tn_deb) tn_debug(tn_msg);
            }
            return(1);
#else
            debug(F100,"netbreak no TNCODE","",0);
            return(0);
#endif /* TNCODE */
        }
        /* Insert other TCP/IP protocols here */
    }
    /* Insert other networks here */
    return(0);
}
#endif /* NETCONN */


#ifdef NETCONN
#ifdef SUNX25
/*
  SunLink X.25 support by Marcello Frutig, Catholic University,
  Rio de Janeiro, Brazil, 1990.
*/

/* 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[];

VOID
initpad() {
  padparms[PAD_BREAK_CHARACTER]        = 0;  /* 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 */
}

/* Set PAD parameters */

VOID
setpad(s,n) CHAR *s; int n; {
    int i;
    CHAR *ps = s;

    if (n < 1) {
        initpad();
    } else {
        for (i = 0; i < n; i++) {
            if (*ps > MAXPADPARMS)
              x29err[i+2] = *ps;
            else
              padparms[*ps] = *(ps+1);
            ps += 2;
        }
    }
}

/* Read PAD parameters */

VOID
readpad(s,n,r) CHAR *s; int n; CHAR *r; {
    int i;
    CHAR *ps = s;
    CHAR *pr = r;

    *pr++ = X29_PARAMETER_INDICATION;
    if (n > 0) {
        for (i = 0; i < n; i++, ps++) {
            if (*ps > MAXPADPARMS) {
                x29err[i+2] = *ps++;
            } else {
                *pr++ = *ps;
                *pr++ = padparms[*ps++];
            }
        }
    } else {
        for (i = 1; i < MAXPADPARMS; i++) {
            *pr++ = i;
            *pr++ = padparms[i];
        }
    }
}

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

    switch (x29cmd) {

        case X29_SET_PARMS:
            setpad (ps+1,n/2);
            if ((int)strlen((char *)x29err) > 2) {
                ttol(x29err,(int)strlen((char *)x29err));
                x29err[2] = '\0';
            }
            return (-2);
        case X29_READ_PARMS:
            readpad (ps+1,n/2,x29resp);
            setqbit ();
            ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1));
            if ((int)strlen((char *)x29err) > 2) {
                ttol(x29err,(int)strlen((char *)x29err));
                x29err[2] = '\0';
            }
            resetqbit();
            break;
        case X29_SET_AND_READ_PARMS:
            setpad (ps+1,n/2);
            readpad (ps+1,n/2,x29resp);
            setqbit();
            ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1));
            if ((int)strlen((char *)x29err) > 2) {
                ttol (x29err,(int)strlen((char *)x29err));
                x29err [2] = '\0';
            }
            resetqbit();
            return (-2);
        case X29_INVITATION_TO_CLEAR:
            (VOID) x25clear();
            return (-1);
        case X29_INDICATION_OF_BREAK:
            break;
    }
    return (0);
}

/* PAD break action processor */

VOID
breakact() {
    extern char x25obuf[MAXOX25];
    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((CHAR *)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,2);    /* set pad to discard input */
                setqbit ();
                /* send indication of break with parameter field */
                ttol (indbrk,sizeof(indbrk));
                resetqbit ();
                break;
     }
}

/* X.25 support functions */

X25_CAUSE_DIAG diag;

/*
  Convert a null-terminated string representing an X.121 address
  to a packed BCD form.
*/
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);
}

/* Reads and prints X.25 diagnostic */

int
x25diag () {
    int i;

    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 < (int)diag.datalen; i++)
          printf(" %02h",diag.data[i])+
        printf ("\r\n");
    }
    return(0);
}

/* X.25 Out-of-Band Signal Handler */

SIGTYP
x25oobh(foo) int foo; {
    int oobtype;
    u_char oobdata;
    int t;

    (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,(char *)&oobdata,1,MSG_OOB) < 0) {
                perror ("Receiving X.25 interrupt data");
                return;
            }
            t = oobdata;
            printf ("\r\nInterrupt received, data = %d\r\n", t);
            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);
}

/* Send a X.25 interrupt packet */

int
#ifdef CK_ANSIC
x25intr(char intr)
#else
x25intr(intr) char intr;
#endif /* CK_ANSIC */
/* x25intr */ {
    if (send(ttyfd,&intr,1,MSG_OOB) < 0) return(-1);
    debug(F100,"X.25 intr","",0);
    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 */ {
    bzero ((char *)&diag,sizeof(diag));
    diag.flags   = 0;
    diag.datalen = 2;
    diag.data[0] = cause;
    diag.data[1] = diagn;
    if (ioctl(ttyfd,X25_WR_CAUSE_DIAG,&diag) < 0)
      return(-1);
    debug(F100,"X.25 reset","",0);
    return(0);
}

/* Clear X.25 virtual circuit */
int
x25clear() {
    int i;
    debug(F100,"X.25 clear","",0);
    bzero ((char *)&diag,sizeof(diag));
    diag.flags = (1 << DIAG_TYPE);
    diag.datalen = 2;
    diag.data[0] = 0;
    diag.data[1] = 0;
    ioctl (ttyfd,X25_WR_CAUSE_DIAG,&diag); /* Send Clear Request */
    return(ttclos(0));                  /* Close socket */
}

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

/* Set Q_BIT on */
VOID
setqbit() {
    static int qbiton = 1 << Q_BIT;
    ioctl (ttyfd,X25_SEND_TYPE,&qbiton);
}

/* Set Q_BIT off */
VOID
resetqbit() {
    static int qbitoff = 0;
    ioctl (ttyfd,X25_SEND_TYPE,&qbitoff);
}

/* Read n characters from X.25 circuit into buf */

int
x25xin(n,buf) int n; CHAR *buf; {
    register int x, c;
    int qpkt;

    do {
        x = read(ttyfd,buf,n);
        if (buf[0] & (1 << Q_BIT)) { /* If Q_BIT packet, process it */
            /* If return -1 : invitation to clear; -2 : PAD changes */
            if ((c=qbitpkt(buf+1,x-2)) < 0) return(c);
            qpkt = 1;
        } else qpkt = 0;
    } while (qpkt);

    if (x < 1) x = -1;
    debug(F101,"x25xin x","",x);

    return(x);
}

#endif /* SUNX25 */

#ifdef IBMX25
/*
 * IBM X25 support - using the NPI streams interface
 * written by Stephen Riehm, pc-plus, Munich Germany
 */

/* riehm: missing functions / TODO list */

/*
  x25intr() - Send an interrupt packet
*/

/* return an error message depending on packet type */
char *
x25err(n) int n; {
    static char buf[30];
    switch (n) {
      case NBADADDR:     return "invalid address";
      case NBADOPT:      return "invalid options";
      case NACCESS:      return "no permission";
      case NNOADDR:      return "unable to allocate address";
      case NOUTSTATE:    return "invalid state";
      case NBADSEQ:      return "invalid sequence number";
      case NSYSERR:      return "system error";
      case NBADDATA:     return "invalid data size";
      case NBADFLAG:     return "invalid flag";
      case NNOTSUPPORT:  return "unsupported primitive";
      case NBOUND:       return "address in use";
      case NBADQOSPARAM: return "bad QOS parameters";
      case NBADQOSTYPE:  return "bad QOS type";
      case NBADTOKEN:    return "bad token value";
      case NNOPROTOID:   return "protocol id could not be allocated";
      case NODDCUD:      return "odd length call user data";
      default:
        ckmakmsg(buf,sizeof(buf),"Unknown NPI error ",ckitoa(n),NULL,NULL);
        return buf;
    }
}

/* turn a meaningless primitive number into a meaningful primitive name */
char *
x25prim(n) int n; {
    static char buf[30];
    switch(n) {
      case N_BIND_ACK:     return "N_BIND_ACK";
      case N_BIND_REQ:     return "N_BIND_REQ";
      case N_CONN_CON:     return "N_CONN_CON";
      case N_CONN_IND:     return "N_CONN_IND";
      case N_CONN_REQ:     return "N_CONN_REQ";
      case N_CONN_RES:     return "N_CONN_RES";
      case N_DATACK_IND:   return "N_DATAACK_IND";
      case N_DATACK_REQ:   return "N_DATAACK_REQ";
      case N_DATA_IND:     return "N_DATA_IND";
      case N_DATA_REQ:     return "N_DATA_REQ";
      case N_DISCON_IND:   return "N_DISCON_IND";
      case N_DISCON_REQ:   return "N_DISCON_REQ";
      case N_ERROR_ACK:    return "N_ERROR_ACK";
      case N_EXDATA_IND:   return "N_EXDATA_IND";
      case N_EXDATA_REQ:   return "N_EXDATA_REQ";
      case N_INFO_ACK:     return "N_INFO_ACK";
      case N_INFO_REQ:     return "N_INFO_REQ";
      case N_OK_ACK:       return "N_OK_ACK";
      case N_OPTMGMT_REQ:  return "N_OPTMGMT_REQ";
      case N_RESET_CON:    return "N_RESET_CON";
      case N_RESET_IND:    return "N_RESET_IND";
      case N_RESET_REQ:    return "N_RESET_REQ";
      case N_RESET_RES:    return "N_RESET_RES";
      case N_UDERROR_IND:  return "N_UDERROR_IND";
      case N_UNBIND_REQ:   return "N_UNBIND_REQ";
      case N_UNITDATA_REQ: return "N_UNITDATA_REQ";
      case N_UNITDATA_IND: return "N_UNITDATA_IND";
      default:
        ckmakmsg(buf,sizeof(buf),"UNKNOWN (",ckitoa(n),")",NULL);
        return buf;
    }
}

/*****************************************************************************
 * Function: x25getmsg()
 * Description: get a STREAMS message, and check it for errors
 *
 * Parameters:
 * fd           - file descriptor to x25 device (opened)
 * control      - control buffer (pre-allocated)
 * ctl_size     - size of control buffer
 * data         - data buffer (pre-allocated)
 * data_size    - size of data buffer
 * flags        - flags for getmsg()
 * expected     - expected Primitive type
 *
 * Return Value:
 *      >= 0    OK (size of data returned)
 *      -1      error
 *
 */
int
x25getmsg( fd, control, ctl_size, data, data_size, get_flags, expected )
    int                 fd;             /* X25 device (opened) */
    N_npi_ctl_t         *control;       /* control buffer (pre-allocated) */
    int                 ctl_size;       /* size of control buffer */
    N_npi_data_t        *data;          /* data buffer (pre-allocated) */
    int                 data_size;      /* size of data buffer */
    int                 *get_flags;     /* getmsg() flags */
    int                 expected;       /* expected primitive type */
/* x25getmsg */ {
    int                 rc = 0;         /* return code */
    struct strbuf       *get_ctl=NULL;  /* getmsg control */
    struct strbuf       *get_data=NULL; /* getmsg data */
    int                 more = 0;       /* flag for more data etc */
    int                 file_status = -1; /* file async status */
    N_npi_ctl_t         * result;       /* pointer to simplify switch() */
    int                 packet_type = -1; /* unknown packet thus far */

#ifdef TRACE
    printf( "TRACE: entering x25getmsg\n" );
#endif /* TRACE */

    debug( F110, "x25getmsg waiting for packet ", x25prim( expected ), 0);
    /* prepare the control structures for getmsg */
    if (control) {
        if ((get_ctl = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL)
          {
              perror("kermit x25getmsg(): get_ctl malloc failed\n");
              debug( F100, "x25getmsg malloc failed for get_ctl\n", "", 0);
              return(-1);
          }
        /* allow getmsg to return an unexpected packet type (which may be
         * larger than the expected one)
         */
        get_ctl->maxlen = NPI_MAX_CTL;
        get_ctl->len = 0;
        get_ctl->buf = (char *)control;
    } else {
        printf(
 "kermit x25getmsg(): internal error. control buffer MUST be pre-allocated!\n"
               );
        debug(F100,"x25getmsg internal error. no buffer pre-allocated","",0);
        return( -1 );
    }
    if (data) {
        if ((get_data = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL)
          {
            perror("kermit x25getmsg(): get_data malloc failed\n");
            debug( F100, "x25getmsg malloc failed for get_data\n", "", 0);
            return(-1);
        }
        get_data->maxlen = (NPI_MAX_DATA < data_size ) ?
          NPI_MAX_DATA :
            data_size;
        get_data->len = 0;
        get_data->buf = (char *)data;
    }

    /* get an X.25 packet -
     * it may be any kind of packet, so check for special cases
     * it may be split into multiple parts - so loop if necessary
     */
    do {
#ifdef DEBUG
        printf( "kermit: x25getmsg(): getting a message\n" );
#endif /* DEBUG */
        errno = 0;
        if ((more = getmsg(fd, get_ctl, get_data, get_flags)) < 0) {
#ifdef DEBUG
            printf( "kermit: x25getmsg(): getmsg returned an error\n" );
            perror( "getmsg error was" );
#endif /* DEBUG */
            debug(F101, "x25getmsg getmsg returned an error\n", "", errno);
            if ((errno == EAGAIN) && (get_data && (get_data->len > 0)) ) {
                /* was in non-blocking mode, nothing to get, but we're
                 * already waiting for the rest of the packet -
                 * switch to blocking mode for the next read.
                 * file_status used to reset file status before returning
                 */
                if ((file_status = fcntl(fd, F_GETFL, 0)) < 0
                    || fcntl(fd, F_SETFL, file_status & ~O_NDELAY) < 0)
                  {
                      perror("x25getmsg(): couldn't change x25 blocking mode");
                      debug(F101,
                            "x25getmsg fcntl returned an error\n", "", errno);
                      /* netclos(); */
                      rc = -1;
                      break;
                  } else {
                      /* loop again into a blocking getmsg() */
                      continue;
                  }
            } else {
                /* no data to get in non-blocking mode - return empty handed */
                perror( "x25getmsg(): getmsg failed" );
                debug(F101,"x25getmsg getmsg returned an error\n", "", errno);
                rc = -1;
                break;
            }
        } else if (more & MORECTL) {
            /* panic - the control information was larger than the
             * maximum control buffer size!
             */
            /* riehm: close connection? */
#ifdef DEBUG
            printf("x25getmsg(): received partial control packet - panic\n");
#endif /* DEBUG */
            debug( F101, "x25getmsg getmsg bad control block\n", "", errno);
            rc = -1;
            break;
        }

        if (result = (N_npi_ctl_t *)control) {
            packet_type = result->bind_ack.PRIM_type;
            if (packet_type != N_OK_ACK) {
                x25lastmsg = packet_type;
            }
        }
#ifdef DEBUG
        /* printf( "kermit: x25getmsg(): getting " ); */
        if (get_ctl->len > 0) {
            x25dump_prim(result);
        }
        debug(F110,
              "x25getmsg got packet ",
              x25prim( result->bind_ack.PRIM_type ),
              0
              );
#endif /* DEBUG */

        if (get_ctl->len >= (int)sizeof(result->bind_ack.PRIM_type)) {
            /* not as pretty as a switch(), but switch can't handle
             * runtime variable values :-(
             */
            if (packet_type == expected ) {
                /* got what we wanted, special case for DATA_IND
                 * packets though */
                /* riehm: check Q-bit ? */
#ifdef DEBUG
                printf("x25getmsg(): got expected packet\nrc is %d\n", rc);
#endif /* DEBUG */
                if (packet_type == N_DATA_IND ) {
                    /* data received. May be incomplete, even though
                     * getmsg returned OK
                     */
                    if (result->data_ind.DATA_xfer_flags & N_MORE_DATA_FLAG)
                        more |= MOREDATA;
                    if (result->data_ind.DATA_xfer_flags & N_RC_FLAG)
                        printf( "x25getmsg(): data packet wants ack\n" );
                }
            } else if( packet_type == N_DISCON_IND) {
                printf( "X25 diconnected\n" );
                /* riehm: need to acknowledge a disconnection? */
                x25clear();
                /* x25unbind( ttyfd ); */
                rc = -1;
            } else if( packet_type == N_ERROR_ACK) {
                errno = result->error_ack.UNIX_error;
                perror( "X25 error received" );
                rc = -1;
            } else {
                printf("x25getmsg(): failed %s\n", x25err(packet_type));
                rc = -1;
            }
        }

        if (get_data && (get_data->len >= 0)) {
            get_data->buf += get_data->len;
            get_data->maxlen -= get_data->len;
        }
    } while ((rc == 0)
             && (get_data && (get_data->maxlen > 0))
             && (more & MOREDATA)
             );

    /* return the file status to its original value, unless its still
     * set to -1, or one of the fcntl's failed */
    if ((file_status >= 0) && fcntl(fd, F_SETFL, file_status) < 0)
        rc = -1;

    /*
     * Verify that we received an expected primitive
     * there is apparantly an error case where the primitive is set
     * correctly, but there is not enough data in the control structure
     */
    if ((packet_type != expected) && (get_ctl->len >= ctl_size) ) {
        fprintf(stderr,
                "x25getmsg(): %s NOT received. Primitive received was %s\n",
                x25prim( expected ), x25prim( packet_type ));
        debug(F110, "x25getmsg got an unexpected packet ",
              x25prim(packet_type),
              0
              );
        rc = -1;
    }

    if (rc == 0) {
        if (get_data && ( get_data->len >= 0)) {
            rc = get_data->len;
        }
    }

    if (get_ctl)  { free(get_ctl); get_ctl = NULL; }
    if (get_data) { free(get_data); get_data = NULL; }

    debug(F110, "x25getmsg returning packet ", x25prim( packet_type ), 0);

#ifdef TRACE
    printf( "TRACE: leaving x25getmsg\n" );
#endif /* TRACE */
    return(rc);
}

/*****************************************************************************
 * Function: x25putmsg()
 *
 * Description:
 *      send a message to a X25 STREAM
 *
 * Parameters:
 *      fd              - file descriptor to x25 device (opened)
 *      control         - control buffer (pre-allocated)
 *      data            - data buffer (pre-allocated)
 *      data_len        - length of data to be transmitted
 *      put_flags       - flags for putmsg()
 *
 * Return Value:
 *      >= 0    number of bytes transmitted
 *      -1      error
 */
int
x25putmsg(fd, control, data, data_len, put_flags)
    int                 fd;             /* X25 device (opened) */
    N_npi_ctl_t         *control;       /* control buffer (pre-allocated) */
    N_npi_data_t        *data;          /* data buffer (pre-allocated) */
    int                 data_len;       /* length of data (not the size of
                                           the buffer) */
    int                 *put_flags;     /* putmsg() flags */
/* x25putmsg */ {
    int                 rc = 0;         /* return code */
    ulong               type;           /* primitive type */
    struct strbuf       *put_ctl = NULL; /* putmsg control */
    struct strbuf       *put_data = NULL; /* putmsg data */

#ifdef TRACE
    printf( "TRACE: entering x25putmsg\n" );
#endif /* TRACE */

#ifdef DEBUG
    printf( "kermit: x25putmsg(): putting " );
    x25dump_prim( control );
    printf( "\tdata:\t\t" );
    x25dump_data( data, 0, data_len );
    debug(F110,"x25putmsg: putting packet ",x25prim(control->PRIM_type),0);
#endif /* DEBUG */

    if (control) {
        put_ctl = (struct strbuf *)malloc( sizeof( struct strbuf ) );
        if (put_ctl == NULL) {
            perror("kermit x25putmsg(): put_ctl malloc failed\n");
            return(-1);
        }
        put_ctl->maxlen = 0;                    /* unused by putmsg */
        put_ctl->len = NPI_MAX_CTL;
        put_ctl->buf = (char *)control;
    }
    if (data && ( data_len > 0)) {
        put_data = (struct strbuf *)malloc( sizeof( struct strbuf ) );
        if( put_data == NULL) {
            perror("kermit x25putmsg(): put_data malloc failed\n");
            return(-1);
        }
        put_data->maxlen = 0;                   /* unused by putmsg */
        put_data->len = data_len;
        put_data->buf = (char *)data;
    }

    errno = 0;
    rc = putmsg (fd, put_ctl, put_data, 0);
    if (rc < 0) {
        printf("x25putmsg(): couldn't put %s\n",x25prim(control->PRIM_type));
        perror("kermit: x25putmsg(): putmsg failed");
        return(-1);
    }

    /* riehm: this should perhaps be discounted! */
    x25lastmsg = control->PRIM_type;

    debug( F101, "x25putmsg block size put ", "", data_len);

#ifdef TRACE
    printf( "TRACE: leaving x25putmsg\n" );
#endif /* TRACE */

    return( data_len );
}

/*****************************************************************************
* Function: x25bind
* Description:  The bind submitted to NPI provides the information required
*               by the packet layer for it to listen for suitable incoming
*               calls.
*
* WARNING:
*
* This routine needs to be called in a completely different manner for
* the client and server side. When starting a client, the
* num_waiting_calls and CUD information should all be set to 0! The
* client's CUD must be inserted in the CONN_REQ data block.
* When starting a server, the CUD must be set to a CUD pattern, and
* the number of waiting calls should be set to a number other than 0.
* (num waiting calls is the number of incomming calls which are to be
* put on hold while the server is servicing another client.)
*
* Who invented this crap?
*
* Parameters:
*       fd              - X25 device (opened)
*       addr            - local address
*       cud             - User Data (null terminated)
*       cud_len         - User Data length
*       num_waiting_calls - number of outstanding calls allowed on this stream
*       line            - logical port number (1)
*       flags           - 0, DEFAULT_LISTENER or TOKEN_REQUEST
*
* Return Value:
*       if binding is successful, 0 is returned for a client, and a token is
*       returned for a server
*
* Return code: 0 if successful
*              -1 if unsuccessful
*****************************************************************************/

ulong
x25bind(fd, addr, cud, cud_len, num_waiting_calls, line, bind_flags)
    int fd;                             /* X25 device (opened) */
    char * addr;                        /* local address */
    char * cud;                         /* Call User Data (null terminated) */
    int cud_len;                        /* User Data length */
    int num_waiting_calls;              /* Outstanding calls allowed */
    int line;                           /* logical port number */
    ulong bind_flags;           /* 0, DEFAULT_LISTENER or TOKEN_REQUEST */
/* x25bind */ {
    ulong rc;                           /* return code */
    int get_flags;                      /* priority flag passed to getmsg */
    int put_flags = 0;                  /* output flags for putmsg, always 0 */
    ulong type;                         /* primitive type */
    N_bind_req_t *bind_req;             /* pointer to N_BIND_REQ primitive */
    N_bind_ack_t *bind_ack;             /* pointer to N_BIND_ACK primitive */
    char *addtl_info;                   /* pointer to info in addition to
                                         * the N_BIND_REQ primitive that is
                                         * passed in the control structure
                                         * to putmsg */
    int addr_len = 0;                   /* length of address string */
    ulong bind_req_t_size;              /* for debugging only */

#ifdef TRACE
    printf("TRACE: entering x25bind\n" );
#endif /* TRACE */

#ifdef DEBUG
    printf("TRACE: x25bind( %d, %s, %s, %d, %d )\n",
           fd, addr, cud, line, bind_flags
           );
#endif /* DEBUG */

    /*
     * Allocate  and zero out space to hold the control portion of the
     * message passed to putmsg. This will contain the N_BIND_REQ
     * primitive and any additional info required for that.
     *
     * Note: allocated space is the size of the union typedef
     * N_npi_ctl_t to allow the use fo the generic x25putmsg routine.
     */
    bind_req = (N_bind_req_t *) malloc(sizeof( N_npi_ctl_t));
    if (bind_req == NULL) {
        perror("kermit: x25bind(): bind_req malloc failed");
        debug(F100, "x25bind bind_req malloc failed", "", 0);
        return(-1);
    }
    bzero((char *)bind_req, sizeof(N_npi_ctl_t));

    /* Build the Bind Request Primitive */
    bind_req->PRIM_type = (ulong) N_BIND_REQ;

    /* Note that the address length is n+2 and NOT n. Two bytes MUST preceed
     * the actual address in an N_BIND_REQ. The first byte contains the
     * line number being used with this address, and the second byte is the
     * X.121 address prefix, which must be zero.
     */
    addr_len = strlen(addr);
    bind_req->ADDR_length = (ulong) (addr_len + 2);
    bind_req->ADDR_offset = (ulong)(sizeof(N_bind_req_t));
    bind_req->CONIND_number = (ulong)num_waiting_calls; /* server only */
    bind_req->BIND_flags = (ulong) bind_flags; /* 0 in client */
    bind_req->PROTOID_length = (ulong) cud_len; /* 0 in client */
    if (cud_len == 0) {
        bind_req->PROTOID_offset = (ulong) 0;
    } else {
        /* need to remember the trailing NULL in the address - not
         * counted in the address length
         */
        bind_req->PROTOID_offset
          = (ulong) (sizeof(N_bind_req_t) + bind_req->ADDR_length);
    }

    /*
     * Now fill in the additional information required with this primitive
     * (address and protocol information (Call User Data))
     */
    addtl_info = (char *) ((void *)bind_req + bind_req->ADDR_offset);
    /*
     * The bitwise "&" ensures that the line number is only one byte long
     */
    *addtl_info++ = (char) line & 0xff;
    *addtl_info++ = (char) 0; /* X.121 format */
    bcopy( addr, addtl_info, addr_len ); /* include trailing null */
    addtl_info += addr_len;
    if (cud_len > 0)
      bcopy( cud, addtl_info, cud_len );
    /*
     * Call putmsg() to put the bind request message on the stream
     */
    if (x25putmsg(fd,
                  (N_npi_ctl_t*)bind_req,
                  (N_npi_data_t *)NULL,
                  0,
                  &put_flags
                  ) < 0) {
        printf( "kermit: x25bind(): x25putmsg failed\n" );
        return(-1);
    }

    /*
     * Allocate and zero out space for the N_BIND_ACK primitive
     */
    bind_ack = (N_bind_ack_t *) malloc(sizeof(N_npi_ctl_t));
    if (bind_ack == NULL){
        perror("kermit: x25bind(): bind_ack malloc failed");
        return(-1);
    }
    bzero(bind_ack, sizeof(N_npi_ctl_t));
    /*
     * Initialize the control structure and flag variable sent to getmsg
     */
    get_flags=0;

    /* get the ACK for the bind */
#ifdef DEBUG
    printf( "kermit: x25bind() trying to get a BIND_ACK\n" );
#endif /* DEBUG */
    rc = (ulong)x25getmsg( fd, (N_npi_ctl_t*)bind_ack,
            (int)sizeof( N_bind_ack_t ), (N_npi_data_t*)NULL, 0, &get_flags,
            N_BIND_ACK );

    /* turn quantitive return code into a qualitative one */
    if (rc > 0) rc = 0;

    /* if all went well, get the token from the acknowledgement packet */
    if ((bind_flags & TOKEN_REQUEST ) && ( rc >= 0)) {
        rc = bind_ack->TOKEN_value;
    }

    /* free up the memory we allocated earlier */
    free(bind_req);
    free(bind_ack);

#ifdef TRACE
    printf( "TRACE: leaving x25bind\n" );
#endif /* TRACE */

    return( rc );
}

/*****************************************************************************
* Function: x25call
* Description:  This routine builds and sends an N_CONN_REQ primitive, then
*               checks for an N_CONN_CON primitive in return.
*
* Parameters:
* fd    - file descriptor of stream
* caddr - called address (remote address)
*
* Functions Referenced:
* malloc()
* bzero()
* getmsg()
* putmsg()
*
* Return code:
* 0 - if successful
* -1 if not successful
*****************************************************************************/
int
x25call(fd, remote_nua, cud)
    int fd;                             /* X25 device (opened) */
    char * remote_nua;                  /* remote address to call */
    char * cud;                         /* call user data */
/* x25call */ {
    int rc;                             /* return code */
    int flags;                          /* Connection flags */
    int get_flags;                      /* priority flags for getmsg */
    ulong type;                         /* primitive type */
    N_conn_req_t *connreq_ctl;          /* pointer to N_CONN_REQ primitive */
    N_npi_data_t *connreq_data;         /* pointer to N_CONN_REQ data (CUD) */
    int connreq_data_len;               /* length of filled data buffer */
    N_conn_con_t *conncon_ctl;          /* pointer to N_CONN_CON primitive */
    N_npi_data_t *conncon_data;         /* pointer to any data associated with
                                         * the N_CONN_CON primitive */
    char *addtl_info;                   /* pointer to additional info needed
                                         * for N_CONN_REQ primitive */
    int addr_len;                       /* length of address */

#ifdef TRACE
    printf( "TRACE: entering x25call\n" );
#endif /* TRACE */

#ifdef DEBUG
    printf( "x25call( %d, %s )\n", fd, remote_nua );
    printf( "connecting to %s on fd %d\n", remote_nua, fd );
#endif /* DEBUG */

    /*
     * Allocate and zero out space for the N_CONN_REQ primitive
     * use the size of the generic NPI primitive control buffer
     */
    connreq_ctl  = (N_conn_req_t *) malloc(sizeof(N_npi_ctl_t));
    if (connreq_ctl == NULL){
        perror("kermit: x25call(): connreq_ctl malloc failed");
        return(-1);
    }
    bzero(connreq_ctl,sizeof(N_npi_ctl_t));
    /*
     * Build the Connection Request Primitive
     */
    flags = 0;
    connreq_ctl->PRIM_type = (ulong) N_CONN_REQ;

    /* Note that the address length is nchai+1 and not n+2. The line number
     * is only passed with the address for the bind. The first byte of
     * the address for the N_CONN primitives contains the X.121
     * address prefix, which must be zero. The remaining bytes are the
     * address itself.
     */
    addr_len = strlen( remote_nua );
    connreq_ctl->DEST_length = (ulong) (addr_len + 1);
    connreq_ctl->DEST_offset = (ulong) sizeof(N_conn_req_t);
    /* connreq_ctl->CONN_flags = (ulong)EX_DATA_OPT | REC_CONF_OPT; */
    connreq_ctl->CONN_flags = (ulong) 0;
    connreq_ctl->QOS_length = (ulong) 0;        /* unsupported in AIX 4.1 */
    connreq_ctl->QOS_offset = (ulong) 0;        /* unsupported in AIX 4.1 */

    addtl_info = (char *) ((void*)connreq_ctl + connreq_ctl->DEST_offset);
    *addtl_info++ = (char) 0; /* X.121 format */
    bcopy( remote_nua, addtl_info, addr_len );

    /*
     * setup the data buffer for the connection request
     */
    connreq_data  = (N_npi_data_t *) malloc(sizeof(N_npi_data_t));
    if (connreq_data == NULL){
        perror("kermit: x25call(): connreq_data malloc failed");
        return(-1);
    }
    bzero(connreq_data,sizeof(N_npi_data_t));

    /* facility selection needs to be put in the front of connreq_data */
    connreq_data_len = 0;
    connreq_data_len += x25facilities( (char *)connreq_data );
    if (cud && *cud) {
        bcopy(cud,
              (char *)((char *)connreq_data + connreq_data_len),
              strlen(cud)
              );
        connreq_data_len += strlen( cud );
        }

    /*
     * Call putmsg() to put the connection request message on the stream
     */
    rc = x25putmsg( fd, (N_npi_ctl_t*)connreq_ctl, connreq_data,
            connreq_data_len, &flags );
    if (rc < 0) {
        return(-1);
    }

    /*
     * Allocate and zero out space for the N_CONN_CON primitive
     */
    if ((conncon_ctl = (N_conn_con_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) {
        perror("kermit: x25call(): conncon_ctl malloc failed");
        return(-1);
    }
    bzero(conncon_ctl, sizeof(N_npi_ctl_t));

    /*
     * Allocate and zero out space for any data associated with N_CONN_CON
     */
    if ( (conncon_data = (N_npi_data_t *) malloc(NPI_MAX_DATA)) == NULL) {
        perror("kermit: x25call(): conncon_data malloc failed");
        return(-1);
    }
    bzero(conncon_data, NPI_MAX_DATA);

    /* Initialize and build the structures for getmsg */
    get_flags=0;

    rc = x25getmsg( fd, (N_npi_ctl_t*)conncon_ctl, (int)sizeof( N_conn_con_t ),
            conncon_data, NPI_MAX_DATA, &get_flags, N_CONN_CON );

    /* turn quantitive return code into a qualitative one */
    if (rc > 0) rc = 0;

    /* Free the space that we no longer need */
    if (connreq_ctl) { free(connreq_ctl); connreq_ctl = NULL; }
    if (conncon_ctl) { free(conncon_ctl); conncon_ctl = NULL; }
    if (conncon_data) { free(conncon_data); conncon_data = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25call\n" );
#endif /* TRACE */

    return(rc);
}

/*****************************************************************************
 * Function: x25getcall
 *
 * Description: This routine checks for an incomming call, verified
 * that it is a CONNIND (connection indication) message, and then
 * accepts the call and returns the file descriptor of the new stream
 *
 * Parameters:
 * fd   - file descriptor of listening stream
 *
 * Return Codes:
 * callfd       - file descriptor of connected incomming call.
 *              - set to -1 if an error occured
 *
 *****************************************************************************/
int
x25getcall(fd) int fd; {
    int x25callfd;                      /* fd of incomming call */
    N_conn_ind_t *connind_ctl;          /* connind controll buffer */
    N_npi_data_t *connind_data;         /* connind data buffer */
    int get_flags;                      /* flags for getmsg */
    ulong flags;                        /* connection flags */
    int rc;                             /* return code */

    extern x25addr_t remote_nua;        /* remote X.25 addr global var */

#ifdef TRACE
    printf( "TRACE: entering x25getcall\n" );
#endif /* TRACE */

    /* allocate space for connection indication buffers */
    if ((connind_ctl = (N_conn_ind_t *)malloc(sizeof(N_npi_ctl_t))) == NULL) {
        perror("kermit: x25getcall(): connind_ctl malloc failed");
        return (-1);
    }
    bzero(connind_ctl, sizeof(N_npi_ctl_t));

    if ((connind_data = (N_npi_data_t *)malloc(NPI_MAX_DATA)) == NULL) {
        perror("kermit: x25getcall(): connind_data malloc failed");
        return (-1);
    }
    bzero(connind_data, NPI_MAX_DATA);

    /* initialise control structures */
    get_flags = 0;

    /* call getmsg to check for a connection indication */
    if (x25getmsg(fd,
                  (N_npi_ctl_t*)connind_ctl,
                  (int)sizeof(N_conn_ind_t),
                  connind_data,
                  NPI_MAX_DATA,
                  &get_flags,
                  N_CONN_IND
                  ) < 0) {
#ifdef DEBUG
        printf( "x25getcall(): errno is: %d\n", errno );
#endif /* DEBUG */
        perror ("x25getcall(): getmsg failed");
        return(-1);
    }

    /* a connection indication was received
     * - pull it to bits and answer the call
     */
    x25seqno = connind_ctl->SEQ_number;
    flags = connind_ctl->CONN_flags;
#ifdef DEBUG
    printf( "setting remote_nua to a new value due to incomming call\n" );
#endif /* DEBUG */
    /*
     * no guarantee that the address is null terminated, ensure that
     * after copying that it is (assumption: remote_nua is longer than
     * the address + 1)
     */
    bzero(remote_nua, sizeof(remote_nua));
    /* note: connind_ctl contains a x121 address, which has a null as
     * the FIRST character - strip it off!
     */
    ckstrncpy(remote_nua,
            (char*)((char*)connind_ctl + connind_ctl->SRC_offset + 1),
            connind_ctl->SRC_length - 1
            );
#ifdef DEBUG
    printf( "remote_nua set to new value of %s\n", remote_nua );
#endif /* DEBUG */

    /* errors handled by callee */
    x25callfd = x25accept(x25seqno, flags);

    /* free the malloc'd buffers */
    if (connind_ctl) { free(connind_ctl); connind_ctl = NULL; }
    if (connind_data) { free(connind_data); connind_data = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25getcall\n" );
#endif /* TRACE */

    /* return the file descriptor (or error if < 0) */
    return( x25callfd );
}

/*****************************************************************************
 * Function: x25accept
 *
 * Description: accept an incomming call
 *              This essentially means opening a new STREAM and sending
 *              an acknowledge back to the caller.
 *
 * Parameters:
 *      seqno   - sequence number for acknowledgement
 *      flags   - flags passed to us by the caller
 *
 * Return Codes:
 *      fd      - file descriptor of new STREAM
 *                set to -1 if an error occured
 *
 *****************************************************************************/
int
x25accept(seqno,flags)
    ulong seqno;                        /* connection sequence number */
    ulong flags;                        /* connection flags */
/* x25accept */ {
    int x25callfd;                      /* fd for incomming call */
    int get_flags;                      /* priority flags for getmsg */
    int put_flags = 0;                  /* flags for putmsg, always 0 */
    int addr_len;                       /* length of local address */
    ulong token;                        /* connection token */
    N_conn_res_t *conn_res;             /* N_CONN_RES primitive */
    N_ok_ack_t *ok_ack;                 /* N_OK_ACK primitive */
    char *addtl_info;                   /* temp pointer */
    int rc;                             /* temporary return code */

/* global variables from ckcmai.c */
    extern int revcall, closgr, cudata;
    extern char udata[];
    extern x25addr_t local_nua;         /* local X.25 address */
    extern char x25name[];              /* x25 device name (sx25a0) */
    extern char x25dev[];               /* x25 device file /dev/x25pkt */
    extern int x25port;                 /* logical port to use */
    ulong bind_flags = 0;               /* flags for binding the X25 stream */

#ifdef TRACE
    printf( "TRACE: entering x25accept\n" );
#endif /* TRACE */

    /* open a new packet level stream */
    if ((x25callfd = open(x25dev, O_RDWR)) < 0) {
        perror ("kermit: x25accept(): X.25 device open error");
        debug(F101,"x25accept() device open error","",errno);
        return(-1);
    }

    /* push the NPI onto the STREAM */
    if (ioctl(x25callfd,I_PUSH,"npi") < 0) {
        perror( "kermit: x25accept(): couldn't push npi on the X25 stream" );
        debug(F101,"x25accept can't push npi on the X25 stream","",errno);
        return (-1);
    }

    /* bind kermit server to the local X25 address */
    /* taken from /usr/samples/sx25/npi/npiserver.c (AIX 4) */
    bind_flags |= TOKEN_REQUEST;
    token = x25bind(x25callfd,local_nua,(char *)NULL,0,0,x25port,bind_flags);
    if (token < 0) {
        printf( "kermit: x25accept(): couldn't bind to local X25 address\n" );
        netclos();
        return(-1);
    }

    /* allocate connection response primitive */
    if ((conn_res = (N_conn_res_t *)malloc( NPI_MAX_CTL )) == NULL) {
        perror("kermit: x25accept(): conn_res malloc failed");
        return (-1);
    }
    bzero((char *)conn_res, NPI_MAX_CTL);

    /* setup connection response primitive */
    addr_len = strlen( local_nua );
    conn_res->PRIM_type = (ulong)N_CONN_RES;
    conn_res->TOKEN_value = token;
    /* note address length is n+1 to accomodate the X.121 address prefix */
    conn_res->RES_length = (ulong)(addr_len + 1);
    conn_res->RES_offset = (ulong)sizeof( N_conn_res_t );
    conn_res->SEQ_number = seqno;
    conn_res->CONN_flags = 0;
    conn_res->QOS_length = 0;           /* unsupported - must be 0 (!?) */
    conn_res->QOS_offset = 0;

    addtl_info = (char *)((char *)conn_res + conn_res->RES_offset);
    *addtl_info++ = (char)0;    /* X.121 address prefix */
    bcopy( local_nua, addtl_info, addr_len );

    /*
     * send off the connect response
     */
    if (x25putmsg(x25callfd,
                  (N_npi_ctl_t*)conn_res,
                  (N_npi_data_t *)NULL,
                  0,
                  &put_flags
                  ) < 0 ) {
        perror("kermit: x25accept(): putmsg connect response failed");
        return(-1);
    }

    /*
     * Allocate and zero out space for the OK_ACK primitive
     */
    if ((ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) {
        perror("kermit: x25call(): ok_ack malloc failed");
        return(-1);
    }
    bzero(ok_ack, sizeof(N_npi_ctl_t));

    /* Initialize and build the structures for getmsg */
    get_flags=0;

    rc = (int)x25getmsg(x25callfd,
                        (N_npi_ctl_t*)ok_ack,
                        (int)sizeof(N_ok_ack_t),
                        (N_npi_data_t*)NULL,
                        0,
                        &get_flags,
                        N_OK_ACK
                        );
    if (rc == 0) {
        /* sequence number is only for disconnecting when not connected !? */
        x25seqno = 0;
    }

    /* free up malloc'ed buffer space */
    if (conn_res) { free(conn_res); conn_res = NULL; }
    if (ok_ack) { free(ok_ack); ok_ack = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25accept\n" );
#endif /* TRACE */

    return( ( rc >= 0 ) ? x25callfd : -1 );
}

/*****************************************************************************
 * Function: x25unbind
 *
 * Description:  This subroutine builds and sends an unbind request and gets
 * the acknowledgement for it.
 *
 * Parameters:
 * fd - File descriptor of the stream
 *
 * Functions Referenced:
 * getmsg()
 * putmsg()
 * malloc()
 * bzero()
 *
 * Return code:
 * 0 - if successful
 * -1 - if not successful
 *****************************************************************************/
int
x25unbind(fd) int fd; {                 /* X25 device (opened) */
    int rc;                             /* return code */
    int flags;                          /* bind flags */
    int get_flags;                      /* priority flag for getmsg */
    ulong type;                         /* primitive type */
    N_unbind_req_t *unbind_req;         /* pointer to N_UNBIND_REQ */
    N_ok_ack_t *ok_ack;                 /* pointer to N_OK_ACK */

#ifdef TRACE
    printf( "TRACE: entering x25unbind\n" );
#endif /* TRACE */

#ifdef DEBUG
    /* printf( "x25unbind( %d )\n", fd ); */
#endif /* DEBUG */
    debug(F101,"x25unbind closing x25 connection #","",fd);

    /* Allocate and zero out space to hold the N_UNBIND_REQ primitive */
    unbind_req = (N_unbind_req_t *) malloc(sizeof(N_npi_ctl_t));
    if (unbind_req == NULL) {
        perror("kermit: x25unbind(): unbind_req malloc failed");
        return(-1);
    }
    bzero(unbind_req, sizeof(N_npi_ctl_t));

    /*
     * Build the Unbind Request Primitive
     */
    flags = 0;
    unbind_req->PRIM_type = (ulong) N_UNBIND_REQ;

    /*
     * Call putmsg() to put the bind request message on the stream
     */
    if (x25putmsg(fd,
                  (N_npi_ctl_t*)unbind_req,
                  (N_npi_data_t *)NULL,
                  0,
                  &flags
                  ) < 0) {
        perror ("kermit: x25unbind(): putmsg failed");
        return(-1);
    }

    /* Allocate and Zero out space for the N_OK_ACK primitive */
    ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t));
    if (ok_ack == NULL) {
        perror("kermit x25unbind(): ok_ack malloc failed\n");
        return(-1);
    }
    bzero(ok_ack, sizeof(N_npi_ctl_t));

    /* Initialize and build the control structure for getmsg */
    get_flags=0;

    /* Call getmsg() to check for an acknowledgement */
    rc = x25getmsg(fd,
                   (N_npi_ctl_t*)ok_ack,
                   (int)sizeof(N_ok_ack_t),
                   (N_npi_data_t*)NULL,
                   0,
                   &get_flags,
                   N_OK_ACK
                   );
    if (rc < 0) {
        perror ("kermit: x25unbind: getmsg failed");
        return(-1);
    }

    /* Free up the space that we no longer need */
    if (unbind_req) { free(unbind_req); unbind_req = NULL; }
    if (ok_ack) { free(ok_ack); ok_ack = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25unbind\n" );
#endif /* TRACE */

    return(0);
}

/*****************************************************************************
 * Function: x25xin
 *
 * Description:
 *      Read n characters from X.25 circuit into buf (AIX only)
 *
 * Parameters:
 *      data_buf_len    maximum size of data buffer
 *      data_buf        pointer to pre-allocated buffer space
 *
 * Return Value:
 *      the number of characters actually read
 */
int
x25xin(data_buf_len,data_buf) int data_buf_len; CHAR *data_buf; {
    struct strbuf getmsg_ctl;           /* streams control structure */
    struct strbuf getmsg_data;          /* streams data structure */
    int rc = 0;                         /* return code */
    int getmsg_flags;                   /* packet priority flags */
    char * ctl_buf;                     /* npi control buffer */
    N_npi_ctl_t * result;               /* pointer to simplify switch() */

#ifdef TRACE
    printf( "TRACE: entering x25xin\n" );
#endif /* TRACE */

    /* ensure that no maximum's are overridden */
    data_buf_len = (NPI_MAX_DATA < data_buf_len) ? NPI_MAX_DATA : data_buf_len;

    /* allocate space for packet control info */
    if ((ctl_buf = (char *)malloc(NPI_MAX_CTL)) == NULL) {
        perror( "kermit: x25xin(): ctl_buf malloc" );
        return(-1);
    }

    getmsg_flags = 0;                   /* get the first packet available */

    rc = x25getmsg(ttyfd,
                   ctl_buf,
                   NPI_MAX_CTL,
                   data_buf,
                   data_buf_len,
                   &getmsg_flags,
                   N_DATA_IND
                   );

    /* free buffers */
    if (ctl_buf) { free(ctl_buf); ctl_buf = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25xi\n" );
#endif /* TRACE */

    return(rc);
}

/*****************************************************************************
 * Function: x25write
 *
 * Description:
 *      write a block of characters to the X25 STREAM (AIX)
 *
 * Parameters:
 *      fd              file descriptor to write to
 *      databuf         buffer containing data to write
 *      databufsize             size of the buffer to write
 *
 * Return Value:
 *      size            the number of bytes actually transmitted
 */
int
x25write(fd, databuf, databufsize)
    int         fd;                  /* X25 STREAMS file descriptor (ttyfd) */
    char        *databuf;               /* buffer to write */
    int         databufsize;            /* buffer size */
/* x25write */ {
    N_data_req_t *data_req_ctl;
    int rc;                             /* return code (size transmitted) */
    int write_flags = 0;                /* always 0 !? */

#ifdef TRACE
    printf( "TRACE: entering x25write\n" );
#endif /* TRACE */

    if ((data_req_ctl = (N_data_req_t *)malloc(NPI_MAX_CTL) ) == NULL) {
        perror( "kermit: x25write(): data_req_ctl malloc" );
        return(-1);
    }
    data_req_ctl->PRIM_type = N_DATA_REQ;
    data_req_ctl->DATA_xfer_flags = 0;

    /* riehm: possible extension
     * possibly need to think about splitting up the data buffer
     * into multiple parts if databufsize > NPI_MAX_DATA
     */

    rc = x25putmsg(fd,
                   (N_npi_ctl_t*)data_req_ctl,
                   (N_npi_data_t*)databuf,
                   databufsize,
                   &write_flags
                   );
    if (data_req) { free(data_req_ctl);  data_req = NULL; }

#ifdef TRACE
    printf( "TRACE: leaving x25write\n" );
#endif /* TRACE */

    return(rc);
}

/*****************************************************************************
 * Function: x25local_nua
 *
 * Description:
 *      This routine is only interesting for IBM computers. In order
 *      to set up a connection (see x25bind()) you need to know the
 *      local NUA (x25 address). Unfortunately, you need all this code
 *      to find that out, I just hope this works for everyone else!
 *
 * Parameters:
 *      a pre-allocated character buffer, long enough to hold an X.25 address
 *      and the tailing null.
 *
 * Return Value:
 *      the length of the address string.
 *      0 = error
 */
int
x25local_nua(char *buf) {
    struct CuAt *response;      /* structure to fill with info from ODM */
    CLASS_SYMBOL retClass;      /* ODM class */
    char query[64];             /* odm database query */
    int rc = 0;                 /* return value (length of local NUA) */
    extern char x25name[];      /* x25 device name (sx25a0) */

#ifdef TRACE
    printf( "TRACE: entering x25local_nua\n" );
#endif /* TRACE */

    /* set up query string */
    if (x25name[0] == '\0') {
#ifdef DEBUG
        printf( "kermit: x25local_nua(): No x25 device set, trying sx25a0\n" );
#endif /* DEBUG */
        strcpy( x25name, "sx25a0" );
    }
    ckmakmsg(query, sizeof(query), "name like ",x25name,
             " and attribute like local_nua");

    /* initialise ODM database */
    odmerrno = 0;
    if (odm_initialize() == -1) {
        printf( "x25local_nua(): can't initialize ODM database");
        switch (odmerrno) {
          case ODMI_INVALID_PATH:
            printf( "invalid path\n" );
            break;
          case ODMI_MALLOC_ERR:
            printf( "malloc failed\n" );
            break;
          default:
            printf( "unknown error %d\nPlease call IBM\n", odmerrno );
        }
        return(rc);
    }

    /* open the CuAt class */
    retClass = odm_open_class(CuAt_CLASS);
    if (((int)retClass) == -1) {
        printf( "kermit: x25local_nua(): can't open CuAt class in odm. " );
        switch (odmerrno) {
          case ODMI_CLASS_DNE:
            printf( "CuAt class doesn't exist\n" );
            break;
          case ODMI_CLASS_PERMS:
            printf( "permission to CuAt class file denied\n" );
            break;
          case ODMI_MAGICNO_ERR:
            printf( "CuAt is an invalid ODM object class\n" );
            break;
          case ODMI_OPEN_ERR:
            printf( "cannot open CuAt class - and don't know why!\n" );
            break;
          case ODMI_INVALID_PATH:
            printf( "invalid path\n" );
            break;
          case ODMI_TOOMANYCLASSES:
            printf( "too many object classes have been opened\n" );
            break;
          default:
            printf( "unknown error %d\nPlease call IBM\n", odmerrno );
        }
        return(rc);
    }

#ifdef DEBUG
    printf("retClass= %d\n", retClass);
#endif /* DEBUG */

    response = (struct CuAt *)odm_get_first( retClass, query, NULL );
    if (((int)response) == -1) {
        printf( "kermit: x25local_nua(): odm query failed " );
        switch (odmerrno) {
          case ODMI_BAD_CRIT:           /* Programming error */
            printf( "bad search criteria\n" );
            break;
          case ODMI_CLASS_DNE:
            printf( "CuAt class doesn't exist\n" );
            break;
          case ODMI_CLASS_PERMS:
            printf( "permission to CuAt class file denied\n" );
            break;
          case ODMI_INTERNAL_ERR:
            printf("odm internal error\nPlease contact your administrator\n" );
            break;
          case ODMI_INVALID_CLXN:
            printf("CuAt is invalid or inconsistent odm class collection\n");
            break;
          case ODMI_INVALID_PATH:
            printf( "invalid path\n" );
            break;
          case ODMI_MAGICNO_ERR:
            printf( "CuAt is an invalid ODM object class\n" );
            break;
          case ODMI_MALLOC_ERR:
            printf( "malloc failed\n" );
            break;
          case ODMI_OPEN_ERR:
            printf( "cannot open CuAt class - and don't know why!\n" );
            break;
          case ODMI_TOOMANYCLASSES:
            printf( "too many object classes have been opened\n" );
            break;
          default:
            printf( "unknown error %d\nPlease call IBM\n", odmerrno );
        }
        return(rc);
    }

    /* check for a meaningfull response */
    if (response != NULL) {
        if (response->value != NULL) {
            strcpy(buf, response->value);
            rc = strlen( buf );
#ifdef DEBUG
/*
            printf( "attribute name is: %s\n", (char *)response->attribute );
            printf( "I think my address is %s\n", (char*)response->value );
*/
#endif /* DEBUG */
        } else {
            printf( "kermit: x25local_nua(): couldn't find the local NUA\n" );
        }
    } else {
        switch (odmerrno) {
          case ODMI_BAD_CRIT:
            printf( "Error: ODMI_BAD_CRIT - bad criteria\n" );
            break;
          case ODMI_CLASS_DNE:
            printf( "Error: ODMI_CLASS_DNE - class doesn't exist\n" );
            break;
          case ODMI_CLASS_PERMS:
            printf( "Error: ODMI_CLASS_PERMS - class permissions\n" );
            break;
          case ODMI_INTERNAL_ERR:
            printf( "Error: ODMI_INTERNAL_ERR - panic\n" );
            break;
          case ODMI_INVALID_CLXN:
            printf( "Error: ODMI_INVALID_CLXN - invalid collection\n" );
            break;
          case ODMI_INVALID_PATH:
            printf( "Error: ODMI_INVALID_PATH - invalid path - what path?\n" );
            break;
          case ODMI_MAGICNO_ERR:
            printf( "Error: ODMI_MAGICNO_ERR - invalid object magic\n" );
            break;
          case ODMI_MALLOC_ERR:
            printf( "Error: ODMI_MALLOC_ERR - malloc failed\n" );
            break;
          case ODMI_OPEN_ERR:
            printf( "Error: ODMI_OPEN_ERR - cannot open class\n" );
            break;
          case ODMI_TOOMANYCLASSES:
            printf( "Error: ODMI_TOOMANYCLASSES - too many classes\n" );
            break;
          default:
            printf( "Unknown error!\n" );
        }
        return(rc);
    }

    /* close the database again */
    odm_close_class( retClass );

    /* forget about ODM all together */
    odm_terminate();

#ifdef TRACE
    printf( "TRACE: leaving x25local_nua\n" );
#endif /* TRACE */

    debug(F110, "x25local_nua local address is ", buf, 0);
    return(rc);
}

/*****************************************************************************
 * Function: x25facilities
 *
 * Description:
 *      build up the facilities data packet for a connection request
 *
 * Parameters:
 *      a pre-allocated char buffer, normally NPI_MAX_DATA big.
 *
 * Return Value:
 *      the number of characters inserted into the buffer
 */
int
x25facilities(buffer) char *buffer; {
    extern int revcall;
    extern int closgr;
    char *p;                            /* temp pointer */
    char *start;                        /* temp pointer */

#ifdef TRACE
    printf( "TRACE: entering x25facilities\n" );
#endif /* TRACE */

    p = buffer + 1;
    start = p;

#ifdef DEBUG
    printf( "kermit: x25facilities(): getting X25 facilities\n" );
#endif /* DEBUG */

    if (revcall != 0) {
#ifdef DEBUG
        printf("reverse charge: %d\n", revcall );
#endif /* DEBUG */
        *++p = 0x01;
        *++p = revcall;
    }
    if (closgr > 0) {
#ifdef DEBUG
        printf("closed user group: %d\n", closgr );
#endif /* DEBUG */
        *++p = 0x03;
        *++p = closgr;
    }

#ifdef DEBUG
    if (p == start) {
        printf( "no facilities\n" );
    }
#endif /* DEBUG */

    /* set the size of the facilities buffer */
    *buffer = (char)( p - start ) & 0xff;

#ifdef DEBUG
    printf( "kermit: x25facilities(): returning %d\n", (int)(p - buffer)  );
#endif /* DEBUG */

#ifdef TRACE
    printf( "TRACE: leaving x25facilities\n" );
#endif /* TRACE */

    /* return the size of the facilities with size byte */
    /* 1 == no facilities, 0 byte returned as facilities size */
    return( (int)(p - buffer) );
}

/*
 * reset the connection
 */
int
x25reset(cause, diagn) char cause; char diagn; {
    /* not implemented */

#ifdef TRACE
    printf( "TRACE: entering x25reset\n" );
#endif /* TRACE */

#ifdef TRACE
    printf( "TRACE: leaving x25reset\n" );
#endif /* TRACE */

    return(0);
}

/*
 * clear the x25 connection - ie: hang up
 */
int
x25clear() {
    int get_flags = 0;                  /* priority flag for getmsg */
    int put_flags = 0;                  /* send flags, always 0 */
    ulong type;                         /* primitive type */
    N_discon_req_t *discon_req;         /* pointer to N_DISCON_REQ */
    N_discon_ind_t *discon_ind;         /* pointer to N_DISCON_IND */
    N_npi_data_t *discon_data;          /* pointer to N_DISCON_IND data */
    int rc = 0;                         /* return code */

#ifdef TRACE
    printf( "TRACE: entering x25clear\n" );
#endif /* TRACE */

#ifdef DEBUG
    /* printf( "x25clear(): checking last msg: %s\n", x25prim(x25lastmsg)); */
#endif /* DEBUG */

    /*
    * The following checks are used to ensure that we don't disconnect
    * or unbind twice - this seems to throw the NPI interface right out of
    * kilter.
    */
    switch(x25lastmsg) {
      case N_BIND_ACK:
      case N_CONN_CON:
      case N_CONN_REQ:
      case N_DATA_REQ:
      case N_DATA_IND:
        {
#ifdef DEBUG
            /* printf("x25clear(): actively disconnecting\n"); */
#endif /* DEBUG */

                discon_req = (N_discon_req_t *)malloc(NPI_MAX_CTL);
                if (discon_req == NULL) {
                    perror("kermit x25clear(): discon_req malloc failed\n");
                    /* fallthrough, try to unbind the NPI anyway */
                } else {
                    discon_req->PRIM_type = N_DISCON_REQ;
                    discon_req->DISCON_reason = 0;      /* not used by AIX */
                    discon_req->RES_length = 0;
                    discon_req->RES_offset = (ulong)(sizeof(N_discon_req_t));
                    discon_req->SEQ_number = x25seqno;  /* global */

                    if (x25putmsg(ttyfd,
                                  (N_npi_ctl_t*)discon_req,
                                  (N_npi_data_t*)NULL,
                                  0,
                                  &put_flags
                                  ) < 0) {
                        perror("x25putmsg failed in x25clear()");
                    }
                    discon_ind = (N_discon_ind_t *)malloc(NPI_MAX_CTL);
                    discon_data = (N_npi_data_t *)malloc(NPI_MAX_DATA);
                    if((discon_ind == NULL) || (discon_data == NULL)) {
                        perror("x25clear(): discon_ind malloc failed\n");
                        /* fallthrough, try to unbind the NPI anyway */
                    } else {
                        if(x25getmsg(ttyfd,
                                     (N_npi_ctl_t*)discon_ind,
                                     NPI_MAX_CTL,
                                     (N_npi_data_t*)discon_data,
                                     NPI_MAX_DATA,
                                     &get_flags,
                                     N_OK_ACK
                                     ) < 0 ) {
                            perror("x25getmsg failed in x25clear()");
                            /* fallthrough, try to unbind the NPI anyway */
                        }
                    }
                }
                break;
            }
    }

    if (x25lastmsg != N_UNBIND_REQ) {
        rc = x25unbind(ttyfd);
    }

#ifdef TRACE
    printf( "TRACE: leaving x25clear\n" );
#endif /* TRACE */

    return(rc);
}

#ifdef DEBUG
/*
 * only for debugging
 *
 * turn the internal representation of a datablock into something
 * half-way readable. Because the length is known, we can print
 * the string including null's etc (important, because the first(!)
 * byte of an X121 address is a null! (X121 addr == 0 + X25 addr)
 */
x25dump_data(char *addr, ulong offset, ulong length) {
    char *ptr = addr + offset;
    ulong i = length;
    /* allocate enough memory for all unprintable chars */
    char *buf = (char *)malloc( length * 4 );
    char *bptr = buf;   /* pointer to current place in the print buffer */

    while (i > 0) {
        if (isprint(*ptr)) {
            *bptr++ = *ptr;
        } else {
            *bptr++ = '[';
            strcpy(bptr,ckctox(*ptr,1)); bptr += 2;
            *bptr++ = ']';
        }
        ptr++;
        i--;
    }
    if (length > 0) {
        *bptr = '\0';
        printf( "%s", buf );
    }
    printf( " (%d+%d)\n", offset, length );

    if (buf) { free(buf); buf = NULL; }
    return;
}

/*
 * only for debugging
 * print as much useful information about a packet as possible
 */
x25dump_prim(primitive)    N_npi_ctl_t *primitive; {
    printf("Primitive");
    switch (primitive->PRIM_type) {
      case N_BIND_ACK:
        printf( "\tN_BIND_ACK\n\taddress:\t" );
        x25dump_data( (char *)primitive,
                     primitive->bind_ack.ADDR_offset,
                     primitive->bind_ack.ADDR_length );
        printf( "\tproto id:\t" );
        x25dump_data( (char *)primitive,
                     primitive->bind_ack.PROTOID_offset,
                     primitive->bind_ack.PROTOID_length );
        printf( "\tconnind:\t%d\n\ttoken:\t\t%d\n",
               primitive->bind_ack.CONIND_number,
               primitive->bind_ack.TOKEN_value );
        break;

      case N_BIND_REQ:
        printf( "\tN_BIND_REQ\n\taddress:\t" );
        x25dump_data( (char *)primitive,
                     primitive->bind_req.ADDR_offset,
                     primitive->bind_req.ADDR_length );
        printf( "\tproto id:\t" );
        x25dump_data( (char *)primitive,
                     primitive->bind_req.PROTOID_offset,
                     primitive->bind_req.PROTOID_length );
        printf( "\tconnind:\t%d\n\tflags:\t\t%d\n",
               primitive->bind_req.CONIND_number,
               primitive->bind_req.BIND_flags );
        break;

      case N_CONN_CON:
        printf( "\tN_CONN_CON\n" );
        printf( "\tRES\t\t" );
        x25dump_data( (char *)primitive,
                     primitive->conn_con.RES_offset,
                     primitive->conn_con.RES_length );
        printf( "\tflags:\t%d\n", primitive->conn_con.CONN_flags );
        break;

      case N_CONN_IND:
        printf( "\tN_CONN_IND\n" );
        printf( "\tsource:\t\t" );
        x25dump_data( (char *)primitive,
                     primitive->conn_ind.SRC_offset,
                     primitive->conn_ind.SRC_length );
        printf( "\tdestination:\t" );
        x25dump_data( (char *)primitive,
                     primitive->conn_ind.DEST_offset,
                     primitive->conn_ind.DEST_length );
        printf( "\tSEQ_number:\t%d\n", primitive->conn_ind.SEQ_number );
        printf( "\tflags:\t%d\n", primitive->conn_ind.CONN_flags );
        break;

      case N_CONN_REQ:
        printf( "\tN_CONN_REQ\n\tdestination:\t" );
        x25dump_data( (char *)primitive,
                     primitive->conn_req.DEST_offset,
                     primitive->conn_req.DEST_length );
        printf( "\tflags:\t%d\n", primitive->conn_req.CONN_flags );
        break;

      case N_CONN_RES:
        printf( "\tN_CONN_RES\n" );
        printf( "\tTOKEN_value\t%d\n", primitive->conn_res.TOKEN_value );
        printf( "\tSEQ_number\t%d\n", primitive->conn_res.SEQ_number );
        printf( "\tCONN_flags\t%d\n", primitive->conn_res.CONN_flags );
        printf( "\tRES\t\t" );
        x25dump_data( (char *)primitive,
                     primitive->conn_res.RES_offset,
                     primitive->conn_res.RES_length );
        break;

      case N_DATACK_IND:
        printf( "\tN_DATACK_IND\n" );
        break;

      case N_DATACK_REQ:
        printf( "\tN_DATACK_REQ\n" );
        printf( "\tflags:\t%d\n", primitive->data_req.DATA_xfer_flags );
        break;

      case N_DATA_IND:
        printf( "\tN_DATA_IND\n" );
        printf( "\tflags:\t%d\n", primitive->data_ind.DATA_xfer_flags );
        break;

      case N_DATA_REQ:
        printf( "\tN_DATA_REQ\n" );
        break;

      case N_DISCON_IND:
        printf( "\tN_DISCON_IND\n" );
        printf( "\torigin:\t%d\n", primitive->discon_ind.DISCON_orig );
        printf( "\treason:\t\t%d\n", primitive->discon_ind.DISCON_reason );
        printf( "\tseq no:\t\t%d\n", primitive->discon_ind.SEQ_number );
        printf( "\tRES:\t" );
        x25dump_data( (char *)primitive,
                     primitive->discon_ind.RES_offset,
                     primitive->discon_ind.RES_length );
        break;

      case N_DISCON_REQ:
        printf( "\tN_DISCON_REQ\n" );
        printf( "\tDISCON_reason:\t%d\n",
               primitive->discon_req.DISCON_reason );
        printf( "\tRES:\t" );
        x25dump_data( (char *)primitive,
                     primitive->discon_req.RES_offset,
                     primitive->discon_req.RES_length );
        printf( "\tSEQ_number:\t%d\n", primitive->discon_req.SEQ_number );
        break;

      case N_ERROR_ACK:
        printf( "\tN_ERROR_ACK\n" );
        printf( "\tCaused by:\t%s\n",
               x25prim( primitive->error_ack.ERROR_prim ) );
        printf( "\tNPI error:\t%s\n",
               x25err( primitive->error_ack.NPI_error ));
        errno = primitive->error_ack.UNIX_error;
        perror( "\t" );
        break;

      case N_EXDATA_IND:
        printf( "\tN_EXDATA_ACK\n" );
        break;

      case N_EXDATA_REQ:
        printf( "\tN_EXDATA_REQ\n" );
        break;

      case N_INFO_ACK:
        printf( "\tN_INFO_ACK\n" );
        printf( "\tNSDU size:\t%d\n", primitive->info_ack.NSDU_size );
        printf( "\tENSDU size:\t%d\n", primitive->info_ack.ENSDU_size );
        printf( "\tCDATA size:\t%d\n", primitive->info_ack.CDATA_size );
        printf( "\tDDATA size:\t%d\n", primitive->info_ack.DDATA_size );
        printf( "\tADDR size:\t%d\n", primitive->info_ack.ADDR_size );
        printf( "\tNIDU size:\t%d\n", primitive->info_ack.NIDU_size );
        break;

      case N_INFO_REQ:
        printf( "\tN_INFO_REQ\n" );
        break;

      case N_OK_ACK:
        printf( "\tN_OK_ACK\n" );
        break;

      case N_OPTMGMT_REQ:
        printf( "\tN_OPTMGMT_REQ\n" );
        break;

      case N_RESET_CON:
        printf( "\tN_RESET_CON\n" );
        break;

      case N_RESET_IND:
        printf( "\tN_RESET_IND\n" );
        printf( "\treason:\t\t%d\n", primitive->reset_ind.RESET_reason );
        printf( "\torigin:\t\t%d\n", primitive->reset_ind.RESET_orig );
        break;

      case N_RESET_REQ:
        printf( "\tN_RESET_REQ\n" );
        printf( "\treason:\t\t%d\n", primitive->reset_req.RESET_reason );
        break;

      case N_RESET_RES:
        printf( "\tN_RESET_RES\n" );
        break;

      case N_UDERROR_IND:
        printf( "\tN_UDERROR_IND\n" );
        break;

      case N_UNBIND_REQ:
        printf( "\tN_UNBIND_REQ\n" );
        break;

      case N_UNITDATA_REQ:
        printf( "\tN_UNITDATA_REQ\n" );
        break;

      case N_UNITDATA_IND:
        printf( "\tN_UNITDATA_IND\n" );
        break;

      default:
        (void) printf( "Unknown NPI error %d", primitive->PRIM_type );
        return 0;
    }
}
#endif /* DEBUG */

/* it looks like signal handling is not needed with streams! */
/* x25oobh()    - handle SIGURG signals - take from isode ? */

#endif /* IBMX25 */

#ifndef NOHTTP
/*
  Which time.h files to include... See ckcdeb.h for defaults.
  Note that 0, 1, 2, or all 3 of these can be included according to
  the symbol definitions.
*/
#ifndef NOTIMEH
#ifdef TIMEH
#include <time.h>
#endif /* TIMEH */
#endif /* NOTIMEH */

#ifndef NOSYSTIMEH
#ifdef SYSTIMEH
#include <sys/time.h>
#endif /* SYSTIMEH */
#endif /* NOSYSTIMEH */

#ifndef NOSYSTIMEBH
#ifdef SYSTIMEBH
#include <sys/timeb.h>
#endif /* SYSTIMEBH */
#endif /* NOSYSTIMEBH */

#ifndef TIMEH
#ifndef SYSTIMEH
#ifndef SYSTIMEBH
#ifdef Plan9
#include <sys/time.h>
#else
#ifdef AIX41
#include <time.h>
#else
#ifdef SUNOS4
#include <sys/time.h>
#else
#ifdef SYSTIMEH
#include <sys/time.h>
#else
#ifdef POSIX
#include <posix/time.h>
#else
#ifdef CLIX
#include <sys/time.h>
#else
#ifdef OS2
#include <time.h>
#else
#include <time.h>
/* #include <utime.h> */
#endif /* OS2 */
#endif /* CLIX */
#endif /* POSIX */
#endif /* SYSTIMEH */
#endif /* SUNOS4 */
#endif /* AIX41 */
#endif /* Plan9 */
#endif
#endif
#endif

#ifdef OS2
#include <sys/utime.h>
#ifdef NT
#define utimbuf _utimbuf
#endif /* NT */
#define utime   _utime
#else
#ifdef SYSUTIMEH                        /* <sys/utime.h> if requested,  */
#include <sys/utime.h>                  /* for extra fields required by */
#else                                   /* 88Open spec. */
#ifdef UTIMEH                           /* or <utime.h> if requested */
#include <utime.h>                      /* (SVR4, POSIX) */
#define SYSUTIMEH                       /* Use this for both cases. */
#endif /* UTIMEH */
#endif /* SYSUTIMEH */
#endif /* OS2 */

#ifdef VMS				/* SMS 2007/02/15 */
#include "ckvrtl.h"
#endif /* def VMS */

#ifndef HTTP_VERSION
#define HTTP_VERSION "HTTP/1.1"
#endif /* HTTP_VERSION */

#ifdef CMDATE2TM
time_t
#ifdef CK_ANSIC
http_date(char * date)
#else
http_date(date) char * date;
#endif /* CK_ANSIC */
/* http_date */ {
    /* HTTP dates are of the form:  "Sun, 06 Oct 1997 20:11:47 GMT" */
    /* There are two older formats which we are required to parse
     * that we currently do not:
     *
     * RFC 850:   "Sunday, 06-Oct-97 20:11:47 GMT"
     * asctime(): "Sun Nov  6 20:11:47 1997"
     *
     * However, it is required that all dates be sent in the form we
     * do accept.  The other two formats are for compatibility with
     * really old servers.
     */
    extern char cmdatebuf[18];
    struct tm t_tm;
    time_t t;
    char ldate[32];
    int j;

    j = ckindex(",",date,0,0,0);
    ckstrncpy(ldate,&date[j+1],25);

    {   /*
           cmcvtate() date changed to return a string pointer.
           fdc, 12 Aug 2001.
        */
        char * dp;
        dp = (char *)cmcvtdate(ldate,0); /* Convert to normal form */
        if (!dp)
          return(0);
        t_tm = *cmdate2tm(dp,1);
    }
/*
  From Lucas Hart, 5 Dec 2001:
  "On the systems to which I have access (SunOS 4.1.1, Solaris 8, and Tru64),
  setting tm_isdst to -1 maintains the correct timezone offsets, i.e., writes
  the specified (GMT) time if the buffer size is 21, or the contemporaneous
  localtime if the buffer size is 25.  Perhaps tm_isdst should be set in
  cmdate2tm(), rather than only in http_date."
*/
#ifndef NOTM_ISDST                      /* For platforms where */
    t_tm.tm_isdst = -1;                 /* tm_isdst doesn't exist. */
#endif /* NOTM_ISDST */

    t = mktime(&t_tm);                  /* NOT PORTABLE */

#ifdef XX_TIMEZONE
    t -= _timezone;
#endif /* XX_TIMEZONE */

    return(t);
}
#endif /* CMDATE2TM */

char *
http_now() {
    static char nowstr[32];
#ifdef CMDATE2TM
    struct tm  *gmt;
    time_t ltime;                       /* NOT PORTABLE */

    time(&ltime);

    gmt = gmtime(&ltime);               /* PROBABLY NOT PORTABLE */
    strftime(nowstr,32,"%a, %d %b %Y %H:%M:%S GMT",gmt); /* NOT PORTABLE */
    /* not only is it not portable but it's locale-dependent */
#else
/*
  This is hopeless.  First of all, it seems that HTTP wants Day and Month
  NAMES?  In English?  Whose idea was that?  Even worse, the date/time must be
  expressed in Zulu (UTC (GMT)), and converting from local time to GMT is a
  nightmare.  Every platform does it differently, if at all -- even if we
  restrict ourselves to UNIX.  For example (quoting from recent C-Kermit edit
  history), "Fixed a longstanding bug in the BSDI version, in which incoming
  file dates were set in GMT rather than local time.  It seems in 4.4BSD,
  localtime() does not return the local time, but rather Zero Meridian (Zulu)
  time (GMT), and must be adjusted by the tm_gmtoff value."  Swell.  For
  greater appreciation of the scope of the problem, just take a look at the
  time-related #ifdefs in ckutio.c.  The only right way to do this is to add
  our own portable API for converting between local time and GMT/UTC/Zulu
  that shields us not only from UNIXisms like time_t and struct tm, but also
  the unbelievable amount of differences in time-related APIs -- e.g. is
  "timezone" an external variable or a function; which header file(s) do we
  include, etc etc etc.  It's a major project.
*/
    int x;
    x = cmcvtdate("",1);

Evidently this code is not used -- if it is, it must be fixed to use
new (aug 2001) cmcvtdate() calling conventions.

    if (x < 0)
      return("");
/*  yyyymmdd hh:mm:ss */
/*  01234567890123456 */
    nowstr[0]  = 'X';                   /* 1st letter of day */
    nowstr[1]  = 'x';                   /* 2nd letter of day */
    nowstr[2]  = 'x';                   /* 3rd letter of day */
    nowstr[3]  = ',';
    nowstr[4]  = ' ';
    nowstr[5]  = cmdate[6];
    nowstr[6]  = cmdate[7];
    nowstr[7]  = ' ';
    nowstr[8]  = ' ';                   /* first letter of month */
    nowstr[9]  = ' ';                   /* second letter of month */
    nowstr[10] = ' ';                   /* third letter of month */
    nowstr[11] = ' ';
    nowstr[12] = cmdate[0];
    nowstr[13] = cmdate[1];
    nowstr[14] = cmdate[2];
    nowstr[15] = cmdate[3];
    nowstr[16] = ' ';
    nowstr[17] = cmdate[9];
    nowstr[18] = cmdate[10];
    nowstr[19] = cmdate[11];
    nowstr[20] = cmdate[12];
    nowstr[21] = cmdate[13];
    nowstr[22] = cmdate[14];
    nowstr[23] = cmdate[15];
    nowstr[24] = cmdate[16];
    nowstr[25] = ' ';
    nowstr[26] = 'G';
    nowstr[27] = 'M';
    nowstr[28] = 'T';
    nowstr[29] = '\0';
#endif /* CMDATE2TM */
    return(nowstr);
}

#ifndef OS2
#ifndef CK_AUTHENTICATION
/* from ckuusr.h, which this module normally doesn't include */
_PROTOTYP( int dclarray, (char, int) );
#endif /* CK_AUTHENTICATION */
#endif /* OS2 */
/*
  Assign http response pairs to given array.
  For best results, response pairs should contain no spaces.

  Call with:
    resp  =  pointer to response list.
    n     =  size of response list.
    array =  array letter.
  Returns:
    0 on failure.
    >= 1, size of array, on success.
*/
static int
#ifdef CK_ANSIC
http_mkarray(char ** resp, int n, char array)
#else
http_mkarray(resp, n, array) char ** resp; int n; char array;
#endif /* CK_ANSIC */
{
#ifndef NOSPL
    int i, x;
    char ** ap;
    extern char ** a_ptr[];
    extern int a_dim[];

    if (!array || n <= 0)
      return(0);
    if ((x = dclarray(array,n)) < 0) {
        printf("?Array declaration failure\n");
        return(-9);
    }
    /* Note: argument array is 0-based but Kermit array is 1-based */
    ap = a_ptr[x];
    ap[0] = NULL;                       /* 0th element is empty */
    for (i = 1; i <= n; i++) {
        ap[i] = resp[i-1];              /* If resp elements were malloc'd */
        resp[i-1] = NULL;
    }
    a_dim[x] = n;
    return(n);
#else
    return(0);
#endif /* NOSPL */
}

#define HTTPHEADCNT 64
int
http_get_chunk_len()
{
    int len = 0;
    int i = 0, j = -1;
    char buf[24];
    int ch;

    while ((ch = http_inc(0)) >= 0 && i < 24) {
        buf[i] = ch;
        if ( buf[i] == ';' )            /* Find chunk-extension (if any) */
            j = i;
        if ( buf[i] == 10 ) {           /* found end of line */
            if (i > 0 && buf[i-1] == 13)
                i--;
            buf[i] = '\0';
            break;
        }
        i++;
    }
    if ( i < 24 ) {                     /* buf now contains len in Hex */
        len = hextoulong(buf, j == -1 ? i : j-1);
    }

    return(len);
}

/*
  Parse an HTTP Content-Length header value.  Unlike atoi(), this
  rejects non-numeric input and clamps out-of-range or negative
  values to 0 rather than letting an overflowed/negative length
  reach the read loops below.
*/
static int
#ifdef CK_ANSIC
http_content_length(char *s)
#else
http_content_length(s) char *s;
#endif /* CK_ANSIC */
{
    long val;
    char *ep;

    while (*s == ' ' || *s == '\t')
      s++;
    errno = 0;
    val = strtol(s, &ep, 10);
    if (ep == s || errno == ERANGE || val < 0 || val > INT_MAX)
      return(0);
    return((int)val);
}

int
http_isconnected()
{
    return(httpfd != -1);
}

char *
http_host()
{
    return(httpfd != -1 ? http_host_port : "");
}

char *
http_security()
{
    if ( httpfd == -1 )
        return("NULL");
#ifdef CK_SSL
    if (tls_http_active_flag) {
        SSL_CIPHER * cipher;
        const char *cipher_list;
        static char buf[128];
        buf[0] = NUL;
        /* cast added by fdc 26 September 2022 */
        cipher = (SSL_CIPHER *)SSL_get_current_cipher(tls_http_con);
        cipher_list = SSL_CIPHER_get_name(cipher);
        SSL_CIPHER_description(cipher,buf,sizeof(buf));
        return(buf);
    }
#endif /* CK_SSL */
    return("NULL");
}

int
http_reopen()
{
    int rc = 0;
    char * s = NULL;                    /* strdup is not portable */
    if ( tcp_http_proxy ) {
        char * p;
        makestr(&s,(char *)http_host_port);
        p = s;
        while (*p != '\0' && *p != ':') p++; /* Look for colon */
        if (*p == ':') {                     /* Have a colon */
            *p++ = '\0';                     /* Get service name or number */
        } else {
            p="http";
        }
        rc = http_open(s,p,http_ssl,NULL,0,http_agent);
    } else {
        makestr(&s,(char *)http_ip);
        rc = http_open(s,ckuitoa(http_port),http_ssl,NULL,0,http_agent);
    }
    free(s);
    return(rc);
}


int
#ifdef CK_ANSIC
http_open(char * hostname, char * svcname, int use_ssl, char * rdns_name,
          int rdns_len, char * agent)
#else /* CK_ANSIC */
http_open(hostname, svcname, use_ssl, rdns_name, rdns_len, agent)
    char * hostname;
    char * svcname;
    int    use_ssl;
    char * rdns_name;
    int    rdns_len;
    char * agent;
#endif /* CK_ANSIC */
{
    char namecopy[NAMECPYL];
    char *p;
#ifndef CK_IPV6
    int i, dns = 0;
#endif /* CK_IPV6 */
#ifdef NON_BLOCK_IO
    int x;
#endif /* NON_BLOCK_IO */
#ifdef TCPSOCKET
#ifndef CK_IPV6
    int isconnect = 0;
#endif /* CK_IPV6 */
#ifdef SO_OOBINLINE
    int on = 1;
#endif /* SO_OOBINLINE */
    struct servent *service=NULL;
#ifndef CK_IPV6
    struct hostent *host=NULL;
    struct sockaddr_in r_addr;
    struct sockaddr_in sin;
    struct sockaddr_in l_addr;
    GSOCKNAME_T l_slen;
#endif /* CK_IPV6 */
#ifdef EXCELAN
    struct sockaddr_in send_socket;
#endif /* EXCELAN */

#ifndef CK_IPV6
#ifdef INADDRX
/* inet_addr() is of type struct in_addr */
#ifdef datageneral
    extern struct in_addr inet_addr();
#else
#ifdef HPUX5WINTCP
    extern struct in_addr inet_addr();
#endif /* HPUX5WINTCP */
#endif /* datageneral */
    struct in_addr iax;
#else
#ifdef INADDR_NONE
    struct in_addr iax;
#else /* INADDR_NONE */
    long iax;
#endif /* INADDR_NONE */
#endif /* INADDRX */
#endif /* CK_IPV6 */

    if ( rdns_name == NULL || rdns_len < 0 )
        rdns_len = 0;

    *http_ip = '\0';                     /* Initialize IP address string */
    namecopy[0] = '\0';

#ifdef DEBUG
    if (deblog) {
        debug(F110,"http_open hostname",hostname,0);
        debug(F110,"http_open svcname",svcname,0);
    }
#endif /* DEBUG */
    if (!hostname) hostname = "";
    if (!svcname) svcname = "";
    if (!*hostname || !*svcname) return(-1);


    service = ckgetservice(hostname,svcname,http_ip,CK_IPADDRLEN);

    if (service == NULL) {
        if ( !quiet )
            printf("?Invalid service: %s\r\n",svcname);
        return(-1);
    }

    /* For HTTP connections we must preserve the original hostname and */
    /* service requested so we can include them in the Host header.    */
    ckmakmsg(http_host_port,sizeof(http_host_port),hostname,":",
              ckuitoa(ntohs(service->s_port)),NULL);
    http_port = ntohs(service->s_port);
    http_ssl = use_ssl;
    debug(F111,"http_open",http_host_port,http_port);

    /* 'http_ip' contains the IP address to which we want to connect        */
    /* 'svcnam'   contains the service name                                 */
    /* 'service->s_port' contains the port number in network byte order     */

    /* If we are using an http proxy, we need to create a buffer containing */
    /*   hostname:port-number                                               */
    /* to pass to the http_connect() function.  Then we need to replace     */
    /* 'namecopy' with the name of the proxy server and the service->s_port */
    /* with the port number of the proxy (default port 80).                 */

    if ( tcp_http_proxy ) {

        ckmakmsg(proxycopy,sizeof(proxycopy),hostname,":",
                 ckuitoa(ntohs(service->s_port)),NULL);
        ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);

        p = namecopy;                       /* Was a service requested? */
        while (*p != '\0' && *p != ':') p++; /* Look for colon */
        if (*p == ':') {                    /* Have a colon */
            debug(F110,"http_open name has colon",namecopy,0);
            *p++ = '\0';                    /* Get service name or number */
        } else {
            strcpy(++p,"http");
        }

        service = ckgetservice(namecopy,p,http_ip,CK_IPADDRLEN);
        if (!service) {
            fprintf(stderr, "Can't find port for service %s\n", p);
#ifdef TGVORWIN
            debug(F101,"http_open can't get service for proxy","",socket_errno);
#else
            debug(F101,"http_open can't get service for proxy","",errno);
#endif /* TGVORWIN */
            errno = 0;                  /* (rather than mislead) */
            return(-1);
        }

        /* copy the proxyname and remove the service if any so we can use
         * it as the hostname
         */
        ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);
        p = namecopy;                       /* Was a service requested? */
        while (*p != '\0' && *p != ':') p++; /* Look for colon */
        if (*p == ':') {                    /* Have a colon */
            *p = '\0';                      /* terminate string */
        }
        hostname = namecopy;                /* use proxy as hostname */
    }

    /* Set up socket structure and get host address */
#ifdef CK_IPV6
    /*
      Resolve and connect using ck_tcp_connect().  This replaces the legacy
      inet_addr() maze below (kept under #else for
      non-CK_IPV6 builds) with getaddrinfo(), whose candidates are tried in
      resolver order.
    */
    {
        struct sockaddr_storage cn_addr;
        GSOCKNAME_T cn_len = sizeof(cn_addr);
        char svcbuf[16];
        int got_addr = 0;

        ckstrncpy(svcbuf,ckuitoa(ntohs(service->s_port)),sizeof(svcbuf));
        httpfd = ck_tcp_connect(http_ip[0] ? http_ip : hostname, svcbuf,
                                quiet, &got_addr, &cn_addr, &cn_len, 0);
        if (httpfd < 0) {
            if (!got_addr)
              fprintf(stderr, "Can't get address for %s\n",
                      http_ip[0]?http_ip:hostname);
            errno = 0;
            return(-1);
        }
        if (ck_straddr((struct sockaddr *)&cn_addr,cn_len,
                        http_ip,CK_IPADDRLEN) < 0)
          http_ip[0] = '\0';
    }
#else /* CK_IPV6 */
    bzero((char *)&r_addr, sizeof(r_addr));
    debug(F100,"http_open bzero ok","",0);

#ifdef INADDR_NONE
    debug(F101,"http_open INADDR_NONE defined","",INADDR_NONE);
#else /* INADDR_NONE */
    debug(F100,"http_open INADDR_NONE not defined","",0);
#endif /* INADDR_NONE */
#ifdef INADDRX
    debug(F100,"http_open INADDRX defined","",0);
#else /* INADDRX */
    debug(F100,"http_open INADDRX not defined","",0);
#endif /* INADDRX */

#ifndef NOMHHOST
#ifdef INADDRX
    iax = inet_addr(http_ip[0]?http_ip:hostname);
    debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr);
#else /* INADDRX */
#ifdef INADDR_NONE
    iax.s_addr = inet_addr(http_ip[0]?http_ip:hostname);
    debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr);
#else /* INADDR_NONE */
#ifndef datageneral
    iax = (unsigned int) inet_addr(http_ip[0]?http_ip:hostname);
#else
    iax = -1L;
#endif /* datageneral */
    debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax);
#endif /* INADDR_NONE */
#endif /* INADDRX */

    dns = 0;
    if (
#ifdef INADDR_NONE
/* This might give warnings on 64-bit platforms but they should be harmless */
/* because INADDR_NONE should be all 1's anyway, thus the OR part is */
/* probably superfluous -- not sure why it's even there, maybe it should be */
/* removed. */
        iax.s_addr == INADDR_NONE /* || iax.s_addr == (unsigned long) -1L */
#else /* INADDR_NONE */
        iax == -1
#endif /* INADDR_NONE */
        ) {
        if (!quiet) {
            printf(" DNS Lookup... ");
            fflush(stdout);
        }
        if ((host = gethostbyname(http_ip[0] ? http_ip : hostname)) != NULL) {
            debug(F100,"http_open gethostbyname != NULL","",0);
            host = ck_copyhostent(host);
            dns = 1;                    /* Remember we performed dns lookup */
            r_addr.sin_family = host->h_addrtype;
            if (tcp_rdns && host->h_name && host->h_name[0] && (rdns_len > 0)
                 && (tcp_http_proxy == NULL)
                 )
                ckmakmsg(rdns_name,rdns_len,host->h_name,":",svcname,NULL);

#ifdef HADDRLIST
#ifdef h_addr
            /* This is for trying multiple IP addresses - see <netdb.h> */
            if (!(host->h_addr_list))
              return(-1);
            bcopy(host->h_addr_list[0],
                  (caddr_t)&r_addr.sin_addr,
                  host->h_length
                  );
#else
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif /* h_addr */
#else  /* HADDRLIST */
            bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
#endif /* HADDRLIST */
            debug(F111,"BCOPY","host->h_length",host->h_length);
        }
    }
#endif /* NOMHHOST */

    if (!dns) {
#ifdef INADDRX
/* inet_addr() is of type struct in_addr */
        struct in_addr ina;
        unsigned long uu;
        debug(F100,"http_open gethostbyname == NULL: INADDRX","",0);
        ina = inet_addr(http_ip[0]?http_ip:hostname);
        uu = *(unsigned int *)&ina;
#else /* Not INADDRX */
/* inet_addr() is unsigned long */
        unsigned long uu;
        debug(F100,"http_open gethostbyname == NULL: Not INADDRX","",0);
        uu = inet_addr(http_ip[0]?http_ip:hostname);
#endif /* INADDRX */
        debug(F101,"http_open uu","",uu);
        if (
#ifdef INADDR_NONE
            !(uu == INADDR_NONE || uu == (unsigned int) -1L)
#else   /* INADDR_NONE */
            uu != ((unsigned long)-1)
#endif /* INADDR_NONE */
            ) {
            r_addr.sin_addr.s_addr = uu;
            r_addr.sin_family = AF_INET;
        } else {
#ifdef VMS
            fprintf(stdout, "\r\n");    /* complete any previous message */
#endif /* VMS */
            fprintf(stderr, "Can't get address for %s\n",
                     http_ip[0]?http_ip:hostname);
#ifdef TGVORWIN
            debug(F101,"http_open can't get address","",socket_errno);
#else
            debug(F101,"http_open can't get address","",errno);
#endif /* TGVORWIN */
            errno = 0;                  /* Rather than mislead */
            return(-1);
        }
    }

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

    r_addr.sin_port = service->s_port;
    ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);
    debug(F110,"http_open trying",http_ip,0);
    if (!quiet && *http_ip) {
        printf(" Trying %s... ", http_ip);
        fflush(stdout);
    }

    /* Loop to try additional IP addresses, if any. */

    do {
#ifdef EXCELAN
        send_socket.sin_family = AF_INET;
        send_socket.sin_addr.s_addr = 0;
        send_socket.sin_port = 0;
        if ((httpfd = socket(SOCK_STREAM, (struct sockproto *)0,
                            &send_socket, SO_REUSEADDR)) < 0)
#else  /* EXCELAN */
        if ((httpfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
#endif /* EXCELAN */
            {
#ifdef EXCELAN
                experror("TCP socket error");
#else
#ifdef TGVORWIN
#ifdef OLD_TWG
                errno = socket_errno;
#endif /* OLD_TWG */
                socket_perror("TCP socket error");
                debug(F101,"http_open socket error","",socket_errno);
#else
                perror("TCP socket error");
                debug(F101,"http_open socket error","",errno);
#endif /* TGVORWIN */
#endif /* EXCELAN */
                return (-1);
            }
        errno = 0;

       /* If a specific TCP address on the local host is desired we */
       /* must bind it to the socket.                               */
#ifndef datageneral
         if (tcp_address) {
             int s_errno;

             debug(F110,"http_open binding socket to",tcp_address,0);
             bzero((char *)&sin,sizeof(sin));
             sin.sin_family = AF_INET;
#ifdef INADDRX
             inaddrx = inet_addr(tcp_address);
             sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
#else
             sin.sin_addr.s_addr = inet_addr(tcp_address);
#endif /* INADDRX */
             sin.sin_port = 0;
             if (bind(httpfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
                 s_errno = socket_errno; /* Save error code */
#ifdef TCPIPLIB
                 socket_close(httpfd);
#else /* TCPIPLIB */
                 close(httpfd);
#endif /* TCPIPLIB */
                 httpfd = -1;
                 errno = s_errno;       /* and report this error */
                 debug(F101,"http_open bind errno","",errno);
                 return(-1);
             }
         }
#endif /* datageneral */

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

#ifdef EXCELAN
        if (connect(httpfd, &r_addr) < 0)
#else
#ifdef NT
          WSASafeToCancel = 1;
#endif /* NT */
        if (connect(httpfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0)
#endif /* EXCELAN */
          {
#ifdef NT
              WSASafeToCancel = 0;
#endif /* NT */
#ifdef OS2
              i = socket_errno;
#else /* OS2 */
#ifdef TGVORWIN
              i = socket_errno;
#else
              i = errno;                /* Save error code */
#endif /* TGVORWIN */
#endif /* OS2 */
#ifdef HADDRLIST
#ifdef h_addr
              if (host && host->h_addr_list && host->h_addr_list[1]) {
                  perror("");
                  host->h_addr_list++;
                  bcopy(host->h_addr_list[0],
                        (caddr_t)&r_addr.sin_addr,
                        host->h_length);

                  ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);
                  debug(F110,"http_open h_addr_list",http_ip,0);
                  if (!quiet && *http_ip) {
                      printf(" Trying %s... ", http_ip);
                      fflush(stdout);
                  }
#ifdef TCPIPLIB
                  socket_close(httpfd); /* Close it. */
#else
                  close(httpfd);
#endif /* TCPIPLIB */
                  continue;
              }
#endif /* h_addr */
#endif  /* HADDRLIST */
              http_close();
              httpfd = -1;
              errno = i;                /* And report this error */
#ifdef EXCELAN
              if (errno) experror("http_open connect");
#else
#ifdef TGVORWIN
              debug(F101,"http_open connect error","",socket_errno);
              /* if (errno) socket_perror("http_open connect"); */
#ifdef OLD_TWG
              errno = socket_errno;
#endif /* OLD_TWG */
              if (!quiet)
                socket_perror("http_open connect");
#else /* TGVORWIN */
              debug(F101,"http_open connect errno","",errno);
#ifdef VMS
              if (!quiet)
                perror("\r\nFailed");
#else
              if (!quiet)
                perror("Failed");
#endif /* VMS */
#ifdef DEC_TCPIP
              if (!quiet)
                perror("http_open connect");
#endif /* DEC_TCPIP */
#ifdef CMU_TCPIP
              if (!quiet)
                perror("http_open connect");
#endif /* CMU_TCPIP */
#endif /* TGVORWIN */
#endif /* EXCELAN */
              return(-1);
          }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
        isconnect = 1;
    } while (!isconnect);
#endif /* CK_IPV6 */

#ifdef NON_BLOCK_IO
    on = 1;
    x = socket_ioctl(httpfd,FIONBIO,&on);
    debug(F101,"http_open FIONBIO","",x);
#endif /* NON_BLOCK_IO */

    /* We have succeeded in connecting to the HTTP PROXY.  So now we   */
    /* need to attempt to connect through the proxy to the actual host */
    /* If that is successful, we have to pretend that we made a direct */
    /* connection to the actual host.                                  */

    if ( tcp_http_proxy ) {
#ifdef OS2
        if (!agent)
	  agent = "Kermit 95";	/* Default user agent */
#else
        if (!agent)
	  agent = "C-Kermit";
#endif /* OS2 */

        if (http_connect(httpfd,
                         tcp_http_proxy_agent ? tcp_http_proxy_agent : agent,
                         NULL,
                         tcp_http_proxy_user,
                         tcp_http_proxy_pwd,
                         0,
                         proxycopy
                         ) < 0) {
            http_close();
            return(-1);
        }
    }

#ifdef SO_OOBINLINE
    /* See note on SO_OOBINLINE in netopen() */
#ifdef datageneral
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef BSD43
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OSF1
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef POSIX
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef MOTSV88R4
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef SOLARIS
/*
  Maybe this applies to all SVR4 versions, but the other (else) way has been
  compiling and working fine on all the others, so best not to change it.
*/
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OSK
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef OS2
    {
        int rc;
        rc = setsockopt(httpfd,
                        SOL_SOCKET,
                        SO_OOBINLINE,
                        (char *) &on,
                        sizeof on
                        );
        debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc);
    }
#else
#ifdef VMS /* or, at least, VMS with gcc */
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
#ifdef CLIX
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
#else
    setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
#endif /* CLIX */
#endif /* VMS */
#endif /* OS2 */
#endif /* OSK */
#endif /* SOLARIS */
#endif /* MOTSV88R4 */
#endif /* POSIX */
#endif /* BSD43 */
#endif /* OSF1 */
#endif /* datageneral */
#endif /* SO_OOBINLINE */

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

#ifndef datageneral
    /* Find out our own IP address. */
    /* We need the l_addr structure for [E]KLOGIN. */
#ifdef CK_IPV6
    {
        struct sockaddr_storage lss;
        GSOCKNAME_T lsslen = sizeof(lss);
        bzero((char *)&lss, sizeof(lss));
        if (!getsockname(httpfd, (struct sockaddr *)&lss, &lsslen)) {
            char lsbuf[CK_IPADDRLEN];
            if (ck_straddr((struct sockaddr *)&lss,lsslen,
                            lsbuf,sizeof(lsbuf)) == 0)
              ckstrncpy(myipaddr, lsbuf, CK_IPADDRLEN);
            debug(F110,"getsockname",myipaddr,0);
        }
    }
#else /* CK_IPV6 */
    l_slen = sizeof(l_addr);
    bzero((char *)&l_addr, l_slen);
#ifndef EXCELAN
    if (!getsockname(httpfd, (struct sockaddr *)&l_addr, &l_slen)) {
        char * s = (char *)inet_ntoa(l_addr.sin_addr);
        ckstrncpy(myipaddr, s, CK_IPADDRLEN);
        debug(F110,"getsockname",myipaddr,0);
    }
#endif /* EXCELAN */
#endif /* CK_IPV6 */
#endif /* datageneral */

/* See note in netopen() on Reverse DNS lookups */
     if (tcp_rdns == SET_ON) {
#ifdef NT
        if (isWin95())
          sleep(1);
#endif /* NT */
        if (!quiet) {
            printf(" Reverse DNS Lookup... ");
            fflush(stdout);
        }
#ifdef CK_IPV6
        /*
          Reverse-resolve via getpeername()/getnameinfo() instead of
          gethostbyaddr(r_addr), since r_addr doesn't exist in CK_IPV6.
          This also works for an IPv6 peer.

          FIXME: should reverse-resolve code be consolidated?
        */
        {
            struct sockaddr_storage pss;
            GSOCKNAME_T pslen = sizeof(pss);
            char nibuf[256];
            int gni = -1;

            bzero((char *)&pss, sizeof(pss));
            if (!getpeername(httpfd,(struct sockaddr *)&pss,&pslen))
              gni = getnameinfo((struct sockaddr *)&pss,(socklen_t)pslen,
                                 nibuf,sizeof(nibuf),NULL,0,NI_NAMEREQD);
            if (gni == 0) {
                char * s = nibuf;
                if (!quiet) {
                    printf("(OK)\n");
                    fflush(stdout);
                }
                ckmakmsg(rdns_name,rdns_len,s,":",svcname,NULL);
                if (!quiet && *s
#ifndef NOICP
                    && !doconx
#endif /* NOICP */
                    )
                  printf(" %s connected on port %s\n",s,
                         ckuitoa(ntohs(service->s_port)));
            } else {
                if (!quiet) printf("Failed.\n");
            }
        }
#else /* CK_IPV6 */
        if ((host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET))) {
            char * s;
            host = ck_copyhostent(host);
            debug(F100,"http_open gethostbyname != NULL","",0);
            if (!quiet) {
                printf("(OK)\n");
                fflush(stdout);
            }
            s = host->h_name;
            if (!s) {                   /* This can happen... */
                debug(F100,"http_open host->h_name is NULL","",0);
                s = "";
            }
            /* Something is wrong with inet_ntoa() on HPUX 10.xx */
            /* The compiler says "Integral value implicitly converted to */
            /* pointer in assignment."  The prototype is right there */
            /* in <arpa/inet.h> so what's the problem? */
            /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */
            if (!*s) {                  /* No name so substitute the address */
                debug(F100,"http_open host->h_name is empty","",0);
                s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */
                if (!s)                 /* Trust No 1 */
                  s = "";
                if (*s) {               /* If it worked, use this string */
                    ckstrncpy(http_ip,s,CK_IPADDRLEN);
                }
                s = http_ip;             /* Otherwise stick with the IP */
                if (!*s)                 /* or failing that */
                  s = http_host_port;    /* the name we were called with. */
            }
            if (*s)                     /* return the rdns name */
                ckmakmsg(rdns_name,rdns_len,s,":",svcname,NULL);

            if (!quiet && *s
#ifndef NOICP
                && !doconx
#endif /* NOICP */
                ) {
                printf(" %s connected on port %s\n",s,
                       ckuitoa(ntohs(service->s_port)));
#ifdef BETADEBUG
                /* This is simply for testing the DNS entries */
                if (host->h_aliases) {
                    char ** a = host->h_aliases;
                    while (*a) {
                        printf(" alias => %s\n",*a);
                        a++;
                    }
                }
#endif /* BETADEBUG */
            }
        } else {
            if (!quiet) printf("Failed.\n");
        }
#endif /* CK_IPV6 */
    } else if (!quiet) printf("(OK)\n");
    if (!quiet) fflush(stdout);


    if ( tcp_http_proxy ) {
        /* Erase the IP address since we cannot reuse it */
        http_ip[0] = '\0';
    }
#ifndef CK_IPV6
    else {
        /* This should already have been done but just in case */
        ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),CK_IPADDRLEN);
    }
#endif /* CK_IPV6 */
    makestr(&http_agent,agent);

#ifdef CK_SSL
    if (use_ssl && ck_ssleay_is_installed()) {
        if (!ssl_http_init(hostname)) {
            if (bio_err!=NULL) {
                BIO_printf(bio_err,"ssl_tn_init() failed\n");
                ERR_print_errors(bio_err);
            } else {
                fflush(stderr);
                fprintf(stderr,"ssl_tn_init() failed\n");
                ERR_print_errors_fp(stderr);
            }
            http_close();
            return(-1);
        } else if ( ck_ssl_http_client(httpfd,hostname) < 0 ) {
            http_close();
            return(-1);
        }
    }
#endif /* CK_SSL */
#endif /* TCPSOCKET */

    return(0);                          /* Done. */
}

int
#ifdef CK_ANSIC
http_close(VOID)
#else /* CK_ANSIC */
http_close()
#endif /* CK_ANSIC */
{
    int x = 0;
    debug(F101,"http_close","",httpfd);

#ifdef HTTP_BUFFERING
    http_count = 0;
    http_bufp = 0;
#endif	/* HTTP_BUFFERING */

    if (httpfd == -1)                    /* Was open? */
      return(0);                        /* Wasn't. */

#ifndef OS2
    if (httpfd > -1)                     /* Was. */
#endif /* OS2 */
      {
#ifdef CK_SSL
          if (tls_http_active_flag) {
              if (ssl_debug_flag)
                BIO_printf(bio_err,"calling SSL_shutdown\n");
              SSL_shutdown(tls_http_con);
              tls_http_active_flag = 0;
          }
#endif /* CK_SSL */
#ifdef TCPIPLIB
          x = socket_close(httpfd);      /* Close it. */
#else
#ifndef OS2
          x = close(httpfd);
#endif /* OS2 */
#endif /* TCPIPLIB */
      }
    httpfd = -1;                          /* Mark it as closed. */
    /* do not erase http_host_port, http_ip, http_port so they */
    /* can be used by http_reopen() */
    return(x);
}


/* http_tol()
 * 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
#ifdef CK_ANSIC
http_tol( CHAR *s, int n )
#else
http_tol(s,n) CHAR *s; int n;
#endif /* CK_ANSIC */
{
    int count = 0;
    int len = n;
    int try = 0;

    if (httpfd == -1) {
        debug(F100,"http_tol socket is closed","",0);
        return -1;
    }
    debug(F101,"http_tol TCPIPLIB ttnet","",ttnet);

#ifdef CK_SSL
    if (tls_http_active_flag) {
        int error, r;
        /* Write using SSL */
      ssl_retry:
          r = SSL_write(tls_http_con, s, len /* >1024?1024:len */);
        switch (SSL_get_error(tls_http_con,r)) {
          case SSL_ERROR_NONE:
            debug(F111,"http_tol","SSL_write",r);
            if ( r == len )
                return(n);
             s += r;
             len -= r;
             goto ssl_retry;
          case SSL_ERROR_WANT_WRITE:
            debug(F100,"http_tol SSL_ERROR_WANT_WRITE","",0);
	      return(-1);
          case SSL_ERROR_WANT_READ:
            debug(F100,"http_tol SSL_ERROR_WANT_READ","",0);
            return(-1);
          case SSL_ERROR_SYSCALL:
              if ( r == 0 ) { /* EOF */
                  http_close();
                  return(-2);
              } else {
                  int rc = -1;
#ifdef NT
                  int gle = GetLastError();
                  debug(F111,"http_tol SSL_ERROR_SYSCALL",
                         "GetLastError()",gle);
                  rc = os2socketerror(gle);
                  if (rc == -1)
                      rc = -2;
                  else if ( rc == -2 )
                      return -1;
#endif /* NT */
                  return(rc);
              }
          case SSL_ERROR_WANT_X509_LOOKUP:
            debug(F100,"http_tol SSL_ERROR_WANT_X509_LOOKUP","",0);
            http_close();
            return(-2);
          case SSL_ERROR_SSL:
            debug(F100,"http_tol SSL_ERROR_SSL","",0);
            http_close();
            return(-2);
          case SSL_ERROR_ZERO_RETURN:
            debug(F100,"http_tol SSL_ERROR_ZERO_RETURN","",0);
            http_close();
            return(-2);
          default:
            debug(F100,"http_tol SSL_ERROR_?????","",0);
            http_close();
            return(-2);
        }
    }
#endif /* CK_SSL */

  http_tol_retry:
    try++;                              /* Increase the try counter */

    {
#ifdef BSDSELECT
        fd_set wfds;
        struct timeval tv;

        debug(F101,"http_tol BSDSELECT","",0);
        tv.tv_usec = 0L;
        tv.tv_sec=30;
#ifdef NT
        WSASafeToCancel = 1;
#endif /* NT */
#ifdef STREAMING
      do_select:
#endif /* STREAMING */
        FD_ZERO(&wfds);
        FD_SET(httpfd, &wfds);
        if (select(FD_SETSIZE, NULL,
#ifdef __DECC
#ifndef __DECC_VER
                    (int *)
#endif /* __DECC_VER */
#endif /* __DECC */
                   &wfds, NULL, &tv) < 0) {
            int s_errno = socket_errno;
            debug(F101,"http_tol select failed","",s_errno);
#ifdef BETADEBUG
            printf("http_tol select failed: %d\n", s_errno);
#endif /* BETADEBUG */
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
        if (!FD_ISSET(httpfd, &wfds)) {
#ifdef STREAMING
            if (streaming)
              goto do_select;
#endif /* STREAMING */
            debug(F111,"http_tol","!FD_ISSET",ttyfd);
#ifdef NT
            WSASafeToCancel = 0;
            if (!win95selectbug)
#endif /* NT */
              return(-1);
        }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
#else /* BSDSELECT */
#ifdef IBMSELECT
        {
            int tries = 0;
            debug(F101,"http_tol IBMSELECT","",0);
            while (select(&httpfd, 0, 1, 0, 1000) != 1) {
                int count;
                if (tries++ >= 60) {
                    /* if after 60 seconds we can't get permission to write */
                    debug(F101,"http_tol select failed","",socket_errno);
                    return(-1);
                }
            }
        }
#endif /* IBMSELECT */
#endif /* BSDSELECT */
#ifdef TCPIPLIB
        if ((count = socket_write(httpfd,s,n)) < 0) {
            int s_errno = socket_errno; /* maybe a function */
            debug(F101,"http_tol socket_write error","",s_errno);
#ifdef OS2
            if (os2socketerror(s_errno) < 0)
              return(-2);
#endif /* OS2 */
            return(-1);                 /* Call it an i/o error */
        }
#else /* TCPIPLIB */
        if ((count = write(httpfd,s,n)) < 0) {
            debug(F101,"http_tol socket_write error","",errno);
            return(-1);                 /* Call it an i/o error */
        }
#endif /* TCPIPLIB */
        if (count < n) {
            debug(F111,"http_tol socket_write",s,count);
            if (try > 25) {
                /* don't try more than 25 times */
                debug(F100,"http_tol tried more than 25 times","",0);
                return(-1);
            }
            if (count > 0) {
                s += count;
                n -= count;
            }
            debug(F111,"http_tol retry",s,n);
            goto http_tol_retry;
        } else {
            debug(F111,"http_tol socket_write",s,count);
            return(len); /* success - return total length */
        }
    }
}

int
#ifdef CK_ANSIC
http_inc( int timo )
#else
http_inc(timo) int timo;
#endif /* CK_ANSIC */
{
    int x=-1; unsigned char c;             /* The locals. */

    if (httpfd == -1) {
#ifdef HTTP_BUFFERING
	http_count = 0;
  	http_bufp = 0;
#endif	/* HTTP_BUFFERING */
        debug(F100,"http_inc socket is closed","",0);
        return(-2);
    }

#ifdef CK_SSL
    /*
     * In the case of OpenSSL, it is possible that there is still
     * data waiting in the SSL session buffers that has not yet
     * been read by Kermit.  If this is the case we must process
     * it without calling select() because select() will not return
     * with an indication that there is data to be read from the
     * socket.  If there is no data pending in the SSL session
     * buffers then fall through to the select() code and wait for
     * some data to arrive.
     */
    if (tls_http_active_flag) {
        int error;

        x = SSL_pending(tls_http_con);
        if (x < 0) {
            debug(F111,"http_inc","SSL_pending error",x);
            http_close();
            return(-1);
        } else if ( x > 0 ) {
	  ssl_read:
            x = SSL_read(tls_http_con, &c, 1);
            error = SSL_get_error(tls_http_con,x);
            switch (error) {
            case SSL_ERROR_NONE:
                debug(F111,"http_inc SSL_ERROR_NONE","x",x);
                if (x > 0) {
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(c);          /* Return character. */
                } else if (x < 0) {
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-1);
                } else {
                    http_close();
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-2);
                }
            case SSL_ERROR_WANT_WRITE:
                debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0);
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            case SSL_ERROR_WANT_READ:
                debug(F100,"http_inc SSL_ERROR_WANT_READ","",0);
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            case SSL_ERROR_SYSCALL:
                if ( x == 0 ) { /* EOF */
                    http_close();
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-2);
                } else {
                    int rc = -1;
#ifdef NT
                    int gle = GetLastError();
                    debug(F111,"http_inc SSL_ERROR_SYSCALL",
                           "GetLastError()",gle);
                    rc = os2socketerror(gle);
                    if (rc == -1)
                        rc = -2;
                    else if ( rc == -2 )
                        rc = -1;
#endif /* NT */
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(rc);
                }
            case SSL_ERROR_WANT_X509_LOOKUP:
                debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            case SSL_ERROR_SSL:
                debug(F100,"http_inc SSL_ERROR_SSL","",0);
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            case SSL_ERROR_ZERO_RETURN:
                debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            default:
                debug(F100,"http_inc SSL_ERROR_?????","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            }
        }
    }
#endif /* CK_SSL */

#ifdef HTTP_BUFFERING
    /* Skip all the select() stuff if we have bytes buffered locally */
    if (http_count > 0)
      goto getfrombuffer;
#endif	/* HTTP_BUFFERING */

    {
#ifdef BSDSELECT
        fd_set rfds;
        struct timeval tv;
        int timeout = timo < 0 ? -timo : 1000 * timo;
        debug(F101,"http_inc BSDSELECT","",timo);

        for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
            int rc;
            debug(F111,"http_inc","timeout",timeout);
            /* Don't move select() initialization out of the loop. */
            FD_ZERO(&rfds);
            FD_SET(httpfd, &rfds);
            tv.tv_sec  = tv.tv_usec = 0L;
            if (timo)
                tv.tv_usec = (long) 100000L;
            else
                tv.tv_sec = 30;
#ifdef NT
            WSASafeToCancel = 1;
#endif /* NT */
            rc = select(FD_SETSIZE,
#ifndef __DECC
                         (fd_set *)
#endif /* __DECC */
                         &rfds, NULL, NULL, &tv);
            if (rc < 0) {
                int s_errno = socket_errno;
                debug(F111,"http_inc","select",rc);
                debug(F111,"http_inc","socket_errno",s_errno);
#ifdef HTTP_BUFFERING
		http_count = 0;
		http_bufp = 0;
#endif	/* HTTP_BUFFERING */
                if (s_errno)
                    return(-1);
            }
            debug(F111,"http_inc","select",rc);
#ifdef NT
            WSASafeToCancel = 0;
#endif /* NT */
            if (FD_ISSET(httpfd, &rfds)) {
                x = 0;
                break;
            } else {
                /* If waiting forever we have no way of knowing if the */
                /* socket closed so try writing a 0-length TCP packet  */
                /* which should force an error if the socket is closed */
                if (!timo) {
#ifdef TCPIPLIB
                    if ((rc = socket_write(httpfd,"",0)) < 0) {
#ifdef HTTP_BUFFERING
			http_count = 0;
			http_bufp = 0;
#endif	/* HTTP_BUFFERING */
                        int s_errno = socket_errno;
                        debug(F101,"http_inc socket_write error","",s_errno);
#ifdef OS2
                        if (os2socketerror(s_errno) < 0)
                            return(-2);
#endif /* OS2 */
                        return(-1); /* Call it an i/o error */
                    }
#else /* TCPIPLIB */
                    if ((rc = write(httpfd,"",0)) < 0) {
#ifdef HTTP_BUFFERING
			http_count = 0;
			http_bufp = 0;
#endif	/* HTTP_BUFFERING */
                        debug(F101,"http_inc socket_write error","",errno);
                        return(-1); /* Call it an i/o error */
                    }
#endif /* TCPIPLIB */
                }
                continue;
            }
        }
#ifdef NT
        WSASafeToCancel = 0;
#endif /* NT */
#else /* !BSDSELECT */
#ifdef IBMSELECT
 /*
  Was used by OS/2, currently not used, but might come in handy some day...
  ... and it came in handy!  For our TCP/IP layer, it avoids all the fd_set
  and timeval stuff since this is the only place where it is used.
 */
        int socket = httpfd;
        int timeout = timo < 0 ? -timo : 1000 * timo;

        debug(F101,"http_inc IBMSELECT","",timo);
        for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
            if (select(&socket, 1, 0, 0, 100L) == 1) {
                x = 0;
                break;
            }
        }
#endif /* IBMSELECT */
#endif /* BSDSELECT */
    }

    if (timo && x < 0) {        /* select() timed out */
#ifdef HTTP_BUFFERING
	http_count = 0;
	http_bufp = 0;
#endif	/* HTTP_BUFFERING */
        debug(F100,"http_inc select() timed out","",0);
        return(-1); /* Call it an i/o error */
    }

#ifdef CK_SSL
        if ( tls_http_active_flag ) {
            int error;
	  ssl_read2:
            x = SSL_read(tls_http_con, &c, 1);
            error = SSL_get_error(tls_http_con,x);
            switch (error) {
            case SSL_ERROR_NONE:
                debug(F111,"http_inc SSL_ERROR_NONE","x",x);
                if (x > 0) {
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(c);          /* Return character. */
                } else if (x < 0) {
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-1);
                } else {
                    http_close();
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-2);
                }
            case SSL_ERROR_WANT_WRITE:
                debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0);
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            case SSL_ERROR_WANT_READ:
                debug(F100,"http_inc SSL_ERROR_WANT_READ","",0);
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-1);
            case SSL_ERROR_SYSCALL:
                if ( x == 0 ) { /* EOF */
                    http_close();
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(-2);
                } else {
                    int rc = -1;
#ifdef NT
                    int gle = GetLastError();
                    debug(F111,"http_inc SSL_ERROR_SYSCALL",
                           "GetLastError()",gle);
                    rc = os2socketerror(gle);
                    if (rc == -1)
                        rc = -2;
                    else if ( rc == -2 )
                        rc = -1;
#endif /* NT */
#ifdef OS2
                    ReleaseTCPIPMutex();
#endif /* OS2 */
                    return(rc);
                }
            case SSL_ERROR_WANT_X509_LOOKUP:
                debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            case SSL_ERROR_SSL:
                debug(F100,"http_inc SSL_ERROR_SSL","",0);
#ifdef COMMENT
                http_close();
#endif /* COMMENT */
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            case SSL_ERROR_ZERO_RETURN:
                debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            default:
                debug(F100,"http_inc SSL_ERROR_?????","",0);
                http_close();
#ifdef OS2
                ReleaseTCPIPMutex();
#endif /* OS2 */
                return(-2);
            }
        }
#endif /* CK_SSL */

#ifdef HTTP_BUFFERING
/*
  Buffering code added by fdc 15 Dec 2005 for non-SSL case only because HTTP
  GETs were orders of magnitude too slow due to the single-byte read()s.  The
  file-descriptor swapping is pretty gross, but the more elegant solution
  (calling a nettchk() like routine with the fd as a parameter) doesn't work,
  because nettchk() relies on too many other routines that, like itself, are
  hardwired for ttyfd.
*/
  getfrombuffer:
	if (--http_count >= 0) {
	    c = http_inbuf[http_bufp++];
	    x = 1;
	} else {
	    int savefd;
	    savefd = ttyfd;
	    ttyfd = httpfd;
	    x = nettchk();
	    ttyfd = savefd;
	    debug(F101,"http_inc nettchk","",x);
	    if (x > HTTP_INBUFLEN)
	      x = HTTP_INBUFLEN;
#ifdef TCPIPLIB
	    x = socket_read(httpfd,http_inbuf,x);
#else  /* Not TCPIPLIB */
	    x = read(httpfd,http_inbuf,x);
#endif	/* TCPIPLIB */
	    http_count = 0;
	    http_bufp = 0;
	    if (x > 0) {
		c = http_inbuf[http_bufp++];
		http_count = x - 1;
	    }
	}
#else  /* Not HTTP_BUFFERING */
#ifdef TCPIPLIB
        x = socket_read(httpfd,&c,1);
#else  /* Not TCPIPLIB */
        x = read(httpfd,&c,1);
#endif	/* TCPIPLIB */
#endif	/* HTTP_BUFFERING */

        if (x <= 0) {
            int s_errno = socket_errno;
            debug(F101,"ttbufr socket_read","",x);
            debug(F101,"ttbufr socket_errno","",s_errno);
#ifdef OS2
            if (x == 0 || os2socketerror(s_errno) < 0) {
                http_close();
                ReleaseTCPIPMutex();
                return(-2);
            }
            ReleaseTCPIPMutex();
            return(-1);
#else /* OS2 */
            http_close();                      /* *** *** */
            return(-2);
#endif /* OS2 */
        }
        return(c);
}

void
#ifdef CK_ANSIC
http_set_code_reply(char * msg)
#else
http_set_code_reply(msg)
    char * msg;
#endif /* CK_ANSIC */
{
    char * p = msg;
    char buf[16];
    int i=0;

    while ( *p != SP && *p != NUL ) {
        buf[i] = *p;
        p++;
        i++;
    }

    http_code = atoi(buf);

    while ( *p == SP )
        p++;

    ckstrncpy(http_reply_str,p,HTTPBUFLEN);
}

int
#ifdef CK_ANSIC
http_get(char * agent, char ** hdrlist, char * user,
         char * pwd, char array, char * local, char * remote,
         int stdio)
#else
http_get(agent, hdrlist, user, pwd, array, local, remote, stdio)
    char * agent; char ** hdrlist; char * user;
    char * pwd; char array; char * local; char * remote;
    int stdio;
#endif /* CK_ANSIC */
{
    char * request = NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
#ifdef OS2
    struct utimbuf u_t;
#else /* OS2 */
#ifdef SYSUTIMEH
    struct utimbuf u_t;
#else
    struct utimbuf {
        time_t atime;
        time_t mtime;
    } u_t;
#endif /* SYSUTIMH */
#endif /* OS2 */
    time_t mod_t = 0;
    time_t srv_t = 0;
    time_t local_t = 0;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    char * headers[HTTPHEADCNT];
    int closecon = 0;
    int chunked = 0;
    int zfile = 0;
    int first = 1;

#ifdef DEBUG
    if (deblog) {
        debug(F101,"http_get httpfd","",httpfd);
        debug(F110,"http_agent",agent,0);
        debug(F110,"http_user",user,0);
        debug(F110,"http_local",local,0);
        debug(F110,"http_remote",remote,0);
    }
#endif /* DEBUG */
    if (!remote) remote = "";

    if (httpfd == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }
    len = 8;                            /* GET */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if (hdrlist) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += (int) strlen(http_host_port) + 8;

    if (agent)
      len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));      /* NOT PORTABLE */
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"GET %s %s\r\n",remote,HTTP_VERSION);       /* safe */
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    ckstrncat(request,"\r\n",len);

  getreq:
    if (http_tol((CHAR *)request,strlen(request)) < 0)
    {
        http_close();
        if ( first ) {
            first--;
            http_reopen();
            goto getreq;
        }
        rc = -1;
        goto getexit;
    }

    /* Process the headers */
    local_t = time(NULL);
    nullline = 0;
    i = 0;
    len = -1;
    while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
        buf[i] = ch;
        if ( buf[i] == 10 ) { /* found end of line */
            if (i > 0 && buf[i-1] == 13)
              i--;
            if (i < 1)
              nullline = 1;
            buf[i] = '\0';
            if (array && !nullline && hdcnt < HTTPHEADCNT)
              makestr(&headers[hdcnt++],buf);
            if (!ckstrcmp(buf,"HTTP",4,0)) {
                http_fnd = 1;
                j = ckindex(" ",buf,0,0,0);
                p = &buf[j];
                while ( isspace(*p) )
                  p++;
                switch ( p[0] ) {
                  case '1':             /* Informational message */
                    break;
                  case '2':             /* Success */
                    break;
                  case '3':             /* Redirection */
                  case '4':             /* Client failure */
                  case '5':             /* Server failure */
                  default:              /* Unknown */
                    if (!quiet)
                      printf("Failure: Server reports %s\n",p);
                    rc = -1;
                    local = NULL;
                }
                http_set_code_reply(p);
#ifdef CMDATE2TM
            } else if (!ckstrcmp(buf,"Last-Modified",13,0)) {
                mod_t = http_date(&buf[15]);
            } else if (!ckstrcmp(buf,"Date",4,0)) {
                srv_t = http_date(&buf[4]);
#endif /* CMDATE2TM */
            } else if (!ckstrcmp(buf,"Connection:",11,0)) {
                if ( ckindex("close",buf,11,0,0) != 0 )
                    closecon = 1;
            } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
                len = http_content_length(&buf[16]);
            } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
                if ( ckindex("chunked",buf,18,0,0) != 0 )
                    chunked = 1;
		debug(F101,"http_get chunked","",chunked);
            }
            i = 0;
        } else {
            i++;
        }
    }
    if (ch < 0 && first) {
        first--;
        http_close();
        http_reopen();
        goto getreq;
    }
    if (http_fnd == 0) {
        rc = -1;
        closecon = 1;
        goto getexit;
    }

    /* Now we have the contents of the file */
    if ( local && local[0] ) {
        if (zopeno(ZOFILE,local,NULL,NULL))
            zfile = 1;
        else
            rc = -1;
    }

    if ( chunked ) {
        while ((len = http_get_chunk_len()) > 0) {
            while (len && (ch = http_inc(0)) >= 0) {
                len--;
                if ( zfile )
                    zchout(ZOFILE,(CHAR)ch);
                if ( stdio )
                    conoc((CHAR)ch);
            }
            if ((ch = http_inc(0)) != CK_CR)
                break;
            if ((ch = http_inc(0)) != LF)
                break;
        }
    } else {
        while (len && (ch = http_inc(0)) >= 0) {
            len--;
            if ( zfile )
                zchout(ZOFILE,(CHAR)ch);
            if ( stdio )
                conoc((CHAR)ch);
        }
    }

    if ( zfile )
        zclose(ZOFILE);

    if ( chunked ) {            /* Parse Trailing Headers */
        nullline = 0;
        while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
            buf[i] = ch;
            if ( buf[i] == 10 ) { /* found end of line */
                if (i > 0 && buf[i-1] == 13)
                  i--;
                if (i < 1)
                  nullline = 1;
                buf[i] = '\0';
                if (array && !nullline && hdcnt < HTTPHEADCNT)
                    makestr(&headers[hdcnt++],buf);
#ifdef CMDATE2TM
                if (!ckstrcmp(buf,"Last-Modified",13,0)) {
                    mod_t = http_date(&buf[15]);
                } else if (!ckstrcmp(buf,"Date",4,0)) {
                    srv_t = http_date(&buf[4]);
                }
#endif /* CMDATE2TM */
                else if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                }
                i = 0;
            } else {
                i++;
            }
        }
    }

    if ( zfile ) {              /* Set timestamp */
#ifndef NOSETTIME
#ifdef OS2
        u_t.actime = srv_t ? srv_t : local_t;
        u_t.modtime = mod_t ? mod_t : local_t;
#else /* OS2 */
#ifdef SYSUTIMEH
        u_t.actime = srv_t ? srv_t : local_t;
        u_t.modtime = mod_t ? mod_t : local_t;
#else
#ifdef BSD44
        u_t[0].tv_sec = srv_t ? srv_t : local_t;
        u_t[1].tv_sec = mod_t ? mod_t : local_t;
#else
        u_t.mtime = srv_t ? srv_t : local_t;
        u_t.atime = mod_t ? mod_t : local_t;
#endif /* BSD44 */
#endif /* SYSUTIMEH */
#endif /* OS2 */
            utime(local,&u_t);
#endif /* NOSETTIME */
    }

  getexit:
    if (array)
      http_mkarray(headers,hdcnt,array);

    if ( closecon )
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_head(char * agent, char ** hdrlist, char * user,
          char * pwd, char array, char * local, char * remote,
          int stdio)
#else
http_head(agent, hdrlist, user, pwd, array, local, remote, stdio)
    char * agent; char ** hdrlist; char * user;
    char * pwd; char array; char * local; char * remote;
    int stdio;
#endif /* CK_ANSIC */
{
    char * request = NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    char * headers[HTTPHEADCNT];
    int  closecon = 0;
    int  first = 1;

    if (httpfd == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }
    len = 9;                            /* HEAD */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += strlen(http_host_port) + 8;

    if (agent)
      len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));      /* NOT PORTABLE */
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = (char *)malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"HEAD %s %s\r\n",remote,HTTP_VERSION);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    ckstrncat(request,"\r\n",len);

    if ( local && local[0] ) {
        if (!zopeno(ZOFILE,local,NULL,NULL)) {
            free(request);
            return(-1);
        }
    }

  headreq:
    if (http_tol((CHAR *)request,strlen(request)) < 0)
    {
        http_close();
        if ( first ) {
            first--;
            http_reopen();
            goto headreq;
        }
        rc = -1;
        goto headexit;
    }

    /* Process the headers */

    local_t = time(NULL);
    nullline = 0;
    i = 0;
    while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
        buf[i] = ch;
        if (buf[i] == 10) {             /* found end of line */
            if (i > 0 && buf[i-1] == 13)
              i--;
            if (i < 1)
              nullline = 1;
            buf[i] = '\0';
            if (array && !nullline && hdcnt < HTTPHEADCNT)
              makestr(&headers[hdcnt++],buf);
            if (!ckstrcmp(buf,"HTTP",4,0)) {
                http_fnd = 1;
                j = ckindex(" ",buf,0,0,0);
                p = &buf[j];
                while (isspace(*p))
                  p++;
                switch (p[0]) {
                  case '1':             /* Informational message */
                    break;
                  case '2':             /* Success */
                    break;
                  case '3':             /* Redirection */
                  case '4':             /* Client failure */
                  case '5':             /* Server failure */
                  default:              /* Unknown */
                    if (!quiet)
                      printf("Failure: Server reports %s\n",p);
                    rc = -1;
                }
                http_set_code_reply(p);
            } else {
                if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                }
                if ( local && local[0] ) {
                    zsout(ZOFILE,buf);
                    zsout(ZOFILE,"\r\n");
                }
                if (stdio)
                    printf("%s\r\n",buf);
            }
            i = 0;
        } else {
            i++;
        }
    }
    if (ch < 0 && first) {
        first--;
        http_close();
        http_reopen();
        goto headreq;
    }
    if ( http_fnd == 0 )
        rc = -1;

    if (array)
      http_mkarray(headers,hdcnt,array);

  headexit:
    if ( local && local[0] )
        zclose(ZOFILE);
    if (closecon)
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_index(char * agent, char ** hdrlist, char * user, char * pwd,
             char array, char * local, char * remote, int stdio)
#else
http_index(agent, hdrlist, user, pwd, array, local, remote, stdio)
    char * agent; char ** hdrlist; char * user; char * pwd;
    char array; char * local; char * remote; int stdio;
#endif /* CK_ANSIC */
{
    char * request = NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    char * headers[HTTPHEADCNT];
    int  closecon = 0;
    int  chunked = 0;
    int  zfile = 0;
    int  first = 1;

    if (httpfd == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }
    len = 10;                            /* INDEX */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += strlen(http_host_port) + 8;

    if (agent)
        len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"INDEX %s\r\n",HTTP_VERSION);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    ckstrncat(request,"\r\n",len);
  indexreq:
    if (http_tol((CHAR *)request,strlen(request)) < 0)
    {
        http_close();
        if ( first ) {
            first--;
            http_reopen();
            goto indexreq;
        }
        rc = -1;
        goto indexexit;
    }

    /* Process the headers */
    local_t = time(NULL);
    nullline = 0;
    i = 0;
    len = -1;
    while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
        buf[i] = ch;
        if (buf[i] == 10) {             /* found end of line */
            if (i > 0 && buf[i-1] == 13)
              i--;
            if (i < 1)
              nullline = 1;
            buf[i] = '\0';
            if (array && !nullline && hdcnt < HTTPHEADCNT)
              makestr(&headers[hdcnt++],buf);
            if (!ckstrcmp(buf,"HTTP",4,0)) {
                http_fnd = 1;
                j = ckindex(" ",buf,0,0,0);
                p = &buf[j];
                while (isspace(*p))
                  p++;
                switch ( p[0] ) {
                  case '1':             /* Informational message */
                    break;
                  case '2':             /* Success */
                    break;
                  case '3':             /* Redirection */
                  case '4':             /* Client failure */
                  case '5':             /* Server failure */
                  default:              /* Unknown */
                    if (!quiet)
                      printf("Failure: Server reports %s\n",p);
                    rc = -1;
                }
                http_set_code_reply(p);
            } else if ( !nullline ) {
                if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
                    len = http_content_length(&buf[16]);
                } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
                    if ( ckindex("chunked",buf,18,0,0) != 0 )
                        chunked = 1;
		    debug(F101,"http_index chunked","",chunked);
                }
                printf("%s\n",buf);
            }
            i = 0;
        } else {
            i++;
        }
    }

    if (ch < 0 && first) {
        first--;
        http_close();
        http_reopen();
        goto indexreq;
    }
    if ( http_fnd == 0 ) {
        rc = -1;
        closecon = 1;
        goto indexexit;
    }

    /* Now we have the contents of the file */
    if ( local && local[0] ) {
        if (zopeno(ZOFILE,local,NULL,NULL))
            zfile = 1;
        else
            rc = -1;
    }

    if ( chunked ) {
        while ((len = http_get_chunk_len()) > 0) {
            while (len && (ch = http_inc(0)) >= 0) {
                len--;
                if ( zfile )
                    zchout(ZOFILE,(CHAR)ch);
                if ( stdio )
                    conoc((CHAR)ch);
            }
            if ((ch = http_inc(0)) != CK_CR)
                break;
            if ((ch = http_inc(0)) != LF)
                break;
        }
    } else {
        while (len && (ch = http_inc(0)) >= 0) {
            len--;
            if ( zfile )
                zchout(ZOFILE,(CHAR)ch);
            if ( stdio )
                conoc((CHAR)ch);
        }
    }

    if ( zfile )
        zclose(ZOFILE);

    if ( chunked ) {            /* Parse Trailing Headers */
        nullline = 0;
        while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
            buf[i] = ch;
            if ( buf[i] == 10 ) { /* found end of line */
                if (i > 0 && buf[i-1] == 13)
                  i--;
                if (i < 1)
                  nullline = 1;
                buf[i] = '\0';
                if (array && !nullline && hdcnt < HTTPHEADCNT)
                    makestr(&headers[hdcnt++],buf);
                if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                }
                i = 0;
            } else {
                i++;
            }
        }
    }
    rc = 0;

  indexexit:
    if (array)
      http_mkarray(headers,hdcnt,array);

    if (closecon)
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_put(char * agent, char ** hdrlist, char * mime, char * user,
         char * pwd, char array, char * local, char * remote,
         char * dest, int stdio)
#else
http_put(agent, hdrlist, mime, user, pwd, array, local, remote, dest, stdio)
    char * agent; char ** hdrlist; char * mime; char * user;
    char * pwd; char array; char * local; char * remote; char * dest;
    int stdio;
#endif /* CK_ANSIC */
{
    char * request=NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    int  filelen;
    char * headers[HTTPHEADCNT];
    int  closecon = 0;
    int  chunked = 0;
    int  first = 1;
    int  zfile = 0;

    if (httpfd == -1)
      return(-1);
    if (!mime) mime = "";
    if (!remote) remote = "";
    if (!local) local = "";
    if (!*local) return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }
    filelen = zchki(local);
    if (filelen < 0)
      return(-1);

    /* Compute length of request header */
    len = 8;                            /* PUT */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += strlen(http_host_port) + 8;

    if (agent)
      len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
    len += 16 + strlen(mime);           /* Content-type: */
    len += 32;                          /* Content-length: */
    len += 32;                          /* Date: */
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"PUT %s %s\r\n",remote,HTTP_VERSION);
    ckstrncat(request,"Date: ",len);
#ifdef CMDATE2TM
    ckstrncat(request,http_now(),len);
#else
    ckstrncat(request,...,len);
#endif /* CMDATE2TM */
    ckstrncat(request,"\r\n",len);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
    ckstrncat(request,"Content-type: ",len);
    ckstrncat(request,mime,len);
    ckstrncat(request,"\r\n",len);
    sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */
    ckstrncat(request,buf,len);
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    ckstrncat(request,"\r\n",len);

    /* Now we have the contents of the file */
    if (zopeni(ZIFILE,local)) {

      putreq:				/* Send request */
        if (http_tol((CHAR *)request,strlen(request)) <= 0) {
            http_close();
            if ( first ) {
                first--;
                http_reopen();
                goto putreq;
            }
            zclose(ZIFILE);
            rc = -1;
            goto putexit;
        }
        /* Request headers have been sent */

        i = 0;
        while (zchin(ZIFILE,&ch) == 0) {
            buf[i++] = ch;
            if (i == HTTPBUFLEN) {
                if (http_tol((CHAR *)buf,HTTPBUFLEN) <= 0) {
                    http_close();
                    if ( first ) {
                        first--;
                        http_reopen();
                        goto putreq;
                    }
                }
                i = 0;
            }
        }
        if (i > 0) {
            if (http_tol((CHAR *)buf,i) < 0) {
                http_close();
                if ( first ) {
                    first--;
                    http_reopen();
                    goto putreq;
                }
            }
        }
        zclose(ZIFILE);

        /* Process the response headers */
        local_t = time(NULL);
        nullline = 0;
        i = 0;
        len = -1;
        while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
            buf[i] = ch;
            if (buf[i] == 10) {         /* found end of line */
                if (i > 0 && buf[i-1] == 13)
                  i--;
                if (i < 1)
                  nullline = 1;
                buf[i] = '\0';
                if (array && !nullline && hdcnt < HTTPHEADCNT)
                  makestr(&headers[hdcnt++],buf);
                if (!ckstrcmp(buf,"HTTP",4,0)) {
                    http_fnd = 1;
                    j = ckindex(" ",buf,0,0,0);
                    p = &buf[j];
                    while (isspace(*p))
                      p++;
                    switch (p[0]) {
                      case '1':         /* Informational message */
                        break;
                      case '2':         /* Success */
                        break;
                      case '3':         /* Redirection */
                      case '4':         /* Client failure */
                      case '5':         /* Server failure */
                      default:          /* Unknown */
                        if (!quiet)
                          printf("Failure: Server reports %s\n",p);
                        rc = -1;
                    }
                    http_set_code_reply(p);
                } else {
                    if (!ckstrcmp(buf,"Connection:",11,0)) {
                        if ( ckindex("close",buf,11,0,0) != 0 )
                            closecon = 1;
                    } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
                        len = http_content_length(&buf[16]);
                    } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
                        if ( ckindex("chunked",buf,18,0,0) != 0 )
                            chunked = 1;
			debug(F101,"http_put chunked","",chunked);
                    }
                    if ( stdio )
                        printf("%s\n",buf);
                }
                i = 0;
            } else {
                i++;
            }
        }
        if (ch < 0 && first) {
            first--;
            http_close();
            http_reopen();
            goto putreq;
        }
        if ( http_fnd == 0 ) {
            closecon = 1;
            rc = -1;
            goto putexit;
        }

        /* Any response data? */
        if ( dest && dest[0] ) {
            if (zopeno(ZOFILE,dest,NULL,NULL))
                zfile = 1;
            else
                rc = -1;
        }

        if ( chunked ) {
            while ((len = http_get_chunk_len()) > 0) {
                while (len && (ch = http_inc(0)) >= 0) {
                    len--;
                    if ( zfile )
                        zchout(ZOFILE,(CHAR)ch);
                    if ( stdio )
                        conoc((CHAR)ch);
                }
                if ((ch = http_inc(0)) != CK_CR)
                    break;
                if ((ch = http_inc(0)) != LF)
                    break;
            }
        } else {
            while (len && (ch = http_inc(0)) >= 0) {
                len--;
                if ( zfile )
                    zchout(ZOFILE,(CHAR)ch);
                if ( stdio )
                    conoc((CHAR)ch);
            }
        }

        if ( zfile )
            zclose(ZOFILE);

        if ( chunked ) {            /* Parse Trailing Headers */
            nullline = 0;
            while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
                buf[i] = ch;
                if ( buf[i] == 10 ) { /* found end of line */
                    if (i > 0 && buf[i-1] == 13)
                      i--;
                    if (i < 1)
                      nullline = 1;
                    buf[i] = '\0';
                    if (array && !nullline && hdcnt < HTTPHEADCNT)
                        makestr(&headers[hdcnt++],buf);
                    if (!ckstrcmp(buf,"Connection:",11,0)) {
                        if ( ckindex("close",buf,11,0,0) != 0 )
                            closecon = 1;
                    }
                    i = 0;
                } else {
                    i++;
                }
            }
        }
    } else {
        rc = -1;
    }

  putexit:
    if ( array )
        http_mkarray(headers,hdcnt,array);

    if (closecon)
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_delete(char * agent, char ** hdrlist, char * user,
          char * pwd, char array, char * remote)
#else
http_delete(agent, hdrlist, user, pwd, array, remote)
    char * agent; char ** hdrlist; char * user;
    char * pwd; char array; char * remote;
#endif /* CK_ANSIC */
{
    char * request=NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    char * headers[HTTPHEADCNT];
    int  closecon = 0;
    int  chunked = 0;
    int  first = 1;

    if (httpfd == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }

    /* Compute length of request header */
    len = 11;                            /* DELETE */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += strlen(http_host_port) + 8;

    if (agent)
      len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
    len += 32;                          /* Date: */
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"DELETE %s %s\r\n",remote,HTTP_VERSION);
    ckstrncat(request,"Date: ",len);
#ifdef CMDATE2TM
    ckstrncat(request,http_now(),len);
#else
    ckstrncat(request,...,len);
#endif /* CMDATE2TM */
    ckstrncat(request,"\r\n",len);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    ckstrncat(request,"\r\n",len);
  delreq:
    if (http_tol((CHAR *)request,strlen(request)) < 0)
    {
        http_close();
        if ( first ) {
            first--;
            http_reopen();
            goto delreq;
        }
        rc = -1;
        goto delexit;
    }

    /* Process the response headers */
    local_t = time(NULL);
    nullline = 0;
    i = 0;
    len = -1;
    while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
        buf[i] = ch;
        if (buf[i] == 10) {         /* found end of line */
            if (i > 0 && buf[i-1] == 13)
              i--;
            if (i < 1)
              nullline = 1;
            buf[i] = '\0';
            if (array && !nullline && hdcnt < HTTPHEADCNT)
                makestr(&headers[hdcnt++],buf);
            if (!ckstrcmp(buf,"HTTP",4,0)) {
                http_fnd = 1;
                j = ckindex(" ",buf,0,0,0);
                p = &buf[j];
                while (isspace(*p))
                  p++;
                switch (p[0]) {
                  case '1':             /* Informational message */
                    break;
                  case '2':             /* Success */
                    break;
                  case '3':             /* Redirection */
                  case '4':             /* Client failure */
                  case '5':             /* Server failure */
                  default:              /* Unknown */
                    if (!quiet)
                      printf("Failure: Server reports %s\n",p);
                    rc = -1;
                }
                http_set_code_reply(p);
            } else {
                if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
                    len = http_content_length(&buf[16]);
                } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
                    if ( ckindex("chunked",buf,18,0,0) != 0 )
                        chunked = 1;
		    debug(F101,"http_delete chunked","",chunked);
                }
                printf("%s\n",buf);
            }
            i = 0;
        } else {
            i++;
        }
    }
    if (ch < 0 && first) {
        first--;
        http_close();
        http_reopen();
        goto delreq;
    }
    if ( http_fnd == 0 ) {
        rc = -1;
        closecon = 1;
        goto delexit;
    }

    /* Any response data? */
    if ( chunked ) {
        while ((len = http_get_chunk_len()) > 0) {
            while (len && (ch = http_inc(0)) >= 0) {
                len--;
                conoc((CHAR)ch);
            }
            if ((ch = http_inc(0)) != CK_CR)
                break;
            if ((ch = http_inc(0)) != LF)
                break;
        }
    } else {
        while (len && (ch = http_inc(0)) >= 0) {
            len--;
            conoc((CHAR)ch);
        }
    }

    if ( chunked ) {            /* Parse Trailing Headers */
        nullline = 0;
        while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
            buf[i] = ch;
            if ( buf[i] == 10 ) { /* found end of line */
                if (i > 0 && buf[i-1] == 13)
                  i--;
                if (i < 1)
                  nullline = 1;
                buf[i] = '\0';
                if (array && !nullline && hdcnt < HTTPHEADCNT)
                    makestr(&headers[hdcnt++],buf);
                if (!ckstrcmp(buf,"Connection:",11,0)) {
                    if ( ckindex("close",buf,11,0,0) != 0 )
                        closecon = 1;
                }
                i = 0;
            } else {
                i++;
            }
        }
    }

  delexit:
    if (array)
        http_mkarray(headers,hdcnt,array);

    if (closecon)
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_post(char * agent, char ** hdrlist, char * mime, char * user,
          char * pwd, char array, char * local, char * remote,
          char * dest, int stdio)
#else
http_post(agent, hdrlist, mime, user, pwd, array, local, remote, dest,
          stdio)
    char * agent; char ** hdrlist; char * mime; char * user;
    char * pwd; char array; char * local; char * remote; char * dest;
    int stdio;
#endif /* CK_ANSIC */
{
    char * request=NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    ch;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    int  filelen;
    char * headers[HTTPHEADCNT];
    int  closecon = 0;
    int  chunked = 0;
    int  zfile = 0;
    int  first = 1;

    if (httpfd == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }
    filelen = zchki(local);
    if (filelen < 0)
      return(-1);

    /* Compute length of request header */
    len = 9;                            /* POST */
    len += strlen(HTTP_VERSION);
    len += strlen(remote);
    len += 16;

    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    len += strlen(http_host_port) + 8;

    if (agent)
      len += 13 + strlen(agent);
    if (user) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 24;
    }
    len += 16 + strlen(mime);           /* Content-type: */
    len += 32;                          /* Content-length: */
    len += 32;                          /* Date: */
#ifdef HTTP_CLOSE
    len += 19;                          /* Connection: close */
#endif
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"POST %s %s\r\n",remote,HTTP_VERSION);
    ckstrncat(request,"Date: ",len);
    ckstrncat(request,http_now(),len);
    ckstrncat(request,"\r\n",len);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user) {
        ckstrncat(request,"Authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
    }
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
    ckstrncat(request,"Content-type: ",len);
    ckstrncat(request,mime,len);
    ckstrncat(request,"\r\n",len);
#ifdef HTTP_CLOSE
    ckstrncat(request,"Connection: close\r\n",len);
#endif
    sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */
    ckstrncat(request,buf,len);
    ckstrncat(request,"\r\n",len);

    /* Now we have the contents of the file */
  postopen:
    if (zopeni(ZIFILE,local)) {
      postreq:
        if (http_tol((CHAR *)request,strlen(request)) < 0)
        {
            http_close();
            if ( first ) {
                first--;
                http_reopen();
                goto postreq;
            }
            rc = -1;
            zclose(ZIFILE);
            goto postexit;
        }

        i = 0;
        while (zchin(ZIFILE,&ch) == 0) {
            buf[i++] = ch;
            if (i == HTTPBUFLEN) {
                http_tol((CHAR *)buf,HTTPBUFLEN);
                i = 0;
            }
        }
        if (i > 0)
          http_tol((CHAR *)buf,HTTPBUFLEN);
        zclose(ZIFILE);

        /* Process the response headers */
        local_t = time(NULL);
        nullline = 0;
        i = 0;
        len = -1;
        while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
            buf[i] = ch;
            if (buf[i] == 10) {         /* found end of line */
                if (i > 0 && buf[i-1] == 13)
                  i--;
                if (i < 1)
                  nullline = 1;
                buf[i] = '\0';
                if (array && !nullline && hdcnt < HTTPHEADCNT)
                  makestr(&headers[hdcnt++],buf);
                if (!ckstrcmp(buf,"HTTP",4,0)) {
                    http_fnd = 1;
                    j = ckindex(" ",buf,0,0,0);
                    p = &buf[j];
                    while (isspace(*p))
                      p++;
                    switch (p[0]) {
                      case '1':         /* Informational message */
                        break;
                      case '2':         /* Success */
                        break;
                      case '3':         /* Redirection */
                      case '4':         /* Client failure */
                      case '5':         /* Server failure */
                      default:          /* Unknown */
                        if (!quiet)
                          printf("Failure: Server reports %s\n",p);
                        rc = -1;
                    }
                    http_set_code_reply(p);
                } else {
                    if (!ckstrcmp(buf,"Connection:",11,0)) {
                        if ( ckindex("close",buf,11,0,0) != 0 )
                            closecon = 1;
                    } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
                        len = http_content_length(&buf[16]);
                    } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
                        if ( ckindex("chunked",buf,18,0,0) != 0 )
                            chunked = 1;
			debug(F101,"http_post chunked","",chunked);
                    }
                    if (stdio)
                        printf("%s\n",buf);
                }
                i = 0;
            } else {
                i++;
            }
        }
        if (ch < 0 && first) {
            first--;
            http_close();
            http_reopen();
            goto postopen;
        }
        if (http_fnd == 0) {
            rc = -1;
            closecon = 1;
            goto postexit;
        }

        /* Any response data? */
        if ( dest && dest[0] ) {
            if (zopeno(ZOFILE,dest,NULL,NULL))
                zfile = 1;
            else
                rc = -1;
        }

        if ( chunked ) {
            while ((len = http_get_chunk_len()) > 0) {
                while (len && (ch = http_inc(0)) >= 0) {
                    len--;
                    if ( zfile )
                        zchout(ZOFILE,(CHAR)ch);
                    if ( stdio )
                        conoc((CHAR)ch);
                }
                if ((ch = http_inc(0)) != CK_CR)
                    break;
                if ((ch = http_inc(0)) != LF)
                    break;
            }
        } else {
            while (len && (ch = http_inc(0)) >= 0) {
                len--;
                if ( zfile )
                    zchout(ZOFILE,(CHAR)ch);
                if ( stdio )
                    conoc((CHAR)ch);
            }
        }

        if ( zfile )
            zclose(ZOFILE);

        if ( chunked ) {            /* Parse Trailing Headers */
            nullline = 0;
            while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
                buf[i] = ch;
                if ( buf[i] == 10 ) { /* found end of line */
                    if (i > 0 && buf[i-1] == 13)
                      i--;
                    if (i < 1)
                      nullline = 1;
                    buf[i] = '\0';
                    if (array && !nullline && hdcnt < HTTPHEADCNT)
                        makestr(&headers[hdcnt++],buf);
                    if (!ckstrcmp(buf,"Connection:",11,0)) {
                        if ( ckindex("close",buf,11,0,0) != 0 )
                            closecon = 1;
                    }
                    i = 0;
                } else {
                    i++;
                }
            }
        }
    } else {
        rc = -1;
    }

  postexit:
    if (array)
        http_mkarray(headers,hdcnt,array);
    if (closecon)
        http_close();
    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}

int
#ifdef CK_ANSIC
http_connect(int socket, char * agent, char ** hdrlist, char * user,
             char * pwd, char array, char * host_port)
#else
http_connect(socket, agent, hdrlist, user, pwd, array, host_port)
    int socket;
    char * agent; char ** hdrlist; char * user;
    char * pwd; char array; char * host_port;
#endif /* CK_ANSIC */
{
    char * request=NULL;
    int    i, j, len = 0, hdcnt = 0, rc = 0;
    int    http_fnd = 0;
    char   buf[HTTPBUFLEN], *p, ch;
    int    nullline;
    time_t local_t;
    char passwd[64];
    char b64in[128];
    char b64out[256];
    char * headers[HTTPHEADCNT];
    int    connected = 0;

    tcp_http_proxy_errno = 0;

    if (socket == -1)
      return(-1);

    if (array) {
        for (i = 0; i < HTTPHEADCNT; i++)
          headers[i] = NULL;
    }

    /* Compute length of request header */
    len = 12;                            /* CONNECT */
    len += strlen(HTTP_VERSION);
    len += strlen(host_port);
    len += (int) strlen(http_host_port) + 8;
    len += 16;
    len += strlen("Proxy-Connection: Keep-Alive\r\n");
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++)
            len += strlen(hdrlist[i]) + 2;
    }
    if (agent && agent[0])
      len += 13 + strlen(agent);
    if (user && user[0]) {
        if (!pwd) {
            readpass("Password: ",passwd,64);
            pwd = passwd;
        }
        ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
        j = b8tob64(b64in,strlen(b64in),b64out,256);
        memset(pwd,0,strlen(pwd));
        if (j < 0)
          return(-1);
        b64out[j] = '\0';
        len += j + 72;
    }
    len += 32;                          /* Date: */
    len += 3;                           /* blank line + null */

    request = malloc(len);
    if (!request)
      return(-1);

    sprintf(request,"CONNECT %s %s\r\n",host_port,HTTP_VERSION);
    ckstrncat(request,"Date: ",len);
#ifdef CMDATE2TM
    ckstrncat(request,http_now(),len);
#else
    strcat(request,...);
#endif /* CMDATE2TM */
    ckstrncat(request,"\r\n",len);
    ckstrncat(request,"Host: ", len);
    ckstrncat(request,http_host_port, len);
    ckstrncat(request,"\r\n",len);
    if (agent && agent[0]) {
        ckstrncat(request,"User-agent: ",len);
        ckstrncat(request,agent,len);
        ckstrncat(request,"\r\n",len);
    }
    if (user && user[0]) {
        ckstrncat(request,"Proxy-authorization: Basic ",len);
        ckstrncat(request,b64out,len);
        ckstrncat(request,"\r\n",len);
        ckstrncat(request,"Extension: Security/Remote-Passphrase\r\n",len);
    }
    ckstrncat(request,"Proxy-Connection: Keep-Alive\r\n",len);
    if ( hdrlist ) {
        for (i = 0; hdrlist[i]; i++) {
            ckstrncat(request,hdrlist[i],len);
            ckstrncat(request,"\r\n",len);
        }
    }
    ckstrncat(request,"\r\n",len);
    len = strlen(request);

#ifdef TCPIPLIB
    /* Send request */
    if (socket_write(socket,(CHAR *)request,strlen(request)) < 0) {
      rc = -1;
      goto connexit;
    }
#else
    if (write(socket,(CHAR *)request,strlen(request)) < 0) { /* Send request */
        rc = -1;
        goto connexit;
    }
#endif /* TCPIPLIB */

    /* Process the response headers */
    local_t = time(NULL);
    nullline = 0;
    i = 0;
    while (!nullline &&
#ifdef TCPIPLIB
           (socket_read(socket,&ch,1) == 1) &&
#else
           (read(socket,&ch,1) == 1) &&
#endif /* TCPIPLIB */
           i < HTTPBUFLEN) {
        buf[i] = ch;
        if (buf[i] == 10) {         /* found end of line */
            if (i > 0 && buf[i-1] == 13)
              i--;
            if (i < 1)
              nullline = 1;
            buf[i] = '\0';

            if (array && !nullline && hdcnt < HTTPHEADCNT)
                makestr(&headers[hdcnt++],buf);
            if (!ckstrcmp(buf,"HTTP",4,0)) {
                http_fnd = 1;
                j = ckindex(" ",buf,0,0,0);
                p = &buf[j];
                while (isspace(*p))
                  p++;
                tcp_http_proxy_errno = atoi(p);
                switch (p[0]) {
                  case '1':             /* Informational message */
                    break;
                  case '2':             /* Success */
                    connected = 1;
                    break;
                  case '3':             /* Redirection */
                  case '4':             /* Client failure */
                  case '5':             /* Server failure */
                  default:              /* Unknown */
                    if (!quiet)
                      printf("Failure: Server reports %s\n",p);
                    rc = -1;
                }
                http_set_code_reply(p);
            } else {
                printf("%s\n",buf);
            }
            i = 0;
        } else {
            i++;
        }
    }
    if ( http_fnd == 0 )
        rc = -1;

    if (array)
        http_mkarray(headers,hdcnt,array);

  connexit:
    if ( !connected ) {
        if ( socket == ttyfd ) {
            ttclos(0);
        }
        else if ( socket == httpfd ) {
            http_close();
        }
    }

    free(request);
    for (i = 0; i < hdcnt; i++) {
        if (headers[i])
          free(headers[i]);
    }
    return(rc);
}
#endif /* NOHTTP */

#ifdef CK_DNS_SRV
/*
  This whole DNS SRV client is IPv4-only.

  See the dns_addrs comment in ckgetservice() above for why this was deferred
  rather than converted.
*/

#define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto dnsout
#define CHECK(x,y) if (x + y > size + answer.bytes) goto dnsout
#define NTOHSP(x,y) x[0] << 8 | x[1]; x += y

#ifndef CKQUERYTYPE
#ifdef UNIXWARE
#ifndef UW7
#define CKQUERYTYPE CHAR
#endif /* UW7 */
#endif /* UNIXWARE */
#endif /* CKQUERYTYPE */

#ifndef CKQUERYTYPE
#define CKQUERYTYPE char
#endif /* CKQUERYTYPE */

/* 1 is success, 0 is failure */
int
#ifdef CK_ANSIC
locate_srv_dns(char *host, char *service, char *protocol,
               struct sockaddr **addr_pp, int *naddrs )
#else
locate_srv_dns(host, service, protocol, addr_pp, naddrs)
    char *host;
    char *service;
    char *protocol;
    struct sockaddr **addr_pp;
    int *naddrs;
#endif /* CK_ANSIC */
{
    int nout, j, count;
    union {
        unsigned char bytes[2048];
        HEADER hdr;
    } answer;
    unsigned char *p=NULL;
    CKQUERYTYPE query[MAX_DNS_NAMELEN];
#ifdef CK_ANSIC
    const char * h;
#else
    char * h;
#endif /* CK_ANSIC */
    struct sockaddr *addr = NULL;
    struct sockaddr_in *sin = NULL;
    struct hostent *hp = NULL;
    int type, class;
    int priority, weight, size, len, numanswers, numqueries, rdlen;
    unsigned short port;
#ifdef CK_ANSIC
    const
#endif /* CK_ANSIC */
      int hdrsize = sizeof(HEADER);
    struct srv_dns_entry {
        struct srv_dns_entry *next;
        int priority;
        int weight;
        unsigned short port;
        char *host;
    };
    struct srv_dns_entry *head = NULL;
    struct srv_dns_entry *srv = NULL, *entry = NULL;
    char * s = NULL;

    nout = 0;
    addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
    if (addr == NULL)
      return 0;

    count = 1;

    /*
     * First build a query of the form:
     *
     *   service.protocol.host
     *
     * which will most likely be something like:
     *
     *   _telnet._tcp.host
     *
     */
    if (((int)strlen(service) + strlen(protocol) + strlen(host) + 5)
        > MAX_DNS_NAMELEN
        )
      goto dnsout;

    /* Realm names don't (normally) end with ".", but if the query
       doesn't end with "." and doesn't get an answer as is, the
       resolv code will try appending the local domain.  Since the
       realm names are absolutes, let's stop that.

       But only if a name has been specified.  If we are performing
       a search on the prefix alone then the intention is to allow
       the local domain or domain search lists to be expanded.
    */
    h = host + strlen (host);
    ckmakxmsg(query, sizeof(query), "_",service,"._",protocol,".", host,
              ((h > host) && (h[-1] != '.')?".":NULL),
               NULL,NULL,NULL,NULL,NULL);

    size = res_search(query, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));

    if (size < hdrsize)
      goto dnsout;

    /* We got a reply - See how many answers it contains. */

    p = answer.bytes;

    numqueries = ntohs(answer.hdr.qdcount);
    numanswers = ntohs(answer.hdr.ancount);

    p += sizeof(HEADER);

    /*
     * We need to skip over all of the questions, so we have to iterate
     * over every query record.  dn_expand() is able to tell us the size
     * of compressed DNS names, so we use it.
     */
    while (numqueries--) {
        len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query));
        if (len < 0)
          goto dnsout;
        INCR_CHECK(p, len + 4);
    }

    /*
     * We're now pointing at the answer records.  Only process them if
     * they're actually T_SRV records (they might be CNAME records,
     * for instance).
     *
     * But in a DNS reply, if you get a CNAME you always get the associated
     * "real" RR for that CNAME.  RFC 1034, 3.6.2:
     *
     * CNAME RRs cause special action in DNS software.  When a name server
     * fails to find a desired RR in the resource set associated with the
     * domain name, it checks to see if the resource set consists of a CNAME
     * record with a matching class.  If so, the name server includes the CNAME
     * record in the response and restarts the query at the domain name
     * specified in the data field of the CNAME record.  The one exception to
     * this rule is that queries which match the CNAME type are not restarted.
     *
     * In other words, CNAMEs do not need to be expanded by the client.
     */
    while (numanswers--) {

        /* First is the name; use dn_expand() to get the compressed size. */
        len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query));
        if (len < 0)
          goto dnsout;
        INCR_CHECK(p, len);

        CHECK(p,2);                     /* Query type */
        type = NTOHSP(p,2);

        CHECK(p, 6);                    /* Query class */
        class = NTOHSP(p,6);            /* Also skip over 4-byte TTL */

        CHECK(p,2);                     /* Record data length */
        rdlen = NTOHSP(p,2);
        /*
         * If this is an SRV record, process it.  Record format is:
         *
         * Priority
         * Weight
         * Port
         * Server name
         */
        if (class == C_IN && type == T_SRV) {
            CHECK(p,2);
            priority = NTOHSP(p,2);
            CHECK(p, 2);
            weight = NTOHSP(p,2);
            CHECK(p, 2);
            port = NTOHSP(p,2);
            len = dn_expand(answer.
                            bytes,
                            answer.bytes + size,
                            p,
                            query,
                            sizeof(query)
                            );
            if (len < 0)
              goto dnsout;
            INCR_CHECK(p, len);
            /*
             * We got everything.  Insert it into our list, but make sure
             * it's in the right order.  Right now we don't do anything
             * with the weight field
             */
            srv = (struct srv_dns_entry *)malloc(sizeof(struct srv_dns_entry));
            if (srv == NULL)
              goto dnsout;

            srv->priority = priority;
            srv->weight = weight;
            srv->port = port;
            makestr(&s,(char *)query);  /* strdup() is not portable */
            srv->host = s;

            if (head == NULL || head->priority > srv->priority) {
                srv->next = head;
                head = srv;
            } else
                /*
                 * Confusing.  Insert an entry into this spot only if:
                 *  . The next person has a higher priority (lower
                 *    priorities are preferred), or:
                 *  . There is no next entry (we're at the end)
                 */
              for (entry = head; entry != NULL; entry = entry->next)
                if ((entry->next &&
                     entry->next->priority > srv->priority) ||
                    entry->next == NULL) {
                    srv->next = entry->next;
                    entry->next = srv;
                    break;
                }
        } else
          INCR_CHECK(p, rdlen);
    }

    /*
     * Now we've got a linked list of entries sorted by priority.
     * Start looking up A records and returning addresses.
     */
    if (head == NULL)
      goto dnsout;

    for (entry = head; entry != NULL; entry = entry->next) {
        hp = gethostbyname(entry->host);
        if (hp != 0) {

            /* Watch out - memset() and memcpy() are not portable... */

            switch (hp->h_addrtype) {
              case AF_INET:
                for (j = 0; hp->h_addr_list[j]; j++) {
                    sin = (struct sockaddr_in *) &addr[nout++];
                    memset ((char *) sin, 0, sizeof (struct sockaddr));
                    sin->sin_family = hp->h_addrtype;
                    sin->sin_port = htons(entry->port);
                    memcpy((char *) &sin->sin_addr,
                           (char *) hp->h_addr_list[j],
                           sizeof(struct in_addr));             /* safe */
                    if (nout + 1 >= count) {
                        count += 5;
                        addr = (struct sockaddr *)
                          realloc((char *) addr,
                                  sizeof(struct sockaddr) * count);
                        if (!addr)
                          goto dnsout;
                    }
                }
                break;
              default:
                break;
            }
        }
    }
    for (entry = head; entry != NULL;) {
        free(entry->host);
        entry->host = NULL;
        srv = entry;
        entry = entry->next;
        free(srv);
        srv = NULL;
    }

  dnsout:
    if (srv)
      free(srv);

    if (nout == 0) {                    /* No good servers */
        if (addr)
          free(addr);
        return 0;
    }
    *addr_pp = addr;
    *naddrs = nout;
    return 1;
}
#undef INCR_CHECK
#undef CHECK
#undef NTOHSP

#define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \
                         return 0
#define CHECK(x, y) if (x + y > size + answer.bytes) \
                         return 0
#define NTOHSP(x, y) x[0] << 8 | x[1]; x += y

int
#ifdef CK_ANSIC
locate_txt_rr( char *prefix, char *name, char **retstr )
#else
locate_txt_rr(prefix, name, retstr) char *prefix, *name; char **retstr;
#endif /* CK_ANSIC */
{
    union {
        unsigned char bytes[2048];
        HEADER hdr;
    } answer;
    unsigned char *p;
    char host[MAX_DNS_NAMELEN], *h;
    int size;
    int type, class, numanswers, numqueries, rdlen, len;

    /*
     * Form our query, and send it via DNS
     */

    if (name == NULL || name[0] == '\0') {
        strcpy(host,prefix);
    } else {
        if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN )
            return 0;

        /* Realm names don't (normally) end with ".", but if the query
           doesn't end with "." and doesn't get an answer as is, the
           resolv code will try appending the local domain.  Since the
           realm names are absolutes, let's stop that.

           But only if a name has been specified.  If we are performing
           a search on the prefix alone then the intention is to allow
           the local domain or domain search lists to be expanded.
        */
        h = host + strlen (host);
        ckmakmsg(host,sizeof(host),prefix, ".", name,
                 ((h > host) && (h[-1] != '.'))?".":NULL);

    }
    size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes));

    if (size < 0)
        return 0;

    p = answer.bytes;

    numqueries = ntohs(answer.hdr.qdcount);
    numanswers = ntohs(answer.hdr.ancount);

    p += sizeof(HEADER);

    /*
     * We need to skip over the questions before we can get to the answers,
     * which means we have to iterate over every query record.  We use
     * dn_expand to tell us how long each compressed name is.
     */

    while (numqueries--) {
        len = dn_expand(answer.bytes, answer.bytes + size, p, host,
                         sizeof(host));
        if (len < 0)
            return 0;
        INCR_CHECK(p, len + 4);         /* Name plus type plus class */
    }

    /*
     * We're now pointing at the answer records.  Process the first
     * TXT record we find.
     */

    while (numanswers--) {

        /* First the name; use dn_expand to get the compressed size */
        len = dn_expand(answer.bytes, answer.bytes + size, p,
                        host, sizeof(host));
        if (len < 0)
            return 0;
        INCR_CHECK(p, len);

        /* Next is the query type */
        CHECK(p, 2);
        type = NTOHSP(p,2);

        /* Next is the query class; also skip over 4 byte TTL */
        CHECK(p,6);
        class = NTOHSP(p,6);

        /* Record data length - make sure we aren't truncated */

        CHECK(p,2);
        rdlen = NTOHSP(p,2);

        if (p + rdlen > answer.bytes + size)
            return 0;

        /*
         * If this is a TXT record, return the string.  Note that the
         * string has a 1-byte length in the front
         */
        /* XXX What about flagging multiple TXT records as an error?  */

        if (class == C_IN && type == T_TXT) {
            len = *p++;
            if (p + len > answer.bytes + size)
                return 0;
            *retstr = malloc(len + 1);
            if (*retstr == NULL)
                return ENOMEM;
            strncpy(*retstr, (char *) p, len);
            (*retstr)[len] = '\0';
            /* Avoid a common error. */
            if ( (*retstr)[len-1] == '.' )
                (*retstr)[len-1] = '\0';
            return 1;
        }
    }

    return 0;
}
#undef INCR_CHECK
#undef CHECK
#undef NTOHSP
#endif /* CK_DNS_SRV */

#ifdef TNCODE
#ifdef CK_FORWARD_X
#ifdef UNIX
#include <sys/un.h>
#define FWDX_UNIX_SOCK
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
#ifndef PF_LOCAL
#define PF_LOCAL PF_UNIX
#endif
#ifndef SUN_LEN
/* Evaluate to actual length of the `sockaddr_un' structure.  */
#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)         \
                      + strlen ((ptr)->sun_path))
#endif
#endif /* UNIX */

#ifdef CK_ANSIC
/* prototypes for static functions - fdc 30 November 2022 */
static SIGTYP rlogoobh( int );
static int rlog_oob( CHAR *, int );
#endif /* CK_ANSIC */

#include "ckcfnp.h"                     /* Prototypes (must be last) */

int
#ifdef CK_ANSIC
fwdx_create_listen_socket(int screen)
#else
fwdx_create_listen_socket(screen) int screen;
#endif  /* CK_ANSIC */
{
#ifdef NOPUTENV
    return(-1);
#else /* NOPUTENV */
    /*
      IPv4-only.  myipaddr is empty on an IPv6-only host (see the
      getlocalipaddr() comment above), so this listener falls back to
      a DISPLAY with no host part rather than binding an IPv6
      address.  IPv6-only host with X11 seems rare and can probably be skipped
      for now.

      myipaddr can also legitimately hold a real IPv6 literal.  inet_addr()
      below fails on that too, so the same INADDR_NONE/-1 check applies
      regardless of which case left myipaddr non-IPv4.
    */
    struct sockaddr_in saddr;
    int display, port, sock=-1, i;
    static char env[512];

    /*
     * X Windows Servers support multiple displays by listening on
     * one socket per display.  Display 0 is port 6000; Display 1 is
     * port 6001; etc.
     *
     * We start by trying to open port 6001 so that display 0 is
     * reserved for the local X Windows Server.
     */

    for ( display=1; display < 1000 ; display++  ) {

        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            debug(F111,"fwdx_create_listen_socket()","socket() < 0",sock);
            return(-1);
        }

        port = 6000 + display;
        bzero((char *)&saddr, sizeof(saddr));
        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = inet_addr(myipaddr);

        /* 2022-12-05  SMS.  64-bit "long" miscompares with 32-bit
         * "in_addr_t".  Any system new enough to have a 64-bit
         * "long" should define __LP64__, so ADDR_TYPE tracks that
         * to keep the comparison the same width as s_addr.
         */
#ifdef __LP64__
#define ADDR_TYPE unsigned int
#else /* def __LP64__ */
#define ADDR_TYPE unsigned long
#endif /* def __LP64__ [else] */

        if (saddr.sin_addr.s_addr == (ADDR_TYPE) -1
#ifdef INADDR_NONE
            || saddr.sin_addr.s_addr == INADDR_NONE
#endif /* INADDR_NONE */
            ) {
            /* myipaddr isn't a valid IPv4 literal here.  Don't fail the
               listener over an address we can't parse as IPv4.  Bind the
               wildcard address instead of restricting to one we don't have. */
            debug(F110,"fwdx_create_listen_socket() myipaddr not IPv4",
                  myipaddr,0);
            saddr.sin_addr.s_addr = INADDR_ANY;
        }
        saddr.sin_port = htons(port);

        if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
            i = errno;                  /* Save error code */
#ifdef TCPIPLIB
            socket_close(sock);
#else /* TCPIPLIB */
            close(sock);
#endif /* TCPIPLIB */
            sock = -1;
            debug(F110,"fwdx_create_listen_socket()","bind() < 0",0);
            continue;
        }

        debug(F100,"fdwx_create_listen_socket() bind OK","",0);
        break;
    }

    if ( display > 1000 ) {
        debug(F100,"fwdx_create_listen_socket() Out of Displays","",0);
        return(-1);
    }

    if (listen(sock, 5) < 0) {
        i = errno;                  /* Save error code */
#ifdef TCPIPLIB
        socket_close(sock);
#else /* TCPIPLIB */
        close(sock);
#endif /* TCPIPLIB */
        debug(F101,"fdwx_create_listen_socket() listen() errno","",errno);
        return(-1);
    }
    debug(F100,"fwdx_create_listen_socket() listen OK","",0);

    TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = sock;
    if (!myipaddr[0])
        getlocalipaddr();
    if ( myipaddr[0] )
        ckmakxmsg(env,sizeof(env),"DISPLAY=",myipaddr,":",
                  ckuitoa(display),":",ckuitoa(screen),
                  NULL,NULL,NULL,NULL,NULL,NULL);
    else
        ckmakmsg(env,sizeof(env),"DISPLAY=",ckuitoa(display),":",
                 ckuitoa(screen));
    putenv(env);
    return(0);
#endif /* NOPUTENV */
}


int
#ifdef CK_ANSIC
fwdx_open_client_channel(int channel)
#else
fwdx_open_client_channel(channel) int channel;
#endif  /* CK_ANSIC */
{
    /* IPv4-only, same reasoning as fwdx_create_listen_socket() above. */
    char * env;
    struct sockaddr_in saddr;
#ifdef FWDX_UNIX_SOCK
    struct sockaddr_un saddr_un = { AF_LOCAL };
#endif /* FWDX_UNIX_SOCK */
    int display, port, sock, i, screen;
    int family;
    char buf[256], * host=NULL, * rest=NULL;
#ifdef TCP_NODELAY
    int on=1;
#endif /* TCP_NODELAY */

    debug(F111,"fwdx_create_client_channel()","channel",channel);

    for ( i=0; i<MAXFWDX ; i++ ) {
        if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel) {
            /* Already open */
            debug(F110,"fwdx_create_client_channel()","already open",0);
            return(0);
        }
    }

    env = getenv("DISPLAY");
    if ( !env )
      env = (char *)tn_get_display();
    if ( env )
      ckstrncpy(buf,env,256);
    else
      ckstrncpy(buf,"127.0.0.1:0.0",256);

    bzero((char *)&saddr,sizeof(saddr));
    saddr.sin_family = AF_INET;

    if (!fwdx_parse_displayname(buf,
                                &family,
                                &host,
                                &display,
                                &screen,
                                &rest
                                )
        ) {
        if ( host ) free(host);
        if ( rest ) free(rest);
        return(0);
    }
    if (rest) free(rest);

#ifndef FWDX_UNIX_SOCK
  /* if $DISPLAY indicates use of unix domain sockets, but we don't support it,
   * we change things to use inet sockets on the ip loopback interface instead,
   * and hope that it works.
   */
    if (family == FamilyLocal) {
        debug(F100,"fwdx_create_client_channel() FamilyLocal","",0);
        family = FamilyInternet;
        if (host) free(host);
        if (host = malloc(strlen("localhost") + 1))
            strcpy(host, "localhost");
        else {
            return(-1);
        }
    }
#else /* FWDX_UNIX_SOCK */
    if (family == FamilyLocal) {
        if (host) free(host);
        sock = socket(PF_LOCAL, SOCK_STREAM, 0);
        if (sock < 0)
            return(-1);

        ckmakmsg(buf,sizeof(buf),"/tmp/.X11-unix/X",ckitoa(display),NULL,NULL);
        strncpy(saddr_un.sun_path, buf, sizeof(saddr_un.sun_path));
        if (connect(sock,(struct sockaddr *)&saddr_un, SUN_LEN(&saddr_un)) < 0)
          return(-1);
    } else
#endif  /* FWDX_UNIX_SOCK */
    {
        /* Otherwise, we are assuming FamilyInternet */
        if (host) {
            ckstrncpy(buf,host,sizeof(buf));
            free(host);
        } else
            ckstrncpy(buf,myipaddr,sizeof(buf));

        debug(F111,"fwdx_create_client_channel()","display",display);

        port = 6000 + display;
        saddr.sin_port = htons(port);

        debug(F110,"fwdx_create_client_channel() ip-address",buf,0);
        saddr.sin_addr.s_addr = inet_addr(buf);

        if ( saddr.sin_addr.s_addr == (ADDR_TYPE) -1
#ifdef INADDR_NONE
             || saddr.sin_addr.s_addr == INADDR_NONE
#endif /* INADDR_NONE */
             )
        {
            struct hostent *host;
            host = gethostbyname(buf);
            if ( host == NULL )
                return(-1);
            host = ck_copyhostent(host);
#ifdef HADDRLIST
#ifdef h_addr
            /* This is for trying multiple IP addresses - see <netdb.h> */
            if (!(host->h_addr_list))
                return(-1);
            bcopy(host->h_addr_list[0],
                   (caddr_t)&saddr.sin_addr,
                   host->h_length
                   );
#else
            bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
#endif /* h_addr */
#else  /* HADDRLIST */
            bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
#endif /* HADDRLIST */
        }

        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            debug(F111,"fwdx_create_client_channel()","socket() < 0",sock);
            return(-1);
        }

        if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
            debug(F110,"fwdx_create_client_channel()","connect() failed",0);
#ifdef TCPIPLIB
            socket_close(sock);
#else /* TCPIPLIB */
            close(sock);
#endif /* TCPIPLIB */
            return(-1);
        }

#ifdef TCP_NODELAY
        setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char *)&on,sizeof(on));
#endif /* TCP_NODELAY */
    }

    for (i = 0; i < MAXFWDX; i++) {
     if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == -1) {
         TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd = sock;
         TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id = channel;
       TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 1;
         debug(F111,"fwdx_create_client_channel()","socket",sock);
         return(0);
     }
    }
    return(-1);
}

/* IPv4-only, same reasoning as fwdx_create_listen_socket() above. */
int
fwdx_server_avail() {
    char * env;
    struct sockaddr_in saddr;
#ifdef FWDX_UNIX_SOCK
    struct sockaddr_un saddr_un = { AF_LOCAL };
#endif  /* FWDX_UNIX_SOCK */
    int display, port, sock, screen;
    char buf[256], *host=NULL, *rest=NULL;
    int family;

    env = getenv("DISPLAY");
    if ( !env )
      env = (char *)tn_get_display();
    if ( env )
      ckstrncpy(buf,env,256);
    else
      ckstrncpy(buf,"127.0.0.1:0.0",256);

    bzero((char *)&saddr,sizeof(saddr));
    saddr.sin_family = AF_INET;

    if (!fwdx_parse_displayname(buf,&family,&host,&display,&screen,&rest)) {
        if ( host ) free(host);
        if ( rest ) free(rest);
        return(0);
    }
    if (rest) free(rest);

#ifndef FWDX_UNIX_SOCK
  /* if $DISPLAY indicates use of unix domain sockets, but we don't support it,
   * we change things to use inet sockets on the ip loopback interface instead,
   * and hope that it works.
   */
    if (family == FamilyLocal) {
        family = FamilyInternet;
        if (host) free(host);
        if (host = malloc(strlen("localhost") + 1))
            strcpy(host, "localhost");
        else {
            return(-1);
        }
    }
#else /* FWDX_UNIX_SOCK */
    if (family == FamilyLocal) {
        debug(F100,"fwdx_server_avail() FamilyLocal","",0);
        if (host) free(host);
        sock = socket(PF_LOCAL, SOCK_STREAM, 0);
        if (sock < 0)
            return(0);

        ckmakmsg(buf,sizeof(buf),"/tmp/.X11-unix/X",ckitoa(display),NULL,NULL);
        strncpy(saddr_un.sun_path, buf, sizeof(saddr_un.sun_path));
        if (connect(sock,(struct sockaddr *)&saddr_un,SUN_LEN(&saddr_un)) < 0)
            return(0);
        close(sock);
        return(1);
    }
#endif  /* FWDX_UNIX_SOCK */

    /* Otherwise, we are assuming FamilyInternet */
    if (host) {
        ckstrncpy(buf,host,sizeof(buf));
        free(host);
    } else
        ckstrncpy(buf,myipaddr,sizeof(buf));

    debug(F111,"fwdx_server_avail()","display",display);

    port = 6000 + display;
    saddr.sin_port = htons(port);

    debug(F110,"fwdx_server_avail() ip-address",buf,0);
    saddr.sin_addr.s_addr = inet_addr(buf);
    if ( saddr.sin_addr.s_addr == (ADDR_TYPE) -1
#ifdef INADDR_NONE
         || saddr.sin_addr.s_addr == INADDR_NONE
#endif /* INADDR_NONE */
         )
    {
        struct hostent *host;
        host = gethostbyname(buf);
        if ( host == NULL ) {
            debug(F110,"fwdx_server_avail() gethostbyname() failed",
                   myipaddr,0);
            return(-1);
        }
        host = ck_copyhostent(host);
#ifdef HADDRLIST
#ifdef h_addr
        /* This is for trying multiple IP addresses - see <netdb.h> */
        if (!(host->h_addr_list))
            return(-1);
        bcopy(host->h_addr_list[0],
               (caddr_t)&saddr.sin_addr,
               host->h_length
               );
#else
        bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
#endif /* h_addr */
#else  /* HADDRLIST */
        bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
#endif /* HADDRLIST */
    }

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        debug(F111,"fwdx_server_avail()","socket() < 0",sock);
        return(0);
    }

    if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
        debug(F110,"fwdx_server_avail()","connect() failed",0);
#ifdef TCPIPLIB
        socket_close(sock);
#else /* TCPIPLIB */
        close(sock);
#endif /* TCPIPLIB */
        return(0);
    }

#ifdef TCPIPLIB
    socket_close(sock);
#else /* TCPIPLIB */
    close(sock);
#endif /* TCPIPLIB */
    return(1);
}

/* IPv4-only, same reasoning as fwdx_create_listen_socket() above. */
int
fwdx_open_server_channel() {
    int sock, ready_to_accept, sock2,channel;
#ifdef TCP_NODELAY
    int on=1;
#endif /* TCP_NODELAY */
#ifdef UCX50
    static u_int saddrlen;
#else
    static SOCKOPT_T saddrlen;
#endif /* UCX50 */
    struct sockaddr_in saddr;
    extern char tn_msg[];
#ifdef BSDSELECT
    fd_set rfds;
    struct timeval tv;
#else
#ifdef BELLSELCT
    fd_set rfds;
#else
    fd_set rfds;
    struct timeval {
        long tv_sec;
        long tv_usec;
    } tv;
#endif /* BELLSELECT */
#endif /* BSDSELECT */

    sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket;

  try_again:

#ifdef BSDSELECT
    tv.tv_sec  = tv.tv_usec = 0L;
    tv.tv_usec = 50;
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    ready_to_accept =
        ((select(FD_SETSIZE,
#ifdef HPUX
#ifdef HPUX1010
                  (fd_set *)
#else

                  (int *)
#endif /* HPUX1010 */
#else
#ifdef __DECC
                  (fd_set *)
#endif /* __DECC */
#endif /* HPUX */
                  &rfds, NULL, NULL, &tv) > 0) &&
          FD_ISSET(sock, &rfds));
#else /* BSDSELECT */
#ifdef IBMSELECT
    ready_to_accept = (select(&sock, 1, 0, 0, 50) == 1);
#else
#ifdef BELLSELECT
    FD_ZERO(rfds);
    FD_SET(sock, rfds);
    ready_to_accept =
        ((select(128, rfds, NULL, NULL, 50) > 0) &&
          FD_ISSET(sock, rfds));
#else
/* Try this - what's the worst that can happen... */

    tv.tv_sec  = tv.tv_usec = 0L;
    tv.tv_usec = 50;
    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    ready_to_accept =
        ((select(FD_SETSIZE,
                  (fd_set *) &rfds, NULL, NULL, &tv) > 0) &&
          FD_ISSET(sock, &rfds));
#endif /* BELLSELECT */
#endif /* IBMSELECT */
#endif /* BSDSELECT */

    if ( !ready_to_accept )
        return(0);

    if ((sock2 = accept(sock,(struct sockaddr *)&saddr,&saddrlen)) < 0) {
        int i = errno;                  /* save error code */
        debug(F101,"tcpsrv_open accept errno","",i);
        return(-1);
    }

    /*
     * Now we have the open socket.  We must now find a channel to store
     * it in, and then notify the client.
     */

    for ( channel=0;channel<MAXFWDX;channel++ ) {
        if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].fd == -1 )
            break;
    }

    if ( channel == MAXFWDX ) {
#ifdef TCPIPLIB
        socket_close(sock2);
#else /* TCPIPLIB */
        close(sock2);
#endif /* TCPIPLIB */
        return(-1);
    }

    if ( fwdx_send_open(channel) < 0 ) {
#ifdef TCPIPLIB
        socket_close(sock2);
#else /* TCPIPLIB */
        close(sock2);
#endif /* TCPIPLIB */
    }

#ifdef TCP_NODELAY
    setsockopt(sock2,IPPROTO_TCP,TCP_NODELAY,(char *)&on,sizeof(on));
#endif /* TCP_NODELAY */

    TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].fd = sock2;
    TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].id = channel;
    goto try_again;

    return(0);  /* never reached */
}

int
#ifdef CK_ANSIC
fwdx_close_channel(int channel)
#else
fwdx_close_channel(channel) int channel;
#endif  /* CK_ANSIC */
{
    int i,fd;

    for ( i=0; i<MAXFWDX ; i++ ) {
        if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel)
            break;
    }
    if ( i == MAXFWDX )
        return(-1);

    fd = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd;
    TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd = -1;
    TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id = -1;
    TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;

#ifdef TCPIPLIB
    socket_close(fd);
#else /* TCPIPLIB */
    close(fd);
#endif /* TCPIPLIB */
    return(0);
}

int
fwdx_close_all() {
    int x,fd;

    debug(F111,"fwdx_close_all()",
          "TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket",
          TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);

    if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket != -1 ) {
#ifdef TCPIPLIB
        socket_close(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);
#else /* TCPIPLIB */
        close(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);
#endif /* TCPIPLIB */
        TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = -1;
    }

    for (x = 0; x < MAXFWDX; x++) {
     if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd != -1) {
      fd = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd;
      TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd = -1;
      TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id = -1;
      TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].need_to_send_xauth = 0;
      TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend = 0;
#ifdef TCPIPLIB
      socket_close(fd);
#else /* TCPIPLIB */
      close(fd);
#endif /* TCPIPLIB */
     }
    }
    return(0);
}

/* The following definitions are for Unix */
#ifndef socket_write
#define socket_write(f,s,n)    write(f,s,n)
#endif /* socket_write */
#ifndef socket_read
#define socket_read(f,s,n)     read(f,s,n)
#endif /* socket_read */

int
#ifdef CK_ANSIC
fwdx_write_data_to_channel(int channel, char *data, int len)
#else
fwdx_write_data_to_channel(channel, data, len)
    int channel; char * data; int len;
#endif  /* CK_ANSIC */
{
    int sock, count, length = len, i;

    if ( len <= 0 )
        return(0);

    for ( i=0; i<MAXFWDX ; i++ ) {
        if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel)
            break;
    }
    if ( i == MAXFWDX ) {
        debug(F110,"fwdx_write_data_to_channel",
               "attempting to write to closed channel",0);
        return(-1);
    }

    sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd;
    debug(F111,"fwdx_write_data_to_channel","socket",sock);
    ckhexdump("fwdx_write_data_to_channel",data,len);

  fwdx_write_data_to_channel_retry:

    if ((count = socket_write(sock,data,len)) < 0) {
        int s_errno = socket_errno; /* maybe a function */
        debug(F101,"fwdx_write_data_to_channel socket_write error","",s_errno);
#ifdef OS2
        if (os2socketerror(s_errno) < 0)
            return(-2);
#endif /* OS2 */
        return(-1);                 /* Call it an i/o error */
    }
    if (count < len) {
        debug(F111,"fwdx_write_data_to_channel socket_write",data,count);
        if (count > 0) {
            data += count;
            len -= count;
        }
        debug(F111,"fwdx_write_data_to_channel retry",data,len);
        if ( len > 0 )
            goto fwdx_write_data_to_channel_retry;
    }

    debug(F111,"fwdx_write_data_to_channel complete",data,length);
    return(length); /* success - return total length */
}

VOID
fwdx_check_sockets(fd_set *ibits)
{
    int x, sock, channel, bytes;
    static char buffer[32000];

    debug(F100,"fwdx_check_sockets()","",0);
    if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) ||
         !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) {
        debug(F110,"fwdx_check_sockets()","TELOPT_FORWARD_X not negotiated",0);
        return;
    }

    for (x = 0; x < MAXFWDX; x++) {
        if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd == -1 ||
             TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend )
            continue;

        sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd;
        if (FD_ISSET(sock, ibits))
        {
            channel = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id;
            debug(F111,"fwdx_check_sockets()","channel set",channel);

            bytes = socket_read(sock, buffer, sizeof(buffer));
            if (bytes > 0)
                fwdx_send_data_from_channel(channel, buffer, bytes);
            else if (bytes == 0) {
                fwdx_close_channel(channel);
                fwdx_send_close(channel);
            }
        }
    }
}

int
fwdx_init_fd_set(fd_set *ibits)
{
    int x,set=0,cnt=0;

    if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) ||
         !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) {
        debug(F110,"fwdx_init_fd_set()","TELOPT_FORWARD_X not negotiated",0);
        return(0);
    }

    if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket != -1) {
        set++;
        FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket, ibits);
    }
    for (x = 0; x < MAXFWDX; x++) {
        if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd != -1) {
            cnt++;
            if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend)
                continue;
            set++;
            FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd, ibits);
        }
    }
    if (set + cnt == 0) {
        return(-1);
    } else {
        return(set);
    }
}

#ifdef NT
VOID
fwdx_thread( VOID * dummy )
{
    fd_set ifds;
    struct timeval tv;
    extern int priority;
    int n;

    setint();
    SetThreadPrty(priority,isWin95() ? 3 : 11);

    while ( !sstelnet && TELOPT_U(TELOPT_FORWARD_X) ||
            sstelnet && TELOPT_ME(TELOPT_FORWARD_X))
    {
        FD_ZERO(&ifds);
        n = fwdx_init_fd_set(&ifds);
        if (n > 0) {
            tv.tv_sec = 0;
            tv.tv_usec = 2500;
            if ( select(FD_SETSIZE, &ifds, NULL, NULL, &tv) > 0 )
                fwdx_check_sockets(&ifds);

        } else if (n < 0) {
            TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 0;
            ckThreadEnd(NULL);
        } else {
            sleep(1);
        }
    }
}
#endif /* NT */
#endif /* CK_FORWARD_X */
#endif /* TNCODE */
#endif /* NETCONN */
