138905Sborman /*
261451Sbostic * Copyright (c) 1989, 1993
361451Sbostic * The Regents of the University of California. All rights reserved.
438905Sborman *
542673Sbostic * %sccs.include.redist.c%
638905Sborman */
738905Sborman
838905Sborman #ifndef lint
9*69786Sdab static char sccsid[] = "@(#)state.c 8.5 (Berkeley) 05/30/95";
1038905Sborman #endif /* not lint */
1138905Sborman
1238905Sborman #include "telnetd.h"
1357212Sdab #if defined(AUTHENTICATION)
1446809Sdab #include <libtelnet/auth.h>
1546809Sdab #endif
1638905Sborman
1765158Sdab unsigned char doopt[] = { IAC, DO, '%', 'c', 0 };
1865158Sdab unsigned char dont[] = { IAC, DONT, '%', 'c', 0 };
1965158Sdab unsigned char will[] = { IAC, WILL, '%', 'c', 0 };
2065158Sdab unsigned char wont[] = { IAC, WONT, '%', 'c', 0 };
2138905Sborman int not42 = 1;
2238905Sborman
2338905Sborman /*
2438905Sborman * Buffer for sub-options, and macros
2538905Sborman * for suboptions buffer manipulations
2638905Sborman */
2746809Sdab unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
2838905Sborman
2958971Sdab #define SB_CLEAR() subpointer = subbuffer
3038905Sborman #define SB_TERM() { subend = subpointer; SB_CLEAR(); }
3138905Sborman #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
3238905Sborman *subpointer++ = (c); \
3338905Sborman }
3438905Sborman #define SB_GET() ((*subpointer++)&0xff)
3538905Sborman #define SB_EOF() (subpointer >= subend)
3644364Sborman #define SB_LEN() (subend - subpointer)
3738905Sborman
3858971Sdab #ifdef ENV_HACK
3958971Sdab unsigned char *subsave;
4058971Sdab #define SB_SAVE() subsave = subpointer;
4158971Sdab #define SB_RESTORE() subpointer = subsave;
4258971Sdab #endif
4338905Sborman
4438905Sborman
4538905Sborman /*
4638905Sborman * State for recv fsm
4738905Sborman */
4838905Sborman #define TS_DATA 0 /* base state */
4938905Sborman #define TS_IAC 1 /* look for double IAC's */
5038905Sborman #define TS_CR 2 /* CR-LF ->'s CR */
5138905Sborman #define TS_SB 3 /* throw away begin's... */
5238905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */
5338905Sborman #define TS_WILL 5 /* will option negotiation */
5438905Sborman #define TS_WONT 6 /* wont " */
5538905Sborman #define TS_DO 7 /* do " */
5638905Sborman #define TS_DONT 8 /* dont " */
5738905Sborman
5846809Sdab void
telrcv()5938905Sborman telrcv()
6038905Sborman {
6138905Sborman register int c;
6238905Sborman static int state = TS_DATA;
6340242Sborman #if defined(CRAY2) && defined(UNICOS5)
6438905Sborman char *opfrontp = pfrontp;
6538905Sborman #endif
6638905Sborman
6738905Sborman while (ncc > 0) {
6838905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6938905Sborman break;
7038905Sborman c = *netip++ & 0377, ncc--;
7160151Sdab #ifdef ENCRYPTION
7246809Sdab if (decrypt_input)
7346809Sdab c = (*decrypt_input)(c);
7460151Sdab #endif /* ENCRYPTION */
7538905Sborman switch (state) {
7638905Sborman
7738905Sborman case TS_CR:
7838905Sborman state = TS_DATA;
7938905Sborman /* Strip off \n or \0 after a \r */
8038905Sborman if ((c == 0) || (c == '\n')) {
8138905Sborman break;
8238905Sborman }
8338905Sborman /* FALL THROUGH */
8438905Sborman
8538905Sborman case TS_DATA:
8638905Sborman if (c == IAC) {
8738905Sborman state = TS_IAC;
8838905Sborman break;
8938905Sborman }
9038905Sborman /*
9138905Sborman * We now map \r\n ==> \r for pragmatic reasons.
9238905Sborman * Many client implementations send \r\n when
9338905Sborman * the user hits the CarriageReturn key.
9438905Sborman *
9538905Sborman * We USED to map \r\n ==> \n, since \r\n says
9638905Sborman * that we want to be in column 1 of the next
9738905Sborman * printable line, and \n is the standard
9838905Sborman * unix way of saying that (\r is only good
9938905Sborman * if CRMOD is set, which it normally is).
10038905Sborman */
10144364Sborman if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
10246809Sdab int nc = *netip;
10360151Sdab #ifdef ENCRYPTION
10446809Sdab if (decrypt_input)
10546809Sdab nc = (*decrypt_input)(nc & 0xff);
10660151Sdab #endif /* ENCRYPTION */
10746809Sdab #ifdef LINEMODE
10838905Sborman /*
10938905Sborman * If we are operating in linemode,
11038905Sborman * convert to local end-of-line.
11138905Sborman */
11246809Sdab if (linemode && (ncc > 0) && (('\n' == nc) ||
11346809Sdab ((0 == nc) && tty_iscrnl())) ) {
11438905Sborman netip++; ncc--;
11538905Sborman c = '\n';
11646809Sdab } else
11746809Sdab #endif
11846809Sdab {
11960151Sdab #ifdef ENCRYPTION
12046809Sdab if (decrypt_input)
12146809Sdab (void)(*decrypt_input)(-1);
12260151Sdab #endif /* ENCRYPTION */
12338905Sborman state = TS_CR;
12438905Sborman }
12538905Sborman }
12638905Sborman *pfrontp++ = c;
12738905Sborman break;
12838905Sborman
12938905Sborman case TS_IAC:
13038905Sborman gotiac: switch (c) {
13138905Sborman
13238905Sborman /*
13338905Sborman * Send the process on the pty side an
13438905Sborman * interrupt. Do this with a NULL or
13538905Sborman * interrupt char; depending on the tty mode.
13638905Sborman */
13738905Sborman case IP:
13846809Sdab DIAG(TD_OPTIONS,
13946809Sdab printoption("td: recv IAC", c));
14038905Sborman interrupt();
14138905Sborman break;
14238905Sborman
14338905Sborman case BREAK:
14446809Sdab DIAG(TD_OPTIONS,
14546809Sdab printoption("td: recv IAC", c));
14638905Sborman sendbrk();
14738905Sborman break;
14838905Sborman
14938905Sborman /*
15038905Sborman * Are You There?
15138905Sborman */
15238905Sborman case AYT:
15346809Sdab DIAG(TD_OPTIONS,
15446809Sdab printoption("td: recv IAC", c));
15545234Sborman recv_ayt();
15638905Sborman break;
15738905Sborman
15838905Sborman /*
15938905Sborman * Abort Output
16038905Sborman */
16138905Sborman case AO:
16238905Sborman {
16346809Sdab DIAG(TD_OPTIONS,
16446809Sdab printoption("td: recv IAC", c));
16538905Sborman ptyflush(); /* half-hearted */
16638905Sborman init_termbuf();
16738905Sborman
16838905Sborman if (slctab[SLC_AO].sptr &&
16945234Sborman *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
17040242Sborman *pfrontp++ =
17140242Sborman (unsigned char)*slctab[SLC_AO].sptr;
17238905Sborman }
17338905Sborman
17438905Sborman netclear(); /* clear buffer back */
17538905Sborman *nfrontp++ = IAC;
17638905Sborman *nfrontp++ = DM;
17738905Sborman neturg = nfrontp-1; /* off by one XXX */
17846809Sdab DIAG(TD_OPTIONS,
17946809Sdab printoption("td: send IAC", DM));
18038905Sborman break;
18138905Sborman }
18238905Sborman
18338905Sborman /*
18438905Sborman * Erase Character and
18538905Sborman * Erase Line
18638905Sborman */
18738905Sborman case EC:
18838905Sborman case EL:
18938905Sborman {
19040242Sborman cc_t ch;
19138905Sborman
19246809Sdab DIAG(TD_OPTIONS,
19346809Sdab printoption("td: recv IAC", c));
19438905Sborman ptyflush(); /* half-hearted */
19538905Sborman init_termbuf();
19644364Sborman if (c == EC)
19744364Sborman ch = *slctab[SLC_EC].sptr;
19844364Sborman else
19944364Sborman ch = *slctab[SLC_EL].sptr;
20045234Sborman if (ch != (cc_t)(_POSIX_VDISABLE))
20140242Sborman *pfrontp++ = (unsigned char)ch;
20238905Sborman break;
20338905Sborman }
20438905Sborman
20538905Sborman /*
20638905Sborman * Check for urgent data...
20738905Sborman */
20838905Sborman case DM:
20946809Sdab DIAG(TD_OPTIONS,
21046809Sdab printoption("td: recv IAC", c));
21138905Sborman SYNCHing = stilloob(net);
21238905Sborman settimer(gotDM);
21338905Sborman break;
21438905Sborman
21538905Sborman
21638905Sborman /*
21738905Sborman * Begin option subnegotiation...
21838905Sborman */
21938905Sborman case SB:
22038905Sborman state = TS_SB;
22138905Sborman SB_CLEAR();
22238905Sborman continue;
22338905Sborman
22438905Sborman case WILL:
22538905Sborman state = TS_WILL;
22638905Sborman continue;
22738905Sborman
22838905Sborman case WONT:
22938905Sborman state = TS_WONT;
23038905Sborman continue;
23138905Sborman
23238905Sborman case DO:
23338905Sborman state = TS_DO;
23438905Sborman continue;
23538905Sborman
23638905Sborman case DONT:
23738905Sborman state = TS_DONT;
23838905Sborman continue;
23938905Sborman case EOR:
24044364Sborman if (his_state_is_will(TELOPT_EOR))
24138905Sborman doeof();
24238905Sborman break;
24338905Sborman
24438905Sborman /*
24538905Sborman * Handle RFC 10xx Telnet linemode option additions
24638905Sborman * to command stream (EOF, SUSP, ABORT).
24738905Sborman */
24838905Sborman case xEOF:
24938905Sborman doeof();
25038905Sborman break;
25138905Sborman
25238905Sborman case SUSP:
25338905Sborman sendsusp();
25438905Sborman break;
25538905Sborman
25638905Sborman case ABORT:
25738905Sborman sendbrk();
25838905Sborman break;
25938905Sborman
26038905Sborman case IAC:
26138905Sborman *pfrontp++ = c;
26238905Sborman break;
26338905Sborman }
26438905Sborman state = TS_DATA;
26538905Sborman break;
26638905Sborman
26738905Sborman case TS_SB:
26838905Sborman if (c == IAC) {
26938905Sborman state = TS_SE;
27038905Sborman } else {
27138905Sborman SB_ACCUM(c);
27238905Sborman }
27338905Sborman break;
27438905Sborman
27538905Sborman case TS_SE:
27638905Sborman if (c != SE) {
27738905Sborman if (c != IAC) {
27838905Sborman /*
27938905Sborman * bad form of suboption negotiation.
28038905Sborman * handle it in such a way as to avoid
28138905Sborman * damage to local state. Parse
28238905Sborman * suboption buffer found so far,
28338905Sborman * then treat remaining stream as
28438905Sborman * another command sequence.
28538905Sborman */
28646809Sdab
28746809Sdab /* for DIAGNOSTICS */
28844364Sborman SB_ACCUM(IAC);
28944364Sborman SB_ACCUM(c);
29044364Sborman subpointer -= 2;
29146809Sdab
29238905Sborman SB_TERM();
29338905Sborman suboption();
29438905Sborman state = TS_IAC;
29538905Sborman goto gotiac;
29638905Sborman }
29738905Sborman SB_ACCUM(c);
29838905Sborman state = TS_SB;
29938905Sborman } else {
30046809Sdab /* for DIAGNOSTICS */
30144364Sborman SB_ACCUM(IAC);
30244364Sborman SB_ACCUM(SE);
30344364Sborman subpointer -= 2;
30446809Sdab
30538905Sborman SB_TERM();
30638905Sborman suboption(); /* handle sub-option */
30738905Sborman state = TS_DATA;
30838905Sborman }
30938905Sborman break;
31038905Sborman
31138905Sborman case TS_WILL:
31239503Sborman willoption(c);
31338905Sborman state = TS_DATA;
31438905Sborman continue;
31538905Sborman
31638905Sborman case TS_WONT:
31739503Sborman wontoption(c);
31838905Sborman state = TS_DATA;
31938905Sborman continue;
32038905Sborman
32138905Sborman case TS_DO:
32239503Sborman dooption(c);
32338905Sborman state = TS_DATA;
32438905Sborman continue;
32538905Sborman
32638905Sborman case TS_DONT:
32739503Sborman dontoption(c);
32838905Sborman state = TS_DATA;
32938905Sborman continue;
33038905Sborman
33138905Sborman default:
33238905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
33338905Sborman printf("telnetd: panic state=%d\n", state);
33438905Sborman exit(1);
33538905Sborman }
33638905Sborman }
33740242Sborman #if defined(CRAY2) && defined(UNICOS5)
33838905Sborman if (!linemode) {
33938905Sborman char xptyobuf[BUFSIZ+NETSLOP];
34038905Sborman char xbuf2[BUFSIZ];
34138905Sborman register char *cp;
34238905Sborman int n = pfrontp - opfrontp, oc;
343*69786Sdab memmove(xptyobuf, opfrontp, n);
34438905Sborman pfrontp = opfrontp;
34538905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
34638905Sborman xbuf2, &oc, BUFSIZ);
34738905Sborman for (cp = xbuf2; oc > 0; --oc)
34838905Sborman if ((*nfrontp++ = *cp++) == IAC)
34938905Sborman *nfrontp++ = IAC;
35038905Sborman }
35140242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */
35238905Sborman } /* end of telrcv */
35338905Sborman
35438905Sborman /*
35538905Sborman * The will/wont/do/dont state machines are based on Dave Borman's
35644364Sborman * Telnet option processing state machine.
35738905Sborman *
35838905Sborman * These correspond to the following states:
35938905Sborman * my_state = the last negotiated state
36038905Sborman * want_state = what I want the state to go to
36138905Sborman * want_resp = how many requests I have sent
36238905Sborman * All state defaults are negative, and resp defaults to 0.
36338905Sborman *
36438905Sborman * When initiating a request to change state to new_state:
365*69786Sdab *
36638905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
36738905Sborman * do nothing;
36838905Sborman * } else {
36938905Sborman * want_state = new_state;
37038905Sborman * send new_state;
37138905Sborman * want_resp++;
37238905Sborman * }
37338905Sborman *
37438905Sborman * When receiving new_state:
37538905Sborman *
37638905Sborman * if (want_resp) {
37738905Sborman * want_resp--;
37838905Sborman * if (want_resp && (new_state == my_state))
37938905Sborman * want_resp--;
38038905Sborman * }
38138905Sborman * if ((want_resp == 0) && (new_state != want_state)) {
38238905Sborman * if (ok_to_switch_to new_state)
38338905Sborman * want_state = new_state;
38438905Sborman * else
38538905Sborman * want_resp++;
38638905Sborman * send want_state;
38738905Sborman * }
38838905Sborman * my_state = new_state;
38938905Sborman *
39038905Sborman * Note that new_state is implied in these functions by the function itself.
39138905Sborman * will and do imply positive new_state, wont and dont imply negative.
39238905Sborman *
39338905Sborman * Finally, there is one catch. If we send a negative response to a
39438905Sborman * positive request, my_state will be the positive while want_state will
39538905Sborman * remain negative. my_state will revert to negative when the negative
39638905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells
39738905Sborman * us not only the last negotiated state, but also tells us what the peer
39838905Sborman * wants to be doing as well. It is important to understand this difference
39938905Sborman * as we may wish to be processing data streams based on our desired state
40038905Sborman * (want_state) or based on what the peer thinks the state is (my_state).
40138905Sborman *
40238905Sborman * This all works fine because if the peer sends a positive request, the data
40338905Sborman * that we receive prior to negative acknowlegment will probably be affected
40438905Sborman * by the positive state, and we can process it as such (if we can; if we
40538905Sborman * can't then it really doesn't matter). If it is that important, then the
40638905Sborman * peer probably should be buffering until this option state negotiation
40738905Sborman * is complete.
40838905Sborman *
40938905Sborman */
41046809Sdab void
send_do(option,init)41139503Sborman send_do(option, init)
41239503Sborman int option, init;
41338905Sborman {
41439503Sborman if (init) {
41544364Sborman if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
41644364Sborman his_want_state_is_will(option))
41739503Sborman return;
41839531Sborman /*
41939531Sborman * Special case for TELOPT_TM: We send a DO, but pretend
42039531Sborman * that we sent a DONT, so that we can send more DOs if
42139531Sborman * we want to.
42239531Sborman */
42339531Sborman if (option == TELOPT_TM)
42444364Sborman set_his_want_state_wont(option);
42539531Sborman else
42644364Sborman set_his_want_state_will(option);
42739503Sborman do_dont_resp[option]++;
42839503Sborman }
42965158Sdab (void) sprintf(nfrontp, (char *)doopt, option);
43039503Sborman nfrontp += sizeof (dont) - 2;
43146809Sdab
43246809Sdab DIAG(TD_OPTIONS, printoption("td: send do", option));
43339503Sborman }
43439503Sborman
43557212Sdab #ifdef AUTHENTICATION
43646809Sdab extern void auth_request();
43746809Sdab #endif
43846809Sdab #ifdef LINEMODE
43946809Sdab extern void doclientstat();
44046809Sdab #endif
44157212Sdab #ifdef ENCRYPTION
44246809Sdab extern void encrypt_send_support();
44360151Sdab #endif /* ENCRYPTION */
44446809Sdab
44546809Sdab void
willoption(option)44639503Sborman willoption(option)
44739503Sborman int option;
44839503Sborman {
44938905Sborman int changeok = 0;
45046809Sdab void (*func)() = 0;
45138905Sborman
45239503Sborman /*
45339503Sborman * process input from peer.
45439503Sborman */
45539503Sborman
45646809Sdab DIAG(TD_OPTIONS, printoption("td: recv will", option));
45744364Sborman
45839503Sborman if (do_dont_resp[option]) {
45939503Sborman do_dont_resp[option]--;
46044364Sborman if (do_dont_resp[option] && his_state_is_will(option))
46139503Sborman do_dont_resp[option]--;
46239503Sborman }
46339531Sborman if (do_dont_resp[option] == 0) {
46444364Sborman if (his_want_state_is_wont(option)) {
46538905Sborman switch (option) {
46638905Sborman
46738905Sborman case TELOPT_BINARY:
46838905Sborman init_termbuf();
46938905Sborman tty_binaryin(1);
47038905Sborman set_termbuf();
47138905Sborman changeok++;
47238905Sborman break;
47338905Sborman
47438905Sborman case TELOPT_ECHO:
47538905Sborman /*
47644364Sborman * See comments below for more info.
47738905Sborman */
47844364Sborman not42 = 0; /* looks like a 4.2 system */
47938905Sborman break;
48038905Sborman
48138905Sborman case TELOPT_TM:
48238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE)
48338905Sborman /*
48439503Sborman * This telnetd implementation does not really
48539503Sborman * support timing marks, it just uses them to
48639503Sborman * support the kludge linemode stuff. If we
48739503Sborman * receive a will or wont TM in response to our
48839503Sborman * do TM request that may have been sent to
48939503Sborman * determine kludge linemode support, process
49039503Sborman * it, otherwise TM should get a negative
49139503Sborman * response back.
49238905Sborman */
49338905Sborman /*
49438905Sborman * Handle the linemode kludge stuff.
49538905Sborman * If we are not currently supporting any
49638905Sborman * linemode at all, then we assume that this
49738905Sborman * is the client telling us to use kludge
49838905Sborman * linemode in response to our query. Set the
49938905Sborman * linemode type that is to be supported, note
50038905Sborman * that the client wishes to use linemode, and
50138905Sborman * eat the will TM as though it never arrived.
50238905Sborman */
50338905Sborman if (lmodetype < KLUDGE_LINEMODE) {
50438905Sborman lmodetype = KLUDGE_LINEMODE;
50538905Sborman clientstat(TELOPT_LINEMODE, WILL, 0);
50639503Sborman send_wont(TELOPT_SGA, 1);
50757212Sdab } else if (lmodetype == NO_AUTOKLUDGE) {
50857212Sdab lmodetype = KLUDGE_OK;
50938905Sborman }
51038905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
51138905Sborman /*
51239531Sborman * We never respond to a WILL TM, and
51344364Sborman * we leave the state WONT.
51438905Sborman */
51538905Sborman return;
51638905Sborman
51738905Sborman case TELOPT_LFLOW:
51838905Sborman /*
51939503Sborman * If we are going to support flow control
52039503Sborman * option, then don't worry peer that we can't
52139503Sborman * change the flow control characters.
52238905Sborman */
52338905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
52438905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
52538905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
52638905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
52738905Sborman case TELOPT_TTYPE:
52838905Sborman case TELOPT_SGA:
52938905Sborman case TELOPT_NAWS:
53038905Sborman case TELOPT_TSPEED:
53144364Sborman case TELOPT_XDISPLOC:
53265158Sdab case TELOPT_NEW_ENVIRON:
53365158Sdab case TELOPT_OLD_ENVIRON:
53439531Sborman changeok++;
53539531Sborman break;
53639531Sborman
53738905Sborman #ifdef LINEMODE
53838905Sborman case TELOPT_LINEMODE:
53939531Sborman # ifdef KLUDGELINEMODE
54039531Sborman /*
54139531Sborman * Note client's desire to use linemode.
54239531Sborman */
54339531Sborman lmodetype = REAL_LINEMODE;
54439531Sborman # endif /* KLUDGELINEMODE */
54546809Sdab func = doclientstat;
54646809Sdab changeok++;
54746809Sdab break;
54839531Sborman #endif /* LINEMODE */
54938905Sborman
55057212Sdab #ifdef AUTHENTICATION
55146809Sdab case TELOPT_AUTHENTICATION:
55246809Sdab func = auth_request;
55346809Sdab changeok++;
55446809Sdab break;
55546809Sdab #endif
55646809Sdab
55757212Sdab #ifdef ENCRYPTION
55846809Sdab case TELOPT_ENCRYPT:
55946809Sdab func = encrypt_send_support;
56046809Sdab changeok++;
56146809Sdab break;
56260151Sdab #endif /* ENCRYPTION */
56346809Sdab
56438905Sborman default:
56538905Sborman break;
56638905Sborman }
56739503Sborman if (changeok) {
56844364Sborman set_his_want_state_will(option);
56939503Sborman send_do(option, 0);
57039503Sborman } else {
57139503Sborman do_dont_resp[option]++;
57239503Sborman send_dont(option, 0);
57338905Sborman }
57444364Sborman } else {
57544364Sborman /*
57644364Sborman * Option processing that should happen when
57744364Sborman * we receive conformation of a change in
57844364Sborman * state that we had requested.
57944364Sborman */
58044364Sborman switch (option) {
58144364Sborman case TELOPT_ECHO:
58244364Sborman not42 = 0; /* looks like a 4.2 system */
58344364Sborman /*
58444364Sborman * Egads, he responded "WILL ECHO". Turn
58544364Sborman * it off right now!
58644364Sborman */
58744364Sborman send_dont(option, 1);
58844364Sborman /*
58944364Sborman * "WILL ECHO". Kludge upon kludge!
59044364Sborman * A 4.2 client is now echoing user input at
59144364Sborman * the tty. This is probably undesireable and
59244364Sborman * it should be stopped. The client will
59344364Sborman * respond WONT TM to the DO TM that we send to
59444364Sborman * check for kludge linemode. When the WONT TM
59544364Sborman * arrives, linemode will be turned off and a
59644364Sborman * change propogated to the pty. This change
59744364Sborman * will cause us to process the new pty state
59844364Sborman * in localstat(), which will notice that
59944364Sborman * linemode is off and send a WILL ECHO
60044364Sborman * so that we are properly in character mode and
60144364Sborman * all is well.
60244364Sborman */
60344364Sborman break;
60444364Sborman #ifdef LINEMODE
60544364Sborman case TELOPT_LINEMODE:
60644364Sborman # ifdef KLUDGELINEMODE
60744364Sborman /*
60844364Sborman * Note client's desire to use linemode.
60944364Sborman */
61044364Sborman lmodetype = REAL_LINEMODE;
61144364Sborman # endif /* KLUDGELINEMODE */
61246809Sdab func = doclientstat;
61346809Sdab break;
61444364Sborman #endif /* LINEMODE */
61546809Sdab
61657212Sdab #ifdef AUTHENTICATION
61746809Sdab case TELOPT_AUTHENTICATION:
61846809Sdab func = auth_request;
61946809Sdab break;
62046809Sdab #endif
62146809Sdab
62257212Sdab #ifdef ENCRYPTION
62346809Sdab case TELOPT_ENCRYPT:
62446809Sdab func = encrypt_send_support;
62546809Sdab break;
62660151Sdab #endif /* ENCRYPTION */
62757597Sdab case TELOPT_LFLOW:
62858971Sdab func = flowstat;
62957597Sdab break;
63044364Sborman }
63139531Sborman }
63238905Sborman }
63344364Sborman set_his_state_will(option);
63446809Sdab if (func)
63546809Sdab (*func)();
63638905Sborman } /* end of willoption */
63738905Sborman
63846809Sdab void
send_dont(option,init)63939503Sborman send_dont(option, init)
64039503Sborman int option, init;
64138905Sborman {
64239503Sborman if (init) {
64344364Sborman if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
64444364Sborman his_want_state_is_wont(option))
64539503Sborman return;
64644364Sborman set_his_want_state_wont(option);
64739503Sborman do_dont_resp[option]++;
64839503Sborman }
64965158Sdab (void) sprintf(nfrontp, (char *)dont, option);
65039503Sborman nfrontp += sizeof (doopt) - 2;
65146809Sdab
65246809Sdab DIAG(TD_OPTIONS, printoption("td: send dont", option));
65339503Sborman }
65439503Sborman
65546809Sdab void
wontoption(option)65639503Sborman wontoption(option)
65739503Sborman int option;
65839503Sborman {
65938905Sborman /*
66038905Sborman * Process client input.
66138905Sborman */
66239503Sborman
66346809Sdab DIAG(TD_OPTIONS, printoption("td: recv wont", option));
66444364Sborman
66539503Sborman if (do_dont_resp[option]) {
66639503Sborman do_dont_resp[option]--;
66744364Sborman if (do_dont_resp[option] && his_state_is_wont(option))
66839503Sborman do_dont_resp[option]--;
66939503Sborman }
67039531Sborman if (do_dont_resp[option] == 0) {
67144364Sborman if (his_want_state_is_will(option)) {
67239503Sborman /* it is always ok to change to negative state */
67338905Sborman switch (option) {
67438905Sborman case TELOPT_ECHO:
67539503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */
67638905Sborman break;
67738905Sborman
67838905Sborman case TELOPT_BINARY:
67938905Sborman init_termbuf();
68038905Sborman tty_binaryin(0);
68138905Sborman set_termbuf();
68238905Sborman break;
68338905Sborman
68438905Sborman #ifdef LINEMODE
68538905Sborman case TELOPT_LINEMODE:
68638905Sborman # ifdef KLUDGELINEMODE
68738905Sborman /*
68838905Sborman * If real linemode is supported, then client is
68938905Sborman * asking to turn linemode off.
69038905Sborman */
69144364Sborman if (lmodetype != REAL_LINEMODE)
69244364Sborman break;
69338905Sborman # endif /* KLUDGELINEMODE */
69444364Sborman clientstat(TELOPT_LINEMODE, WONT, 0);
69538905Sborman break;
69646809Sdab #endif /* LINEMODE */
69738905Sborman
69838905Sborman case TELOPT_TM:
69938905Sborman /*
70039503Sborman * If we get a WONT TM, and had sent a DO TM,
70139503Sborman * don't respond with a DONT TM, just leave it
70239503Sborman * as is. Short circut the state machine to
70339531Sborman * achive this.
70438905Sborman */
70544364Sborman set_his_want_state_wont(TELOPT_TM);
70638905Sborman return;
70738905Sborman
70838905Sborman case TELOPT_LFLOW:
70938905Sborman /*
71039503Sborman * If we are not going to support flow control
71139503Sborman * option, then let peer know that we can't
71239503Sborman * change the flow control characters.
71338905Sborman */
71438905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
71538905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
71638905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
71738905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
71838905Sborman break;
71938905Sborman
72057212Sdab #if defined(AUTHENTICATION)
72146809Sdab case TELOPT_AUTHENTICATION:
72246809Sdab auth_finished(0, AUTH_REJECT);
72346809Sdab break;
72446809Sdab #endif
72546809Sdab
72644364Sborman /*
72744364Sborman * For options that we might spin waiting for
72844364Sborman * sub-negotiation, if the client turns off the
72944364Sborman * option rather than responding to the request,
73044364Sborman * we have to treat it here as if we got a response
73144364Sborman * to the sub-negotiation, (by updating the timers)
73244364Sborman * so that we'll break out of the loop.
73344364Sborman */
73444364Sborman case TELOPT_TTYPE:
73544364Sborman settimer(ttypesubopt);
73644364Sborman break;
73744364Sborman
73844364Sborman case TELOPT_TSPEED:
73944364Sborman settimer(tspeedsubopt);
74044364Sborman break;
74144364Sborman
74244364Sborman case TELOPT_XDISPLOC:
74344364Sborman settimer(xdisplocsubopt);
74444364Sborman break;
74544364Sborman
74665158Sdab case TELOPT_OLD_ENVIRON:
74765158Sdab settimer(oenvironsubopt);
74865158Sdab break;
74965158Sdab
75065158Sdab case TELOPT_NEW_ENVIRON:
75144364Sborman settimer(environsubopt);
75244364Sborman break;
75344364Sborman
75438905Sborman default:
75538905Sborman break;
75638905Sborman }
75744364Sborman set_his_want_state_wont(option);
75844364Sborman if (his_state_is_will(option))
75944364Sborman send_dont(option, 0);
76039531Sborman } else {
76139531Sborman switch (option) {
76239531Sborman case TELOPT_TM:
76339531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE)
76457212Sdab if (lmodetype < NO_AUTOKLUDGE) {
76539531Sborman lmodetype = NO_LINEMODE;
76639531Sborman clientstat(TELOPT_LINEMODE, WONT, 0);
76739531Sborman send_will(TELOPT_SGA, 1);
76845234Sborman send_will(TELOPT_ECHO, 1);
76939531Sborman }
77039531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
77146809Sdab break;
77246809Sdab
77357212Sdab #if defined(AUTHENTICATION)
77446809Sdab case TELOPT_AUTHENTICATION:
77546809Sdab auth_finished(0, AUTH_REJECT);
77646809Sdab break;
77746809Sdab #endif
77839531Sborman default:
77939531Sborman break;
78039531Sborman }
78139531Sborman }
78238905Sborman }
78344364Sborman set_his_state_wont(option);
78438905Sborman
78539503Sborman } /* end of wontoption */
78638905Sborman
78746809Sdab void
send_will(option,init)78839503Sborman send_will(option, init)
78939503Sborman int option, init;
79039503Sborman {
79139503Sborman if (init) {
79244364Sborman if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
79344364Sborman my_want_state_is_will(option))
79439503Sborman return;
79544364Sborman set_my_want_state_will(option);
79639503Sborman will_wont_resp[option]++;
79738905Sborman }
79865158Sdab (void) sprintf(nfrontp, (char *)will, option);
79939503Sborman nfrontp += sizeof (doopt) - 2;
80046809Sdab
80146809Sdab DIAG(TD_OPTIONS, printoption("td: send will", option));
80239503Sborman }
80338905Sborman
80445234Sborman #if !defined(LINEMODE) || !defined(KLUDGELINEMODE)
80545234Sborman /*
80645234Sborman * When we get a DONT SGA, we will try once to turn it
80745234Sborman * back on. If the other side responds DONT SGA, we
80845234Sborman * leave it at that. This is so that when we talk to
80945234Sborman * clients that understand KLUDGELINEMODE but not LINEMODE,
81045234Sborman * we'll keep them in char-at-a-time mode.
81145234Sborman */
81245234Sborman int turn_on_sga = 0;
81345234Sborman #endif
81445234Sborman
81546809Sdab void
dooption(option)81639503Sborman dooption(option)
81739503Sborman int option;
81838905Sborman {
81938905Sborman int changeok = 0;
82038905Sborman
82138905Sborman /*
82238905Sborman * Process client input.
82338905Sborman */
82439503Sborman
82546809Sdab DIAG(TD_OPTIONS, printoption("td: recv do", option));
82644364Sborman
82739503Sborman if (will_wont_resp[option]) {
82839503Sborman will_wont_resp[option]--;
82944364Sborman if (will_wont_resp[option] && my_state_is_will(option))
83039503Sborman will_wont_resp[option]--;
83139503Sborman }
83244364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
83338905Sborman switch (option) {
83438905Sborman case TELOPT_ECHO:
83538905Sborman #ifdef LINEMODE
83645234Sborman # ifdef KLUDGELINEMODE
83745234Sborman if (lmodetype == NO_LINEMODE)
83845234Sborman # else
83945234Sborman if (his_state_is_wont(TELOPT_LINEMODE))
84045234Sborman # endif
84138905Sborman #endif
84245234Sborman {
84338905Sborman init_termbuf();
84438905Sborman tty_setecho(1);
84538905Sborman set_termbuf();
84638905Sborman }
84738905Sborman changeok++;
84838905Sborman break;
84938905Sborman
85038905Sborman case TELOPT_BINARY:
85138905Sborman init_termbuf();
85238905Sborman tty_binaryout(1);
85338905Sborman set_termbuf();
85438905Sborman changeok++;
85538905Sborman break;
85638905Sborman
85738905Sborman case TELOPT_SGA:
85838905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE)
85938905Sborman /*
86039503Sborman * If kludge linemode is in use, then we must
86139503Sborman * process an incoming do SGA for linemode
86239503Sborman * purposes.
86338905Sborman */
86438905Sborman if (lmodetype == KLUDGE_LINEMODE) {
86538905Sborman /*
86639503Sborman * Receipt of "do SGA" in kludge
86739503Sborman * linemode is the peer asking us to
86839503Sborman * turn off linemode. Make note of
86939503Sborman * the request.
87038905Sborman */
87138905Sborman clientstat(TELOPT_LINEMODE, WONT, 0);
87238905Sborman /*
87339503Sborman * If linemode did not get turned off
87439503Sborman * then don't tell peer that we did.
87539503Sborman * Breaking here forces a wont SGA to
87639503Sborman * be returned.
87738905Sborman */
87838905Sborman if (linemode)
87938905Sborman break;
88038905Sborman }
88145234Sborman #else
88245234Sborman turn_on_sga = 0;
88338905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
88438905Sborman changeok++;
88538905Sborman break;
88638905Sborman
88738905Sborman case TELOPT_STATUS:
88838905Sborman changeok++;
88938905Sborman break;
89038905Sborman
89138905Sborman case TELOPT_TM:
89239503Sborman /*
89339503Sborman * Special case for TM. We send a WILL, but
89439503Sborman * pretend we sent a WONT.
89539503Sborman */
89639503Sborman send_will(option, 0);
89744364Sborman set_my_want_state_wont(option);
89844364Sborman set_my_state_wont(option);
89939503Sborman return;
90039503Sborman
90146809Sdab case TELOPT_LOGOUT:
90246809Sdab /*
90346809Sdab * When we get a LOGOUT option, respond
90446809Sdab * with a WILL LOGOUT, make sure that
90546809Sdab * it gets written out to the network,
90646809Sdab * and then just go away...
90746809Sdab */
90846809Sdab set_my_want_state_will(TELOPT_LOGOUT);
90946809Sdab send_will(TELOPT_LOGOUT, 0);
91046809Sdab set_my_state_will(TELOPT_LOGOUT);
91146809Sdab (void)netflush();
91246809Sdab cleanup(0);
91346809Sdab /* NOT REACHED */
91446809Sdab break;
91546809Sdab
91660151Sdab #ifdef ENCRYPTION
91746809Sdab case TELOPT_ENCRYPT:
91846809Sdab changeok++;
91946809Sdab break;
92060151Sdab #endif /* ENCRYPTION */
92138905Sborman case TELOPT_LINEMODE:
92238905Sborman case TELOPT_TTYPE:
92338905Sborman case TELOPT_NAWS:
92438905Sborman case TELOPT_TSPEED:
92538905Sborman case TELOPT_LFLOW:
92644364Sborman case TELOPT_XDISPLOC:
92765158Sdab #ifdef TELOPT_ENVIRON
92865158Sdab case TELOPT_NEW_ENVIRON:
92965158Sdab #endif
93065158Sdab case TELOPT_OLD_ENVIRON:
93138905Sborman default:
93238905Sborman break;
93338905Sborman }
93439503Sborman if (changeok) {
93544364Sborman set_my_want_state_will(option);
93639503Sborman send_will(option, 0);
93739503Sborman } else {
93839503Sborman will_wont_resp[option]++;
93939503Sborman send_wont(option, 0);
94038905Sborman }
94138905Sborman }
94244364Sborman set_my_state_will(option);
94338905Sborman
94438905Sborman } /* end of dooption */
94538905Sborman
94646809Sdab void
send_wont(option,init)94739503Sborman send_wont(option, init)
94839503Sborman int option, init;
94939503Sborman {
95039503Sborman if (init) {
95144364Sborman if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
95244364Sborman my_want_state_is_wont(option))
95339503Sborman return;
95444364Sborman set_my_want_state_wont(option);
95539503Sborman will_wont_resp[option]++;
95639503Sborman }
95765158Sdab (void) sprintf(nfrontp, (char *)wont, option);
95839503Sborman nfrontp += sizeof (wont) - 2;
95946809Sdab
96046809Sdab DIAG(TD_OPTIONS, printoption("td: send wont", option));
96139503Sborman }
96238905Sborman
96346809Sdab void
dontoption(option)96439503Sborman dontoption(option)
96539503Sborman int option;
96638905Sborman {
96738905Sborman /*
96838905Sborman * Process client input.
96938905Sborman */
97040242Sborman
97146809Sdab
97246809Sdab DIAG(TD_OPTIONS, printoption("td: recv dont", option));
97346809Sdab
97439503Sborman if (will_wont_resp[option]) {
97539503Sborman will_wont_resp[option]--;
97644364Sborman if (will_wont_resp[option] && my_state_is_wont(option))
97739503Sborman will_wont_resp[option]--;
97839503Sborman }
97944364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
98038905Sborman switch (option) {
98138905Sborman case TELOPT_BINARY:
98238905Sborman init_termbuf();
98338905Sborman tty_binaryout(0);
98438905Sborman set_termbuf();
98538905Sborman break;
98638905Sborman
98739503Sborman case TELOPT_ECHO: /* we should stop echoing */
98838905Sborman #ifdef LINEMODE
98945234Sborman # ifdef KLUDGELINEMODE
99057212Sdab if ((lmodetype != REAL_LINEMODE) &&
99157212Sdab (lmodetype != KLUDGE_LINEMODE))
99245234Sborman # else
99345234Sborman if (his_state_is_wont(TELOPT_LINEMODE))
99445234Sborman # endif
99538905Sborman #endif
99645234Sborman {
99738905Sborman init_termbuf();
99838905Sborman tty_setecho(0);
99938905Sborman set_termbuf();
100038905Sborman }
100138905Sborman break;
100238905Sborman
100338905Sborman case TELOPT_SGA:
100438905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE)
100538905Sborman /*
100639503Sborman * If kludge linemode is in use, then we
100739503Sborman * must process an incoming do SGA for
100839503Sborman * linemode purposes.
100938905Sborman */
101057212Sdab if ((lmodetype == KLUDGE_LINEMODE) ||
101157212Sdab (lmodetype == KLUDGE_OK)) {
101238905Sborman /*
101339503Sborman * The client is asking us to turn
101439503Sborman * linemode on.
101538905Sborman */
101657212Sdab lmodetype = KLUDGE_LINEMODE;
101738905Sborman clientstat(TELOPT_LINEMODE, WILL, 0);
101838905Sborman /*
101939503Sborman * If we did not turn line mode on,
102039503Sborman * then what do we say? Will SGA?
102139503Sborman * This violates design of telnet.
102239503Sborman * Gross. Very Gross.
102338905Sborman */
102438905Sborman }
102545234Sborman break;
102645234Sborman #else
102745234Sborman set_my_want_state_wont(option);
102845234Sborman if (my_state_is_will(option))
102945234Sborman send_wont(option, 0);
103045234Sborman set_my_state_wont(option);
103145234Sborman if (turn_on_sga ^= 1)
103257212Sdab send_will(option, 1);
103345234Sborman return;
103438905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
103538905Sborman
103638905Sborman default:
103738905Sborman break;
103838905Sborman }
103938905Sborman
104044364Sborman set_my_want_state_wont(option);
104144364Sborman if (my_state_is_will(option))
104244364Sborman send_wont(option, 0);
104338905Sborman }
104444364Sborman set_my_state_wont(option);
104538905Sborman
104638905Sborman } /* end of dontoption */
104738905Sborman
104858971Sdab #ifdef ENV_HACK
104965158Sdab int env_ovar = -1;
105065158Sdab int env_ovalue = -1;
105158971Sdab #else /* ENV_HACK */
105265158Sdab # define env_ovar OLD_ENV_VAR
105365158Sdab # define env_ovalue OLD_ENV_VALUE
105458971Sdab #endif /* ENV_HACK */
105558971Sdab
105638905Sborman /*
105738905Sborman * suboption()
105838905Sborman *
105938905Sborman * Look at the sub-option buffer, and try to be helpful to the other
106038905Sborman * side.
106138905Sborman *
106238905Sborman * Currently we recognize:
106338905Sborman *
106438905Sborman * Terminal type is
106538905Sborman * Linemode
106638905Sborman * Window size
106738905Sborman * Terminal speed
106838905Sborman */
106946809Sdab void
suboption()107038905Sborman suboption()
107138905Sborman {
107238905Sborman register int subchar;
107338905Sborman
107446809Sdab DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
107546809Sdab
107638905Sborman subchar = SB_GET();
107738905Sborman switch (subchar) {
107838905Sborman case TELOPT_TSPEED: {
107938905Sborman register int xspeed, rspeed;
108038905Sborman
108144364Sborman if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */
108238905Sborman break;
108338905Sborman
108438905Sborman settimer(tspeedsubopt);
108538905Sborman
108638905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS)
108738905Sborman return;
108838905Sborman
108946809Sdab xspeed = atoi((char *)subpointer);
109038905Sborman
109138905Sborman while (SB_GET() != ',' && !SB_EOF());
109238905Sborman if (SB_EOF())
109338905Sborman return;
109438905Sborman
109546809Sdab rspeed = atoi((char *)subpointer);
109638905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed);
109738905Sborman
109838905Sborman break;
109938905Sborman
110038905Sborman } /* end of case TELOPT_TSPEED */
110138905Sborman
110238905Sborman case TELOPT_TTYPE: { /* Yaaaay! */
110344364Sborman static char terminalname[41];
110438905Sborman
110544364Sborman if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */
110638905Sborman break;
110738905Sborman settimer(ttypesubopt);
110838905Sborman
110946809Sdab if (SB_EOF() || SB_GET() != TELQUAL_IS) {
111038905Sborman return; /* ??? XXX but, this is the most robust */
111138905Sborman }
111238905Sborman
111344364Sborman terminaltype = terminalname;
111438905Sborman
111538905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
111638905Sborman !SB_EOF()) {
111738905Sborman register int c;
111838905Sborman
111938905Sborman c = SB_GET();
112038905Sborman if (isupper(c)) {
112138905Sborman c = tolower(c);
112238905Sborman }
112338905Sborman *terminaltype++ = c; /* accumulate name */
112438905Sborman }
112538905Sborman *terminaltype = 0;
112638905Sborman terminaltype = terminalname;
112738905Sborman break;
112838905Sborman } /* end of case TELOPT_TTYPE */
112938905Sborman
113038905Sborman case TELOPT_NAWS: {
113138905Sborman register int xwinsize, ywinsize;
113238905Sborman
113344364Sborman if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */
113438905Sborman break;
113538905Sborman
113638905Sborman if (SB_EOF())
113738905Sborman return;
113838905Sborman xwinsize = SB_GET() << 8;
113938905Sborman if (SB_EOF())
114038905Sborman return;
114138905Sborman xwinsize |= SB_GET();
114238905Sborman if (SB_EOF())
114338905Sborman return;
114438905Sborman ywinsize = SB_GET() << 8;
114538905Sborman if (SB_EOF())
114638905Sborman return;
114738905Sborman ywinsize |= SB_GET();
114838905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize);
114938905Sborman
115038905Sborman break;
115138905Sborman
115238905Sborman } /* end of case TELOPT_NAWS */
115338905Sborman
115438905Sborman #ifdef LINEMODE
115538905Sborman case TELOPT_LINEMODE: {
115638905Sborman register int request;
115738905Sborman
115844364Sborman if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */
115938905Sborman break;
116038905Sborman /*
116138905Sborman * Process linemode suboptions.
116238905Sborman */
116346809Sdab if (SB_EOF())
116446809Sdab break; /* garbage was sent */
116546809Sdab request = SB_GET(); /* get will/wont */
116638905Sborman
116746809Sdab if (SB_EOF())
116846809Sdab break; /* another garbage check */
116946809Sdab
117038905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */
117138905Sborman /*
117238905Sborman * Process suboption buffer of slc's
117338905Sborman */
117438905Sborman start_slc(1);
117538905Sborman do_opt_slc(subpointer, subend - subpointer);
117646809Sdab (void) end_slc(0);
117746809Sdab break;
117838905Sborman } else if (request == LM_MODE) {
117946809Sdab if (SB_EOF())
118046809Sdab return;
118138905Sborman useeditmode = SB_GET(); /* get mode flag */
118238905Sborman clientstat(LM_MODE, 0, 0);
118346809Sdab break;
118438905Sborman }
118538905Sborman
118646809Sdab if (SB_EOF())
118746809Sdab break;
118838905Sborman switch (SB_GET()) { /* what suboption? */
118938905Sborman case LM_FORWARDMASK:
119038905Sborman /*
119138905Sborman * According to spec, only server can send request for
119238905Sborman * forwardmask, and client can only return a positive response.
119338905Sborman * So don't worry about it.
119438905Sborman */
119538905Sborman
119638905Sborman default:
119738905Sborman break;
119838905Sborman }
119940242Sborman break;
120038905Sborman } /* end of case TELOPT_LINEMODE */
120138905Sborman #endif
120238905Sborman case TELOPT_STATUS: {
120338905Sborman int mode;
120438905Sborman
120546809Sdab if (SB_EOF())
120646809Sdab break;
120738905Sborman mode = SB_GET();
120838905Sborman switch (mode) {
120938905Sborman case TELQUAL_SEND:
121044364Sborman if (my_state_is_will(TELOPT_STATUS))
121138905Sborman send_status();
121238905Sborman break;
121338905Sborman
121438905Sborman case TELQUAL_IS:
121538905Sborman break;
121638905Sborman
121738905Sborman default:
121838905Sborman break;
121938905Sborman }
122040242Sborman break;
122140242Sborman } /* end of case TELOPT_STATUS */
122238905Sborman
122344364Sborman case TELOPT_XDISPLOC: {
122444364Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS)
122544364Sborman return;
122644364Sborman settimer(xdisplocsubopt);
122744364Sborman subpointer[SB_LEN()] = '\0';
122846809Sdab (void)setenv("DISPLAY", (char *)subpointer, 1);
122944364Sborman break;
123044364Sborman } /* end of case TELOPT_XDISPLOC */
123144364Sborman
123265158Sdab #ifdef TELOPT_NEW_ENVIRON
123365158Sdab case TELOPT_NEW_ENVIRON:
123465158Sdab #endif
123565158Sdab case TELOPT_OLD_ENVIRON: {
123644364Sborman register int c;
123744364Sborman register char *cp, *varp, *valp;
123844364Sborman
123944364Sborman if (SB_EOF())
124044364Sborman return;
124144364Sborman c = SB_GET();
124265158Sdab if (c == TELQUAL_IS) {
124365158Sdab if (subchar == TELOPT_OLD_ENVIRON)
124465158Sdab settimer(oenvironsubopt);
124565158Sdab else
124665158Sdab settimer(environsubopt);
124765158Sdab } else if (c != TELQUAL_INFO) {
124844364Sborman return;
124965158Sdab }
125044364Sborman
125165158Sdab #ifdef TELOPT_NEW_ENVIRON
125265158Sdab if (subchar == TELOPT_NEW_ENVIRON) {
125365158Sdab while (!SB_EOF()) {
125465158Sdab c = SB_GET();
125565158Sdab if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
125665158Sdab break;
125765158Sdab }
125865158Sdab } else
125965158Sdab #endif
126065158Sdab {
126158971Sdab #ifdef ENV_HACK
126265158Sdab /*
126365158Sdab * We only want to do this if we haven't already decided
126465158Sdab * whether or not the other side has its VALUE and VAR
126565158Sdab * reversed.
126665158Sdab */
126765158Sdab if (env_ovar < 0) {
126858971Sdab register int last = -1; /* invalid value */
126958971Sdab int empty = 0;
127058971Sdab int got_var = 0, got_value = 0, got_uservar = 0;
127158971Sdab
127258971Sdab /*
127358971Sdab * The other side might have its VALUE and VAR values
127458971Sdab * reversed. To be interoperable, we need to determine
127558971Sdab * which way it is. If the first recognized character
127658971Sdab * is a VAR or VALUE, then that will tell us what
127758971Sdab * type of client it is. If the fist recognized
127858971Sdab * character is a USERVAR, then we continue scanning
127958971Sdab * the suboption looking for two consecutive
128058971Sdab * VAR or VALUE fields. We should not get two
128158971Sdab * consecutive VALUE fields, so finding two
128258971Sdab * consecutive VALUE or VAR fields will tell us
128358971Sdab * what the client is.
128458971Sdab */
128558971Sdab SB_SAVE();
128658971Sdab while (!SB_EOF()) {
128758971Sdab c = SB_GET();
128858971Sdab switch(c) {
128965158Sdab case OLD_ENV_VAR:
129065158Sdab if (last < 0 || last == OLD_ENV_VAR
129165158Sdab || (empty && (last == OLD_ENV_VALUE)))
129265158Sdab goto env_ovar_ok;
129358971Sdab got_var++;
129465158Sdab last = OLD_ENV_VAR;
129558971Sdab break;
129665158Sdab case OLD_ENV_VALUE:
129765158Sdab if (last < 0 || last == OLD_ENV_VALUE
129865158Sdab || (empty && (last == OLD_ENV_VAR)))
129965158Sdab goto env_ovar_wrong;
130058971Sdab got_value++;
130165158Sdab last = OLD_ENV_VALUE;
130258971Sdab break;
130358971Sdab case ENV_USERVAR:
130458971Sdab /* count strings of USERVAR as one */
130558971Sdab if (last != ENV_USERVAR)
130658971Sdab got_uservar++;
130758971Sdab if (empty) {
130865158Sdab if (last == OLD_ENV_VALUE)
130965158Sdab goto env_ovar_ok;
131065158Sdab if (last == OLD_ENV_VAR)
131165158Sdab goto env_ovar_wrong;
131258971Sdab }
131358971Sdab last = ENV_USERVAR;
131458971Sdab break;
131558971Sdab case ENV_ESC:
131658971Sdab if (!SB_EOF())
131758971Sdab c = SB_GET();
131858971Sdab /* FALL THROUGH */
131958971Sdab default:
132058971Sdab empty = 0;
132158971Sdab continue;
132258971Sdab }
132358971Sdab empty = 1;
132458971Sdab }
132558971Sdab if (empty) {
132665158Sdab if (last == OLD_ENV_VALUE)
132765158Sdab goto env_ovar_ok;
132865158Sdab if (last == OLD_ENV_VAR)
132965158Sdab goto env_ovar_wrong;
133058971Sdab }
133158971Sdab /*
133258971Sdab * Ok, the first thing was a USERVAR, and there
133358971Sdab * are not two consecutive VAR or VALUE commands,
133458971Sdab * and none of the VAR or VALUE commands are empty.
133558971Sdab * If the client has sent us a well-formed option,
133658971Sdab * then the number of VALUEs received should always
133758971Sdab * be less than or equal to the number of VARs and
133858971Sdab * USERVARs received.
133958971Sdab *
134058971Sdab * If we got exactly as many VALUEs as VARs and
134158971Sdab * USERVARs, the client has the same definitions.
134258971Sdab *
134358971Sdab * If we got exactly as many VARs as VALUEs and
134458971Sdab * USERVARS, the client has reversed definitions.
134558971Sdab */
134660151Sdab if (got_uservar + got_var == got_value) {
134765158Sdab env_ovar_ok:
134865158Sdab env_ovar = OLD_ENV_VAR;
134965158Sdab env_ovalue = OLD_ENV_VALUE;
135060151Sdab } else if (got_uservar + got_value == got_var) {
135165158Sdab env_ovar_wrong:
135265158Sdab env_ovar = OLD_ENV_VALUE;
135365158Sdab env_ovalue = OLD_ENV_VAR;
135458971Sdab DIAG(TD_OPTIONS, {sprintf(nfrontp,
135558971Sdab "ENVIRON VALUE and VAR are reversed!\r\n");
135658971Sdab nfrontp += strlen(nfrontp);});
135758971Sdab
135858971Sdab }
135965158Sdab }
136065158Sdab SB_RESTORE();
136158971Sdab #endif
136258971Sdab
136365158Sdab while (!SB_EOF()) {
136457212Sdab c = SB_GET();
136565158Sdab if ((c == env_ovar) || (c == ENV_USERVAR))
136657212Sdab break;
136765158Sdab }
136857212Sdab }
136944364Sborman
137044364Sborman if (SB_EOF())
137144364Sborman return;
137244364Sborman
137346809Sdab cp = varp = (char *)subpointer;
137444364Sborman valp = 0;
137544364Sborman
137644364Sborman while (!SB_EOF()) {
137758971Sdab c = SB_GET();
137865158Sdab if (subchar == TELOPT_OLD_ENVIRON) {
137965158Sdab if (c == env_ovar)
138065158Sdab c = NEW_ENV_VAR;
138165158Sdab else if (c == env_ovalue)
138265158Sdab c = NEW_ENV_VALUE;
138365158Sdab }
138458971Sdab switch (c) {
138558971Sdab
138665158Sdab case NEW_ENV_VALUE:
138744364Sborman *cp = '\0';
138846809Sdab cp = valp = (char *)subpointer;
138944364Sborman break;
139044364Sborman
139165158Sdab case NEW_ENV_VAR:
139257212Sdab case ENV_USERVAR:
139344364Sborman *cp = '\0';
139444364Sborman if (valp)
139546809Sdab (void)setenv(varp, valp, 1);
139644364Sborman else
139744364Sborman unsetenv(varp);
139846809Sdab cp = varp = (char *)subpointer;
139944364Sborman valp = 0;
140044364Sborman break;
140144364Sborman
140244364Sborman case ENV_ESC:
140344364Sborman if (SB_EOF())
140444364Sborman break;
140544364Sborman c = SB_GET();
140644364Sborman /* FALL THROUGH */
140744364Sborman default:
140844364Sborman *cp++ = c;
140944364Sborman break;
141044364Sborman }
141144364Sborman }
141244364Sborman *cp = '\0';
141344364Sborman if (valp)
141446809Sdab (void)setenv(varp, valp, 1);
141544364Sborman else
141644364Sborman unsetenv(varp);
141744364Sborman break;
141865158Sdab } /* end of case TELOPT_NEW_ENVIRON */
141957212Sdab #if defined(AUTHENTICATION)
142046809Sdab case TELOPT_AUTHENTICATION:
142146809Sdab if (SB_EOF())
142246809Sdab break;
142346809Sdab switch(SB_GET()) {
142446809Sdab case TELQUAL_SEND:
142546809Sdab case TELQUAL_REPLY:
142646809Sdab /*
142746809Sdab * These are sent by us and cannot be sent by
142846809Sdab * the client.
142946809Sdab */
143046809Sdab break;
143146809Sdab case TELQUAL_IS:
143246809Sdab auth_is(subpointer, SB_LEN());
143346809Sdab break;
143447611Sdab case TELQUAL_NAME:
143547611Sdab auth_name(subpointer, SB_LEN());
143647611Sdab break;
143746809Sdab }
143846809Sdab break;
143946809Sdab #endif
144060151Sdab #ifdef ENCRYPTION
144146809Sdab case TELOPT_ENCRYPT:
144246809Sdab if (SB_EOF())
144346809Sdab break;
144446809Sdab switch(SB_GET()) {
144546809Sdab case ENCRYPT_SUPPORT:
144646809Sdab encrypt_support(subpointer, SB_LEN());
144746809Sdab break;
144846809Sdab case ENCRYPT_IS:
144946809Sdab encrypt_is(subpointer, SB_LEN());
145046809Sdab break;
145146809Sdab case ENCRYPT_REPLY:
145246809Sdab encrypt_reply(subpointer, SB_LEN());
145346809Sdab break;
145446809Sdab case ENCRYPT_START:
145547611Sdab encrypt_start(subpointer, SB_LEN());
145646809Sdab break;
145746809Sdab case ENCRYPT_END:
145846809Sdab encrypt_end();
145946809Sdab break;
146046809Sdab case ENCRYPT_REQSTART:
146147611Sdab encrypt_request_start(subpointer, SB_LEN());
146246809Sdab break;
146346809Sdab case ENCRYPT_REQEND:
146446809Sdab /*
146546809Sdab * We can always send an REQEND so that we cannot
146646809Sdab * get stuck encrypting. We should only get this
146746809Sdab * if we have been able to get in the correct mode
146846809Sdab * anyhow.
146946809Sdab */
147046809Sdab encrypt_request_end();
147146809Sdab break;
147247611Sdab case ENCRYPT_ENC_KEYID:
147347611Sdab encrypt_enc_keyid(subpointer, SB_LEN());
147447611Sdab break;
147547611Sdab case ENCRYPT_DEC_KEYID:
147647611Sdab encrypt_dec_keyid(subpointer, SB_LEN());
147747611Sdab break;
147846809Sdab default:
147946809Sdab break;
148046809Sdab }
148146809Sdab break;
148260151Sdab #endif /* ENCRYPTION */
148344364Sborman
148438905Sborman default:
148538905Sborman break;
148638905Sborman } /* end of switch */
148738905Sborman
148838905Sborman } /* end of suboption */
148938905Sborman
149046809Sdab void
doclientstat()149146809Sdab doclientstat()
149246809Sdab {
149346809Sdab clientstat(TELOPT_LINEMODE, WILL, 0);
149446809Sdab }
149546809Sdab
149668347Sdab #define ADD(c) *ncp++ = c
149768347Sdab #define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
149846809Sdab void
send_status()149938905Sborman send_status()
150038905Sborman {
150144364Sborman unsigned char statusbuf[256];
150244364Sborman register unsigned char *ncp;
150344364Sborman register unsigned char i;
150438905Sborman
150538905Sborman ncp = statusbuf;
150638905Sborman
150738905Sborman netflush(); /* get rid of anything waiting to go out */
150838905Sborman
150938905Sborman ADD(IAC);
151038905Sborman ADD(SB);
151138905Sborman ADD(TELOPT_STATUS);
151238905Sborman ADD(TELQUAL_IS);
151338905Sborman
151446809Sdab /*
151546809Sdab * We check the want_state rather than the current state,
151646809Sdab * because if we received a DO/WILL for an option that we
151746809Sdab * don't support, and the other side didn't send a DONT/WONT
151846809Sdab * in response to our WONT/DONT, then the "state" will be
151946809Sdab * WILL/DO, and the "want_state" will be WONT/DONT. We
152046809Sdab * need to go by the latter.
152146809Sdab */
152265158Sdab for (i = 0; i < (unsigned char)NTELOPTS; i++) {
152346809Sdab if (my_want_state_is_will(i)) {
152438905Sborman ADD(WILL);
152538905Sborman ADD_DATA(i);
152638905Sborman }
152746809Sdab if (his_want_state_is_will(i)) {
152838905Sborman ADD(DO);
152938905Sborman ADD_DATA(i);
153038905Sborman }
153138905Sborman }
153238905Sborman
153346809Sdab if (his_want_state_is_will(TELOPT_LFLOW)) {
153446809Sdab ADD(SB);
153546809Sdab ADD(TELOPT_LFLOW);
153657212Sdab if (flowmode) {
153757212Sdab ADD(LFLOW_ON);
153857212Sdab } else {
153957212Sdab ADD(LFLOW_OFF);
154057212Sdab }
154146809Sdab ADD(SE);
154257212Sdab
154357212Sdab if (restartany >= 0) {
154468347Sdab ADD(SB);
154557212Sdab ADD(TELOPT_LFLOW);
154657212Sdab if (restartany) {
154757212Sdab ADD(LFLOW_RESTART_ANY);
154857212Sdab } else {
154957212Sdab ADD(LFLOW_RESTART_XON);
155057212Sdab }
155168347Sdab ADD(SE);
155257212Sdab }
155346809Sdab }
155446809Sdab
155538905Sborman #ifdef LINEMODE
155646809Sdab if (his_want_state_is_will(TELOPT_LINEMODE)) {
155744364Sborman unsigned char *cp, *cpe;
155838905Sborman int len;
155938905Sborman
156038905Sborman ADD(SB);
156138905Sborman ADD(TELOPT_LINEMODE);
156238905Sborman ADD(LM_MODE);
156338905Sborman ADD_DATA(editmode);
156438905Sborman ADD(SE);
156538905Sborman
156638905Sborman ADD(SB);
156738905Sborman ADD(TELOPT_LINEMODE);
156838905Sborman ADD(LM_SLC);
156938905Sborman start_slc(0);
157038905Sborman send_slc();
157138905Sborman len = end_slc(&cp);
157238905Sborman for (cpe = cp + len; cp < cpe; cp++)
157338905Sborman ADD_DATA(*cp);
157438905Sborman ADD(SE);
157538905Sborman }
157638905Sborman #endif /* LINEMODE */
157738905Sborman
157838905Sborman ADD(IAC);
157938905Sborman ADD(SE);
158038905Sborman
158138905Sborman writenet(statusbuf, ncp - statusbuf);
158238905Sborman netflush(); /* Send it on its way */
158346809Sdab
158446809Sdab DIAG(TD_OPTIONS,
158546809Sdab {printsub('>', statusbuf, ncp - statusbuf); netflush();});
158638905Sborman }
1587