* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of California at Berkeley. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
static char sccsid
[] = "@(#)telnet.c 5.31 (Berkeley) 5/15/88";
/* By the way, we need to include curses.h before telnet.h since,
* among other things, telnet.h #defines 'DO', which is a variable
#endif /* defined(unix) */
#else /* defined(unix) */
#endif /* defined(unix) */
#define strip(x) ((x)&0x7f)
static char subbuffer
[SUBBUFSIZE
],
*subpointer
, *subend
; /* buffer for sub-options */
#define SB_CLEAR() subpointer = subbuffer;
#define SB_TERM() subend = subpointer;
#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
char doopt
[] = { IAC
, DO
, '%', 'c', 0 };
char dont
[] = { IAC
, DONT
, '%', 'c', 0 };
char will
[] = { IAC
, WILL
, '%', 'c', 0 };
char wont
[] = { IAC
, WONT
, '%', 'c', 0 };
In3270
, /* Are we in 3270 mode? */
ISend
, /* trying to send network data in */
netdata
, /* Print out network data flow */
crlf
, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
noasynch
= 0, /* User specified "-noasynch" on command line */
askedSGA
= 0, /* We have talked about suppress go ahead */
SYNCHing
, /* we are in TELNET SYNCH mode */
flushout
, /* flush output */
autoflush
= 0, /* flush output when interrupting? */
autosynch
, /* send interrupt characters with SYNCH? */
localchars
, /* we recognize interrupt/quit */
donelclchars
, /* the user has set "localchars" */
donebinarytoggle
, /* the user has put us in binary */
dontlecho
, /* do we suppress local echoing right now? */
#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
* Telnet receiver states for fsm
#define TS_SB 7 /* sub-option collection */
#define TS_SE 8 /* looking for sub-option end */
jmp_buf toplevel
= { 0 };
* The following are some clocks used to decide how to interpret
* the relationship between various variables.
{ "telnet command mode", COMMAND_LINE
},
{ "character-at-a-time mode", 0 },
{ "character-at-a-time mode (local echo)", LOCAL_ECHO
|LOCAL_CHARS
},
{ "line-by-line mode (remote echo)", LINE
| LOCAL_CHARS
},
{ "line-by-line mode", LINE
| LOCAL_ECHO
| LOCAL_CHARS
},
{ "line-by-line mode (local echoing suppressed)", LINE
| LOCAL_CHARS
},
* Initialize telnet environment.
connected
= In3270
= ISend
= donebinarytoggle
= 0;
#if defined(unix) && defined(TN3270)
#endif /* defined(unix) && defined(TN3270) */
/* Don't change NetTrace */
char buffer
[100]; /* where things go */
ring
= va_arg(ap
, Ring
*);
format
= va_arg(ap
, char *);
while ((i
= *format
++) != 0) {
*ptr
++ = va_arg(ap
, int);
string
= va_arg(ap
, char *);
ring_supply_data(ring
, buffer
, ptr
-buffer
);
ring_supply_data(ring
, string
, strlen(string
));
ExitString("printring: trailing %%.\n", 1);
ExitString("printring: unknown format character.\n", 1);
ring_supply_data(ring
, buffer
, ptr
-buffer
);
willoption(option
, reply
)
* The following is a pain in the rear-end.
* Various IBM servers (some versions of Wiscnet,
* possibly Fibronics/Spartacus, and who knows who
* else) will NOT allow us to send "DO SGA" too early
* in the setup proceedings. On the other hand,
* 4.2 servers (telnetd) won't set SGA correctly.
* So, we are stuck. Empirically (but, based on
* a VERY small sample), the IBM servers don't send
* out anything about ECHO, so we postpone our sending
* "DO SGA" until we see "WILL ECHO" (which 4.2 servers
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
#endif /* defined(TN3270) */
settimer(modenegotiated
);
setconnmode(); /* possibly set new tty mode */
return; /* Never reply to TM will's/wont's */
printring(&netoring
, fmt
, option
);
printoption(">SENT", fmt
, option
, reply
);
printoption("<SENT", fmt
, option
, reply
);
wontoption(option
, reply
)
settimer(modenegotiated
);
setconnmode(); /* Set new tty mode */
return; /* Never reply to TM will's/wont's */
printring(&netoring
, fmt
, option
);
printoption(">SENT", fmt
, option
, reply
);
printoption("<SENT", fmt
, option
, reply
);
# endif /* defined(TN3270) */
case TELOPT_TTYPE
: /* terminal type option */
case TELOPT_SGA
: /* no big deal */
case TELOPT_ECHO
: /* We're never going to echo... */
printring(&netoring
, fmt
, option
);
printoption(">SENT", fmt
, option
, 0);
* Look at the sub-option buffer, and try to be helpful to the other
* Currently we recognize:
* Terminal type, send request.
printsub("<", subbuffer
, subend
-subbuffer
+1);
switch (subbuffer
[0]&0xff) {
if ((subbuffer
[1]&0xff) != TELQUAL_SEND
) {
#endif /* defined(TN3270) */
if ((name
== 0) || ((len
= strlen(name
)) > 40)) {
if ((len
+ 4+2) < NETROOM()) {
printring(&netoring
, "%c%c%c%c%s%c%c", IAC
, SB
, TELOPT_TTYPE
,
TELQUAL_IS
, namebuf
, IAC
, SE
);
/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
ExitString("No room in buffer for terminal type.\n",
ring_consumed(&netiring
, count
);
scc
= ring_full_consecutive(&netiring
);
/* No more data coming in */
c
= *sbp
++ & 0xff, scc
--; count
++;
break; /* Ignore \0 after CR */
if ((!hisopts
[TELOPT_ECHO
]) && !crmod
) {
c
= *sbp
++ & 0377, scc
--; count
++;
# endif /* defined(TN3270) */
* The 'crmod' hack (see following) is needed
* since we can't * set CRMOD on output only.
* Machines like MULTICS like to send \r without
* \n; since we must turn off CRMOD to get proper
* input, the mapping is done here (sigh).
if ((c
== '\r') && !hisopts
[TELOPT_BINARY
]) {
} else if (!hisopts
[TELOPT_ECHO
] &&
* We may have missed an urgent notification,
* so make sure we flush whatever is in the
Ibackp
+= DataFromNetwork(Ibackp
, Ifrontp
-Ibackp
, 1);
ISend
= 0; /* should have been! */
# endif /* defined(TN3270) */
# else /* !defined(TN3270) */
# endif /* !defined(TN3270) */
printoption(">RCVD", will
, c
, !hisopts
[c
]);
} else if (!hisopts
[c
]) {
printoption(">RCVD", wont
, c
, hisopts
[c
]);
printoption(">RCVD", doopt
, c
, !myopts
[c
]);
printoption(">RCVD", dont
, c
, myopts
[c
]);
printring(&netoring
, wont
, c
);
setconnmode(); /* set new tty mode (maybe) */
printoption(">SENT", wont
, c
, 0);
suboption(); /* handle sub-option */
ring_consumed(&netiring
, count
);
return returnValue
||count
;
ring_consumed(&ttyiring
, count
);
tcc
= ring_full_consecutive(&ttyiring
);
c
= *tbp
++ & 0xff, sc
= strip(c
), tcc
--; count
++;
} else if (MODE_LINE(globalmode
) && (sc
== echoc
)) {
if (tcc
> 0 && strip(*tbp
) == echoc
) {
if (TerminalSpecialChars(sc
) == 0) {
if (!myopts
[TELOPT_BINARY
]) {
* If we are in CRMOD mode (\r ==> \n)
* on our local machine, then probably
* a newline (unix) is CRLF (TELNET).
if (MODE_LOCAL_CHARS(globalmode
)) {
ring_consumed(&ttyiring
, count
);
return returnValue
||count
; /* Non-zero if we did anything */
* If we do something useful, return 1; else return 0.
int block
; /* should we block in the select ? */
/* One wants to be a bit careful about setting returnValue
* to one, since a one implies we did some useful work,
* and therefore probably won't be called to block next
* time (TN3270 mode only).
int netin
, netout
, netex
, ttyin
, ttyout
;
/* Decide which rings should be processed */
netout
= ring_full_count(&netoring
) &&
(!MODE_LINE(globalmode
) || flushline
|| myopts
[TELOPT_BINARY
]);
ttyout
= ring_full_count(&ttyoring
);
ttyin
= ring_empty_count(&ttyiring
) && (shell_active
== 0);
#else /* defined(TN3270) */
ttyin
= ring_empty_count(&ttyiring
);
#endif /* defined(TN3270) */
netin
= ring_empty_count(&netiring
);
# else /* !defined(TN3270) */
netin
= !ISend
&& ring_empty_count(&netiring
);
# endif /* !defined(TN3270) */
/* If we have seen a signal recently, reset things */
# if defined(TN3270) && defined(unix)
signal(SIGIO
, inputAvailable
);
#endif /* defined(TN3270) && defined(unix) */
/* Call to system code to process rings */
returnValue
= process_rings(netin
, netout
, netex
, ttyin
, ttyout
, !block
);
/* Now, look at the input rings, looking for work to do. */
if (ring_full_count(&ttyiring
)) {
c
= DataFromTerminal(ttyiring
.consume
,
ring_full_consecutive(&ttyiring
));
ring_consumed(&ttyiring
, c
);
# endif /* defined(TN3270) */
# endif /* defined(TN3270) */
if (ring_full_count(&netiring
)) {
# else /* !defined(TN3270) */
returnValue
= Push3270();
# endif /* !defined(TN3270) */
* Select from tty and network...
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
if (!myopts
[TELOPT_TTYPE
]) {
dooption(TELOPT_TTYPE
, 0);
# endif /* !defined(TN3270) */
while ((schedValue
= Scheduler(0)) != 0) {
if (Scheduler(1) == -1) {
# else /* !defined(TN3270) */
while (!In3270
&& !shell_active
) {
if (Scheduler(1) == -1) {
while ((schedValue
= Scheduler(0)) != 0) {
/* If there is data waiting to go out to terminal, don't
* schedule any more data for the terminal.
if (ring_full_count(&ttyoring
)) {
if (shell_continue() == 0) {
schedValue
= DoTerminalOutput();
if (schedValue
&& (shell_active
== 0)) {
if (Scheduler(1) == -1) {
# endif /* !defined(TN3270) */
* Return the address of the next "item" in the TELNET data
* stream. This will be the address of the next character if
* the current address is a user data character, or it will
* be the address of the character following the TELNET command
* if the current address is a TELNET IAC ("I Am a Command")
if ((*current
&0xff) != IAC
) {
switch (*(current
+1)&0xff) {
case SB
: /* loop forever looking for the SE */
register char *look
= current
+2;
if ((*look
++&0xff) == IAC
) {
if ((*look
++&0xff) == SE
) {
* We are about to do a TELNET SYNCH operation. Clear
* the path to the network.
* Things are a bit tricky since we may have sent the first
* byte or so of a previous TELNET command into the network.
* So, we have to scan the network buffer from the beginning
* until we are up to where we want to be.
* A side effect of what we do, just to keep things
* simple, is to clear the urgent data pointer. The principal
* caller should be setting the urgent data pointer AFTER calling
register char *thisitem
, *next
;
#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
while ((next
= nextitem(thisitem
)) <= netobuf
.send
) {
/* Now, thisitem is first before/at boundary. */
good
= netobuf
; /* where the good bytes go */
while (netoring
.add
> thisitem
) {
} while (wewant(next
) && (nfrontp
> next
));
memcpy(good
, thisitem
, length
);
thisitem
= nextitem(thisitem
);
* These routines add various telnet commands to the data stream.
ttyflush(1); /* Flush/drop output */
/* do printoption AFTER flush, otherwise the output gets tossed... */
printoption("<SENT", doopt
, TELOPT_TM
, 0);
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
netclear(); /* clear the path to the network */
#endif /* defined(NOT43) */