129088Smarkm/* 229088Smarkm * Copyright (c) 1989, 1993 329088Smarkm * The Regents of the University of California. All rights reserved. 429088Smarkm * 529088Smarkm * Redistribution and use in source and binary forms, with or without 629088Smarkm * modification, are permitted provided that the following conditions 729088Smarkm * are met: 829088Smarkm * 1. Redistributions of source code must retain the above copyright 929088Smarkm * notice, this list of conditions and the following disclaimer. 1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1129088Smarkm * notice, this list of conditions and the following disclaimer in the 1229088Smarkm * documentation and/or other materials provided with the distribution. 1329088Smarkm * 3. All advertising materials mentioning features or use of this software 1429088Smarkm * must display the following acknowledgement: 1529088Smarkm * This product includes software developed by the University of 1629088Smarkm * California, Berkeley and its contributors. 1729088Smarkm * 4. Neither the name of the University nor the names of its contributors 1829088Smarkm * may be used to endorse or promote products derived from this software 1929088Smarkm * without specific prior written permission. 2029088Smarkm * 2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2429088Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3129088Smarkm * SUCH DAMAGE. 3229088Smarkm */ 3329088Smarkm 34114630Sobrien#if 0 3529088Smarkm#ifndef lint 3629181Smarkmstatic const char sccsid[] = "@(#)state.c 8.5 (Berkeley) 5/30/95"; 3731622Scharnier#endif 38114630Sobrien#endif 39114630Sobrien#include <sys/cdefs.h> 40114630Sobrien__FBSDID("$FreeBSD$"); 4129088Smarkm 4279981Sru#include <stdarg.h> 4329088Smarkm#include "telnetd.h" 4487139Smarkm#ifdef AUTHENTICATION 4529088Smarkm#include <libtelnet/auth.h> 4629088Smarkm#endif 4787139Smarkm#ifdef ENCRYPTION 4829181Smarkm#include <libtelnet/encrypt.h> 4929181Smarkm#endif 5029088Smarkm 5129088Smarkmunsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; 5229088Smarkmunsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; 5329088Smarkmunsigned char will[] = { IAC, WILL, '%', 'c', 0 }; 5429088Smarkmunsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; 5529088Smarkmint not42 = 1; 5629088Smarkm 5729088Smarkm/* 5829088Smarkm * Buffer for sub-options, and macros 5929088Smarkm * for suboptions buffer manipulations 6029088Smarkm */ 6129088Smarkmunsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer; 6229088Smarkm 6329088Smarkm#define SB_CLEAR() subpointer = subbuffer 6429088Smarkm#define SB_TERM() { subend = subpointer; SB_CLEAR(); } 6529088Smarkm#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 6629088Smarkm *subpointer++ = (c); \ 6729088Smarkm } 6829088Smarkm#define SB_GET() ((*subpointer++)&0xff) 6929088Smarkm#define SB_EOF() (subpointer >= subend) 7029088Smarkm#define SB_LEN() (subend - subpointer) 7129088Smarkm 7229088Smarkm#ifdef ENV_HACK 7329088Smarkmunsigned char *subsave; 7429088Smarkm#define SB_SAVE() subsave = subpointer; 7529088Smarkm#define SB_RESTORE() subpointer = subsave; 7629088Smarkm#endif 7729088Smarkm 7829088Smarkm 7929088Smarkm/* 8029088Smarkm * State for recv fsm 8129088Smarkm */ 8229088Smarkm#define TS_DATA 0 /* base state */ 8329088Smarkm#define TS_IAC 1 /* look for double IAC's */ 8429088Smarkm#define TS_CR 2 /* CR-LF ->'s CR */ 8529088Smarkm#define TS_SB 3 /* throw away begin's... */ 8629088Smarkm#define TS_SE 4 /* ...end's (suboption negotiation) */ 8729088Smarkm#define TS_WILL 5 /* will option negotiation */ 8829088Smarkm#define TS_WONT 6 /* wont " */ 8929088Smarkm#define TS_DO 7 /* do " */ 9029088Smarkm#define TS_DONT 8 /* dont " */ 9129088Smarkm 9287139Smarkmstatic void doclientstat(void); 9387139Smarkm 9487139Smarkmvoid 9587139Smarkmtelrcv(void) 9629088Smarkm{ 9787139Smarkm int c; 9829088Smarkm static int state = TS_DATA; 9929088Smarkm 10029088Smarkm while (ncc > 0) { 10129088Smarkm if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 10229088Smarkm break; 10329088Smarkm c = *netip++ & 0377, ncc--; 10429088Smarkm#ifdef ENCRYPTION 10529088Smarkm if (decrypt_input) 10629088Smarkm c = (*decrypt_input)(c); 10729088Smarkm#endif /* ENCRYPTION */ 10829088Smarkm switch (state) { 10929088Smarkm 11029088Smarkm case TS_CR: 11129088Smarkm state = TS_DATA; 11229088Smarkm /* Strip off \n or \0 after a \r */ 11329088Smarkm if ((c == 0) || (c == '\n')) { 11429088Smarkm break; 11529088Smarkm } 116103956Smarkm /* FALLTHROUGH */ 11729088Smarkm 11829088Smarkm case TS_DATA: 11929088Smarkm if (c == IAC) { 12029088Smarkm state = TS_IAC; 12129088Smarkm break; 12229088Smarkm } 12329088Smarkm /* 12429088Smarkm * We now map \r\n ==> \r for pragmatic reasons. 12529088Smarkm * Many client implementations send \r\n when 12629088Smarkm * the user hits the CarriageReturn key. 12729088Smarkm * 12829088Smarkm * We USED to map \r\n ==> \n, since \r\n says 12929088Smarkm * that we want to be in column 1 of the next 13029088Smarkm * printable line, and \n is the standard 13129088Smarkm * unix way of saying that (\r is only good 13229088Smarkm * if CRMOD is set, which it normally is). 13329088Smarkm */ 13429088Smarkm if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { 13529088Smarkm int nc = *netip; 13629088Smarkm#ifdef ENCRYPTION 13729088Smarkm if (decrypt_input) 13829088Smarkm nc = (*decrypt_input)(nc & 0xff); 13929088Smarkm#endif /* ENCRYPTION */ 14029088Smarkm#ifdef LINEMODE 14129088Smarkm /* 14229088Smarkm * If we are operating in linemode, 14329088Smarkm * convert to local end-of-line. 14429088Smarkm */ 14529088Smarkm if (linemode && (ncc > 0) && (('\n' == nc) || 14629088Smarkm ((0 == nc) && tty_iscrnl())) ) { 14729088Smarkm netip++; ncc--; 14829088Smarkm c = '\n'; 14929088Smarkm } else 15029088Smarkm#endif 15129088Smarkm { 15229088Smarkm#ifdef ENCRYPTION 15329088Smarkm if (decrypt_input) 15429088Smarkm (void)(*decrypt_input)(-1); 15529088Smarkm#endif /* ENCRYPTION */ 15629088Smarkm state = TS_CR; 15729088Smarkm } 15829088Smarkm } 15929088Smarkm *pfrontp++ = c; 16029088Smarkm break; 16129088Smarkm 16229088Smarkm case TS_IAC: 16329088Smarkmgotiac: switch (c) { 16429088Smarkm 16529088Smarkm /* 16629088Smarkm * Send the process on the pty side an 16729088Smarkm * interrupt. Do this with a NULL or 16829088Smarkm * interrupt char; depending on the tty mode. 16929088Smarkm */ 17029088Smarkm case IP: 17129088Smarkm DIAG(TD_OPTIONS, 17229088Smarkm printoption("td: recv IAC", c)); 17329088Smarkm interrupt(); 17429088Smarkm break; 17529088Smarkm 17629088Smarkm case BREAK: 17729088Smarkm DIAG(TD_OPTIONS, 17829088Smarkm printoption("td: recv IAC", c)); 17929088Smarkm sendbrk(); 18029088Smarkm break; 18129088Smarkm 18229088Smarkm /* 18329088Smarkm * Are You There? 18429088Smarkm */ 18529088Smarkm case AYT: 18629088Smarkm DIAG(TD_OPTIONS, 18729088Smarkm printoption("td: recv IAC", c)); 18829088Smarkm recv_ayt(); 18929088Smarkm break; 19029088Smarkm 19129088Smarkm /* 19229088Smarkm * Abort Output 19329088Smarkm */ 19429088Smarkm case AO: 19529088Smarkm { 19629088Smarkm DIAG(TD_OPTIONS, 19729088Smarkm printoption("td: recv IAC", c)); 19829088Smarkm ptyflush(); /* half-hearted */ 19929088Smarkm init_termbuf(); 20029088Smarkm 20129088Smarkm if (slctab[SLC_AO].sptr && 20229088Smarkm *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { 20329088Smarkm *pfrontp++ = 20429088Smarkm (unsigned char)*slctab[SLC_AO].sptr; 20529088Smarkm } 20629088Smarkm 20729088Smarkm netclear(); /* clear buffer back */ 20879981Sru output_data("%c%c", IAC, DM); 20929088Smarkm neturg = nfrontp-1; /* off by one XXX */ 21029088Smarkm DIAG(TD_OPTIONS, 21129088Smarkm printoption("td: send IAC", DM)); 21229088Smarkm break; 21329088Smarkm } 21429088Smarkm 21529088Smarkm /* 21629088Smarkm * Erase Character and 21729088Smarkm * Erase Line 21829088Smarkm */ 21929088Smarkm case EC: 22029088Smarkm case EL: 22129088Smarkm { 22229088Smarkm cc_t ch; 22329088Smarkm 22429088Smarkm DIAG(TD_OPTIONS, 22529088Smarkm printoption("td: recv IAC", c)); 22629088Smarkm ptyflush(); /* half-hearted */ 22729088Smarkm init_termbuf(); 22829088Smarkm if (c == EC) 22929088Smarkm ch = *slctab[SLC_EC].sptr; 23029088Smarkm else 23129088Smarkm ch = *slctab[SLC_EL].sptr; 23229088Smarkm if (ch != (cc_t)(_POSIX_VDISABLE)) 23329088Smarkm *pfrontp++ = (unsigned char)ch; 23429088Smarkm break; 23529088Smarkm } 23629088Smarkm 23729088Smarkm /* 23829088Smarkm * Check for urgent data... 23929088Smarkm */ 24029088Smarkm case DM: 24129088Smarkm DIAG(TD_OPTIONS, 24229088Smarkm printoption("td: recv IAC", c)); 24329088Smarkm SYNCHing = stilloob(net); 24429088Smarkm settimer(gotDM); 24529088Smarkm break; 24629088Smarkm 24729088Smarkm 24829088Smarkm /* 24929088Smarkm * Begin option subnegotiation... 25029088Smarkm */ 25129088Smarkm case SB: 25229088Smarkm state = TS_SB; 25329088Smarkm SB_CLEAR(); 25429088Smarkm continue; 25529088Smarkm 25629088Smarkm case WILL: 25729088Smarkm state = TS_WILL; 25829088Smarkm continue; 25929088Smarkm 26029088Smarkm case WONT: 26129088Smarkm state = TS_WONT; 26229088Smarkm continue; 26329088Smarkm 26429088Smarkm case DO: 26529088Smarkm state = TS_DO; 26629088Smarkm continue; 26729088Smarkm 26829088Smarkm case DONT: 26929088Smarkm state = TS_DONT; 27029088Smarkm continue; 27129088Smarkm case EOR: 27229088Smarkm if (his_state_is_will(TELOPT_EOR)) 27329088Smarkm doeof(); 27429088Smarkm break; 27529088Smarkm 27629088Smarkm /* 27729088Smarkm * Handle RFC 10xx Telnet linemode option additions 27829088Smarkm * to command stream (EOF, SUSP, ABORT). 27929088Smarkm */ 28029088Smarkm case xEOF: 28129088Smarkm doeof(); 28229088Smarkm break; 28329088Smarkm 28429088Smarkm case SUSP: 28529088Smarkm sendsusp(); 28629088Smarkm break; 28729088Smarkm 28829088Smarkm case ABORT: 28929088Smarkm sendbrk(); 29029088Smarkm break; 29129088Smarkm 29229088Smarkm case IAC: 29329088Smarkm *pfrontp++ = c; 29429088Smarkm break; 29529088Smarkm } 29629088Smarkm state = TS_DATA; 29729088Smarkm break; 29829088Smarkm 29929088Smarkm case TS_SB: 30029088Smarkm if (c == IAC) { 30129088Smarkm state = TS_SE; 30229088Smarkm } else { 30329088Smarkm SB_ACCUM(c); 30429088Smarkm } 30529088Smarkm break; 30629088Smarkm 30729088Smarkm case TS_SE: 30829088Smarkm if (c != SE) { 30929088Smarkm if (c != IAC) { 31029088Smarkm /* 31129088Smarkm * bad form of suboption negotiation. 31229088Smarkm * handle it in such a way as to avoid 31329088Smarkm * damage to local state. Parse 31429088Smarkm * suboption buffer found so far, 31529088Smarkm * then treat remaining stream as 31629088Smarkm * another command sequence. 31729088Smarkm */ 31829088Smarkm 31929088Smarkm /* for DIAGNOSTICS */ 32029088Smarkm SB_ACCUM(IAC); 32129088Smarkm SB_ACCUM(c); 32229088Smarkm subpointer -= 2; 32329088Smarkm 32429088Smarkm SB_TERM(); 32529088Smarkm suboption(); 32629088Smarkm state = TS_IAC; 32729088Smarkm goto gotiac; 32829088Smarkm } 32929088Smarkm SB_ACCUM(c); 33029088Smarkm state = TS_SB; 33129088Smarkm } else { 33229088Smarkm /* for DIAGNOSTICS */ 33329088Smarkm SB_ACCUM(IAC); 33429088Smarkm SB_ACCUM(SE); 33529088Smarkm subpointer -= 2; 33629088Smarkm 33729088Smarkm SB_TERM(); 33829088Smarkm suboption(); /* handle sub-option */ 33929088Smarkm state = TS_DATA; 34029088Smarkm } 34129088Smarkm break; 34229088Smarkm 34329088Smarkm case TS_WILL: 34429088Smarkm willoption(c); 34529088Smarkm state = TS_DATA; 34629088Smarkm continue; 34729088Smarkm 34829088Smarkm case TS_WONT: 34929088Smarkm wontoption(c); 35029088Smarkm state = TS_DATA; 35129088Smarkm continue; 35229088Smarkm 35329088Smarkm case TS_DO: 35429088Smarkm dooption(c); 35529088Smarkm state = TS_DATA; 35629088Smarkm continue; 35729088Smarkm 35829088Smarkm case TS_DONT: 35929088Smarkm dontoption(c); 36029088Smarkm state = TS_DATA; 36129088Smarkm continue; 36229088Smarkm 36329088Smarkm default: 36431622Scharnier syslog(LOG_ERR, "panic state=%d", state); 36529088Smarkm printf("telnetd: panic state=%d\n", state); 36629088Smarkm exit(1); 36729088Smarkm } 36829088Smarkm } 36929088Smarkm} /* end of telrcv */ 37029088Smarkm 37129088Smarkm/* 37229088Smarkm * The will/wont/do/dont state machines are based on Dave Borman's 37329088Smarkm * Telnet option processing state machine. 37429088Smarkm * 37529088Smarkm * These correspond to the following states: 37629088Smarkm * my_state = the last negotiated state 37729088Smarkm * want_state = what I want the state to go to 37829088Smarkm * want_resp = how many requests I have sent 37929088Smarkm * All state defaults are negative, and resp defaults to 0. 38029088Smarkm * 38129088Smarkm * When initiating a request to change state to new_state: 38229088Smarkm * 38329088Smarkm * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 38429088Smarkm * do nothing; 38529088Smarkm * } else { 38629088Smarkm * want_state = new_state; 38729088Smarkm * send new_state; 38829088Smarkm * want_resp++; 38929088Smarkm * } 39029088Smarkm * 39129088Smarkm * When receiving new_state: 39229088Smarkm * 39329088Smarkm * if (want_resp) { 39429088Smarkm * want_resp--; 39529088Smarkm * if (want_resp && (new_state == my_state)) 39629088Smarkm * want_resp--; 39729088Smarkm * } 39829088Smarkm * if ((want_resp == 0) && (new_state != want_state)) { 39929088Smarkm * if (ok_to_switch_to new_state) 40029088Smarkm * want_state = new_state; 40129088Smarkm * else 40229088Smarkm * want_resp++; 40329088Smarkm * send want_state; 40429088Smarkm * } 40529088Smarkm * my_state = new_state; 40629088Smarkm * 40729088Smarkm * Note that new_state is implied in these functions by the function itself. 40829088Smarkm * will and do imply positive new_state, wont and dont imply negative. 40929088Smarkm * 41029088Smarkm * Finally, there is one catch. If we send a negative response to a 41129088Smarkm * positive request, my_state will be the positive while want_state will 41229088Smarkm * remain negative. my_state will revert to negative when the negative 41329088Smarkm * acknowlegment arrives from the peer. Thus, my_state generally tells 41429088Smarkm * us not only the last negotiated state, but also tells us what the peer 41529088Smarkm * wants to be doing as well. It is important to understand this difference 41629088Smarkm * as we may wish to be processing data streams based on our desired state 41729088Smarkm * (want_state) or based on what the peer thinks the state is (my_state). 41829088Smarkm * 41929088Smarkm * This all works fine because if the peer sends a positive request, the data 42029088Smarkm * that we receive prior to negative acknowlegment will probably be affected 42129088Smarkm * by the positive state, and we can process it as such (if we can; if we 42229088Smarkm * can't then it really doesn't matter). If it is that important, then the 42329088Smarkm * peer probably should be buffering until this option state negotiation 42429088Smarkm * is complete. 42529088Smarkm * 42629088Smarkm */ 42787139Smarkmvoid 42887139Smarkmsend_do(int option, int init) 42929088Smarkm{ 43029088Smarkm if (init) { 43129088Smarkm if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || 43229088Smarkm his_want_state_is_will(option)) 43329088Smarkm return; 43429088Smarkm /* 43529088Smarkm * Special case for TELOPT_TM: We send a DO, but pretend 43629088Smarkm * that we sent a DONT, so that we can send more DOs if 43729088Smarkm * we want to. 43829088Smarkm */ 43929088Smarkm if (option == TELOPT_TM) 44029088Smarkm set_his_want_state_wont(option); 44129088Smarkm else 44229088Smarkm set_his_want_state_will(option); 44329088Smarkm do_dont_resp[option]++; 44429088Smarkm } 44579981Sru output_data((const char *)doopt, option); 44629088Smarkm 44729088Smarkm DIAG(TD_OPTIONS, printoption("td: send do", option)); 44829088Smarkm} 44929088Smarkm 45087139Smarkmvoid 45187139Smarkmwilloption(int option) 45229088Smarkm{ 45329088Smarkm int changeok = 0; 45487139Smarkm void (*func)(void) = 0; 45529088Smarkm 45629088Smarkm /* 45729088Smarkm * process input from peer. 45829088Smarkm */ 45929088Smarkm 46029088Smarkm DIAG(TD_OPTIONS, printoption("td: recv will", option)); 46129088Smarkm 46229088Smarkm if (do_dont_resp[option]) { 46329088Smarkm do_dont_resp[option]--; 46429088Smarkm if (do_dont_resp[option] && his_state_is_will(option)) 46529088Smarkm do_dont_resp[option]--; 46629088Smarkm } 46729088Smarkm if (do_dont_resp[option] == 0) { 46829088Smarkm if (his_want_state_is_wont(option)) { 46929088Smarkm switch (option) { 47029088Smarkm 47129088Smarkm case TELOPT_BINARY: 47229088Smarkm init_termbuf(); 47329088Smarkm tty_binaryin(1); 47429088Smarkm set_termbuf(); 47529088Smarkm changeok++; 47629088Smarkm break; 47729088Smarkm 47829088Smarkm case TELOPT_ECHO: 47929088Smarkm /* 48029088Smarkm * See comments below for more info. 48129088Smarkm */ 48229088Smarkm not42 = 0; /* looks like a 4.2 system */ 48329088Smarkm break; 48429088Smarkm 48529088Smarkm case TELOPT_TM: 48629088Smarkm#if defined(LINEMODE) && defined(KLUDGELINEMODE) 48729088Smarkm /* 48829088Smarkm * This telnetd implementation does not really 48929088Smarkm * support timing marks, it just uses them to 49029088Smarkm * support the kludge linemode stuff. If we 49129088Smarkm * receive a will or wont TM in response to our 49229088Smarkm * do TM request that may have been sent to 49329088Smarkm * determine kludge linemode support, process 49429088Smarkm * it, otherwise TM should get a negative 49529088Smarkm * response back. 49629088Smarkm */ 49729088Smarkm /* 49829088Smarkm * Handle the linemode kludge stuff. 49929088Smarkm * If we are not currently supporting any 50029088Smarkm * linemode at all, then we assume that this 50129088Smarkm * is the client telling us to use kludge 50229088Smarkm * linemode in response to our query. Set the 50329088Smarkm * linemode type that is to be supported, note 50429088Smarkm * that the client wishes to use linemode, and 50529088Smarkm * eat the will TM as though it never arrived. 50629088Smarkm */ 50729088Smarkm if (lmodetype < KLUDGE_LINEMODE) { 50829088Smarkm lmodetype = KLUDGE_LINEMODE; 50929088Smarkm clientstat(TELOPT_LINEMODE, WILL, 0); 51029088Smarkm send_wont(TELOPT_SGA, 1); 51129088Smarkm } else if (lmodetype == NO_AUTOKLUDGE) { 51229088Smarkm lmodetype = KLUDGE_OK; 51329088Smarkm } 51429088Smarkm#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 51529088Smarkm /* 51629088Smarkm * We never respond to a WILL TM, and 51729088Smarkm * we leave the state WONT. 51829088Smarkm */ 51929088Smarkm return; 52029088Smarkm 52129088Smarkm case TELOPT_LFLOW: 52229088Smarkm /* 52329088Smarkm * If we are going to support flow control 52429088Smarkm * option, then don't worry peer that we can't 52529088Smarkm * change the flow control characters. 52629088Smarkm */ 52729088Smarkm slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 52829088Smarkm slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 52929088Smarkm slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 53029088Smarkm slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 53129088Smarkm case TELOPT_TTYPE: 53229088Smarkm case TELOPT_SGA: 53329088Smarkm case TELOPT_NAWS: 53429088Smarkm case TELOPT_TSPEED: 53529088Smarkm case TELOPT_XDISPLOC: 53629088Smarkm case TELOPT_NEW_ENVIRON: 53729088Smarkm case TELOPT_OLD_ENVIRON: 53829088Smarkm changeok++; 53929088Smarkm break; 54029088Smarkm 54129088Smarkm#ifdef LINEMODE 54229088Smarkm case TELOPT_LINEMODE: 54329088Smarkm# ifdef KLUDGELINEMODE 54429088Smarkm /* 54529088Smarkm * Note client's desire to use linemode. 54629088Smarkm */ 54729088Smarkm lmodetype = REAL_LINEMODE; 54829088Smarkm# endif /* KLUDGELINEMODE */ 54929088Smarkm func = doclientstat; 55029088Smarkm changeok++; 55129088Smarkm break; 55229088Smarkm#endif /* LINEMODE */ 55329088Smarkm 55429088Smarkm#ifdef AUTHENTICATION 55529088Smarkm case TELOPT_AUTHENTICATION: 556180931Sjhb if (auth_level >= 0) { 557180931Sjhb func = auth_request; 558180931Sjhb changeok++; 559180931Sjhb } 56029088Smarkm break; 56129088Smarkm#endif 56229088Smarkm 56329088Smarkm#ifdef ENCRYPTION 56429088Smarkm case TELOPT_ENCRYPT: 56529088Smarkm func = encrypt_send_support; 56629088Smarkm changeok++; 56729088Smarkm break; 56829088Smarkm#endif /* ENCRYPTION */ 56929088Smarkm 57029088Smarkm default: 57129088Smarkm break; 57229088Smarkm } 57329088Smarkm if (changeok) { 57429088Smarkm set_his_want_state_will(option); 57529088Smarkm send_do(option, 0); 57629088Smarkm } else { 57729088Smarkm do_dont_resp[option]++; 57829088Smarkm send_dont(option, 0); 57929088Smarkm } 58029088Smarkm } else { 58129088Smarkm /* 58229088Smarkm * Option processing that should happen when 58329088Smarkm * we receive conformation of a change in 58429088Smarkm * state that we had requested. 58529088Smarkm */ 58629088Smarkm switch (option) { 58729088Smarkm case TELOPT_ECHO: 58829088Smarkm not42 = 0; /* looks like a 4.2 system */ 58929088Smarkm /* 59029088Smarkm * Egads, he responded "WILL ECHO". Turn 59129088Smarkm * it off right now! 59229088Smarkm */ 59329088Smarkm send_dont(option, 1); 59429088Smarkm /* 59529088Smarkm * "WILL ECHO". Kludge upon kludge! 59629088Smarkm * A 4.2 client is now echoing user input at 59729088Smarkm * the tty. This is probably undesireable and 59829088Smarkm * it should be stopped. The client will 59929088Smarkm * respond WONT TM to the DO TM that we send to 60029088Smarkm * check for kludge linemode. When the WONT TM 60129088Smarkm * arrives, linemode will be turned off and a 60229088Smarkm * change propogated to the pty. This change 60329088Smarkm * will cause us to process the new pty state 60429088Smarkm * in localstat(), which will notice that 60529088Smarkm * linemode is off and send a WILL ECHO 60629088Smarkm * so that we are properly in character mode and 60729088Smarkm * all is well. 60829088Smarkm */ 60929088Smarkm break; 61029088Smarkm#ifdef LINEMODE 61129088Smarkm case TELOPT_LINEMODE: 61229088Smarkm# ifdef KLUDGELINEMODE 61329088Smarkm /* 61429088Smarkm * Note client's desire to use linemode. 61529088Smarkm */ 61629088Smarkm lmodetype = REAL_LINEMODE; 61729088Smarkm# endif /* KLUDGELINEMODE */ 61829088Smarkm func = doclientstat; 61929088Smarkm break; 62029088Smarkm#endif /* LINEMODE */ 62129088Smarkm 62229088Smarkm#ifdef AUTHENTICATION 62329088Smarkm case TELOPT_AUTHENTICATION: 62429088Smarkm func = auth_request; 62529088Smarkm break; 62629088Smarkm#endif 62729088Smarkm 62829088Smarkm#ifdef ENCRYPTION 62929088Smarkm case TELOPT_ENCRYPT: 63029088Smarkm func = encrypt_send_support; 63129088Smarkm break; 63229088Smarkm#endif /* ENCRYPTION */ 63329088Smarkm case TELOPT_LFLOW: 63429088Smarkm func = flowstat; 63529088Smarkm break; 63629088Smarkm } 63729088Smarkm } 63829088Smarkm } 63929088Smarkm set_his_state_will(option); 64029088Smarkm if (func) 64129088Smarkm (*func)(); 64229088Smarkm} /* end of willoption */ 64329088Smarkm 64487139Smarkmvoid 64587139Smarkmsend_dont(int option, int init) 64629088Smarkm{ 64729088Smarkm if (init) { 64829088Smarkm if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 64929088Smarkm his_want_state_is_wont(option)) 65029088Smarkm return; 65129088Smarkm set_his_want_state_wont(option); 65229088Smarkm do_dont_resp[option]++; 65329088Smarkm } 65479981Sru output_data((const char *)dont, option); 65529088Smarkm 65629088Smarkm DIAG(TD_OPTIONS, printoption("td: send dont", option)); 65729088Smarkm} 65829088Smarkm 65987139Smarkmvoid 66087139Smarkmwontoption(int option) 66129088Smarkm{ 66229088Smarkm /* 66329088Smarkm * Process client input. 66429088Smarkm */ 66529088Smarkm 66629088Smarkm DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 66729088Smarkm 66829088Smarkm if (do_dont_resp[option]) { 66929088Smarkm do_dont_resp[option]--; 67029088Smarkm if (do_dont_resp[option] && his_state_is_wont(option)) 67129088Smarkm do_dont_resp[option]--; 67229088Smarkm } 67329088Smarkm if (do_dont_resp[option] == 0) { 67429088Smarkm if (his_want_state_is_will(option)) { 67529088Smarkm /* it is always ok to change to negative state */ 67629088Smarkm switch (option) { 67729088Smarkm case TELOPT_ECHO: 67829088Smarkm not42 = 1; /* doesn't seem to be a 4.2 system */ 67929088Smarkm break; 68029088Smarkm 68129088Smarkm case TELOPT_BINARY: 68229088Smarkm init_termbuf(); 68329088Smarkm tty_binaryin(0); 68429088Smarkm set_termbuf(); 68529088Smarkm break; 68629088Smarkm 68729088Smarkm#ifdef LINEMODE 68829088Smarkm case TELOPT_LINEMODE: 68929088Smarkm# ifdef KLUDGELINEMODE 69029088Smarkm /* 69129088Smarkm * If real linemode is supported, then client is 69229088Smarkm * asking to turn linemode off. 69329088Smarkm */ 69429088Smarkm if (lmodetype != REAL_LINEMODE) 69529088Smarkm break; 69681965Smarkm lmodetype = KLUDGE_LINEMODE; 69729088Smarkm# endif /* KLUDGELINEMODE */ 69829088Smarkm clientstat(TELOPT_LINEMODE, WONT, 0); 69929088Smarkm break; 70029088Smarkm#endif /* LINEMODE */ 70129088Smarkm 70229088Smarkm case TELOPT_TM: 70329088Smarkm /* 70429088Smarkm * If we get a WONT TM, and had sent a DO TM, 70529088Smarkm * don't respond with a DONT TM, just leave it 70629088Smarkm * as is. Short circut the state machine to 70729088Smarkm * achive this. 70829088Smarkm */ 70929088Smarkm set_his_want_state_wont(TELOPT_TM); 71029088Smarkm return; 71129088Smarkm 71229088Smarkm case TELOPT_LFLOW: 71329088Smarkm /* 71429088Smarkm * If we are not going to support flow control 71529088Smarkm * option, then let peer know that we can't 71629088Smarkm * change the flow control characters. 71729088Smarkm */ 71829088Smarkm slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 71929088Smarkm slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 72029088Smarkm slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 72129088Smarkm slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 72229088Smarkm break; 72329088Smarkm 72487139Smarkm#ifdef AUTHENTICATION 72529088Smarkm case TELOPT_AUTHENTICATION: 72629088Smarkm auth_finished(0, AUTH_REJECT); 72729088Smarkm break; 72829088Smarkm#endif 72929088Smarkm 73029088Smarkm /* 73129088Smarkm * For options that we might spin waiting for 73229088Smarkm * sub-negotiation, if the client turns off the 73329088Smarkm * option rather than responding to the request, 73429088Smarkm * we have to treat it here as if we got a response 73529088Smarkm * to the sub-negotiation, (by updating the timers) 73629088Smarkm * so that we'll break out of the loop. 73729088Smarkm */ 73829088Smarkm case TELOPT_TTYPE: 73929088Smarkm settimer(ttypesubopt); 74029088Smarkm break; 74129088Smarkm 74229088Smarkm case TELOPT_TSPEED: 74329088Smarkm settimer(tspeedsubopt); 74429088Smarkm break; 74529088Smarkm 74629088Smarkm case TELOPT_XDISPLOC: 74729088Smarkm settimer(xdisplocsubopt); 74829088Smarkm break; 74929088Smarkm 75029088Smarkm case TELOPT_OLD_ENVIRON: 75129088Smarkm settimer(oenvironsubopt); 75229088Smarkm break; 75329088Smarkm 75429088Smarkm case TELOPT_NEW_ENVIRON: 75529088Smarkm settimer(environsubopt); 75629088Smarkm break; 75729088Smarkm 75829088Smarkm default: 75929088Smarkm break; 76029088Smarkm } 76129088Smarkm set_his_want_state_wont(option); 76229088Smarkm if (his_state_is_will(option)) 76329088Smarkm send_dont(option, 0); 76429088Smarkm } else { 76529088Smarkm switch (option) { 76629088Smarkm case TELOPT_TM: 76729088Smarkm#if defined(LINEMODE) && defined(KLUDGELINEMODE) 76829088Smarkm if (lmodetype < NO_AUTOKLUDGE) { 76929088Smarkm lmodetype = NO_LINEMODE; 77029088Smarkm clientstat(TELOPT_LINEMODE, WONT, 0); 77129088Smarkm send_will(TELOPT_SGA, 1); 77229088Smarkm send_will(TELOPT_ECHO, 1); 77329088Smarkm } 77429088Smarkm#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 77529088Smarkm break; 77629088Smarkm 77787139Smarkm#ifdef AUTHENTICATION 77829088Smarkm case TELOPT_AUTHENTICATION: 77929088Smarkm auth_finished(0, AUTH_REJECT); 78029088Smarkm break; 78129088Smarkm#endif 78229088Smarkm default: 78329088Smarkm break; 78429088Smarkm } 78529088Smarkm } 78629088Smarkm } 78729088Smarkm set_his_state_wont(option); 78829088Smarkm 78929088Smarkm} /* end of wontoption */ 79029088Smarkm 79187139Smarkmvoid 79287139Smarkmsend_will(int option, int init) 79329088Smarkm{ 79429088Smarkm if (init) { 79529088Smarkm if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 79629088Smarkm my_want_state_is_will(option)) 79729088Smarkm return; 79829088Smarkm set_my_want_state_will(option); 79929088Smarkm will_wont_resp[option]++; 80029088Smarkm } 80179981Sru output_data((const char *)will, option); 80229088Smarkm 80329088Smarkm DIAG(TD_OPTIONS, printoption("td: send will", option)); 80429088Smarkm} 80529088Smarkm 80629088Smarkm#if !defined(LINEMODE) || !defined(KLUDGELINEMODE) 80729088Smarkm/* 80829088Smarkm * When we get a DONT SGA, we will try once to turn it 80929088Smarkm * back on. If the other side responds DONT SGA, we 81029088Smarkm * leave it at that. This is so that when we talk to 81129088Smarkm * clients that understand KLUDGELINEMODE but not LINEMODE, 81229088Smarkm * we'll keep them in char-at-a-time mode. 81329088Smarkm */ 81429088Smarkmint turn_on_sga = 0; 81529088Smarkm#endif 81629088Smarkm 81787139Smarkmvoid 81887139Smarkmdooption(int option) 81929088Smarkm{ 82029088Smarkm int changeok = 0; 82129088Smarkm 82229088Smarkm /* 82329088Smarkm * Process client input. 82429088Smarkm */ 82529088Smarkm 82629088Smarkm DIAG(TD_OPTIONS, printoption("td: recv do", option)); 82729088Smarkm 82829088Smarkm if (will_wont_resp[option]) { 82929088Smarkm will_wont_resp[option]--; 83029088Smarkm if (will_wont_resp[option] && my_state_is_will(option)) 83129088Smarkm will_wont_resp[option]--; 83229088Smarkm } 83329088Smarkm if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 83429088Smarkm switch (option) { 83529088Smarkm case TELOPT_ECHO: 83629088Smarkm#ifdef LINEMODE 83729088Smarkm# ifdef KLUDGELINEMODE 83829088Smarkm if (lmodetype == NO_LINEMODE) 83929088Smarkm# else 84029088Smarkm if (his_state_is_wont(TELOPT_LINEMODE)) 84129088Smarkm# endif 84229088Smarkm#endif 84329088Smarkm { 84429088Smarkm init_termbuf(); 84529088Smarkm tty_setecho(1); 84629088Smarkm set_termbuf(); 84729088Smarkm } 84829088Smarkm changeok++; 84929088Smarkm break; 85029088Smarkm 85129088Smarkm case TELOPT_BINARY: 85229088Smarkm init_termbuf(); 85329088Smarkm tty_binaryout(1); 85429088Smarkm set_termbuf(); 85529088Smarkm changeok++; 85629088Smarkm break; 85729088Smarkm 85829088Smarkm case TELOPT_SGA: 85929088Smarkm#if defined(LINEMODE) && defined(KLUDGELINEMODE) 86029088Smarkm /* 86129088Smarkm * If kludge linemode is in use, then we must 86229088Smarkm * process an incoming do SGA for linemode 86329088Smarkm * purposes. 86429088Smarkm */ 86529088Smarkm if (lmodetype == KLUDGE_LINEMODE) { 86629088Smarkm /* 86729088Smarkm * Receipt of "do SGA" in kludge 86829088Smarkm * linemode is the peer asking us to 86929088Smarkm * turn off linemode. Make note of 87029088Smarkm * the request. 87129088Smarkm */ 87229088Smarkm clientstat(TELOPT_LINEMODE, WONT, 0); 87329088Smarkm /* 87429088Smarkm * If linemode did not get turned off 87529088Smarkm * then don't tell peer that we did. 87629088Smarkm * Breaking here forces a wont SGA to 87729088Smarkm * be returned. 87829088Smarkm */ 87929088Smarkm if (linemode) 88029088Smarkm break; 88129088Smarkm } 88229088Smarkm#else 88329088Smarkm turn_on_sga = 0; 88429088Smarkm#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 88529088Smarkm changeok++; 88629088Smarkm break; 88729088Smarkm 88829088Smarkm case TELOPT_STATUS: 88929088Smarkm changeok++; 89029088Smarkm break; 89129088Smarkm 89229088Smarkm case TELOPT_TM: 89329088Smarkm /* 89429088Smarkm * Special case for TM. We send a WILL, but 89529088Smarkm * pretend we sent a WONT. 89629088Smarkm */ 89729088Smarkm send_will(option, 0); 89829088Smarkm set_my_want_state_wont(option); 89929088Smarkm set_my_state_wont(option); 90029088Smarkm return; 90129088Smarkm 90229088Smarkm case TELOPT_LOGOUT: 90329088Smarkm /* 90429088Smarkm * When we get a LOGOUT option, respond 90529088Smarkm * with a WILL LOGOUT, make sure that 90629088Smarkm * it gets written out to the network, 90729088Smarkm * and then just go away... 90829088Smarkm */ 90929088Smarkm set_my_want_state_will(TELOPT_LOGOUT); 91029088Smarkm send_will(TELOPT_LOGOUT, 0); 91129088Smarkm set_my_state_will(TELOPT_LOGOUT); 91229088Smarkm (void)netflush(); 91329088Smarkm cleanup(0); 91429088Smarkm /* NOT REACHED */ 91529088Smarkm break; 91629088Smarkm 91729088Smarkm#ifdef ENCRYPTION 91829088Smarkm case TELOPT_ENCRYPT: 91929088Smarkm changeok++; 92029088Smarkm break; 92129088Smarkm#endif /* ENCRYPTION */ 92229088Smarkm case TELOPT_LINEMODE: 92329088Smarkm case TELOPT_TTYPE: 92429088Smarkm case TELOPT_NAWS: 92529088Smarkm case TELOPT_TSPEED: 92629088Smarkm case TELOPT_LFLOW: 92729088Smarkm case TELOPT_XDISPLOC: 92829088Smarkm#ifdef TELOPT_ENVIRON 92929088Smarkm case TELOPT_NEW_ENVIRON: 93029088Smarkm#endif 93129088Smarkm case TELOPT_OLD_ENVIRON: 93229088Smarkm default: 93329088Smarkm break; 93429088Smarkm } 93529088Smarkm if (changeok) { 93629088Smarkm set_my_want_state_will(option); 93729088Smarkm send_will(option, 0); 93829088Smarkm } else { 93929088Smarkm will_wont_resp[option]++; 94029088Smarkm send_wont(option, 0); 94129088Smarkm } 94229088Smarkm } 94329088Smarkm set_my_state_will(option); 94429088Smarkm 94529088Smarkm} /* end of dooption */ 94629088Smarkm 94787139Smarkmvoid 94887139Smarkmsend_wont(int option, int init) 94929088Smarkm{ 95029088Smarkm if (init) { 95129088Smarkm if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 95229088Smarkm my_want_state_is_wont(option)) 95329088Smarkm return; 95429088Smarkm set_my_want_state_wont(option); 95529088Smarkm will_wont_resp[option]++; 95629088Smarkm } 95779981Sru output_data((const char *)wont, option); 95829088Smarkm 95929088Smarkm DIAG(TD_OPTIONS, printoption("td: send wont", option)); 96029088Smarkm} 96129088Smarkm 96287139Smarkmvoid 96387139Smarkmdontoption(int option) 96429088Smarkm{ 96529088Smarkm /* 96629088Smarkm * Process client input. 96729088Smarkm */ 96829088Smarkm 96929088Smarkm 97029088Smarkm DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 97129088Smarkm 97229088Smarkm if (will_wont_resp[option]) { 97329088Smarkm will_wont_resp[option]--; 97429088Smarkm if (will_wont_resp[option] && my_state_is_wont(option)) 97529088Smarkm will_wont_resp[option]--; 97629088Smarkm } 97729088Smarkm if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 97829088Smarkm switch (option) { 97929088Smarkm case TELOPT_BINARY: 98029088Smarkm init_termbuf(); 98129088Smarkm tty_binaryout(0); 98229088Smarkm set_termbuf(); 98329088Smarkm break; 98429088Smarkm 98529088Smarkm case TELOPT_ECHO: /* we should stop echoing */ 98629088Smarkm#ifdef LINEMODE 98729088Smarkm# ifdef KLUDGELINEMODE 98829088Smarkm if ((lmodetype != REAL_LINEMODE) && 98929088Smarkm (lmodetype != KLUDGE_LINEMODE)) 99029088Smarkm# else 99129088Smarkm if (his_state_is_wont(TELOPT_LINEMODE)) 99229088Smarkm# endif 99329088Smarkm#endif 99429088Smarkm { 99529088Smarkm init_termbuf(); 99629088Smarkm tty_setecho(0); 99729088Smarkm set_termbuf(); 99829088Smarkm } 99929088Smarkm break; 100029088Smarkm 100129088Smarkm case TELOPT_SGA: 100229088Smarkm#if defined(LINEMODE) && defined(KLUDGELINEMODE) 100329088Smarkm /* 100429088Smarkm * If kludge linemode is in use, then we 100529088Smarkm * must process an incoming do SGA for 100629088Smarkm * linemode purposes. 100729088Smarkm */ 100829088Smarkm if ((lmodetype == KLUDGE_LINEMODE) || 100929088Smarkm (lmodetype == KLUDGE_OK)) { 101029088Smarkm /* 101129088Smarkm * The client is asking us to turn 101229088Smarkm * linemode on. 101329088Smarkm */ 101429088Smarkm lmodetype = KLUDGE_LINEMODE; 101529088Smarkm clientstat(TELOPT_LINEMODE, WILL, 0); 101629088Smarkm /* 101729088Smarkm * If we did not turn line mode on, 101829088Smarkm * then what do we say? Will SGA? 101929088Smarkm * This violates design of telnet. 102029088Smarkm * Gross. Very Gross. 102129088Smarkm */ 102229088Smarkm } 102329088Smarkm break; 102429088Smarkm#else 102529088Smarkm set_my_want_state_wont(option); 102629088Smarkm if (my_state_is_will(option)) 102729088Smarkm send_wont(option, 0); 102829088Smarkm set_my_state_wont(option); 102929088Smarkm if (turn_on_sga ^= 1) 103029088Smarkm send_will(option, 1); 103129088Smarkm return; 103229088Smarkm#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 103329088Smarkm 103429088Smarkm default: 103529088Smarkm break; 103629088Smarkm } 103729088Smarkm 103829088Smarkm set_my_want_state_wont(option); 103929088Smarkm if (my_state_is_will(option)) 104029088Smarkm send_wont(option, 0); 104129088Smarkm } 104229088Smarkm set_my_state_wont(option); 104329088Smarkm 104429088Smarkm} /* end of dontoption */ 104529088Smarkm 104629088Smarkm#ifdef ENV_HACK 104729088Smarkmint env_ovar = -1; 104829088Smarkmint env_ovalue = -1; 104929088Smarkm#else /* ENV_HACK */ 105029088Smarkm# define env_ovar OLD_ENV_VAR 105129088Smarkm# define env_ovalue OLD_ENV_VALUE 105229088Smarkm#endif /* ENV_HACK */ 105329088Smarkm 105429088Smarkm/* 105529088Smarkm * suboption() 105629088Smarkm * 105729088Smarkm * Look at the sub-option buffer, and try to be helpful to the other 105829088Smarkm * side. 105929088Smarkm * 106029088Smarkm * Currently we recognize: 106129088Smarkm * 106229088Smarkm * Terminal type is 106329088Smarkm * Linemode 106429088Smarkm * Window size 106529088Smarkm * Terminal speed 106629088Smarkm */ 106787139Smarkmvoid 106887139Smarkmsuboption(void) 106929088Smarkm{ 107087139Smarkm int subchar; 107129088Smarkm 107229088Smarkm DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 107329088Smarkm 107429088Smarkm subchar = SB_GET(); 107529088Smarkm switch (subchar) { 107629088Smarkm case TELOPT_TSPEED: { 107787139Smarkm int xspeed, rspeed; 107829088Smarkm 107929088Smarkm if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 108029088Smarkm break; 108129088Smarkm 108229088Smarkm settimer(tspeedsubopt); 108329088Smarkm 108429088Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) 108529088Smarkm return; 108629088Smarkm 108729088Smarkm xspeed = atoi((char *)subpointer); 108829088Smarkm 108929088Smarkm while (SB_GET() != ',' && !SB_EOF()); 109029088Smarkm if (SB_EOF()) 109129088Smarkm return; 109229088Smarkm 109329088Smarkm rspeed = atoi((char *)subpointer); 109429088Smarkm clientstat(TELOPT_TSPEED, xspeed, rspeed); 109529088Smarkm 109629088Smarkm break; 109729088Smarkm 109829088Smarkm } /* end of case TELOPT_TSPEED */ 109929088Smarkm 110029088Smarkm case TELOPT_TTYPE: { /* Yaaaay! */ 110129088Smarkm static char terminalname[41]; 110229088Smarkm 110329088Smarkm if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 110429088Smarkm break; 110529088Smarkm settimer(ttypesubopt); 110629088Smarkm 110729088Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) { 110829088Smarkm return; /* ??? XXX but, this is the most robust */ 110929088Smarkm } 111029088Smarkm 111129088Smarkm terminaltype = terminalname; 111229088Smarkm 111329088Smarkm while ((terminaltype < (terminalname + sizeof terminalname-1)) && 111429088Smarkm !SB_EOF()) { 111587139Smarkm int c; 111629088Smarkm 111729088Smarkm c = SB_GET(); 111829088Smarkm if (isupper(c)) { 111929088Smarkm c = tolower(c); 112029088Smarkm } 112129088Smarkm *terminaltype++ = c; /* accumulate name */ 112229088Smarkm } 112329088Smarkm *terminaltype = 0; 112429088Smarkm terminaltype = terminalname; 112529088Smarkm break; 112629088Smarkm } /* end of case TELOPT_TTYPE */ 112729088Smarkm 112829088Smarkm case TELOPT_NAWS: { 112987139Smarkm int xwinsize, ywinsize; 113029088Smarkm 113129088Smarkm if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 113229088Smarkm break; 113329088Smarkm 113429088Smarkm if (SB_EOF()) 113529088Smarkm return; 113629088Smarkm xwinsize = SB_GET() << 8; 113729088Smarkm if (SB_EOF()) 113829088Smarkm return; 113929088Smarkm xwinsize |= SB_GET(); 114029088Smarkm if (SB_EOF()) 114129088Smarkm return; 114229088Smarkm ywinsize = SB_GET() << 8; 114329088Smarkm if (SB_EOF()) 114429088Smarkm return; 114529088Smarkm ywinsize |= SB_GET(); 114629088Smarkm clientstat(TELOPT_NAWS, xwinsize, ywinsize); 114729088Smarkm 114829088Smarkm break; 114929088Smarkm 115029088Smarkm } /* end of case TELOPT_NAWS */ 115129088Smarkm 115229088Smarkm#ifdef LINEMODE 115329088Smarkm case TELOPT_LINEMODE: { 115487139Smarkm int request; 115529088Smarkm 115629088Smarkm if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ 115729088Smarkm break; 115829088Smarkm /* 115929088Smarkm * Process linemode suboptions. 116029088Smarkm */ 116129088Smarkm if (SB_EOF()) 116229088Smarkm break; /* garbage was sent */ 116329088Smarkm request = SB_GET(); /* get will/wont */ 116429088Smarkm 116529088Smarkm if (SB_EOF()) 116629088Smarkm break; /* another garbage check */ 116729088Smarkm 116829088Smarkm if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 116929088Smarkm /* 117029088Smarkm * Process suboption buffer of slc's 117129088Smarkm */ 117229088Smarkm start_slc(1); 117329088Smarkm do_opt_slc(subpointer, subend - subpointer); 117429088Smarkm (void) end_slc(0); 117529088Smarkm break; 117629088Smarkm } else if (request == LM_MODE) { 117729088Smarkm if (SB_EOF()) 117829088Smarkm return; 117929088Smarkm useeditmode = SB_GET(); /* get mode flag */ 118029088Smarkm clientstat(LM_MODE, 0, 0); 118129088Smarkm break; 118229088Smarkm } 118329088Smarkm 118429088Smarkm if (SB_EOF()) 118529088Smarkm break; 118629088Smarkm switch (SB_GET()) { /* what suboption? */ 118729088Smarkm case LM_FORWARDMASK: 118829088Smarkm /* 118929088Smarkm * According to spec, only server can send request for 119029088Smarkm * forwardmask, and client can only return a positive response. 119129088Smarkm * So don't worry about it. 119229088Smarkm */ 119329088Smarkm 119429088Smarkm default: 119529088Smarkm break; 119629088Smarkm } 119729088Smarkm break; 119829088Smarkm } /* end of case TELOPT_LINEMODE */ 119929088Smarkm#endif 120029088Smarkm case TELOPT_STATUS: { 120129088Smarkm int mode; 120229088Smarkm 120329088Smarkm if (SB_EOF()) 120429088Smarkm break; 120529088Smarkm mode = SB_GET(); 120629088Smarkm switch (mode) { 120729088Smarkm case TELQUAL_SEND: 120829088Smarkm if (my_state_is_will(TELOPT_STATUS)) 120929088Smarkm send_status(); 121029088Smarkm break; 121129088Smarkm 121229088Smarkm case TELQUAL_IS: 121329088Smarkm break; 121429088Smarkm 121529088Smarkm default: 121629088Smarkm break; 121729088Smarkm } 121829088Smarkm break; 121929088Smarkm } /* end of case TELOPT_STATUS */ 122029088Smarkm 122129088Smarkm case TELOPT_XDISPLOC: { 122229088Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) 122329088Smarkm return; 122429088Smarkm settimer(xdisplocsubopt); 122529088Smarkm subpointer[SB_LEN()] = '\0'; 122629088Smarkm (void)setenv("DISPLAY", (char *)subpointer, 1); 122729088Smarkm break; 122829088Smarkm } /* end of case TELOPT_XDISPLOC */ 122929088Smarkm 123029088Smarkm#ifdef TELOPT_NEW_ENVIRON 123129088Smarkm case TELOPT_NEW_ENVIRON: 123229088Smarkm#endif 123329088Smarkm case TELOPT_OLD_ENVIRON: { 123487139Smarkm int c; 123587139Smarkm char *cp, *varp, *valp; 123629088Smarkm 123729088Smarkm if (SB_EOF()) 123829088Smarkm return; 123929088Smarkm c = SB_GET(); 124029088Smarkm if (c == TELQUAL_IS) { 124129088Smarkm if (subchar == TELOPT_OLD_ENVIRON) 124229088Smarkm settimer(oenvironsubopt); 124329088Smarkm else 124429088Smarkm settimer(environsubopt); 124529088Smarkm } else if (c != TELQUAL_INFO) { 124629088Smarkm return; 124729088Smarkm } 124829088Smarkm 124929088Smarkm#ifdef TELOPT_NEW_ENVIRON 125029088Smarkm if (subchar == TELOPT_NEW_ENVIRON) { 125129088Smarkm while (!SB_EOF()) { 125229088Smarkm c = SB_GET(); 125329088Smarkm if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) 125429088Smarkm break; 125529088Smarkm } 125629088Smarkm } else 125729088Smarkm#endif 125829088Smarkm { 125929088Smarkm#ifdef ENV_HACK 126029088Smarkm /* 126129088Smarkm * We only want to do this if we haven't already decided 126229088Smarkm * whether or not the other side has its VALUE and VAR 126329088Smarkm * reversed. 126429088Smarkm */ 126529088Smarkm if (env_ovar < 0) { 126687139Smarkm int last = -1; /* invalid value */ 126729088Smarkm int empty = 0; 126829088Smarkm int got_var = 0, got_value = 0, got_uservar = 0; 126929088Smarkm 127029088Smarkm /* 127129088Smarkm * The other side might have its VALUE and VAR values 127229088Smarkm * reversed. To be interoperable, we need to determine 127329088Smarkm * which way it is. If the first recognized character 127429088Smarkm * is a VAR or VALUE, then that will tell us what 127529088Smarkm * type of client it is. If the fist recognized 127629088Smarkm * character is a USERVAR, then we continue scanning 127729088Smarkm * the suboption looking for two consecutive 127829088Smarkm * VAR or VALUE fields. We should not get two 127929088Smarkm * consecutive VALUE fields, so finding two 128029088Smarkm * consecutive VALUE or VAR fields will tell us 128129088Smarkm * what the client is. 128229088Smarkm */ 128329088Smarkm SB_SAVE(); 128429088Smarkm while (!SB_EOF()) { 128529088Smarkm c = SB_GET(); 128629088Smarkm switch(c) { 128729088Smarkm case OLD_ENV_VAR: 128829088Smarkm if (last < 0 || last == OLD_ENV_VAR 128929088Smarkm || (empty && (last == OLD_ENV_VALUE))) 129029088Smarkm goto env_ovar_ok; 129129088Smarkm got_var++; 129229088Smarkm last = OLD_ENV_VAR; 129329088Smarkm break; 129429088Smarkm case OLD_ENV_VALUE: 129529088Smarkm if (last < 0 || last == OLD_ENV_VALUE 129629088Smarkm || (empty && (last == OLD_ENV_VAR))) 129729088Smarkm goto env_ovar_wrong; 129829088Smarkm got_value++; 129929088Smarkm last = OLD_ENV_VALUE; 130029088Smarkm break; 130129088Smarkm case ENV_USERVAR: 130229088Smarkm /* count strings of USERVAR as one */ 130329088Smarkm if (last != ENV_USERVAR) 130429088Smarkm got_uservar++; 130529088Smarkm if (empty) { 130629088Smarkm if (last == OLD_ENV_VALUE) 130729088Smarkm goto env_ovar_ok; 130829088Smarkm if (last == OLD_ENV_VAR) 130929088Smarkm goto env_ovar_wrong; 131029088Smarkm } 131129088Smarkm last = ENV_USERVAR; 131229088Smarkm break; 131329088Smarkm case ENV_ESC: 131429088Smarkm if (!SB_EOF()) 131529088Smarkm c = SB_GET(); 1316103956Smarkm /* FALLTHROUGH */ 131729088Smarkm default: 131829088Smarkm empty = 0; 131929088Smarkm continue; 132029088Smarkm } 132129088Smarkm empty = 1; 132229088Smarkm } 132329088Smarkm if (empty) { 132429088Smarkm if (last == OLD_ENV_VALUE) 132529088Smarkm goto env_ovar_ok; 132629088Smarkm if (last == OLD_ENV_VAR) 132729088Smarkm goto env_ovar_wrong; 132829088Smarkm } 132929088Smarkm /* 133029088Smarkm * Ok, the first thing was a USERVAR, and there 133129088Smarkm * are not two consecutive VAR or VALUE commands, 133229088Smarkm * and none of the VAR or VALUE commands are empty. 133329088Smarkm * If the client has sent us a well-formed option, 133429088Smarkm * then the number of VALUEs received should always 133529088Smarkm * be less than or equal to the number of VARs and 133629088Smarkm * USERVARs received. 133729088Smarkm * 133829088Smarkm * If we got exactly as many VALUEs as VARs and 133929088Smarkm * USERVARs, the client has the same definitions. 134029088Smarkm * 134129088Smarkm * If we got exactly as many VARs as VALUEs and 134229088Smarkm * USERVARS, the client has reversed definitions. 134329088Smarkm */ 134429088Smarkm if (got_uservar + got_var == got_value) { 134529088Smarkm env_ovar_ok: 134629088Smarkm env_ovar = OLD_ENV_VAR; 134729088Smarkm env_ovalue = OLD_ENV_VALUE; 134829088Smarkm } else if (got_uservar + got_value == got_var) { 134929088Smarkm env_ovar_wrong: 135029088Smarkm env_ovar = OLD_ENV_VALUE; 135129088Smarkm env_ovalue = OLD_ENV_VAR; 135279981Sru DIAG(TD_OPTIONS, 135379981Sru output_data("ENVIRON VALUE and VAR are reversed!\r\n")); 135429088Smarkm 135529088Smarkm } 135629088Smarkm } 135729088Smarkm SB_RESTORE(); 135829088Smarkm#endif 135929088Smarkm 136029088Smarkm while (!SB_EOF()) { 136129088Smarkm c = SB_GET(); 136229088Smarkm if ((c == env_ovar) || (c == ENV_USERVAR)) 136329088Smarkm break; 136429088Smarkm } 136529088Smarkm } 136629088Smarkm 136729088Smarkm if (SB_EOF()) 136829088Smarkm return; 136929088Smarkm 137029088Smarkm cp = varp = (char *)subpointer; 137129088Smarkm valp = 0; 137229088Smarkm 137329088Smarkm while (!SB_EOF()) { 137429088Smarkm c = SB_GET(); 137529088Smarkm if (subchar == TELOPT_OLD_ENVIRON) { 137629088Smarkm if (c == env_ovar) 137729088Smarkm c = NEW_ENV_VAR; 137829088Smarkm else if (c == env_ovalue) 137929088Smarkm c = NEW_ENV_VALUE; 138029088Smarkm } 138129088Smarkm switch (c) { 138229088Smarkm 138329088Smarkm case NEW_ENV_VALUE: 138429088Smarkm *cp = '\0'; 138529088Smarkm cp = valp = (char *)subpointer; 138629088Smarkm break; 138729088Smarkm 138829088Smarkm case NEW_ENV_VAR: 138929088Smarkm case ENV_USERVAR: 139029088Smarkm *cp = '\0'; 139129088Smarkm if (valp) 139229088Smarkm (void)setenv(varp, valp, 1); 139329088Smarkm else 139429088Smarkm unsetenv(varp); 139529088Smarkm cp = varp = (char *)subpointer; 139629088Smarkm valp = 0; 139729088Smarkm break; 139829088Smarkm 139929088Smarkm case ENV_ESC: 140029088Smarkm if (SB_EOF()) 140129088Smarkm break; 140229088Smarkm c = SB_GET(); 1403103956Smarkm /* FALLTHROUGH */ 140429088Smarkm default: 140529088Smarkm *cp++ = c; 140629088Smarkm break; 140729088Smarkm } 140829088Smarkm } 140929088Smarkm *cp = '\0'; 141029088Smarkm if (valp) 141129088Smarkm (void)setenv(varp, valp, 1); 141229088Smarkm else 141329088Smarkm unsetenv(varp); 141429088Smarkm break; 141529088Smarkm } /* end of case TELOPT_NEW_ENVIRON */ 141687139Smarkm#ifdef AUTHENTICATION 141729088Smarkm case TELOPT_AUTHENTICATION: 141829088Smarkm if (SB_EOF()) 141929088Smarkm break; 142029088Smarkm switch(SB_GET()) { 142129088Smarkm case TELQUAL_SEND: 142229088Smarkm case TELQUAL_REPLY: 142329088Smarkm /* 142429088Smarkm * These are sent by us and cannot be sent by 142529088Smarkm * the client. 142629088Smarkm */ 142729088Smarkm break; 142829088Smarkm case TELQUAL_IS: 142929088Smarkm auth_is(subpointer, SB_LEN()); 143029088Smarkm break; 143129088Smarkm case TELQUAL_NAME: 143229088Smarkm auth_name(subpointer, SB_LEN()); 143329088Smarkm break; 143429088Smarkm } 143529088Smarkm break; 143629088Smarkm#endif 143729088Smarkm#ifdef ENCRYPTION 143829088Smarkm case TELOPT_ENCRYPT: 143929088Smarkm if (SB_EOF()) 144029088Smarkm break; 144129088Smarkm switch(SB_GET()) { 144229088Smarkm case ENCRYPT_SUPPORT: 144329088Smarkm encrypt_support(subpointer, SB_LEN()); 144429088Smarkm break; 144529088Smarkm case ENCRYPT_IS: 144629088Smarkm encrypt_is(subpointer, SB_LEN()); 144729088Smarkm break; 144829088Smarkm case ENCRYPT_REPLY: 144929088Smarkm encrypt_reply(subpointer, SB_LEN()); 145029088Smarkm break; 145129088Smarkm case ENCRYPT_START: 145229088Smarkm encrypt_start(subpointer, SB_LEN()); 145329088Smarkm break; 145429088Smarkm case ENCRYPT_END: 145529088Smarkm encrypt_end(); 145629088Smarkm break; 145729088Smarkm case ENCRYPT_REQSTART: 145829088Smarkm encrypt_request_start(subpointer, SB_LEN()); 145929088Smarkm break; 146029088Smarkm case ENCRYPT_REQEND: 146129088Smarkm /* 146229088Smarkm * We can always send an REQEND so that we cannot 146329088Smarkm * get stuck encrypting. We should only get this 146429088Smarkm * if we have been able to get in the correct mode 146529088Smarkm * anyhow. 146629088Smarkm */ 146729088Smarkm encrypt_request_end(); 146829088Smarkm break; 146929088Smarkm case ENCRYPT_ENC_KEYID: 147029088Smarkm encrypt_enc_keyid(subpointer, SB_LEN()); 147129088Smarkm break; 147229088Smarkm case ENCRYPT_DEC_KEYID: 147329088Smarkm encrypt_dec_keyid(subpointer, SB_LEN()); 147429088Smarkm break; 147529088Smarkm default: 147629088Smarkm break; 147729088Smarkm } 147829088Smarkm break; 147929088Smarkm#endif /* ENCRYPTION */ 148029088Smarkm 148129088Smarkm default: 148229088Smarkm break; 148329088Smarkm } /* end of switch */ 148429088Smarkm 148529088Smarkm} /* end of suboption */ 148629088Smarkm 148787139Smarkmstatic void 148887139Smarkmdoclientstat(void) 148929088Smarkm{ 149029088Smarkm clientstat(TELOPT_LINEMODE, WILL, 0); 149129088Smarkm} 149229088Smarkm 149329088Smarkm#define ADD(c) *ncp++ = c 149429088Smarkm#define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; } 149587139Smarkmvoid 149687139Smarkmsend_status(void) 149729088Smarkm{ 149829088Smarkm unsigned char statusbuf[256]; 149987139Smarkm unsigned char *ncp; 150087139Smarkm unsigned char i; 150129088Smarkm 150229088Smarkm ncp = statusbuf; 150329088Smarkm 150429088Smarkm netflush(); /* get rid of anything waiting to go out */ 150529088Smarkm 150629088Smarkm ADD(IAC); 150729088Smarkm ADD(SB); 150829088Smarkm ADD(TELOPT_STATUS); 150929088Smarkm ADD(TELQUAL_IS); 151029088Smarkm 151129088Smarkm /* 151229088Smarkm * We check the want_state rather than the current state, 151329088Smarkm * because if we received a DO/WILL for an option that we 151429088Smarkm * don't support, and the other side didn't send a DONT/WONT 151529088Smarkm * in response to our WONT/DONT, then the "state" will be 151629088Smarkm * WILL/DO, and the "want_state" will be WONT/DONT. We 151729088Smarkm * need to go by the latter. 151829088Smarkm */ 151929088Smarkm for (i = 0; i < (unsigned char)NTELOPTS; i++) { 152029088Smarkm if (my_want_state_is_will(i)) { 152129088Smarkm ADD(WILL); 152229088Smarkm ADD_DATA(i); 152381965Smarkm if (i == IAC) 152481965Smarkm ADD(IAC); 152529088Smarkm } 152629088Smarkm if (his_want_state_is_will(i)) { 152729088Smarkm ADD(DO); 152829088Smarkm ADD_DATA(i); 152981965Smarkm if (i == IAC) 153081965Smarkm ADD(IAC); 153129088Smarkm } 153229088Smarkm } 153329088Smarkm 153429088Smarkm if (his_want_state_is_will(TELOPT_LFLOW)) { 153529088Smarkm ADD(SB); 153629088Smarkm ADD(TELOPT_LFLOW); 153729088Smarkm if (flowmode) { 153829088Smarkm ADD(LFLOW_ON); 153929088Smarkm } else { 154029088Smarkm ADD(LFLOW_OFF); 154129088Smarkm } 154229088Smarkm ADD(SE); 154329088Smarkm 154429088Smarkm if (restartany >= 0) { 154529088Smarkm ADD(SB); 154629088Smarkm ADD(TELOPT_LFLOW); 154729088Smarkm if (restartany) { 154829088Smarkm ADD(LFLOW_RESTART_ANY); 154929088Smarkm } else { 155029088Smarkm ADD(LFLOW_RESTART_XON); 155129088Smarkm } 155229088Smarkm ADD(SE); 155329088Smarkm } 155429088Smarkm } 155529088Smarkm 155629088Smarkm#ifdef LINEMODE 155729088Smarkm if (his_want_state_is_will(TELOPT_LINEMODE)) { 155829088Smarkm unsigned char *cp, *cpe; 155929088Smarkm int len; 156029088Smarkm 156129088Smarkm ADD(SB); 156229088Smarkm ADD(TELOPT_LINEMODE); 156329088Smarkm ADD(LM_MODE); 156429088Smarkm ADD_DATA(editmode); 156529088Smarkm ADD(SE); 156629088Smarkm 156729088Smarkm ADD(SB); 156829088Smarkm ADD(TELOPT_LINEMODE); 156929088Smarkm ADD(LM_SLC); 157029088Smarkm start_slc(0); 157129088Smarkm send_slc(); 157229088Smarkm len = end_slc(&cp); 157329088Smarkm for (cpe = cp + len; cp < cpe; cp++) 157429088Smarkm ADD_DATA(*cp); 157529088Smarkm ADD(SE); 157629088Smarkm } 157729088Smarkm#endif /* LINEMODE */ 157829088Smarkm 157929088Smarkm ADD(IAC); 158029088Smarkm ADD(SE); 158129088Smarkm 158280038Sru output_datalen(statusbuf, ncp - statusbuf); 158329088Smarkm netflush(); /* Send it on its way */ 158429088Smarkm 158529088Smarkm DIAG(TD_OPTIONS, 158629088Smarkm {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 158729088Smarkm} 158879981Sru 158979981Sru/* 159079981Sru * This function appends data to nfrontp and advances nfrontp. 159180224Skris * Returns the number of characters written altogether (the 159280224Skris * buffer may have been flushed in the process). 159379981Sru */ 159479981Sru 159579981Sruint 159679981Sruoutput_data(const char *format, ...) 159779981Sru{ 159879981Sru va_list args; 159980224Skris int len; 160080224Skris char *buf; 160179981Sru 160279981Sru va_start(args, format); 160380224Skris if ((len = vasprintf(&buf, format, args)) == -1) 160480224Skris return -1; 160580224Skris output_datalen(buf, len); 160679981Sru va_end(args); 160780224Skris free(buf); 160880224Skris return (len); 160979981Sru} 161079981Sru 161180224Skrisvoid 161280224Skrisoutput_datalen(const char *buf, int len) 161379981Sru{ 161480224Skris int remaining, copied; 161580224Skris 161680224Skris remaining = BUFSIZ - (nfrontp - netobuf); 161780224Skris while (len > 0) { 161880224Skris /* Free up enough space if the room is too low*/ 161980224Skris if ((len > BUFSIZ ? BUFSIZ : len) > remaining) { 162080224Skris netflush(); 162180224Skris remaining = BUFSIZ - (nfrontp - netobuf); 162280224Skris } 162379981Sru 162480224Skris /* Copy out as much as will fit */ 162580224Skris copied = remaining > len ? len : remaining; 162680224Skris memmove(nfrontp, buf, copied); 162780224Skris nfrontp += copied; 162880224Skris len -= copied; 162980224Skris remaining -= copied; 163080224Skris buf += copied; 163179981Sru } 163280224Skris return; 163379981Sru} 1634