blob: f743820db7cc1fc3b7a30551e10b67f8ff779295 [file] [log] [blame]
char *cktelv = "Telnet support, 8.0.269, 4 Mar 2004";
#define CKCTEL_C
int sstelnet = 0; /* Do server-side Telnet negotiation */
/* C K C T E L -- Telnet support */
/*
Authors:
Telnet protocol by Frank da Cruz and Jeffrey Altman.
Telnet Forward X by Jeffrey Altman
Telnet START_TLS support by Jeffrey Altman
Telnet AUTH and ENCRYPT support by Jeffrey Altman
Telnet COMPORT support by Jeffrey Altman
Telnet NEW-ENVIRONMENT support by Jeffrey Altman
Telnet NAWS support by Frank da Cruz and Jeffrey Altman
Telnet TERMTYPE support by Jeffrey Altman
Telnet KERMIT support by Jeffrey Altman
Other contributions as indicated in the code.
Copyright (C) 1985, 2004,
Trustees of Columbia University in the City of New York.
All rights reserved. See the C-Kermit COPYING.TXT file or the
copyright text in the ckcmai.c module for disclaimer and permissions.
*/
/*
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.
*/
#include "ckcsym.h"
#include "ckcdeb.h"
#ifdef TNCODE
#include "ckcker.h"
#define TELCMDS /* to define name array */
#define TELOPTS /* to define name array */
#define SLC_NAMES /* to define name array */
#define ENCRYPT_NAMES
#define AUTH_NAMES
#define TELOPT_STATES
#define TELOPT_MODES
#define TNC_NAMES
#include "ckcnet.h"
#include "ckctel.h"
#ifdef CK_AUTHENTICATION
#include "ckuath.h"
#endif /* CK_AUTHENTICATION */
#ifdef CK_SSL
#include "ck_ssl.h"
#endif /* CK_SSL */
#ifndef NOTERM
#ifdef OS2 /* For terminal type name string */
#include "ckuusr.h"
#ifndef NT
#include <os2.h>
#undef COMMENT
#else
#define isascii __isascii
#endif /* NT */
#include "ckocon.h"
extern int tt_type, max_tt;
extern struct tt_info_rec tt_info[];
#endif /* OS2 */
#endif /* NOTERM */
#ifdef OS2
#include <assert.h>
#ifdef NT
#include <setjmpex.h>
#else /* NT */
#include <setjmp.h>
#endif /* NT */
#include <signal.h>
#include "ckcsig.h"
#include "ckosyn.h"
#endif /* OS2 */
#ifdef CK_NAWS /* Negotiate About Window Size */
#ifdef RLOGCODE
_PROTOTYP( int rlog_naws, (void) );
#endif /* RLOGCODE */
#endif /* CK_NAWS */
int tn_init = 0; /* Telnet protocol initialized flag */
int tn_begun = 0; /* Telnet protocol started flag */
static int tn_first = 1; /* First time init flag */
extern int tn_exit; /* Exit on disconnect */
extern int inserver; /* Running as IKSD */
char *tn_term = NULL; /* Terminal type override */
#ifdef CK_SNDLOC
char *tn_loc = NULL; /* Location override */
#endif /* CK_SNDLOC */
int tn_nlm = TNL_CRLF; /* Telnet CR -> CR LF mode */
int tn_b_nlm = TNL_CR; /* Telnet Binary CR RAW mode */
int tn_b_meu = 0; /* Telnet Binary ME means U too */
int tn_b_ume = 0; /* Telnet Binary U means ME too */
int tn_wait_flg = 1; /* Telnet Wait for Negotiations */
int tn_infinite = 0; /* Telnet Bug Infinite-Loop-Check */
int tn_rem_echo = 1; /* We will echo if WILL ECHO */
int tn_b_xfer = 0; /* Telnet Binary for Xfers? */
int tn_sb_bug = 1; /* Telnet BUG - SB w/o WILL or DO */
int tn_auth_krb5_des_bug = 1; /* Telnet BUG - AUTH KRB5 DES */
/* truncates long keys */
int tn_no_encrypt_xfer = 0; /* Turn off Telnet Encrypt? */
int tn_delay_sb = 1; /* Delay SBs until safe */
int tn_auth_how = TN_AUTH_HOW_ANY;
int tn_auth_enc = TN_AUTH_ENC_ANY;
int tn_deb = 0; /* Telnet Debug mode */
int tn_sfu = 0; /* Microsoft SFU compatibility */
#ifdef CK_FORWARD_X
char * tn_fwdx_xauthority = NULL; /* Xauthority File */
int fwdx_no_encrypt = 0; /* Forward-X requires encryption */
#endif /* CK_FORWARD_X */
#ifdef OS2
int ttnum = -1; /* Last Telnet Terminal Type sent */
int ttnumend = 0; /* Has end of list been found */
#endif /* OS2 */
char tn_msg[TN_MSG_LEN]; /* Telnet data can be rather long */
char hexbuf[TN_MSG_LEN];
char tn_msg_out[TN_MSG_LEN];
#ifdef CK_FORWARD_X
CHAR fwdx_msg_out[TN_MSG_LEN];
#endif /* CK_FORWARD_X */
/*
In order to prevent an infinite telnet negotiation loop we maintain a
count of the number of times the same telnet negotiation message is
sent. When this count hits MAXTNCNT, we do not send any more of the
message. The count is stored in the tncnts[][] array.
The tncnts[][] array is indexed by negotiation option (SUPPRESS GO AHEAD,
TERMINAL TYPE, NAWS, etc. - see the tnopts[] array) and the four
negotiation message types (WILL, WONT, DO, DONT). All telnet negotiations
are kept track of in this way.
The count for a message is zeroed when the "opposite" message is sent.
WILL is the opposite of WONT, and DO is the opposite of DONT.
For example sending "WILL SGA" increments tncnts[TELOPT_SGA][0]
and zeroes tncnts[TELOPT_SGA][1].
The code that does this is in tn_sopt().
rogersh@fsj.co.jp, 18/3/1995
8/16/1998 - with the recent rewrite of the telnet state machine I don't
think this code is necessary anymore. However, it can't do any harm so
I am leaving it in. - Jeff
12/28/1998 - all references to tncnts[] must be done with TELOPT_INDEX(opt)
because the Telnet option list is no longer contiguous. We also must
allocate NTELOPTS + 1 because the TELOPT_INDEX() macro returns NTELOPTS
for an invalid option number.
*/
#define MAXTNCNT 4 /* Permits 4 intermediate telnet firewalls/gateways */
char tncnts[NTELOPTS+1][4]; /* Counts */
char tnopps[4] = { 1,0,3,2 }; /* Opposites */
#ifdef CK_ENVIRONMENT
#ifdef CK_FORWARD_X
#define TSBUFSIZ 2056
#else /* CK_FORWARD_X */
#define TSBUFSIZ 1024
#endif /* CK_FORWARD_X */
char tn_env_acct[64];
char tn_env_disp[64];
char tn_env_job[64];
char tn_env_prnt[64];
char tn_env_sys[64];
char * tn_env_uservar[8][2];
int tn_env_flg = 1;
#else /* CK_ENVIRONMENT */
#define TSBUFSIZ 41
int tn_env_flg = 0;
#endif /* CK_ENVIRONMENT */
#ifdef COMMENT
/* SIGWINCH handler moved to ckuusx.c */
#ifndef NOSIGWINCH
#ifdef CK_NAWS /* Window size business */
#ifdef UNIX
#include <signal.h>
#endif /* UNIX */
#endif /* CK_NAWS */
#endif /* NOSIGWINCH */
#endif /* COMMENT */
CHAR sb[TSBUFSIZ]; /* Buffer - incoming subnegotiations */
CHAR sb_out[TSBUFSIZ]; /* Buffer - outgoing subnegotiations */
int tn_duplex = 1; /* Local echo */
extern char uidbuf[]; /* User ID buffer */
extern int quiet, ttnet, ttnproto, debses, what, duplex, oldplex, local;
extern int seslog, sessft, whyclosed;
#ifdef OS2
#ifndef NOTERM
extern int tt_rows[], tt_cols[];
extern int tt_status[VNUM];
extern int scrninitialized[];
#endif /* NOTERM */
#else /* OS2 */
extern int tt_rows, tt_cols; /* Everybody has this */
#endif /* OS2 */
extern int cmd_cols, cmd_rows;
extern char namecopy[];
extern char myipaddr[]; /* Global copy of my IP address */
#ifndef TELOPT_MACRO
int
telopt_index(opt) int opt; {
if (opt >= 0 && opt <= TELOPT_STDERR)
return(opt);
else if (opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT)
return(opt-88);
else if (opt == TELOPT_IBM_SAK)
return(opt-147);
else
return(NTELOPTS);
}
int
telopt_ok(opt) int opt; {
return((opt >= TELOPT_BINARY && opt <= TELOPT_STDERR) ||
(opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT) ||
(opt == TELOPT_IBM_SAK));
}
CHAR *
telopt(opt) int opt; {
if (telopt_ok(opt))
return((CHAR *)telopts[telopt_index(opt)]);
else
return((CHAR *)"UNKNOWN");
}
int
telopt_mode_ok(opt) int opt; {
return((unsigned int)(opt) <= TN_NG_MU);
}
CHAR *
telopt_mode(opt) int opt; {
if (telopt_mode_ok(opt))
return((CHAR *)telopt_modes[opt-TN_NG_RF]);
else
return((CHAR *)"UNKNOWN");
}
#endif /* TELOPT_MACRO */
static int
tn_outst(notquiet) int notquiet; {
int outstanding = 0;
int x = 0;
#ifdef CK_ENCRYPTION
int e = 0;
int d = 0;
#endif /* CK_ENCRYPTION */
if (tn_wait_flg) {
for (x = TELOPT_FIRST; x <= TELOPT_LAST; x++) {
if (TELOPT_OK(x)) {
if (TELOPT_UNANSWERED_WILL(x)) {
if ( notquiet )
printf("?Telnet waiting for response to WILL %s\r\n",
TELOPT(x));
debug(F111,"tn_outst","unanswered WILL",x);
outstanding = 1;
if ( !notquiet )
break;
}
if (TELOPT_UNANSWERED_DO(x)) {
if ( notquiet )
printf("?Telnet waiting for response to DO %s\r\n",
TELOPT(x));
debug(F111,"tn_outst","unanswered DO",x);
outstanding = 1;
if ( !notquiet )
break;
}
if (TELOPT_UNANSWERED_WONT(x)) {
if ( notquiet )
printf("?Telnet waiting for response to WONT %s\r\n",
TELOPT(x));
debug(F111,"tn_outst","unanswered WONT",x);
outstanding = 1;
if ( !notquiet )
break;
}
if (TELOPT_UNANSWERED_DONT(x)) {
if ( notquiet )
printf("?Telnet waiting for response to DONT %s\r\n",
TELOPT(x));
debug(F111,"tn_outst","unanswered DONT",x);
outstanding = 1;
if ( !notquiet )
break;
}
if (TELOPT_UNANSWERED_SB(x)) {
if ( notquiet )
printf("?Telnet waiting for response to SB %s\r\n",
TELOPT(x));
debug(F111,"tn_outst","unanswered SB",x);
outstanding = 1;
if ( !notquiet )
break;
}
}
}
#ifdef CK_AUTHENTICATION
if (ck_tn_auth_in_progress()) {
if (TELOPT_ME(TELOPT_AUTHENTICATION)) {
if (notquiet)
printf("?Telnet waiting for WILL %s subnegotiation\r\n",
TELOPT(TELOPT_AUTHENTICATION));
debug(F111,
"tn_outst",
"ME authentication in progress",
TELOPT_AUTHENTICATION
);
outstanding = 1;
} else if (TELOPT_U(TELOPT_AUTHENTICATION)) {
if (notquiet)
printf("?Telnet waiting for DO %s subnegotiation\r\n",
TELOPT(TELOPT_AUTHENTICATION));
debug(F111,
"tn_outst",
"U authentication in progress",
TELOPT_AUTHENTICATION
);
outstanding = 1;
}
}
#endif /* CK_AUTHENTICATION */
#ifdef CK_ENCRYPTION
if (!outstanding) {
e = ck_tn_encrypting();
d = ck_tn_decrypting();
if (TELOPT_ME(TELOPT_ENCRYPTION)) {
if (TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && e ||
!TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && !e
) {
if ( notquiet )
printf("?Telnet waiting for WILL %s subnegotiation\r\n",
TELOPT(TELOPT_ENCRYPTION));
debug(F111,
"tn_outst",
"encryption mode switch",
TELOPT_ENCRYPTION
);
outstanding = 1;
}
}
if (TELOPT_U(TELOPT_ENCRYPTION)) {
if (TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && d ||
!TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && !d
) {
if ( notquiet )
printf("?Telnet waiting for DO %s subnegotiation\r\n",
TELOPT(TELOPT_ENCRYPTION));
debug(F111,
"tn_outst",
"decryption mode switch",
TELOPT_ENCRYPTION
);
outstanding = 1;
}
}
}
#endif /* CK_ENCRYPTION */
} /* if (tn_wait_flg) */
#ifdef IKS_OPTION
/* Even if we are not waiting for Telnet options we must wait for */
/* Kermit Telnet Subnegotiations if we have sent a request to the */
/* other guy. Otherwise we will get out of sync. */
if (!outstanding) {
if (TELOPT_U(TELOPT_KERMIT) &&
(TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start ||
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop ||
!TELOPT_SB(TELOPT_KERMIT).kermit.sop)
) {
if ( notquiet )
printf("?Telnet waiting for SB %s negotiation\r\n",
TELOPT(TELOPT_KERMIT));
debug(F111,"tn_outst","U kermit in progress",TELOPT_KERMIT);
outstanding = 1;
}
}
#endif /* IKS_OPTION */
#ifdef TN_COMPORT
if (!outstanding) {
if (TELOPT_ME(TELOPT_COMPORT)) {
if (TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb) {
if (notquiet)
printf("?Telnet waiting for SB %s negotiation\r\n",
TELOPT(TELOPT_COMPORT));
debug(F111,"tn_outst","ComPort SB in progress",TELOPT_COMPORT);
outstanding = 1;
}
if (TELOPT_SB(TELOPT_COMPORT).comport.wait_for_ms) {
if (notquiet)
printf("?Telnet waiting for SB %s MODEM_STATUS negotiation\r\n",
TELOPT(TELOPT_COMPORT));
debug(F111,"tn_outst","ComPort SB MS in progress",TELOPT_COMPORT);
outstanding = 1;
}
}
}
#endif /* TN_COMPORT */
return(outstanding);
}
int
istncomport() {
#ifdef TN_COMPORT
if (!local) return(0);
if (ttnet != NET_TCPB) return(0);
if (ttnproto != NP_TELNET) return(0);
if (TELOPT_ME(TELOPT_COMPORT))
return(1);
else
#endif /* TN_COMPORT */
return(0);
}
/* tn_wait() -- Wait for response to Telnet negotiation. */
/*
Wait for up to <timeout> seconds for the response to arrive.
Place all non-telnet data into Telnet Wait Buffer.
If response does arrive return 1, else return 0.
*/
#ifndef TN_WAIT_BUF_SZ
#define TN_WAIT_BUF_SZ 4096
#endif /* TN_WAIT_BUF_SZ */
static char tn_wait_buf[TN_WAIT_BUF_SZ];
static int tn_wait_idx = 0;
#ifndef TN_TIMEOUT
#define TN_TIMEOUT 120
#endif /* TN_TIMEOUT */
static int tn_wait_tmo = TN_TIMEOUT;
#ifdef CKSPINNER
VOID
prtwait(state) int state; {
switch (state % 4) {
case 0:
printf("/");
break;
case 1:
printf("-");
break;
case 2:
printf("\\");
break;
case 3:
printf("|");
break;
}
}
#endif /* CKSPINNER */
static int nflag = 0;
int
#ifdef CK_ANSIC
tn_wait(char * where)
#else
tn_wait(where) char * where;
#endif /* CK_ANSIC */
/* tn_wait */ {
extern int ckxech, local;
int ch = 0, count = 0;
#ifndef NOHINTS
int nohintgiven = 1;
extern int hints;
#endif /* NOHINTS */
int outstanding;
#ifdef TN_COMPORT
int savcarr;
extern int ttcarr;
#endif /* TN_COMPORT */
rtimer();
debug(F110,"tn_wait waiting for",where,0);
tn_wait_tmo = TN_TIMEOUT;
debug(F111,"tn_wait","timeout",tn_wait_tmo);
outstanding = tn_outst(0);
debug(F111,"tn_wait","outstanding",outstanding);
debug(F111,"tn_wait","tn_wait_flg",tn_wait_flg);
/* The following is meant to be !(||). We only want to return */
/* immediately if both the tn_wait_flg && tn_outst() are false */
if (!(outstanding || tn_wait_flg)) /* If no need to wait */
return(1); /* Don't. */
if (tn_deb || debses) tn_debug("<wait for outstanding negotiations>");
#ifdef CKSPINNER
if (!sstelnet && !quiet)
prtwait(0);
#endif /* CKSPINNER */
/* Wait up to TN_TIMEOUT sec for responses to outstanding telnet negs */
do {
#ifdef NTSIG
ck_ih();
#endif /* NTSIG */
ch = ttinc(1);
if (ch == -1) { /* Timed out */
if (!sstelnet && !quiet) { /* Let user know... */
#ifdef CKSPINNER
printf("\b");
prtwait(gtimer());
#else
if (nflag == 0) {
printf(" Negotiations.");
nflag++;
}
if (nflag > 0) {
printf(".");
nflag++;
fflush(stdout);
}
#endif /* CKSPINNER */
}
} else if (ch < -1) {
printf("\r\n?Connection closed by peer.\r\n");
if (tn_deb || debses) tn_debug("<connection closed by peer>");
return(-1);
} else
switch (ch) {
case IAC:
#ifdef CKSPINNER
if (!sstelnet && !quiet)
printf("\b");
#endif /* CKSPINNER */
ch = tn_doop((CHAR)(ch & 0xff),inserver?ckxech:duplex,ttinc);
#ifdef CKSPINNER
if (!sstelnet && !quiet) {
prtwait(gtimer());
}
#endif /* CKSPINNER */
debug(F101,"tn_wait tn_doop","",ch);
switch (ch) {
case 1:
duplex = 1; /* Turn on echoing */
if (inserver)
ckxech = 1;
break;
case 2:
duplex = 0; /* Turn off echoing */
if (inserver)
ckxech = 0;
break;
case 3:
tn_wait_buf[tn_wait_idx++] = IAC;
break;
case 4: /* IKS event */
case 6: /* Logout */
break;
case -1:
if (!quiet)
printf("?Telnet Option negotiation error.\n");
if (tn_deb || debses)
tn_debug("<Telnet Option negotiation error>");
return(-1);
case -2:
printf("?Connection closed by peer.\n");
if (tn_deb || debses) tn_debug("<Connection closed by peer>");
ttclos(0);
return(-2);
default:
if (ch < 0) {
if (tn_deb || debses) tn_debug("<Unknown connection error>");
return(ch);
}
} /* switch */
break;
default:
#ifdef CKSPINNER
if (!sstelnet && !quiet) {
printf("\b");
prtwait(gtimer());
}
#endif /* CKSPINNER */
tn_wait_buf[tn_wait_idx++] = (CHAR)(ch & 0xff);
} /* switch */
outstanding = tn_outst(0);
if ( outstanding && ch != IAC ) {
int timer = gtimer();
if ( timer > tn_wait_tmo ) {
if (!sstelnet) {
printf(
"\r\n?Telnet Protocol Timeout - connection closed\r\n");
if (tn_deb || debses)
tn_debug(
"<telnet protocol timeout - connection closed>");
tn_outst(1);
}
/* if we do not close the connection, then we will block */
/* the next time we hit a wait. and if we don't we will */
/* do the wrong thing if the host sends 0xFF and does */
/* not intend it to be an IAC. */
ttclos(0);
whyclosed = WC_TELOPT;
return(-1);
}
#ifndef NOHINTS
else if ( hints && timer > 30 && nohintgiven && !inserver ) {
#ifdef CKSPINNER
printf("\b");
#else /* CKSPINNER */
printf("\r\n");
#endif /* CKSPINNER */
printf("*************************\r\n");
printf("The Telnet %s is not sending required responses.\r\n\r\n",
sstelnet?"client":"server");
tn_outst(1);
printf("\nYou can continue to wait or you can cancel with Ctrl-C.\r\n");
printf("In case the Telnet server never responds as required,\r\n");
printf("you can try connecting to this host with TELNET /NOWAIT.\r\n");
printf("Use SET HINTS OFF to suppress further hints.\r\n");
printf("*************************\r\n");
nohintgiven = 0;
}
#endif /* NOHINTS */
}
#ifdef TN_COMPORT
/* Must disable carrier detect check if we are using Telnet Comport */
savcarr = ttcarr;
ttscarr(CAR_OFF);
count = ttchk();
ttscarr(savcarr);
#else /* TN_COMPORT */
count = ttchk();
#endif /* TN_COMPORT */
} while ((tn_wait_idx < TN_WAIT_BUF_SZ) &&
(outstanding && count >= 0));
if (tn_wait_idx == TN_WAIT_BUF_SZ) {
if (tn_deb || debses) tn_debug("<Telnet Wait Buffer filled>");
return(0);
}
if (!sstelnet && !quiet) {
#ifdef CKSPINNER
printf("\b \b");
#else
if (nflag > 0) {
printf(" (OK)\n");
nflag = -1;
}
#endif /* CKSPINNER */
}
if (tn_deb || debses) tn_debug("<no outstanding negotiations>");
return(0);
}
/* Push data from the Telnet Wait Buffer into the I/O Queue */
/* Return 1 on success */
int
tn_push() {
#ifdef NETLEBUF
extern int tt_push_inited;
#endif /* NETLEBUF */
if (tn_wait_idx) {
hexdump((CHAR *)"tn_push",tn_wait_buf,tn_wait_idx);
#ifdef NETLEBUF
if (!tt_push_inited) /* Local handling */
le_init();
le_puts((CHAR *)tn_wait_buf,tn_wait_idx);
#else /* External handling... */
#ifdef OS2 /* K95 has its own way */
le_puts((CHAR *)tn_wait_buf,tn_wait_idx);
#else
#ifdef TTLEBUF /* UNIX, etc */
le_puts((CHAR *)tn_wait_buf,tn_wait_idx);
#else
/*
If you see this message in AOS/VS, OS-9, VOS, etc, you need to copy
the #ifdef TTLEBUF..#endif code from ckutio.c to the corresponding
places in your ck?tio.c module.
*/
printf("tn_push called but not implemented - data lost.\n");
#endif /* TTLEBUF */
#endif /* OS2 */
#endif /* NETLEBUF */
tn_wait_idx = 0;
}
tn_wait_tmo = TN_TIMEOUT; /* Reset wait timer stats */
return(1);
}
/* T N _ S O P T */
/*
Sends a telnet option, avoids loops.
Returns 1 if command was sent, 0 if not, -1 on error.
*/
int
tn_sopt(cmd,opt) int cmd, opt; { /* TELNET SEND OPTION */
CHAR buf[5];
char msg[128];
int rc;
if (ttnet != NET_TCPB) return(-1); /* Must be TCP/IP */
if (ttnproto != NP_TELNET) return(-1); /* Must be telnet protocol */
if (!TELCMD_OK(cmd)) return(-1);
if (TELOPT_OK(opt)) {
if (cmd == DO && TELOPT_UNANSWERED_DO(opt)) return(0);
if (cmd == WILL && TELOPT_UNANSWERED_WILL(opt)) return(0);
if (cmd == DONT && TELOPT_UNANSWERED_DONT(opt)) return(0);
if (cmd == WONT && TELOPT_UNANSWERED_WONT(opt)) return(0);
}
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
if (cmd == DO && opt == TELOPT_AUTHENTICATION)
buf[0] = 0;
if (tn_infinite && TELOPT_OK(opt)) { /* See comment above about */
int index = TELOPT_INDEX(opt); /* preventing infinite loops */
int m = cmd - WILL;
if (tncnts[index][m] > MAXTNCNT) {
#ifdef DEBUG
if (tn_deb || debses || deblog) {
ckmakmsg(msg,sizeof(msg),
"TELNET negotiation loop ",
TELCMD(cmd), " ",
TELOPT(opt));
debug(F101,msg,"",opt);
if (tn_deb || debses) tn_debug(msg);
}
#endif /* DEBUG */
return(0);
}
tncnts[index][m]++;
tncnts[index][tnopps[m]] = 0;
}
buf[0] = (CHAR) IAC;
buf[1] = (CHAR) (cmd & 0xff);
buf[2] = (CHAR) (opt & 0xff);
buf[3] = (CHAR) 0;
#ifdef DEBUG
if ((tn_deb || debses || deblog) && cmd != SB)
ckmakmsg(msg,sizeof(msg),"TELNET SENT ",TELCMD(cmd)," ",
TELOPT(opt));
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
debug(F101,msg,"",opt);
if ((tn_deb || debses) && cmd != SB)
tn_debug(msg);
rc = (ttol(buf,3) < 3);
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
if (TELOPT_OK(opt)) {
if (cmd == DONT && TELOPT_UNANSWERED_DO(opt))
TELOPT_UNANSWERED_DO(opt) = 0;
if (cmd == WONT && TELOPT_UNANSWERED_WILL(opt))
TELOPT_UNANSWERED_WILL(opt) = 0;
if (cmd == DO && TELOPT_UNANSWERED_DONT(opt))
TELOPT_UNANSWERED_DONT(opt) = 0;
if (cmd == WILL && TELOPT_UNANSWERED_WONT(opt))
TELOPT_UNANSWERED_WONT(opt) = 0;
}
return(1);
}
/* Send a telnet sub-option */
/* Returns 1 if command was sent, 0 if not, -1 on error */
int
tn_ssbopt(opt,sub,data,len) int opt, sub; CHAR * data; int len; {
CHAR buf[256];
int n,m,rc;
if (ttnet != NET_TCPB) return(0); /* Must be TCP/IP */
if (ttnproto != NP_TELNET) return(0); /* Must be telnet protocol */
if (!TELOPT_OK(opt)) return(-1);
if (len < 0 || len > 250) {
debug(F111,"Unable to Send TELNET SB - data too long","len",len);
return(-1); /* Data too long */
}
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
if (ttchk() < 0)
return(-1);
else
return(1);
}
#endif /* CK_SSL */
if (!data) len = 0;
buf[0] = (CHAR) IAC;
buf[1] = (CHAR) (SB & 0xff);
buf[2] = (CHAR) (opt & 0xff);
buf[3] = (CHAR) (sub & 0xff);
if (data && len > 0) {
memcpy(&buf[4],data,len);
}
buf[4+len] = (CHAR) IAC;
buf[5+len] = (CHAR) SE;
#ifdef DEBUG
if (tn_deb || debses || deblog) {
if (opt == TELOPT_START_TLS && sub == 1)
ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(opt)," FOLLOWS IAC SE",NULL);
else if (opt == TELOPT_TTYPE && sub == 1)
ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(opt),
" SEND IAC SE",NULL);
else if (opt == TELOPT_TTYPE && sub == 0)
ckmakxmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ",TELOPT(opt)," IS ",
(char *)data," IAC SE",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
else if (opt == TELOPT_NEWENVIRON) {
int i, quote;
ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_NEWENVIRON)," ",
sub == TELQUAL_SEND ? "SEND" :
sub == TELQUAL_IS ? "IS" :
sub == TELQUAL_INFO ?"INFO" : "UNKNOWN" );
for (i = 0, quote = 0; i < len; i++) {
if (quote) {
sprintf(hexbuf,"%02x",data[i]); /* safe but ugly */
ckstrncat(tn_msg_out,hexbuf,TN_MSG_LEN);
quote = 0;
} else {
switch (data[i]) {
case TEL_ENV_USERVAR:
ckstrncat(tn_msg_out," USERVAR ",TN_MSG_LEN);
break;
case TEL_ENV_VAR:
ckstrncat(tn_msg_out," VAR ",TN_MSG_LEN);
break;
case TEL_ENV_VALUE:
ckstrncat(tn_msg_out," VALUE ",TN_MSG_LEN);
break;
case TEL_ENV_ESC:
ckstrncat(tn_msg_out," ESC ",TN_MSG_LEN);
quote = 1;
break;
case IAC:
ckstrncat(tn_msg_out," IAC ",TN_MSG_LEN);
break;
default:
sprintf(hexbuf,"%c",data[i]); /* safe but ugly */
ckstrncat(tn_msg_out,hexbuf,TN_MSG_LEN);
}
}
}
ckstrncat(tn_msg_out," IAC SE",TN_MSG_LEN);
} else {
sprintf(hexbuf,"%02x",sub); /* safe but ugly */
ckmakxmsg(tn_msg_out,TN_MSG_LEN,
"TELNET SENT SB ",TELOPT(opt),
" ",
hexbuf,
" <data> IAC SE",
NULL,NULL,NULL,NULL,NULL,NULL,NULL
);
}
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif /* OS2 */
#ifdef DEBUG
debug(F101,tn_msg_out,"",opt);
if (tn_deb || debses)
tn_debug(tn_msg_out);
#endif /* DEBUG */
rc = (ttol(buf,6+len) < 6+len);
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
return(1);
}
/*
tn_flui() -- Processes all waiting data for Telnet commands.
All non-Telnet data is to be stored into the Telnet Wait Buffer.
Returns 1 on success.
*/
int
tn_flui() {
extern int ckxech;
int x = 0;
/* Wait up to 5 sec for responses to outstanding telnet negotiations */
while (x >= 0 && ttchk() > 0 && tn_wait_idx < TN_WAIT_BUF_SZ) {
x = ttinc(1);
switch (x) {
case IAC:
x = tn_doop((CHAR)(x & 0xff),inserver?ckxech:duplex,ttinc);
debug(F101,"tn_flui tn_doop","",x);
switch (x) {
case 1: /* Turn on echoing */
duplex = 1;
if (inserver)
ckxech = 1;
break;
case 2: /* Turn off echoing */
duplex = 0;
if (inserver)
ckxech = 0;
break;
case 3:
tn_wait_buf[tn_wait_idx++] = IAC;
break;
case 4: /* IKS event */
case 6: /* Logout */
break;
}
break;
default:
if (x >= 0)
tn_wait_buf[tn_wait_idx++] = x;
}
}
return(1);
}
unsigned char *
tn_get_display()
{
char * disp = NULL;
static char tmploc[256];
/* Must compute the DISPLAY string we are going to send to the host */
/* If one is not assigned, do not send a string unless the user has */
/* explicitedly requested we try to send one via X-Display Location */
/* But do not send a string at all if FORWARD_X is in use. */
debug(F110,"tn_get_display() myipaddr",myipaddr,0);
#ifdef CK_ENVIRONMENT
debug(F110,"tn_get_display() tn_env_disp",tn_env_disp,0);
if (tn_env_disp[0]) {
int colon = ckindex(":",tn_env_disp,0,0,1);
if ( !colon ) {
ckmakmsg(tmploc,256,myipaddr,":",tn_env_disp,NULL);
disp = tmploc;
} else if ( ckindex("localhost:",tn_env_disp,0,0,0) ||
ckindex("unix:",tn_env_disp,0,0,0) ||
ckindex("127.0.0.1:",tn_env_disp,0,0,0) ||
!ckstrcmp("0:",tn_env_disp,2,1) ||
tn_env_disp[0] == ':' ) {
ckmakmsg(tmploc,256,myipaddr,":",&tn_env_disp[colon],NULL);
disp = tmploc;
} else
disp = tn_env_disp;
}
else
#endif /* CK_ENVIRONMENT */
if (TELOPT_ME(TELOPT_XDISPLOC) ||
TELOPT_U(TELOPT_FORWARD_X)) {
ckmakmsg(tmploc,256,myipaddr,":0.0",NULL,NULL);
disp = tmploc;
}
debug(F110,"tn_get_display() returns",disp,0);
return((CHAR *)disp);
}
#ifdef CK_FORWARD_X
static Xauth fake_xauth = {0,0,NULL,0,NULL,0,NULL,0,NULL};
static Xauth *real_xauth=NULL;
/*
* Author: Jim Fulton, MIT X Consortium
*
* fwdx_parse_displayname -
* display a display string up into its component parts
*/
#ifdef UNIX
#define UNIX_CONNECTION "unix"
#define UNIX_CONNECTION_LENGTH 4
#endif
/*
* private utility routines
*/
static int
#ifdef CK_ANSIC
XmuGetHostname (char *buf, int maxlen)
#else
XmuGetHostname (buf, maxlen)
char *buf;
int maxlen;
#endif /* CK_ANSIC */
{
int len;
#ifdef NEED_UTSNAME
/*
* same host name crock as in server and xinit.
*/
struct utsname name;
uname (&name);
len = strlen (name.nodename);
if (len >= maxlen) len = maxlen - 1;
strncpy (buf, name.nodename, len);
buf[len] = '\0';
#else
buf[0] = '\0';
(void) gethostname (buf, maxlen);
buf [maxlen - 1] = '\0';
len = strlen(buf);
#endif /* hpux */
return len;
}
static char *
#ifdef CK_ANSIC
copystring (char *src, int len)
#else
copystring (src, len)
char *src;
int len;
#endif /* CK_ANSIC */
{
char *cp;
if (!src && len != 0) return NULL;
cp = malloc (len + 1);
if (cp) {
if (src) strncpy (cp, src, len);
cp[len] = '\0';
}
return cp;
}
static char *
#ifdef CK_ANSIC
get_local_hostname (char *buf, int maxlen)
#else
get_local_hostname (buf, maxlen)
char *buf;
int maxlen;
#endif
{
buf[0] = '\0';
(void) XmuGetHostname (buf, maxlen);
return (buf[0] ? buf : NULL);
}
#ifndef UNIX
static char *
copyhostname ()
{
char buf[256];
return (get_local_hostname (buf, sizeof buf) ?
copystring (buf, strlen (buf)) : NULL);
}
#endif
int
#ifdef CK_ANSIC
fwdx_parse_displayname (char *displayname, int *familyp, char **hostp,
int *dpynump, int *scrnump, char **restp)
#else
fwdx_parse_displayname (displayname, familyp, hostp, dpynump, scrnump, restp)
char *displayname;
int *familyp; /* return */
char **hostp; /* return */
int *dpynump, *scrnump; /* return */
char **restp; /* return */
#endif /* CK_ANSIC */
{
char *ptr; /* work variables */
int len; /* work variable */
int family = -1; /* value to be returned */
char *host = NULL; /* must free if set and error return */
int dpynum = -1; /* value to be returned */
int scrnum = 0; /* value to be returned */
char *rest = NULL; /* must free if set and error return */
int dnet = 0; /* if 1 then using DECnet */
/* check the name */
if (!displayname || !displayname[0])
return 0;
/* must have at least :number */
ptr = (char *)strchr(displayname, ':');
if (!ptr || !ptr[1]) return 0;
if (ptr[1] == ':') {
if (ptr[2] == '\0') return 0;
dnet = 1;
}
/*
* get the host string; if none is given, use the most effiecient path
*/
len = (ptr - displayname); /* length of host name */
if (len == 0) { /* choose most efficient path */
#ifdef UNIX
host = copystring (UNIX_CONNECTION, UNIX_CONNECTION_LENGTH);
family = FamilyLocal;
#else
if (dnet) {
host = copystring ("0", 1);
family = FamilyDECnet;
} else {
host = copyhostname ();
family = FamilyInternet;
}
#endif
} else {
host = copystring (displayname, len);
if (dnet) {
family = dnet;
} else {
#ifdef UNIX
if (host && strcmp (host, UNIX_CONNECTION) == 0)
family = FamilyLocal;
else
#endif
family = FamilyInternet;
}
}
if (!host) return 0;
/*
* get the display number; we know that there is something after the
* colon (or colons) from above. note that host is now set and must
* be freed if there is an error.
*/
if (dnet) ptr++; /* skip the extra DECnet colon */
ptr++; /* move to start of display num */
{
register char *cp;
for (cp = ptr; *cp && isascii(*cp) && isdigit(*cp); cp++) ;
len = (cp - ptr);
/* check present and valid follow */
if (len == 0 || (*cp && *cp != '.')) {
free (host);
return 0;
}
dpynum = atoi (ptr); /* it will handle num. as well */
ptr = cp;
}
/*
* now get screen number if given; ptr may point to nul at this point
*/
if (ptr[0] == '.') {
register char *cp;
ptr++;
for (cp = ptr; *cp && isascii(*cp) && isdigit(*cp); cp++) ;
len = (cp - ptr);
if (len == 0 || (*cp && *cp != '.')) { /* all prop name */
free (host);
return 0;
}
scrnum = atoi (ptr); /* it will handle num. as well */
ptr = cp;
}
/*
* and finally, get any additional stuff that might be following the
* the screen number; ptr must point to a period if there is anything
*/
if (ptr[0] == '.') {
ptr++;
len = strlen (ptr);
if (len > 0) {
rest = copystring (ptr, len);
if (!rest) {
free (host);
return 1;
}
}
}
/*
* and we are done!
*/
if ( familyp )
*familyp = family;
if ( hostp )
*hostp = host;
else
free(host);
if ( dpynump )
*dpynump = dpynum;
if ( scrnump )
*scrnump = scrnum;
if ( restp )
*restp = rest;
else
free(rest);
return 1;
}
int
#ifdef CK_ANSIC
fwdx_tn_sb( unsigned char * sb, int n )
#else
fwdx_tn_sb( sb, n ) unsigned char * sb; int n;
#endif /* CK_ANSIC */
{
unsigned short hchannel, nchannel;
unsigned char * p;
int i;
int rc = -1;
/* check to ensure we have negotiated Forward X */
if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) ||
!sstelnet && !TELOPT_U(TELOPT_FORWARD_X) ) {
debug(F100,"fwdx_tn_sb() not negotiated","",0);
return(0);
}
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
switch (sb[0]) {
case FWDX_SCREEN:
if (sstelnet && n == 4)
rc = fwdx_create_listen_socket(sb[1]);
break;
case FWDX_OPEN:
if ( !sstelnet && n >= 5 ) {
p = (unsigned char *) &nchannel;
i = 1;
/* IAC quoting has been stripped in tn_sb() */
p[0] = sb[i++];
p[1] = sb[i++];
hchannel = ntohs(nchannel);
rc = fwdx_open_client_channel(hchannel);
if ( rc < 0 ) {
/* Failed; Send CLOSE channel */
fwdx_send_close(hchannel);
rc = 0; /* Do not consider this to be a telnet error */
}
#ifdef NT
if ( !TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started ) {
ckThreadBegin( &fwdx_thread,32655, 0, FALSE, 0 ) ;
TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 1;
}
#endif /* NT */
}
break;
case FWDX_CLOSE:
p = (unsigned char *) &nchannel;
i = 1;
/* IAC quoting has been stripped in tn_sb() */
p[0] = sb[i++];
p[1] = sb[i++];
hchannel = ntohs(nchannel);
fwdx_close_channel(hchannel);
rc = 0; /* no errors when closing */
break;
case FWDX_DATA:
p = (unsigned char *) &nchannel;
i = 1;
/* IAC quoting has been stripped in tn_sb() */
p[0] = sb[i++];
p[1] = sb[i++];
hchannel = ntohs(nchannel);
rc = fwdx_send_xauth_to_xserver(hchannel,(char *)&sb[3],n-5);
if ( rc >= 0 && n-5-rc > 0) {
rc = fwdx_write_data_to_channel(hchannel,(char *)&sb[3+rc],n-5-rc);
if ( rc < 0 ) {
/* Failed; Send CLOSE channel */
rc = fwdx_send_close(hchannel);
}
}
break;
case FWDX_OPTIONS:
if ( sstelnet ) {
#ifndef FWDX_SERVER
rc = 0;
#else
rc = fwdx_server_accept_options((char*)&sb[2],n-3);
#endif
} else {
rc = fwdx_client_reply_options((char *)&sb[2],n-3);
if ( rc >= 0 ) {
rc = tn_sndfwdx();
}
}
break;
case FWDX_OPT_DATA:
switch ( sb[1] ) {
default:
rc = 0; /* we don't recognize, not an error */
}
break;
case FWDX_XOFF:
case FWDX_XON:
if ( !sstelnet ) {
p = (unsigned char *) &nchannel;
i = 1;
/* IAC quoting has been stripped in tn_sb() */
p[0] = sb[i++];
p[1] = sb[i++];
hchannel = ntohs(nchannel);
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[hchannel].suspend =
(sb[0] == FWDX_XOFF);
rc = 0;
}
break;
}
return(rc < 0 ? -1 : 0);
}
int
#ifdef CK_ANSIC
fwdx_send_xauth_to_xserver(int channel, unsigned char * data, int len)
#else
fwdx_send_xauth_to_xserver(channel, data, len)
int channel; unsigned char * data; int len;
#endif /* CK_ANSIC */
{
int name_len, data_len, i;
for (i = 0; i < MAXFWDX ; i++) {
if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel)
break;
}
if ( i == MAXFWDX )
goto auth_err;
if (!TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth)
return(0);
if (len < 12)
goto auth_err;
/* Parse the lengths of variable-length fields. */
if (data[0] == 0x42) { /* byte order MSB first. */
/* Xauth packets appear to always have this format */
if ( data[1] != 0x00 ||
data[2] != 0x00 ||
data[3] != 0x0B ||
data[4] != 0x00 ||
data[5] != 0x00 )
goto auth_err;
name_len = (data[6] << 8) + data[7];
data_len = (data[8] << 8) + data[9];
} else if (data[0] == 0x6c) { /* Byte order LSB first. */
/* Xauth packets appear to always have this format */
if ( data[1] != 0x00 ||
data[2] != 0x0B ||
data[3] != 0x00 ||
data[4] != 0x00 ||
data[5] != 0x00 )
goto auth_err;
name_len = data[6] + (data[7] << 8);
data_len = data[8] + (data[9] << 8);
} else {
/* bad byte order byte */
goto auth_err;
}
/* Check if the whole packet is in buffer. */
if (len < 12 + ((name_len + 3) & ~3) + ((data_len + 3) & ~3))
goto auth_err;
/* If the Telnet Server allows a real Xauth message to be sent */
/* Then let the message be processed by the Xserver. */
if (name_len + data_len > 0) {
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
return(0);
}
else
/* If an empty Xauth message was received. We are going to */
/* send our own Xauth message using the real Xauth data. And */
/* then send any other data in the buffer. */
{
int c, err, dpynum, scrnum, family, sb_len;
char *display, *host = NULL, *rest = NULL;
unsigned char *sb, *p;
/* parse the local DISPLAY env var */
display = getenv("DISPLAY");
if ( !display )
display = "127.0.0.1:0.0";
if (fwdx_parse_displayname(display,
&family, &host, &dpynum, &scrnum, &rest)) {
char * disp_no = ckitoa(dpynum); /* should be unsigned */
if (family == FamilyLocal) {
/* call with address = "<local host name>" */
char address[300] = "localhost";
gethostname(address, sizeof(address) - 1);
real_xauth = XauGetAuthByAddr(family,
strlen(address),
address,
strlen(disp_no),
disp_no, 0, NULL);
}
else if (family == FamilyInternet) {
/* call with address = 4 bytes numeric ip addr (MSB) */
struct hostent *hi;
if (hi = gethostbyname(host))
real_xauth = XauGetAuthByAddr(family, 4,
hi->h_addr, strlen(disp_no),
disp_no, 0, NULL);
}
}
if (host) free(host);
if (rest) free(rest);
if (!real_xauth) {
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
return(0);
}
if (!strncmp(real_xauth->name,
"MIT-MAGIC-COOKIE-1",
real_xauth->name_length)) {
char msg[64];
name_len = real_xauth->name_length;
data_len = 16;
if ( data[0] == 0x42 ) {
msg[0] = 0x42; /* MSB order */
msg[1] = msg[2] = 0;
msg[3] = 0x0B;
msg[4] = msg[5] = 0;
msg[6] = (name_len >> 8);
msg[7] = (name_len & 0xFF);
msg[8] = (data_len >> 8);
msg[9] = (data_len & 0xFF);
} else {
msg[0] = 0x6c; /* LSB order */
msg[1] = 0;
msg[2] = 0x0B;
msg[3] = msg[4] = msg[5] = 0;
msg[6] = (name_len & 0xFF);
msg[7] = (name_len >> 8);
msg[8] = (data_len & 0xFF);
msg[9] = (data_len >> 8);
}
msg[10] = msg[11] = 0;
memcpy(&msg[12],real_xauth->name,18);
msg[30] = msg[31] = 0;
memcpy(&msg[32],real_xauth->data,16);
if (fwdx_write_data_to_channel(channel,(char *)msg,48) < 0) {
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
return(-1);
} else {
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
return(12);
}
} else {
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
return(0); /* we do not know how to handle this type yet */
}
}
auth_err:
debug(F100,"fwdx_send_xauth_to_xserver error","",0);
return(-1);
}
#ifdef COMMENT
int
#ifdef CK_ANSIC
fwdx_authorize_channel(int channel, unsigned char * data, int len)
#else
fwdx_authorize_channel(channel, data, len)
int channel; unsigned char * data; int len;
#endif /* CK_ANSIC */
{
/* XXX maybe we should have some retry handling if not the whole first
* authorization packet arrives complete
*/
if ( !TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].authorized ) {
int name_len, data_len;
if (len < 12)
goto auth_err;
/* Parse the lengths of variable-length fields. */
if (data[0] == 0x42) { /* byte order MSB first. */
/* Xauth packets appear to always have this format */
if ( data[1] != 0x00 ||
data[2] != 0x00 ||
data[3] != 0x0B ||
data[4] != 0x00 ||
data[5] != 0x00 )
goto auth_err;
name_len = (data[6] << 8) + data[7];
data_len = (data[8] << 8) + data[9];
} else if (data[0] == 0x6c) { /* Byte order LSB first. */
/* Xauth packets appear to always have this format */
if ( data[1] != 0x00 ||
data[2] != 0x0B ||
data[3] != 0x00 ||
data[4] != 0x00 ||
data[5] != 0x00 )
goto auth_err;
name_len = data[6] + (data[7] << 8);
data_len = data[8] + (data[9] << 8);
} else {
/* bad byte order byte */
goto auth_err;
}
/* Check if authentication protocol matches. */
if (name_len != fake_xauth.name_length ||
memcmp(data + 12, fake_xauth.name, name_len) != 0) {
/* connection uses different authentication protocol */
goto auth_err;
}
/* Check if authentication data matches our fake data. */
if (data_len != fake_xauth.data_length ||
memcmp(data + 12 + ((name_len + 3) & ~3),
fake_xauth.data, fake_xauth.data_length) != 0) {
/* auth data does not match fake data */
goto auth_err;
}
/* substitute the fake data with real data if we have any */
if (real_xauth && real_xauth->data)
memcpy(data + 12 + ((name_len + 3) & ~3),
real_xauth->data, data_len);
TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].authorized = 1;
}
return(0);
auth_err:
return(-1);
}
#endif /* COMMENT */
int
#ifdef CK_ANSIC
fwdx_send_close(int channel)
#else
fwdx_send_close(channel) int channel;
#endif /* CK_ANSIC */
{
unsigned short nchannel;
int i,rc;
CHAR * p;
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
nchannel = htons(channel);
p = (unsigned char *) &nchannel;
i = 0;
sb_out[i++] = (CHAR) IAC; /* I Am a Command */
sb_out[i++] = (CHAR) SB; /* Subnegotiation */
sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */
sb_out[i++] = FWDX_CLOSE; /* Open */
sb_out[i++] = p[0]; /* First Byte of Channel */
if ( p[0] == IAC )
sb_out[i++] = IAC;
sb_out[i++] = p[1]; /* Second Byte of Channel */
if ( p[1] == IAC )
sb_out[i++] = IAC;
sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */
sb_out[i++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakxmsg(fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_FORWARD_X),
" CLOSE CHANNEL=",ckitoa(channel)," IAC SE",
NULL,NULL,NULL,NULL,NULL,NULL,NULL
);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
return(0);
}
int
#ifdef CK_ANSIC
fwdx_send_open(int channel)
#else /* CK_ANSIC */
fwdx_send_open(channel) int channel;
#endif /* CK_ANSIC */
{
unsigned short nchannel;
int i, rc;
CHAR * p;
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
nchannel = htons(channel);
p = (unsigned char *) &nchannel;
i = 0;
sb_out[i++] = (CHAR) IAC; /* I Am a Command */
sb_out[i++] = (CHAR) SB; /* Subnegotiation */
sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */
sb_out[i++] = FWDX_OPEN; /* Open */
sb_out[i++] = p[0]; /* First Byte of Channel */
if ( p[0] == IAC )
sb_out[i++] = IAC;
sb_out[i++] = p[1]; /* Second Byte of Channel */
if ( p[1] == IAC )
sb_out[i++] = IAC;
sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */
sb_out[i++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakxmsg(fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_FORWARD_X),
" OPEN CHANNEL=",ckitoa(channel)," IAC SE",
NULL,NULL,NULL,NULL,NULL,NULL,NULL);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
return(0);
}
int
#ifdef CK_ANSIC
fwdx_client_reply_options(char *opts, int n)
#else
fwdx_client_reply_options(opts, n) char *opts; int n;
#endif /* CK_ANSIC */
{
int i,j,rc;
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
i = 0;
sb_out[i++] = (CHAR) IAC; /* I Am a Command */
sb_out[i++] = (CHAR) SB; /* Subnegotiation */
sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */
sb_out[i++] = FWDX_OPTIONS; /* Options */
/* Look for the options we recognize and will support for this session */
/* and reply with their bytes set */
for (j=0; j<n; j++,i++) {
sb_out[i] = FWDX_OPT_NONE; /* Add zero byte - no options */
#ifdef COMMENT
/* If we had any options to support, this is how we would do it */
if ( j == 0 ) {
if (opts[j] & FWDX_OPT_XXXX) {
/* set flag to remember option is in use */
flag = 1;
sb_out[i] |= FWDX_OPT_XXXX;
}
}
#endif /* COMMENT */
}
sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */
sb_out[i++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakxmsg(fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_FORWARD_X),
" OPTIONS ",ckctox(sb_out[4],1)," IAC SE",
NULL,NULL,NULL,NULL,NULL,NULL,NULL);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
return(0);
}
int
fwdx_send_options() {
int i, rc;
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
i = 0;
sb_out[i++] = (CHAR) IAC; /* I Am a Command */
sb_out[i++] = (CHAR) SB; /* Subnegotiation */
sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */
sb_out[i++] = FWDX_OPTIONS; /* Options */
sb_out[i] = FWDX_OPT_NONE;
/* activate options here */
i++;
sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */
sb_out[i++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakmsg(fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_FORWARD_X),
" OPTIONS 00 IAC SE",NULL);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc)
return(-1);
return(0);
}
int
#ifdef CK_ANSIC
fwdx_send_data_from_channel(int channel, char * data, int len)
#else
fwdx_send_data_from_channel(channel, data, len)
int channel; char * data; int len;
#endif
{
unsigned short nchannel;
/* static */ CHAR sb_priv[2048];
CHAR * p;
int i, j, j_sav, rc;
unsigned int tmp;
debug(F111,"fwdx_send_data_from_channel()","channel",channel);
#ifdef CK_SSL
if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) {
return(0);
}
#endif /* CK_SSL */
nchannel = htons(channel);
p = (unsigned char *) &nchannel;
j = 0;
sb_priv[j++] = (CHAR) IAC; /* I Am a Command */
sb_priv[j++] = (CHAR) SB; /* Subnegotiation */
sb_priv[j++] = TELOPT_FORWARD_X; /* Forward X */
sb_priv[j++] = FWDX_DATA; /* Data */
sb_priv[j++] = p[0]; /* First Byte of Channel */
if ( p[0] == IAC )
sb_priv[j++] = IAC;
sb_priv[j++] = p[1]; /* Second Byte of Channel */
if ( p[1] == IAC )
sb_priv[j++] = IAC;
j_sav = j;
for (i = 0; i < len; i++) {
tmp = (unsigned int)data[i];
if ( tmp == IAC ) {
sb_priv[j++] = IAC;
sb_priv[j++] = IAC;
} else {
sb_priv[j++] = tmp;
}
if ( j >= 2045 && (i < len-1) ) {
sb_priv[j++] = (CHAR) IAC; /* End of Subnegotiation */
sb_priv[j++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakxmsg( fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ",
TELOPT(TELOPT_FORWARD_X),
" DATA CHANNEL=",ckitoa(channel)," ",
NULL,NULL,NULL,NULL,NULL,NULL,NULL );
tn_hex(fwdx_msg_out,TN_MSG_LEN,&sb_priv[j_sav],j-(j_sav+2));
ckstrncat(fwdx_msg_out," IAC SE",TN_MSG_LEN);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol(sb_priv,j) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc) {
debug(F110,"fwdx_send_data_from_channel()","ttol() failed",0);
return(-1);
}
j = 0;
sb_priv[j++] = (CHAR) IAC; /* I Am a Command */
sb_priv[j++] = (CHAR) SB; /* Subnegotiation */
sb_priv[j++] = TELOPT_FORWARD_X; /* Forward X */
sb_priv[j++] = FWDX_DATA; /* Data */
sb_priv[j++] = p[0]; /* First Byte of Channel */
if ( p[0] == IAC )
sb_priv[j++] = IAC;
sb_priv[j++] = p[1]; /* Second Byte of Channel */
if ( p[1] == IAC )
sb_priv[j++] = IAC;
}
}
sb_priv[j++] = (CHAR) IAC; /* End of Subnegotiation */
sb_priv[j++] = (CHAR) SE; /* marked by IAC SE */
#ifdef DEBUG
if (deblog || tn_deb || debses) {
ckmakxmsg( fwdx_msg_out,TN_MSG_LEN,
"TELNET SENT SB ",TELOPT(TELOPT_FORWARD_X),
" DATA ",ckctox(p[0],1)," ",ckctox(p[1],1)," ",
NULL,NULL,NULL,NULL,NULL);
tn_hex(fwdx_msg_out,TN_MSG_LEN,&sb_priv[6],j-8);
ckstrncat(fwdx_msg_out," IAC SE",TN_MSG_LEN);
}
#endif /* DEBUG */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = (ttol(sb_priv,j) < 0); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if ( rc ) {
debug(F110,"fwdx_send_data_from_channel()","ttol() failed",0);
return(-1);
}
return(0);
}
static unsigned char *
#ifdef CK_ANSIC
fwdx_add_quoted_twobyte(unsigned char *p, unsigned short twobyte)
#else
fwdx_add_quoted_twobyte(p, twobyte)
unsigned char *p; unsigned short twobyte;
#endif /* CK_ANSIC */
/* adds the IAC quoted (MSB) representation of 'channel' at buffer pointer 'p',
* returning pointer to new buffer position. NO OVERFLOW CHECK!
*/
{
*p++ = (unsigned char)((twobyte >> 8) & 0xFF);
if (*(p - 1) == 0xFF)
*p++ = 0xFF;
*p++ = (unsigned char)(twobyte & 0xFF);
if (*(p - 1) == 0xFF)
*p++ = 0xFF;
return p;
}
int
#ifdef CK_ANSIC
fwdx_create_fake_xauth(char *name, int name_len, int data_len)
#else
fwdx_create_fake_xauth(name, name_len, data_len)
char *name; int name_len; int data_len;
#endif /* CK_ANSIC */
{
char stackdata[256];
unsigned int c, n;
if (!name_len || !data_len)
return 1;
fake_xauth.name = malloc(name_len);
fake_xauth.data = malloc(data_len);
if (!fake_xauth.name || !fake_xauth.data)
return 2;
fake_xauth.name_length = name_len;
memcpy(fake_xauth.name, name, name_len);
fake_xauth.data_length = data_len;
/* try to make a random unsigned int to feed srand() */
c = time(NULL);
c *= getpid();
for (n = 0; n < sizeof(stackdata); n++)
c += stackdata[n];
srand((unsigned int)c);
for (c = 0; c < data_len; c++)
fake_xauth.data[c] = (unsigned char)rand();
return 0;
}
#ifdef COMMENT
/* No longer used */
int
fwdx_send_xauth(void)
{
int c, err, dpynum, family, sb_len, rc;
char *display, *host = NULL;
unsigned char *sb_priv, *p;
/* parse the local DISPLAY env var */
if (!(display = tn_get_display()))
return (-1);
if (fwdx_parse_displayname(display, &family, &host, &dpynum, NULL, NULL)) {
char * disp_no = ckitoa(dpynum);
if (family == FamilyLocal) {
/* call with address = "<local host name>" */
char address[300] = "localhost";
gethostname(address, sizeof(address) - 1);
real_xauth = XauGetAuthByAddr(family,
strlen(address),
address,
strlen(disp_no),
disp_no, 0, NULL
);
}
else if (family == FamilyInternet) {
/* call with address = 4 bytes numeric ip addr (MSB) */
struct hostent *hi;
if (hi = gethostbyname(host))
real_xauth = XauGetAuthByAddr(family, 4,
hi->h_addr,
strlen(disp_no),
disp_no, 0, NULL
);
}
}
if (host) {
free(host);
host = NULL;
}
if (real_xauth)
err = fwdx_create_fake_xauth(real_xauth->name,
real_xauth->name_length,
real_xauth->data_length
);
else
err = fwdx_create_fake_xauth("MIT-MAGIC-COOKIE-1",
strlen("MIT-MAGIC-COOKIE-1"), 16);
if (err)
return(-1);
/* allocate memory for the SB block, alloc for worst case */
/* the following sprintf() calls are safe due to length checking */
/* buffer is twice as big as the input just in case every byte was IAC */
sb_len = 5 + 2 + 2 + fake_xauth.name_length + fake_xauth.data_length + 2;
if (!(sb_priv = malloc(2 * sb_len)))
return(-1);
p = sb_priv;
sprintf(p, "%c%c%c%c%c", IAC, SB, TELOPT_FORWARD_X,
FWDX_OPT_DATA, FWDX_OPT_XAUTH);
p += 5;
p = fwdx_add_quoted_twobyte(p, fake_xauth.name_length);
p = fwdx_add_quoted_twobyte(p, fake_xauth.data_length);
for (c = 0; c < fake_xauth.name_length; c++) {
*p++ = fake_xauth.name[c];
if ((unsigned char)fake_xauth.name[c] == 0xFF)
*p++ = 0xFF;
}
for (c = 0; c < fake_xauth.data_length; c++) {
*p++ = fake_xauth.data[c];
if ((unsigned char)fake_xauth.data[c] == 0xFF)
*p++ = 0xFF;
}
sprintf(p, "%c%c", IAC, SE);
p += 2;
#ifdef DEBUG
if (deblog || tn_deb || debses) {
sprintf(fwdx_msg_out,"TELNET SENT SB %s OPTION_DATA XAUTH ",
TELOPT(TELOPT_FORWARD_X));
tn_hex(fwdx_msg_out,TN_MSG_LEN,&sb_priv[5],(p-sb_priv)-7);
ckstrncat(fwdx_msg_out," IAC SE",TN_MSG_LEN);
}
#endif /* DEBUG */
/* Add Telnet Debug info here */
#ifdef OS2
RequestTelnetMutex( SEM_INDEFINITE_WAIT );
#endif
#ifdef DEBUG
debug(F100,fwdx_msg_out,"",0);
if (tn_deb || debses) tn_debug(fwdx_msg_out);
#endif /* DEBUG */
rc = ( ttol(sb_priv,p-sb_priv) < 0 ); /* Send it. */
#ifdef OS2
ReleaseTelnetMutex();
#endif
if (rc) {
debug(F110,"fwdx_send_xauth()","ttol() failed",0);
return(-1);
}
free(sb_priv);
return(0);
}
#endif /* COMMENT */
#ifdef FWDX_SERVER
/* Only if we ever become a server - not yet ported to Kermit */
/* And even so most of this code does not belong in this module */
int
fwdx_write_xauthfile(void)
{
int dpynum, scrnum, family;
char myhost[300], *host, *rest = NULL;
FILE *file;
struct sockaddr_in saddr;
struct hostent *hi;
if (!fwdx_display && !fwdx_xauthfile)
return 1;
if (!parse_displayname(fwdx_display,
&family, &host, &dpynum, &scrnum, &rest))
return 2;
if (rest) free(rest);
if (host) free(host);
if (family != FamilyInternet)
return 3; /* every thing but FamilyInternet is unexpected */
/* X connections to localhost:1 is actually treated as local unix sockets,
* see the 'xauth' man page.
*/
xauth.family = FamilyLocal;
if (gethostname(myhost, sizeof(myhost) - 1))
return 5;
xauth.address_length = strlen(myhost);
if (!(xauth.address = malloc(xauth.address_length)))
return 5;
memcpy(xauth.address, myhost, xauth.address_length);
/* the display number is written as a string, not numeric */
if (!(xauth.number = malloc(6)))
return 6;
snprintf(xauth.number, 5, "%u", dpynum);
xauth.number_length = strlen(xauth.number);
if (!(file = fopen(fwdx_xauthfile, "wb")))
return 7;
if (!XauWriteAuth(file, &xauth))
return 8;
fclose(file);
setenv("XAUTHORITY", fwdx_xauthfile, 1);
return 0;
}
int
fwdx_setup_xauth(unsigned char *sp, int len)
/* called with 'len' xauth bytes, starting at 'sp'
* the data format is: <uint16 name_length> <uint16 data_length> <name> <data>
*/
{
int xauthfd;
if (!fwdx_options[FWDX_OPT_XAUTH])
return 1;
if (len < 4)
return 2;
/* setup the xauth struct */
xauth.name_length = (sp[0] << 8) + sp[1];
xauth.data_length = (sp[2] << 8) + sp[3];
if (len != 4 + xauth.name_length + xauth.data_length)
return 3;
xauth.name = malloc(xauth.name_length);
xauth.data = malloc(xauth.data_length);
if (!xauth.name || !xauth.data)
return 4;
memcpy(xauth.name, sp + 4, xauth.name_length);
memcpy(xauth.data, sp + 4 + xauth.name_length, xauth.data_length);
/* Setup to always have a local .Xauthority. */
fwdx_xauthfile = malloc(MAXPATHLEN+1);
snprintf(fwdx_xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX");
if ((xauthfd = mkstemp(fwdx_xauthfile)) != -1)
/* we change file ownership later, when we know who is to be owner! */
close(xauthfd);
else {
free(fwdx_xauthfile);
fwdx_xauthfile = NULL;
return 5;
}
/* Must have the subshell's new DISPLAY env var to write xauth to xauthfile */
if (fwdx_display)
if (fwdx_write_xauthfile())
return 6;
return 0;
}
void fwdx_set_xauthfile_owner(int uid)
{
struct passwd *pwd;
if (!fwdx_xauthfile || !(pwd = getpwuid(uid)))
return;
chown(fwdx_xauthfile, pwd->pw_uid, pwd->pw_gid);
}
int
fwdx_server_accept_options(unsigned char *sp, int len)
/* called with 'len' option bytes, starting at 'sp' */
{
int c;
for (c = 0; c < len-2; c++) {
if (c == 0) {
if (sp[c] & FWDX_OPT_XAUTH)
flag = 1;
}
}
return(0);
}
#endif /* FWDX_SERVER */
#endif /* CK_FORWARD_X */
#ifdef IKS_OPTION
/*
iks_wait() -- Wait for an IKS subnegotiation response.
sb - is either KERMIT_REQ_START or KERMIT_REQ_STOP depending on the desired
state of the peer's Kermit server.
flushok - specifies whether it is ok to throw away non-Telnet data
if so, then we call ttflui() instead of tn_flui().
Returns:
1 if the desired state is achieved or if it is unknown.
0 if the desired state is not achieved.
*/
int
#ifdef CK_ANSIC
iks_wait(int sb, int flushok)
#else /* CK_ANSIC */
iks_wait(sb,flushok) int sb; int flushok;
#endif /* CK_ANSIC */
{
int tn_wait_save = tn_wait_flg;
int x;
if (TELOPT_U(TELOPT_KERMIT)) {
switch (sb) {
case KERMIT_REQ_START:
debug(F111,
"iks_wait KERMIT_REQ_START",
"u_start",
TELOPT_SB(TELOPT_KERMIT).kermit.u_start
);
tn_siks(KERMIT_REQ_START);
tn_wait_flg = 1; /* Kermit Option MUST wait */
do {
if (flushok)
tn_wait_idx = 0;
x = tn_wait("iks_wait() me_iks_req_start");
} while (x == 0 && flushok && tn_wait_idx == TN_WAIT_BUF_SZ);
tn_wait_flg = tn_wait_save;
if (flushok)
tn_wait_idx = 0;
if (tn_wait_idx == TN_WAIT_BUF_SZ) {
/*
* We are attempting to start a kermit server on the peer
* the most likely reason is because we want to perform a
* file transfer. But there is a huge amount of non telnet
* negotiation data coming in and so we have not been able
* to find the response. So we will lie and assume that
* response is 'yes'. The worse that will happen is that
* a RESP_STOP is received after we enter protocol mode.
* And the protocol operation will be canceled.
*/
tn_push();
return(1);
} else {
tn_push();
return(TELOPT_SB(TELOPT_KERMIT).kermit.u_start);
}
case KERMIT_REQ_STOP:
debug(F111,
"iks_wait KERMIT_REQ_STOP",
"u_start",
TELOPT_SB(TELOPT_KERMIT).kermit.u_start
);
tn_siks(KERMIT_REQ_STOP);
tn_wait_flg = 1; /* Kermit Option MUST wait */
do {
if (flushok)
tn_wait_idx = 0;
x = tn_wait("iks_wait() me_iks_req_stop");
} while (x == 0 && flushok && tn_wait_idx == TN_WAIT_BUF_SZ);
tn_wait_flg = tn_wait_save;
if (flushok)
tn_wait_idx = 0;
if (tn_wait_idx == TN_WAIT_BUF_SZ) {
/*
* We are attempting to stop a kermit server on the peer
* the most likely reason being that we want to enter
* CONNECT mode. But there is a huge amount of non telnet
* negotiation data coming in and so we have not been able
* to find the response. So we will lie and assume that
* the answer is 'yes' and allow the CONNECT command to
* succeed. The worst that happens is that CONNECT mode
* swallows the incoming data displaying it to the user
* and then it resumes Kermit client mode.
*/
tn_push();
return(1);
} else {
tn_push();
return(!TELOPT_SB(TELOPT_KERMIT).kermit.u_start);
}
}
tn_push();
}
return(1);
}
int
#ifdef CK_ANSIC
iks_tn_sb(CHAR * sb, int n)
#else
iks_tn_sb(sb, n) CHAR * sb; int n;
#endif /* CK_ANSIC */
{
extern int server;
extern CHAR sstate;
#ifdef NOICP
extern int autodl;
int inautodl = 0, cmdadl = 1;
#else
#ifdef CK_AUTODL
extern int autodl, inautodl, cmdadl;
#endif /* CK_AUTODL */
#endif /* NOICP */
switch (sb[0]) {
case KERMIT_START: /* START */
TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 1;
return(4);
case KERMIT_STOP: /* STOP */
TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0;
return(4);
case KERMIT_REQ_START: /* REQ-START */
#ifndef NOXFER
if (inserver) {
#ifdef CK_AUTODL
cmdadl = 1; /* Turn on packet detection */
#endif /* CK_AUTODL */
TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 1;
tn_siks(KERMIT_RESP_START);
} else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
tn_siks(KERMIT_RESP_START);
} else {
#ifndef IKSDONLY
#ifdef CK_AUTODL
#ifdef OS2
if (local && (IsConnectMode() && autodl) ||
(!IsConnectMode() &&
(inautodl || sstate == 'x' || sstate == 'v'))
)
tn_siks(KERMIT_RESP_START); /* START */
else
#else /* OS2 */
if ((local && what == W_CONNECT && autodl) ||
(local && what != W_CONNECT &&
(inautodl || sstate == 'x' || sstate == 'v')
))
tn_siks(KERMIT_RESP_START); /* START */
else
#endif /* OS2 */
#endif /* CK_AUTODL */
#endif /* IKSDONLY */
tn_siks(KERMIT_RESP_STOP);
}
#else /* NOXFER */
tn_siks(KERMIT_RESP_STOP);
#endif /* NOXFER */
return(4);
case KERMIT_REQ_STOP: /* REQ-STOP */
/* The protocol requires that the request be responded to */
/* either by changing states or by reporting the current */
/* state. */
/* We need to provide the user some way of dictating what */
/* the policies should be. For instance, if we are in */
/* CONNECT mode with autodownload ON and we get a REQ-STOP*/
/* what should the proper response be? */
#ifndef NOXFER
if (inserver
#ifdef CK_AUTODL
|| !local && cmdadl
#endif /* CK_AUTODL */
) {
#ifdef CK_AUTODL
cmdadl = 0; /* Turn off packet detection */
#endif /* CK_AUTODL */
tn_siks(KERMIT_RESP_STOP);
} else if (server) {
extern int en_fin;
if (en_fin) { /* If the server is allowed to stop */
tn_siks(KERMIT_RESP_STOP);
} else { /* We are not allowed to stop */
tn_siks(KERMIT_RESP_START);
}
}
#ifndef IKSDONLY
#ifdef CK_AUTODL
#ifdef OS2
else if (local && (IsConnectMode() && autodl) ||
(!IsConnectMode() && inautodl)
) {
/* If we are a pseudo-server and the other side requests */
/* that we stop, tell then that we have even though we */
/* have not. Otherwise, the other side might refuse to */
/* enter SERVER mode. */
tn_siks(KERMIT_RESP_STOP); /* STOP */
}
#else /* OS2 */
else if ((local && what == W_CONNECT && autodl) ||
(local && what != W_CONNECT && inautodl)
) {
/* If we are a pseudo-server and the other side requests */
/* that we stop, tell then that we have even though we */
/* have not. Otherwise, the other side might refuse to */
/* enter SERVER mode. */
tn_siks(KERMIT_RESP_STOP); /* STOP */
}
#endif /* OS2 */
#endif /* CK_AUTODL */
#endif /* IKSDONLY */
else
#endif /* NOXFER */
{
/* If we are not currently in any mode that accepts */
/* Kermit packets then of course report that we are */
/* not being a Kermit server. */
tn_siks(KERMIT_RESP_STOP); /* STOP */
}
return(4);
case KERMIT_SOP: { /* SOP */
#ifndef NOXFER
extern CHAR stchr; /* Incoming SOP character */
stchr = sb[1];
#endif /* NOXFER */
TELOPT_SB(TELOPT_KERMIT).kermit.sop = 1;
return(4);
}
case KERMIT_RESP_START: /* START */
TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 1;
if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start) {
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0;
} else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop) {
/* If we have issued a request to stop a Kermit Server */
/* and the response is Start, then we must report this */
/* to the caller. */
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0;
}
return(4);
case KERMIT_RESP_STOP: /* STOP */
TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0;
if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start) {
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0;
/* If we have issued a request to start a Kermit Server */
/* and the response is Stop, then we must report this */
/* to the caller. */
} else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop) {
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0;
}
return(4);
default:
return(0);
} /* switch (sb[0]) */
}
#endif /* IKS_OPTION */
/* Initialize telnet settings - set default values for ME and U modes */
int
tn_set_modes() {
int opt,cmd;
#ifdef CK_FORWARD_X
int x;
#endif /* CK_FORWARD_X */
#ifdef CK_ENVIRONMENT
{
int i,j;
for (i = 0; i < 8; i++) {
tn_env_uservar[i][0] = NULL;
tn_env_uservar[i][1] = NULL;
}
}
#endif /* CK_ENVIRONMENT */
/* initialize all options to refuse in both directions */
for (opt = 0; opt < NTELOPTS; opt++) {
TELOPT_ME(opt) = 0;
TELOPT_U(opt) = 0;
TELOPT_UNANSWERED_WILL(opt) = 0;
TELOPT_UNANSWERED_DO(opt) = 0;
TELOPT_UNANSWERED_WONT(opt) = 0;
TELOPT_UNANSWERED_DONT(opt) = 0;
TELOPT_UNANSWERED_SB(opt) = 0;
TELOPT_ME_MODE(opt) = TN_NG_RF;
TELOPT_U_MODE(opt) = TN_NG_RF;
TELOPT_DEF_S_ME_MODE(opt) = TN_NG_RF;
TELOPT_DEF_S_U_MODE(opt) = TN_NG_RF;
TELOPT_DEF_C_ME_MODE(opt) = TN_NG_RF;
TELOPT_DEF_C_U_MODE(opt) = TN_NG_RF;
for (cmd = 0; cmd < 4; cmd ++)
tncnts[TELOPT_INDEX(opt)][cmd] = 0;
}
#ifdef IKS_OPTION
TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0;
TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0;
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0;
TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0;
TELOPT_SB(TELOPT_KERMIT).kermit.sop = 0;
#endif /* IKS_OPTION */
#ifdef CK_ENCRYPTION
TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop = 0;
#endif /* CK_ENCRYPTION */
#ifdef CK_NAWS
TELOPT_SB(TELOPT_NAWS).naws.x = 0;
TELOPT_SB(TELOPT_NAWS).naws.y = 0;
#endif /* CK_NAWS */
#ifdef CK_SSL
TELOPT_SB(TELOPT_START_TLS).start_tls.u_follows = 0;
TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows = 0;
TELOPT_SB(TELOPT_START_TLS).start_tls.auth_request = 0;
#endif /* CK_SSL */
/* Now set the ones we want to accept to the proper values */
TELOPT_DEF_S_ME_MODE(TELOPT_SGA) = TN_NG_RQ;
TELOPT_DEF_S_U_MODE(TELOPT_SGA) = TN_NG_RQ;
TELOPT_DEF_C_ME_MODE(TELOPT_SGA) = TN_NG_AC;
TELOPT_DEF_C_U_MODE(TELOPT_SGA) = TN_NG_AC;
TELOPT_DEF_S_ME_MODE(TELOPT_BINARY) = TN_NG_AC;
TELOPT_DEF_S_U_MODE(TELOPT_BINARY) = TN_NG_AC;
TELOPT_DEF_C_ME_MODE(TELOPT_BINARY) = TN_NG_AC;
TELOPT_DEF_C_U_MODE(TELOPT_BINARY) = TN_NG_AC;
TELOPT_DEF_S_ME_MODE(TELOPT_LOGOUT) = TN_NG_AC;
TELOPT_DEF_S_U_MODE(TELOPT_LOGOUT) = TN_NG_AC;
TELOPT_DEF_C_ME_MODE(TELOPT_LOGOUT) = TN_NG_AC;
TELOPT_DEF_C_U_MODE(TELOPT_LOGOUT) = TN_NG_AC;
#ifdef IKS_OPTION
TELOPT_DEF_S_ME_MODE(TELOPT_KERMIT) = TN_NG_RQ;
TELOPT_DEF_S_U_MODE(TELOPT_KERMIT) = TN_NG_RQ;
TELOPT_DEF_C_ME_MODE(TELOPT_KERMIT) = TN_NG_RQ;
TELOPT_DEF_C_U_MODE(TELOPT_KERMIT) = TN_NG_RQ;
#endif /* IKS_OPTION */
#ifdef CK_ENCRYPTION
TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
#endif /* CK_ENCRYPTION */
TELOPT_DEF_S_ME_MODE(TELOPT_ECHO) = TN_NG_RQ;
#ifdef IKSD
if ( !inserver )
#endif /* IKSD */
TELOPT_DEF_S_U_MODE(TELOPT_TTYPE) = TN_NG_RQ;
#ifdef CK_ENVIRONMENT
TELOPT_DEF_S_U_MODE(TELOPT_NEWENVIRON) = TN_NG_RQ;
#endif /* CK_ENVIRONMENT */
#ifdef CK_AUTHENTICATION
TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ;
#endif /* CK_AUTHENTICATION */
#ifdef CK_SSL
if (ck_ssleay_is_installed()) {
TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RQ;
TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_AC;
}
#endif /* CK_SSL */
#ifdef CK_NAWS
TELOPT_DEF_S_U_MODE(TELOPT_NAWS) = TN_NG_RQ;
#endif /* CK_NAWS */
TELOPT_DEF_C_U_MODE(TELOPT_ECHO) = TN_NG_AC;
TELOPT_DEF_C_ME_MODE(TELOPT_TTYPE) = TN_NG_RQ;
#ifdef CK_ENVIRONMENT
TELOPT_DEF_C_ME_MODE(TELOPT_NEWENVIRON) = TN_NG_RQ;
#endif /* CK_ENVIRONMENT */
#ifdef CK_AUTHENTICATION
TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ;
#endif /* CK_AUTHENTICATION */
#ifdef CK_NAWS
TELOPT_DEF_C_ME_MODE(TELOPT_NAWS) = TN_NG_RQ;
#endif /* CK_NAWS */
#ifdef CK_SNDLOC
TELOPT_DEF_C_ME_MODE(TELOPT_SNDLOC) = TN_NG_RQ;
#endif /* CK_SNDLOC */
#ifdef CK_FORWARD_X
TELOPT_DEF_C_U_MODE(TELOPT_FORWARD_X) = TN_NG_AC;
TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = -1;
for (x = 0; x < MAXFWDX; x++) {
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;
}
#endif /* CK_FORWARD_X */
#ifdef TN_COMPORT
TELOPT_DEF_C_ME_MODE(TELOPT_COMPORT) = TN_NG_RQ;
#endif /* TN_COMPORT */
/* Set the initial values for currently known mode */
for (opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) {
if (TELOPT_OK(opt)) {
TELOPT_ME_MODE(opt) = sstelnet ?
TELOPT_DEF_S_ME_MODE(opt) :