157416Smarkm/* 257416Smarkm * Copyright (c) 1989, 1993 357416Smarkm * The Regents of the University of California. All rights reserved. 457416Smarkm * 557416Smarkm * Redistribution and use in source and binary forms, with or without 657416Smarkm * modification, are permitted provided that the following conditions 757416Smarkm * are met: 857416Smarkm * 1. Redistributions of source code must retain the above copyright 957416Smarkm * notice, this list of conditions and the following disclaimer. 1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1157416Smarkm * notice, this list of conditions and the following disclaimer in the 1257416Smarkm * documentation and/or other materials provided with the distribution. 1357416Smarkm * 3. All advertising materials mentioning features or use of this software 1457416Smarkm * must display the following acknowledgement: 1557416Smarkm * This product includes software developed by the University of 1657416Smarkm * California, Berkeley and its contributors. 1757416Smarkm * 4. Neither the name of the University nor the names of its contributors 1857416Smarkm * may be used to endorse or promote products derived from this software 1957416Smarkm * without specific prior written permission. 2057416Smarkm * 2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2457416Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3157416Smarkm * SUCH DAMAGE. 3257416Smarkm */ 3357416Smarkm 3457416Smarkm#include "telnetd.h" 3557416Smarkm 36233294SstasRCSID("$Id$"); 3757416Smarkm 3857416Smarkmunsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; 3957416Smarkmunsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; 4057416Smarkmunsigned char will[] = { IAC, WILL, '%', 'c', 0 }; 4157416Smarkmunsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; 4257416Smarkmint not42 = 1; 4357416Smarkm 4457416Smarkm/* 4557416Smarkm * Buffer for sub-options, and macros 4657416Smarkm * for suboptions buffer manipulations 4757416Smarkm */ 48142403Snectarunsigned char subbuffer[1024*64], *subpointer= subbuffer, *subend= subbuffer; 4957416Smarkm 5057416Smarkm#define SB_CLEAR() subpointer = subbuffer 5157416Smarkm#define SB_TERM() { subend = subpointer; SB_CLEAR(); } 5257416Smarkm#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 5357416Smarkm *subpointer++ = (c); \ 5457416Smarkm } 5557416Smarkm#define SB_GET() ((*subpointer++)&0xff) 5657416Smarkm#define SB_EOF() (subpointer >= subend) 5757416Smarkm#define SB_LEN() (subend - subpointer) 5857416Smarkm 5957416Smarkm#ifdef ENV_HACK 6057416Smarkmunsigned char *subsave; 6157416Smarkm#define SB_SAVE() subsave = subpointer; 6257416Smarkm#define SB_RESTORE() subpointer = subsave; 6357416Smarkm#endif 6457416Smarkm 6557416Smarkm 6657416Smarkm/* 6757416Smarkm * State for recv fsm 6857416Smarkm */ 6957416Smarkm#define TS_DATA 0 /* base state */ 7057416Smarkm#define TS_IAC 1 /* look for double IAC's */ 7157416Smarkm#define TS_CR 2 /* CR-LF ->'s CR */ 7257416Smarkm#define TS_SB 3 /* throw away begin's... */ 7357416Smarkm#define TS_SE 4 /* ...end's (suboption negotiation) */ 7457416Smarkm#define TS_WILL 5 /* will option negotiation */ 7557416Smarkm#define TS_WONT 6 /* wont -''- */ 7657416Smarkm#define TS_DO 7 /* do -''- */ 7757416Smarkm#define TS_DONT 8 /* dont -''- */ 7857416Smarkm 7957416Smarkmvoid 8057416Smarkmtelrcv(void) 8157416Smarkm{ 8257416Smarkm int c; 8357416Smarkm static int state = TS_DATA; 8457416Smarkm 8557416Smarkm while (ncc > 0) { 8657416Smarkm if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 8757416Smarkm break; 8857416Smarkm c = *netip++ & 0377, ncc--; 8957416Smarkm#ifdef ENCRYPTION 9057416Smarkm if (decrypt_input) 9157416Smarkm c = (*decrypt_input)(c); 9257416Smarkm#endif 9357416Smarkm switch (state) { 9457416Smarkm 9557416Smarkm case TS_CR: 9657416Smarkm state = TS_DATA; 9757416Smarkm /* Strip off \n or \0 after a \r */ 9857416Smarkm if ((c == 0) || (c == '\n')) { 9957416Smarkm break; 10057416Smarkm } 10157416Smarkm /* FALL THROUGH */ 10257416Smarkm 10357416Smarkm case TS_DATA: 10457416Smarkm if (c == IAC) { 10557416Smarkm state = TS_IAC; 10657416Smarkm break; 10757416Smarkm } 10857416Smarkm /* 10957416Smarkm * We now map \r\n ==> \r for pragmatic reasons. 11057416Smarkm * Many client implementations send \r\n when 11157416Smarkm * the user hits the CarriageReturn key. 11257416Smarkm * 11357416Smarkm * We USED to map \r\n ==> \n, since \r\n says 11457416Smarkm * that we want to be in column 1 of the next 11557416Smarkm * printable line, and \n is the standard 11657416Smarkm * unix way of saying that (\r is only good 11757416Smarkm * if CRMOD is set, which it normally is). 11857416Smarkm */ 11957416Smarkm if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { 12057416Smarkm int nc = *netip; 12157416Smarkm#ifdef ENCRYPTION 12257416Smarkm if (decrypt_input) 12357416Smarkm nc = (*decrypt_input)(nc & 0xff); 12457416Smarkm#endif 12557416Smarkm { 12657416Smarkm#ifdef ENCRYPTION 12757416Smarkm if (decrypt_input) 12857416Smarkm (void)(*decrypt_input)(-1); 12957416Smarkm#endif 13057416Smarkm state = TS_CR; 13157416Smarkm } 13257416Smarkm } 13357416Smarkm *pfrontp++ = c; 13457416Smarkm break; 13557416Smarkm 13657416Smarkm case TS_IAC: 13757416Smarkm gotiac: switch (c) { 13857416Smarkm 13957416Smarkm /* 14057416Smarkm * Send the process on the pty side an 14157416Smarkm * interrupt. Do this with a NULL or 14257416Smarkm * interrupt char; depending on the tty mode. 14357416Smarkm */ 14457416Smarkm case IP: 14557416Smarkm DIAG(TD_OPTIONS, 14657416Smarkm printoption("td: recv IAC", c)); 14757416Smarkm interrupt(); 14857416Smarkm break; 14957416Smarkm 15057416Smarkm case BREAK: 15157416Smarkm DIAG(TD_OPTIONS, 15257416Smarkm printoption("td: recv IAC", c)); 15357416Smarkm sendbrk(); 15457416Smarkm break; 15557416Smarkm 15657416Smarkm /* 15757416Smarkm * Are You There? 15857416Smarkm */ 15957416Smarkm case AYT: 16057416Smarkm DIAG(TD_OPTIONS, 16157416Smarkm printoption("td: recv IAC", c)); 16257416Smarkm recv_ayt(); 16357416Smarkm break; 16457416Smarkm 16557416Smarkm /* 16657416Smarkm * Abort Output 16757416Smarkm */ 16857416Smarkm case AO: 16957416Smarkm { 17057416Smarkm DIAG(TD_OPTIONS, 17157416Smarkm printoption("td: recv IAC", c)); 17257416Smarkm ptyflush(); /* half-hearted */ 17357416Smarkm init_termbuf(); 17457416Smarkm 17557416Smarkm if (slctab[SLC_AO].sptr && 17657416Smarkm *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { 17757416Smarkm *pfrontp++ = 17857416Smarkm (unsigned char)*slctab[SLC_AO].sptr; 17957416Smarkm } 18057416Smarkm 18157416Smarkm netclear(); /* clear buffer back */ 18257416Smarkm output_data ("%c%c", IAC, DM); 18357416Smarkm neturg = nfrontp-1; /* off by one XXX */ 18457416Smarkm DIAG(TD_OPTIONS, 18557416Smarkm printoption("td: send IAC", DM)); 18657416Smarkm break; 18757416Smarkm } 18857416Smarkm 18957416Smarkm /* 19057416Smarkm * Erase Character and 19157416Smarkm * Erase Line 19257416Smarkm */ 19357416Smarkm case EC: 19457416Smarkm case EL: 19557416Smarkm { 19657416Smarkm cc_t ch; 19757416Smarkm 19857416Smarkm DIAG(TD_OPTIONS, 19957416Smarkm printoption("td: recv IAC", c)); 20057416Smarkm ptyflush(); /* half-hearted */ 20157416Smarkm init_termbuf(); 20257416Smarkm if (c == EC) 20357416Smarkm ch = *slctab[SLC_EC].sptr; 20457416Smarkm else 20557416Smarkm ch = *slctab[SLC_EL].sptr; 20657416Smarkm if (ch != (cc_t)(_POSIX_VDISABLE)) 20757416Smarkm *pfrontp++ = (unsigned char)ch; 20857416Smarkm break; 20957416Smarkm } 21057416Smarkm 21157416Smarkm /* 21257416Smarkm * Check for urgent data... 21357416Smarkm */ 21457416Smarkm case DM: 21557416Smarkm DIAG(TD_OPTIONS, 21657416Smarkm printoption("td: recv IAC", c)); 21757416Smarkm SYNCHing = stilloob(net); 21857416Smarkm settimer(gotDM); 21957416Smarkm break; 22057416Smarkm 22157416Smarkm 22257416Smarkm /* 22357416Smarkm * Begin option subnegotiation... 22457416Smarkm */ 22557416Smarkm case SB: 22657416Smarkm state = TS_SB; 22757416Smarkm SB_CLEAR(); 22857416Smarkm continue; 22957416Smarkm 23057416Smarkm case WILL: 23157416Smarkm state = TS_WILL; 23257416Smarkm continue; 23357416Smarkm 23457416Smarkm case WONT: 23557416Smarkm state = TS_WONT; 23657416Smarkm continue; 23757416Smarkm 23857416Smarkm case DO: 23957416Smarkm state = TS_DO; 24057416Smarkm continue; 24157416Smarkm 24257416Smarkm case DONT: 24357416Smarkm state = TS_DONT; 24457416Smarkm continue; 24557416Smarkm case EOR: 24657416Smarkm if (his_state_is_will(TELOPT_EOR)) 24757416Smarkm doeof(); 24857416Smarkm break; 24957416Smarkm 25057416Smarkm /* 25157416Smarkm * Handle RFC 10xx Telnet linemode option additions 25257416Smarkm * to command stream (EOF, SUSP, ABORT). 25357416Smarkm */ 25457416Smarkm case xEOF: 25557416Smarkm doeof(); 25657416Smarkm break; 25757416Smarkm 25857416Smarkm case SUSP: 25957416Smarkm sendsusp(); 26057416Smarkm break; 26157416Smarkm 26257416Smarkm case ABORT: 26357416Smarkm sendbrk(); 26457416Smarkm break; 26557416Smarkm 26657416Smarkm case IAC: 26757416Smarkm *pfrontp++ = c; 26857416Smarkm break; 26957416Smarkm } 27057416Smarkm state = TS_DATA; 27157416Smarkm break; 27257416Smarkm 27357416Smarkm case TS_SB: 27457416Smarkm if (c == IAC) { 27557416Smarkm state = TS_SE; 27657416Smarkm } else { 27757416Smarkm SB_ACCUM(c); 27857416Smarkm } 27957416Smarkm break; 28057416Smarkm 28157416Smarkm case TS_SE: 28257416Smarkm if (c != SE) { 28357416Smarkm if (c != IAC) { 28457416Smarkm /* 28557416Smarkm * bad form of suboption negotiation. 28657416Smarkm * handle it in such a way as to avoid 28757416Smarkm * damage to local state. Parse 28857416Smarkm * suboption buffer found so far, 28957416Smarkm * then treat remaining stream as 29057416Smarkm * another command sequence. 29157416Smarkm */ 29257416Smarkm 29357416Smarkm /* for DIAGNOSTICS */ 29457416Smarkm SB_ACCUM(IAC); 29557416Smarkm SB_ACCUM(c); 29657416Smarkm subpointer -= 2; 29757416Smarkm 29857416Smarkm SB_TERM(); 29957416Smarkm suboption(); 30057416Smarkm state = TS_IAC; 30157416Smarkm goto gotiac; 30257416Smarkm } 30357416Smarkm SB_ACCUM(c); 30457416Smarkm state = TS_SB; 30557416Smarkm } else { 30657416Smarkm /* for DIAGNOSTICS */ 30757416Smarkm SB_ACCUM(IAC); 30857416Smarkm SB_ACCUM(SE); 30957416Smarkm subpointer -= 2; 31057416Smarkm 31157416Smarkm SB_TERM(); 31257416Smarkm suboption(); /* handle sub-option */ 31357416Smarkm state = TS_DATA; 31457416Smarkm } 31557416Smarkm break; 31657416Smarkm 31757416Smarkm case TS_WILL: 31857416Smarkm willoption(c); 31957416Smarkm state = TS_DATA; 32057416Smarkm continue; 32157416Smarkm 32257416Smarkm case TS_WONT: 32357416Smarkm wontoption(c); 32457416Smarkm if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) ) 32557416Smarkm dontoption(c); 32657416Smarkm state = TS_DATA; 32757416Smarkm continue; 32857416Smarkm 32957416Smarkm case TS_DO: 33057416Smarkm dooption(c); 33157416Smarkm state = TS_DATA; 33257416Smarkm continue; 33357416Smarkm 33457416Smarkm case TS_DONT: 33557416Smarkm dontoption(c); 33657416Smarkm state = TS_DATA; 33757416Smarkm continue; 33857416Smarkm 33957416Smarkm default: 34057416Smarkm syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 34157416Smarkm printf("telnetd: panic state=%d\n", state); 34257416Smarkm exit(1); 34357416Smarkm } 34457416Smarkm } 34557416Smarkm} /* end of telrcv */ 34657416Smarkm 34757416Smarkm/* 34857416Smarkm * The will/wont/do/dont state machines are based on Dave Borman's 34957416Smarkm * Telnet option processing state machine. 35057416Smarkm * 35157416Smarkm * These correspond to the following states: 35257416Smarkm * my_state = the last negotiated state 35357416Smarkm * want_state = what I want the state to go to 35457416Smarkm * want_resp = how many requests I have sent 35557416Smarkm * All state defaults are negative, and resp defaults to 0. 35657416Smarkm * 35757416Smarkm * When initiating a request to change state to new_state: 35857416Smarkm * 35957416Smarkm * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 36057416Smarkm * do nothing; 36157416Smarkm * } else { 36257416Smarkm * want_state = new_state; 36357416Smarkm * send new_state; 36457416Smarkm * want_resp++; 36557416Smarkm * } 36657416Smarkm * 36757416Smarkm * When receiving new_state: 36857416Smarkm * 36957416Smarkm * if (want_resp) { 37057416Smarkm * want_resp--; 37157416Smarkm * if (want_resp && (new_state == my_state)) 37257416Smarkm * want_resp--; 37357416Smarkm * } 37457416Smarkm * if ((want_resp == 0) && (new_state != want_state)) { 37557416Smarkm * if (ok_to_switch_to new_state) 37657416Smarkm * want_state = new_state; 37757416Smarkm * else 37857416Smarkm * want_resp++; 37957416Smarkm * send want_state; 38057416Smarkm * } 38157416Smarkm * my_state = new_state; 38257416Smarkm * 38357416Smarkm * Note that new_state is implied in these functions by the function itself. 38457416Smarkm * will and do imply positive new_state, wont and dont imply negative. 38557416Smarkm * 38657416Smarkm * Finally, there is one catch. If we send a negative response to a 38757416Smarkm * positive request, my_state will be the positive while want_state will 38857416Smarkm * remain negative. my_state will revert to negative when the negative 38957416Smarkm * acknowlegment arrives from the peer. Thus, my_state generally tells 39057416Smarkm * us not only the last negotiated state, but also tells us what the peer 39157416Smarkm * wants to be doing as well. It is important to understand this difference 39257416Smarkm * as we may wish to be processing data streams based on our desired state 39357416Smarkm * (want_state) or based on what the peer thinks the state is (my_state). 39457416Smarkm * 39557416Smarkm * This all works fine because if the peer sends a positive request, the data 39657416Smarkm * that we receive prior to negative acknowlegment will probably be affected 39757416Smarkm * by the positive state, and we can process it as such (if we can; if we 39857416Smarkm * can't then it really doesn't matter). If it is that important, then the 39957416Smarkm * peer probably should be buffering until this option state negotiation 40057416Smarkm * is complete. 40157416Smarkm * 40257416Smarkm */ 40357416Smarkmvoid 40457416Smarkmsend_do(int option, int init) 40557416Smarkm{ 40657416Smarkm if (init) { 40757416Smarkm if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || 40857416Smarkm his_want_state_is_will(option)) 40957416Smarkm return; 41057416Smarkm /* 41157416Smarkm * Special case for TELOPT_TM: We send a DO, but pretend 41257416Smarkm * that we sent a DONT, so that we can send more DOs if 41357416Smarkm * we want to. 41457416Smarkm */ 41557416Smarkm if (option == TELOPT_TM) 41657416Smarkm set_his_want_state_wont(option); 41757416Smarkm else 41857416Smarkm set_his_want_state_will(option); 41957416Smarkm do_dont_resp[option]++; 42057416Smarkm } 42157416Smarkm output_data((const char *)doopt, option); 42257416Smarkm 42357416Smarkm DIAG(TD_OPTIONS, printoption("td: send do", option)); 42457416Smarkm} 42557416Smarkm 42657416Smarkm#ifdef AUTHENTICATION 42757416Smarkmextern void auth_request(void); 42857416Smarkm#endif 42957416Smarkm#ifdef ENCRYPTION 430178825Sdfrextern void encrypt_send_support(void); 43157416Smarkm#endif 43257416Smarkm 43357416Smarkmvoid 43457416Smarkmwilloption(int option) 43557416Smarkm{ 43657416Smarkm int changeok = 0; 437178825Sdfr void (*func)(void) = NULL; 43857416Smarkm 43957416Smarkm /* 44057416Smarkm * process input from peer. 44157416Smarkm */ 44257416Smarkm 44357416Smarkm DIAG(TD_OPTIONS, printoption("td: recv will", option)); 44457416Smarkm 44557416Smarkm if (do_dont_resp[option]) { 44657416Smarkm do_dont_resp[option]--; 44757416Smarkm if (do_dont_resp[option] && his_state_is_will(option)) 44857416Smarkm do_dont_resp[option]--; 44957416Smarkm } 45057416Smarkm if (do_dont_resp[option] == 0) { 45157416Smarkm if (his_want_state_is_wont(option)) { 45257416Smarkm switch (option) { 45357416Smarkm 45457416Smarkm case TELOPT_BINARY: 45557416Smarkm init_termbuf(); 45657416Smarkm tty_binaryin(1); 45757416Smarkm set_termbuf(); 45857416Smarkm changeok++; 45957416Smarkm break; 46057416Smarkm 46157416Smarkm case TELOPT_ECHO: 46257416Smarkm /* 46357416Smarkm * See comments below for more info. 46457416Smarkm */ 46557416Smarkm not42 = 0; /* looks like a 4.2 system */ 46657416Smarkm break; 46757416Smarkm 46857416Smarkm case TELOPT_TM: 46957416Smarkm /* 47057416Smarkm * We never respond to a WILL TM, and 47157416Smarkm * we leave the state WONT. 47257416Smarkm */ 47357416Smarkm return; 47457416Smarkm 47557416Smarkm case TELOPT_LFLOW: 47657416Smarkm /* 47757416Smarkm * If we are going to support flow control 47857416Smarkm * option, then don't worry peer that we can't 47957416Smarkm * change the flow control characters. 48057416Smarkm */ 48157416Smarkm slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 48257416Smarkm slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 48357416Smarkm slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 48457416Smarkm slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 48557416Smarkm case TELOPT_TTYPE: 48657416Smarkm case TELOPT_SGA: 48757416Smarkm case TELOPT_NAWS: 48857416Smarkm case TELOPT_TSPEED: 48957416Smarkm case TELOPT_XDISPLOC: 49057416Smarkm case TELOPT_NEW_ENVIRON: 49157416Smarkm case TELOPT_OLD_ENVIRON: 49257416Smarkm changeok++; 49357416Smarkm break; 49457416Smarkm 49557416Smarkm 49657416Smarkm#ifdef AUTHENTICATION 49757416Smarkm case TELOPT_AUTHENTICATION: 49857416Smarkm func = auth_request; 49957416Smarkm changeok++; 50057416Smarkm break; 50157416Smarkm#endif 50257416Smarkm 50357416Smarkm#ifdef ENCRYPTION 50457416Smarkm case TELOPT_ENCRYPT: 50557416Smarkm func = encrypt_send_support; 50657416Smarkm changeok++; 50757416Smarkm break; 50857416Smarkm#endif 509233294Sstas 51057416Smarkm default: 51157416Smarkm break; 51257416Smarkm } 51357416Smarkm if (changeok) { 51457416Smarkm set_his_want_state_will(option); 51557416Smarkm send_do(option, 0); 51657416Smarkm } else { 51757416Smarkm do_dont_resp[option]++; 51857416Smarkm send_dont(option, 0); 51957416Smarkm } 52057416Smarkm } else { 52157416Smarkm /* 52257416Smarkm * Option processing that should happen when 52357416Smarkm * we receive conformation of a change in 52457416Smarkm * state that we had requested. 52557416Smarkm */ 52657416Smarkm switch (option) { 52757416Smarkm case TELOPT_ECHO: 52857416Smarkm not42 = 0; /* looks like a 4.2 system */ 52957416Smarkm /* 53057416Smarkm * Egads, he responded "WILL ECHO". Turn 53157416Smarkm * it off right now! 53257416Smarkm */ 53357416Smarkm send_dont(option, 1); 53457416Smarkm /* 53557416Smarkm * "WILL ECHO". Kludge upon kludge! 53657416Smarkm * A 4.2 client is now echoing user input at 53757416Smarkm * the tty. This is probably undesireable and 53857416Smarkm * it should be stopped. The client will 53957416Smarkm * respond WONT TM to the DO TM that we send to 54057416Smarkm * check for kludge linemode. When the WONT TM 54157416Smarkm * arrives, linemode will be turned off and a 54257416Smarkm * change propogated to the pty. This change 54357416Smarkm * will cause us to process the new pty state 54457416Smarkm * in localstat(), which will notice that 54557416Smarkm * linemode is off and send a WILL ECHO 54657416Smarkm * so that we are properly in character mode and 54757416Smarkm * all is well. 54857416Smarkm */ 54957416Smarkm break; 55057416Smarkm 55157416Smarkm#ifdef AUTHENTICATION 55257416Smarkm case TELOPT_AUTHENTICATION: 55357416Smarkm func = auth_request; 55457416Smarkm break; 55557416Smarkm#endif 55657416Smarkm 55757416Smarkm#ifdef ENCRYPTION 55857416Smarkm case TELOPT_ENCRYPT: 55957416Smarkm func = encrypt_send_support; 56057416Smarkm break; 56157416Smarkm#endif 56257416Smarkm 56357416Smarkm case TELOPT_LFLOW: 56457416Smarkm func = flowstat; 56557416Smarkm break; 56657416Smarkm } 56757416Smarkm } 56857416Smarkm } 56957416Smarkm set_his_state_will(option); 57057416Smarkm if (func) 57157416Smarkm (*func)(); 57257416Smarkm} /* end of willoption */ 57357416Smarkm 57457416Smarkmvoid 57557416Smarkmsend_dont(int option, int init) 57657416Smarkm{ 57757416Smarkm if (init) { 57857416Smarkm if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 57957416Smarkm his_want_state_is_wont(option)) 58057416Smarkm return; 58157416Smarkm set_his_want_state_wont(option); 58257416Smarkm do_dont_resp[option]++; 58357416Smarkm } 58457416Smarkm output_data((const char *)dont, option); 58557416Smarkm 58657416Smarkm DIAG(TD_OPTIONS, printoption("td: send dont", option)); 58757416Smarkm} 58857416Smarkm 58957416Smarkmvoid 59057416Smarkmwontoption(int option) 59157416Smarkm{ 59257416Smarkm /* 59357416Smarkm * Process client input. 59457416Smarkm */ 59557416Smarkm 59657416Smarkm DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 59757416Smarkm 59857416Smarkm if (do_dont_resp[option]) { 59957416Smarkm do_dont_resp[option]--; 60057416Smarkm if (do_dont_resp[option] && his_state_is_wont(option)) 60157416Smarkm do_dont_resp[option]--; 60257416Smarkm } 60357416Smarkm if (do_dont_resp[option] == 0) { 60457416Smarkm if (his_want_state_is_will(option)) { 60557416Smarkm /* it is always ok to change to negative state */ 60657416Smarkm switch (option) { 60757416Smarkm case TELOPT_ECHO: 60857416Smarkm not42 = 1; /* doesn't seem to be a 4.2 system */ 60957416Smarkm break; 61057416Smarkm 61157416Smarkm case TELOPT_BINARY: 61257416Smarkm init_termbuf(); 61357416Smarkm tty_binaryin(0); 61457416Smarkm set_termbuf(); 61557416Smarkm break; 61657416Smarkm 61757416Smarkm case TELOPT_TM: 61857416Smarkm /* 61957416Smarkm * If we get a WONT TM, and had sent a DO TM, 62057416Smarkm * don't respond with a DONT TM, just leave it 62157416Smarkm * as is. Short circut the state machine to 62257416Smarkm * achive this. 62357416Smarkm */ 62457416Smarkm set_his_want_state_wont(TELOPT_TM); 62557416Smarkm return; 62657416Smarkm 62757416Smarkm case TELOPT_LFLOW: 62857416Smarkm /* 62957416Smarkm * If we are not going to support flow control 63057416Smarkm * option, then let peer know that we can't 63157416Smarkm * change the flow control characters. 63257416Smarkm */ 63357416Smarkm slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 63457416Smarkm slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 63557416Smarkm slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 63657416Smarkm slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 63757416Smarkm break; 63857416Smarkm 63957416Smarkm#ifdef AUTHENTICATION 64057416Smarkm case TELOPT_AUTHENTICATION: 64157416Smarkm auth_finished(0, AUTH_REJECT); 64257416Smarkm break; 64357416Smarkm#endif 64457416Smarkm 64557416Smarkm /* 64657416Smarkm * For options that we might spin waiting for 64757416Smarkm * sub-negotiation, if the client turns off the 64857416Smarkm * option rather than responding to the request, 64957416Smarkm * we have to treat it here as if we got a response 65057416Smarkm * to the sub-negotiation, (by updating the timers) 65157416Smarkm * so that we'll break out of the loop. 65257416Smarkm */ 65357416Smarkm case TELOPT_TTYPE: 65457416Smarkm settimer(ttypesubopt); 65557416Smarkm break; 65657416Smarkm 65757416Smarkm case TELOPT_TSPEED: 65857416Smarkm settimer(tspeedsubopt); 65957416Smarkm break; 66057416Smarkm 66157416Smarkm case TELOPT_XDISPLOC: 66257416Smarkm settimer(xdisplocsubopt); 66357416Smarkm break; 66457416Smarkm 66557416Smarkm case TELOPT_OLD_ENVIRON: 66657416Smarkm settimer(oenvironsubopt); 66757416Smarkm break; 66857416Smarkm 66957416Smarkm case TELOPT_NEW_ENVIRON: 67057416Smarkm settimer(environsubopt); 67157416Smarkm break; 67257416Smarkm 67357416Smarkm default: 67457416Smarkm break; 67557416Smarkm } 67657416Smarkm set_his_want_state_wont(option); 67757416Smarkm if (his_state_is_will(option)) 67857416Smarkm send_dont(option, 0); 67957416Smarkm } else { 68057416Smarkm switch (option) { 68157416Smarkm case TELOPT_TM: 68257416Smarkm break; 68357416Smarkm 68457416Smarkm#ifdef AUTHENTICATION 68557416Smarkm case TELOPT_AUTHENTICATION: 68657416Smarkm auth_finished(0, AUTH_REJECT); 68757416Smarkm break; 68857416Smarkm#endif 68957416Smarkm default: 69057416Smarkm break; 69157416Smarkm } 69257416Smarkm } 69357416Smarkm } 69457416Smarkm set_his_state_wont(option); 69557416Smarkm 69657416Smarkm} /* end of wontoption */ 69757416Smarkm 69857416Smarkmvoid 69957416Smarkmsend_will(int option, int init) 70057416Smarkm{ 70157416Smarkm if (init) { 70257416Smarkm if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 70357416Smarkm my_want_state_is_will(option)) 70457416Smarkm return; 70557416Smarkm set_my_want_state_will(option); 70657416Smarkm will_wont_resp[option]++; 70757416Smarkm } 70857416Smarkm output_data ((const char *)will, option); 70957416Smarkm 71057416Smarkm DIAG(TD_OPTIONS, printoption("td: send will", option)); 71157416Smarkm} 71257416Smarkm 71357416Smarkm/* 71457416Smarkm * When we get a DONT SGA, we will try once to turn it 71557416Smarkm * back on. If the other side responds DONT SGA, we 71657416Smarkm * leave it at that. This is so that when we talk to 71757416Smarkm * clients that understand KLUDGELINEMODE but not LINEMODE, 71857416Smarkm * we'll keep them in char-at-a-time mode. 71957416Smarkm */ 72057416Smarkmint turn_on_sga = 0; 72157416Smarkm 72257416Smarkmvoid 72357416Smarkmdooption(int option) 72457416Smarkm{ 72557416Smarkm int changeok = 0; 72657416Smarkm 72757416Smarkm /* 72857416Smarkm * Process client input. 72957416Smarkm */ 73057416Smarkm 73157416Smarkm DIAG(TD_OPTIONS, printoption("td: recv do", option)); 73257416Smarkm 73357416Smarkm if (will_wont_resp[option]) { 73457416Smarkm will_wont_resp[option]--; 73557416Smarkm if (will_wont_resp[option] && my_state_is_will(option)) 73657416Smarkm will_wont_resp[option]--; 73757416Smarkm } 73857416Smarkm if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 73957416Smarkm switch (option) { 74057416Smarkm case TELOPT_ECHO: 74157416Smarkm { 74257416Smarkm init_termbuf(); 74357416Smarkm tty_setecho(1); 74457416Smarkm set_termbuf(); 74557416Smarkm } 74657416Smarkm changeok++; 74757416Smarkm break; 74857416Smarkm 74957416Smarkm case TELOPT_BINARY: 75057416Smarkm init_termbuf(); 75157416Smarkm tty_binaryout(1); 75257416Smarkm set_termbuf(); 75357416Smarkm changeok++; 75457416Smarkm break; 75557416Smarkm 75657416Smarkm case TELOPT_SGA: 75757416Smarkm turn_on_sga = 0; 75857416Smarkm changeok++; 75957416Smarkm break; 76057416Smarkm 76157416Smarkm case TELOPT_STATUS: 76257416Smarkm changeok++; 76357416Smarkm break; 76457416Smarkm 76557416Smarkm case TELOPT_TM: 76657416Smarkm /* 76757416Smarkm * Special case for TM. We send a WILL, but 76857416Smarkm * pretend we sent a WONT. 76957416Smarkm */ 77057416Smarkm send_will(option, 0); 77157416Smarkm set_my_want_state_wont(option); 77257416Smarkm set_my_state_wont(option); 77357416Smarkm return; 77457416Smarkm 77557416Smarkm case TELOPT_LOGOUT: 77657416Smarkm /* 77757416Smarkm * When we get a LOGOUT option, respond 77857416Smarkm * with a WILL LOGOUT, make sure that 77957416Smarkm * it gets written out to the network, 78057416Smarkm * and then just go away... 78157416Smarkm */ 78257416Smarkm set_my_want_state_will(TELOPT_LOGOUT); 78357416Smarkm send_will(TELOPT_LOGOUT, 0); 78457416Smarkm set_my_state_will(TELOPT_LOGOUT); 78557416Smarkm netflush(); 78657416Smarkm cleanup(0); 78757416Smarkm /* NOT REACHED */ 78857416Smarkm break; 78957416Smarkm 79057416Smarkm#ifdef ENCRYPTION 79157416Smarkm case TELOPT_ENCRYPT: 79257416Smarkm changeok++; 79357416Smarkm break; 79457416Smarkm#endif 79557416Smarkm case TELOPT_LINEMODE: 79657416Smarkm case TELOPT_TTYPE: 79757416Smarkm case TELOPT_NAWS: 79857416Smarkm case TELOPT_TSPEED: 79957416Smarkm case TELOPT_LFLOW: 80057416Smarkm case TELOPT_XDISPLOC: 80157416Smarkm#ifdef TELOPT_ENVIRON 80257416Smarkm case TELOPT_NEW_ENVIRON: 80357416Smarkm#endif 80457416Smarkm case TELOPT_OLD_ENVIRON: 80557416Smarkm default: 80657416Smarkm break; 80757416Smarkm } 80857416Smarkm if (changeok) { 80957416Smarkm set_my_want_state_will(option); 81057416Smarkm send_will(option, 0); 81157416Smarkm } else { 81257416Smarkm will_wont_resp[option]++; 81357416Smarkm send_wont(option, 0); 81457416Smarkm } 81557416Smarkm } 81657416Smarkm set_my_state_will(option); 81757416Smarkm 81857416Smarkm} /* end of dooption */ 81957416Smarkm 82057416Smarkmvoid 82157416Smarkmsend_wont(int option, int init) 82257416Smarkm{ 82357416Smarkm if (init) { 82457416Smarkm if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 82557416Smarkm my_want_state_is_wont(option)) 82657416Smarkm return; 82757416Smarkm set_my_want_state_wont(option); 82857416Smarkm will_wont_resp[option]++; 82957416Smarkm } 83057416Smarkm output_data ((const char *)wont, option); 83157416Smarkm 83257416Smarkm DIAG(TD_OPTIONS, printoption("td: send wont", option)); 83357416Smarkm} 83457416Smarkm 83557416Smarkmvoid 83657416Smarkmdontoption(int option) 83757416Smarkm{ 83857416Smarkm /* 83957416Smarkm * Process client input. 84057416Smarkm */ 84157416Smarkm 84257416Smarkm 84357416Smarkm DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 84457416Smarkm 84557416Smarkm if (will_wont_resp[option]) { 84657416Smarkm will_wont_resp[option]--; 84757416Smarkm if (will_wont_resp[option] && my_state_is_wont(option)) 84857416Smarkm will_wont_resp[option]--; 84957416Smarkm } 85057416Smarkm if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 85157416Smarkm switch (option) { 85257416Smarkm case TELOPT_BINARY: 85357416Smarkm init_termbuf(); 85457416Smarkm tty_binaryout(0); 85557416Smarkm set_termbuf(); 85657416Smarkm break; 85757416Smarkm 85857416Smarkm case TELOPT_ECHO: /* we should stop echoing */ 85957416Smarkm { 86057416Smarkm init_termbuf(); 86157416Smarkm tty_setecho(0); 86257416Smarkm set_termbuf(); 86357416Smarkm } 86457416Smarkm break; 86557416Smarkm 86657416Smarkm case TELOPT_SGA: 86757416Smarkm set_my_want_state_wont(option); 86857416Smarkm if (my_state_is_will(option)) 86957416Smarkm send_wont(option, 0); 87057416Smarkm set_my_state_wont(option); 87157416Smarkm if (turn_on_sga ^= 1) 87257416Smarkm send_will(option, 1); 87357416Smarkm return; 87457416Smarkm 87557416Smarkm default: 87657416Smarkm break; 87757416Smarkm } 87857416Smarkm 87957416Smarkm set_my_want_state_wont(option); 88057416Smarkm if (my_state_is_will(option)) 88157416Smarkm send_wont(option, 0); 88257416Smarkm } 88357416Smarkm set_my_state_wont(option); 88457416Smarkm 88557416Smarkm} /* end of dontoption */ 88657416Smarkm 88757416Smarkm#ifdef ENV_HACK 88857416Smarkmint env_ovar = -1; 88957416Smarkmint env_ovalue = -1; 89057416Smarkm#else /* ENV_HACK */ 89157416Smarkm# define env_ovar OLD_ENV_VAR 89257416Smarkm# define env_ovalue OLD_ENV_VALUE 89357416Smarkm#endif /* ENV_HACK */ 89457416Smarkm 89557416Smarkm/* 89657416Smarkm * suboption() 89757416Smarkm * 89857416Smarkm * Look at the sub-option buffer, and try to be helpful to the other 89957416Smarkm * side. 90057416Smarkm * 90157416Smarkm * Currently we recognize: 90257416Smarkm * 90357416Smarkm * Terminal type is 90457416Smarkm * Linemode 90557416Smarkm * Window size 90657416Smarkm * Terminal speed 90757416Smarkm */ 90857416Smarkmvoid 90957416Smarkmsuboption(void) 91057416Smarkm{ 91157416Smarkm int subchar; 91257416Smarkm 91357416Smarkm DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 91457416Smarkm 91557416Smarkm subchar = SB_GET(); 91657416Smarkm switch (subchar) { 91757416Smarkm case TELOPT_TSPEED: { 91857416Smarkm int xspeed, rspeed; 91957416Smarkm 92057416Smarkm if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 92157416Smarkm break; 92257416Smarkm 92357416Smarkm settimer(tspeedsubopt); 92457416Smarkm 92557416Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) 92657416Smarkm return; 92757416Smarkm 92857416Smarkm xspeed = atoi((char *)subpointer); 92957416Smarkm 93057416Smarkm while (SB_GET() != ',' && !SB_EOF()); 93157416Smarkm if (SB_EOF()) 93257416Smarkm return; 93357416Smarkm 93457416Smarkm rspeed = atoi((char *)subpointer); 93557416Smarkm clientstat(TELOPT_TSPEED, xspeed, rspeed); 93657416Smarkm 93757416Smarkm break; 93857416Smarkm 93957416Smarkm } /* end of case TELOPT_TSPEED */ 94057416Smarkm 94157416Smarkm case TELOPT_TTYPE: { /* Yaaaay! */ 942178825Sdfr char *p; 94357416Smarkm 94457416Smarkm if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 94557416Smarkm break; 94657416Smarkm settimer(ttypesubopt); 94757416Smarkm 94857416Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) { 94957416Smarkm return; /* ??? XXX but, this is the most robust */ 95057416Smarkm } 95157416Smarkm 952178825Sdfr p = terminaltype; 95357416Smarkm 954178825Sdfr while ((p < (terminaltype + sizeof terminaltype-1)) && 95557416Smarkm !SB_EOF()) { 95657416Smarkm int c; 95757416Smarkm 95857416Smarkm c = SB_GET(); 95957416Smarkm if (isupper(c)) { 96057416Smarkm c = tolower(c); 96157416Smarkm } 962178825Sdfr *p++ = c; /* accumulate name */ 96357416Smarkm } 964178825Sdfr *p = 0; 96557416Smarkm break; 96657416Smarkm } /* end of case TELOPT_TTYPE */ 96757416Smarkm 96857416Smarkm case TELOPT_NAWS: { 96957416Smarkm int xwinsize, ywinsize; 97057416Smarkm 97157416Smarkm if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 97257416Smarkm break; 97357416Smarkm 97457416Smarkm if (SB_EOF()) 97557416Smarkm return; 97657416Smarkm xwinsize = SB_GET() << 8; 97757416Smarkm if (SB_EOF()) 97857416Smarkm return; 97957416Smarkm xwinsize |= SB_GET(); 98057416Smarkm if (SB_EOF()) 98157416Smarkm return; 98257416Smarkm ywinsize = SB_GET() << 8; 98357416Smarkm if (SB_EOF()) 98457416Smarkm return; 98557416Smarkm ywinsize |= SB_GET(); 98657416Smarkm clientstat(TELOPT_NAWS, xwinsize, ywinsize); 98757416Smarkm 98857416Smarkm break; 98957416Smarkm 99057416Smarkm } /* end of case TELOPT_NAWS */ 99157416Smarkm 99257416Smarkm case TELOPT_STATUS: { 99357416Smarkm int mode; 99457416Smarkm 99557416Smarkm if (SB_EOF()) 99657416Smarkm break; 99757416Smarkm mode = SB_GET(); 99857416Smarkm switch (mode) { 99957416Smarkm case TELQUAL_SEND: 100057416Smarkm if (my_state_is_will(TELOPT_STATUS)) 100157416Smarkm send_status(); 100257416Smarkm break; 100357416Smarkm 100457416Smarkm case TELQUAL_IS: 100557416Smarkm break; 100657416Smarkm 100757416Smarkm default: 100857416Smarkm break; 100957416Smarkm } 101057416Smarkm break; 101157416Smarkm } /* end of case TELOPT_STATUS */ 101257416Smarkm 101357416Smarkm case TELOPT_XDISPLOC: { 101457416Smarkm if (SB_EOF() || SB_GET() != TELQUAL_IS) 101557416Smarkm return; 101657416Smarkm settimer(xdisplocsubopt); 101757416Smarkm subpointer[SB_LEN()] = '\0'; 101872445Sassar esetenv("DISPLAY", (char *)subpointer, 1); 101957416Smarkm break; 102057416Smarkm } /* end of case TELOPT_XDISPLOC */ 102157416Smarkm 102257416Smarkm#ifdef TELOPT_NEW_ENVIRON 102357416Smarkm case TELOPT_NEW_ENVIRON: 102457416Smarkm#endif 102557416Smarkm case TELOPT_OLD_ENVIRON: { 102657416Smarkm int c; 102757416Smarkm char *cp, *varp, *valp; 102857416Smarkm 102957416Smarkm if (SB_EOF()) 103057416Smarkm return; 103157416Smarkm c = SB_GET(); 103257416Smarkm if (c == TELQUAL_IS) { 103357416Smarkm if (subchar == TELOPT_OLD_ENVIRON) 103457416Smarkm settimer(oenvironsubopt); 103557416Smarkm else 103657416Smarkm settimer(environsubopt); 103757416Smarkm } else if (c != TELQUAL_INFO) { 103857416Smarkm return; 103957416Smarkm } 104057416Smarkm 104157416Smarkm#ifdef TELOPT_NEW_ENVIRON 104257416Smarkm if (subchar == TELOPT_NEW_ENVIRON) { 104357416Smarkm while (!SB_EOF()) { 104457416Smarkm c = SB_GET(); 104557416Smarkm if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) 104657416Smarkm break; 104757416Smarkm } 104857416Smarkm } else 104957416Smarkm#endif 105057416Smarkm { 105157416Smarkm#ifdef ENV_HACK 105257416Smarkm /* 105357416Smarkm * We only want to do this if we haven't already decided 105457416Smarkm * whether or not the other side has its VALUE and VAR 105557416Smarkm * reversed. 105657416Smarkm */ 105757416Smarkm if (env_ovar < 0) { 105857416Smarkm int last = -1; /* invalid value */ 105957416Smarkm int empty = 0; 106057416Smarkm int got_var = 0, got_value = 0, got_uservar = 0; 106157416Smarkm 106257416Smarkm /* 106357416Smarkm * The other side might have its VALUE and VAR values 106457416Smarkm * reversed. To be interoperable, we need to determine 106557416Smarkm * which way it is. If the first recognized character 106657416Smarkm * is a VAR or VALUE, then that will tell us what 106757416Smarkm * type of client it is. If the fist recognized 106857416Smarkm * character is a USERVAR, then we continue scanning 106957416Smarkm * the suboption looking for two consecutive 107057416Smarkm * VAR or VALUE fields. We should not get two 107157416Smarkm * consecutive VALUE fields, so finding two 107257416Smarkm * consecutive VALUE or VAR fields will tell us 107357416Smarkm * what the client is. 107457416Smarkm */ 107557416Smarkm SB_SAVE(); 107657416Smarkm while (!SB_EOF()) { 107757416Smarkm c = SB_GET(); 107857416Smarkm switch(c) { 107957416Smarkm case OLD_ENV_VAR: 108057416Smarkm if (last < 0 || last == OLD_ENV_VAR 108157416Smarkm || (empty && (last == OLD_ENV_VALUE))) 108257416Smarkm goto env_ovar_ok; 108357416Smarkm got_var++; 108457416Smarkm last = OLD_ENV_VAR; 108557416Smarkm break; 108657416Smarkm case OLD_ENV_VALUE: 108757416Smarkm if (last < 0 || last == OLD_ENV_VALUE 108857416Smarkm || (empty && (last == OLD_ENV_VAR))) 108957416Smarkm goto env_ovar_wrong; 109057416Smarkm got_value++; 109157416Smarkm last = OLD_ENV_VALUE; 109257416Smarkm break; 109357416Smarkm case ENV_USERVAR: 109457416Smarkm /* count strings of USERVAR as one */ 109557416Smarkm if (last != ENV_USERVAR) 109657416Smarkm got_uservar++; 109757416Smarkm if (empty) { 109857416Smarkm if (last == OLD_ENV_VALUE) 109957416Smarkm goto env_ovar_ok; 110057416Smarkm if (last == OLD_ENV_VAR) 110157416Smarkm goto env_ovar_wrong; 110257416Smarkm } 110357416Smarkm last = ENV_USERVAR; 110457416Smarkm break; 110557416Smarkm case ENV_ESC: 110657416Smarkm if (!SB_EOF()) 110757416Smarkm c = SB_GET(); 110857416Smarkm /* FALL THROUGH */ 110957416Smarkm default: 111057416Smarkm empty = 0; 111157416Smarkm continue; 111257416Smarkm } 111357416Smarkm empty = 1; 111457416Smarkm } 111557416Smarkm if (empty) { 111657416Smarkm if (last == OLD_ENV_VALUE) 111757416Smarkm goto env_ovar_ok; 111857416Smarkm if (last == OLD_ENV_VAR) 111957416Smarkm goto env_ovar_wrong; 112057416Smarkm } 112157416Smarkm /* 112257416Smarkm * Ok, the first thing was a USERVAR, and there 112357416Smarkm * are not two consecutive VAR or VALUE commands, 112457416Smarkm * and none of the VAR or VALUE commands are empty. 112557416Smarkm * If the client has sent us a well-formed option, 112657416Smarkm * then the number of VALUEs received should always 112757416Smarkm * be less than or equal to the number of VARs and 112857416Smarkm * USERVARs received. 112957416Smarkm * 113057416Smarkm * If we got exactly as many VALUEs as VARs and 113157416Smarkm * USERVARs, the client has the same definitions. 113257416Smarkm * 113357416Smarkm * If we got exactly as many VARs as VALUEs and 113457416Smarkm * USERVARS, the client has reversed definitions. 113557416Smarkm */ 113657416Smarkm if (got_uservar + got_var == got_value) { 113757416Smarkm env_ovar_ok: 113857416Smarkm env_ovar = OLD_ENV_VAR; 113957416Smarkm env_ovalue = OLD_ENV_VALUE; 114057416Smarkm } else if (got_uservar + got_value == got_var) { 114157416Smarkm env_ovar_wrong: 114257416Smarkm env_ovar = OLD_ENV_VALUE; 114357416Smarkm env_ovalue = OLD_ENV_VAR; 114457416Smarkm DIAG(TD_OPTIONS, { 114557416Smarkm output_data("ENVIRON VALUE and VAR are reversed!\r\n"); 114657416Smarkm }); 114757416Smarkm 114857416Smarkm } 114957416Smarkm } 115057416Smarkm SB_RESTORE(); 115157416Smarkm#endif 115257416Smarkm 115357416Smarkm while (!SB_EOF()) { 115457416Smarkm c = SB_GET(); 115557416Smarkm if ((c == env_ovar) || (c == ENV_USERVAR)) 115657416Smarkm break; 115757416Smarkm } 115857416Smarkm } 115957416Smarkm 116057416Smarkm if (SB_EOF()) 116157416Smarkm return; 116257416Smarkm 116357416Smarkm cp = varp = (char *)subpointer; 116457416Smarkm valp = 0; 116557416Smarkm 116657416Smarkm while (!SB_EOF()) { 116757416Smarkm c = SB_GET(); 116857416Smarkm if (subchar == TELOPT_OLD_ENVIRON) { 116957416Smarkm if (c == env_ovar) 117057416Smarkm c = NEW_ENV_VAR; 117157416Smarkm else if (c == env_ovalue) 117257416Smarkm c = NEW_ENV_VALUE; 117357416Smarkm } 117457416Smarkm switch (c) { 117557416Smarkm 117657416Smarkm case NEW_ENV_VALUE: 117757416Smarkm *cp = '\0'; 117857416Smarkm cp = valp = (char *)subpointer; 117957416Smarkm break; 118057416Smarkm 118157416Smarkm case NEW_ENV_VAR: 118257416Smarkm case ENV_USERVAR: 118357416Smarkm *cp = '\0'; 118457416Smarkm if (valp) 118572445Sassar esetenv(varp, valp, 1); 118657416Smarkm else 118757416Smarkm unsetenv(varp); 118857416Smarkm cp = varp = (char *)subpointer; 118957416Smarkm valp = 0; 119057416Smarkm break; 119157416Smarkm 119257416Smarkm case ENV_ESC: 119357416Smarkm if (SB_EOF()) 119457416Smarkm break; 119557416Smarkm c = SB_GET(); 119657416Smarkm /* FALL THROUGH */ 119757416Smarkm default: 119857416Smarkm *cp++ = c; 119957416Smarkm break; 120057416Smarkm } 120157416Smarkm } 120257416Smarkm *cp = '\0'; 120357416Smarkm if (valp) 120472445Sassar esetenv(varp, valp, 1); 120557416Smarkm else 120657416Smarkm unsetenv(varp); 120757416Smarkm break; 120857416Smarkm } /* end of case TELOPT_NEW_ENVIRON */ 120957416Smarkm#ifdef AUTHENTICATION 121057416Smarkm case TELOPT_AUTHENTICATION: 121157416Smarkm if (SB_EOF()) 121257416Smarkm break; 121357416Smarkm switch(SB_GET()) { 121457416Smarkm case TELQUAL_SEND: 121557416Smarkm case TELQUAL_REPLY: 121657416Smarkm /* 121757416Smarkm * These are sent by us and cannot be sent by 121857416Smarkm * the client. 121957416Smarkm */ 122057416Smarkm break; 122157416Smarkm case TELQUAL_IS: 122257416Smarkm auth_is(subpointer, SB_LEN()); 122357416Smarkm break; 122457416Smarkm case TELQUAL_NAME: 122557416Smarkm auth_name(subpointer, SB_LEN()); 122657416Smarkm break; 122757416Smarkm } 122857416Smarkm break; 122957416Smarkm#endif 123057416Smarkm#ifdef ENCRYPTION 123157416Smarkm case TELOPT_ENCRYPT: 123257416Smarkm if (SB_EOF()) 123357416Smarkm break; 123457416Smarkm switch(SB_GET()) { 123557416Smarkm case ENCRYPT_SUPPORT: 123657416Smarkm encrypt_support(subpointer, SB_LEN()); 123757416Smarkm break; 123857416Smarkm case ENCRYPT_IS: 123957416Smarkm encrypt_is(subpointer, SB_LEN()); 124057416Smarkm break; 124157416Smarkm case ENCRYPT_REPLY: 124257416Smarkm encrypt_reply(subpointer, SB_LEN()); 124357416Smarkm break; 124457416Smarkm case ENCRYPT_START: 124557416Smarkm encrypt_start(subpointer, SB_LEN()); 124657416Smarkm break; 124757416Smarkm case ENCRYPT_END: 1248178825Sdfr if (require_encryption) 1249178825Sdfr fatal(net, "Output encryption is not possible to turn off"); 125057416Smarkm encrypt_end(); 125157416Smarkm break; 125257416Smarkm case ENCRYPT_REQSTART: 125357416Smarkm encrypt_request_start(subpointer, SB_LEN()); 125457416Smarkm break; 125557416Smarkm case ENCRYPT_REQEND: 125657416Smarkm /* 125757416Smarkm * We can always send an REQEND so that we cannot 125857416Smarkm * get stuck encrypting. We should only get this 125957416Smarkm * if we have been able to get in the correct mode 126057416Smarkm * anyhow. 126157416Smarkm */ 1262178825Sdfr if (require_encryption) 1263178825Sdfr fatal(net, "Input encryption is not possible to turn off"); 126457416Smarkm encrypt_request_end(); 126557416Smarkm break; 126657416Smarkm case ENCRYPT_ENC_KEYID: 126757416Smarkm encrypt_enc_keyid(subpointer, SB_LEN()); 126857416Smarkm break; 126957416Smarkm case ENCRYPT_DEC_KEYID: 127057416Smarkm encrypt_dec_keyid(subpointer, SB_LEN()); 127157416Smarkm break; 127257416Smarkm default: 127357416Smarkm break; 127457416Smarkm } 127557416Smarkm break; 127657416Smarkm#endif 127757416Smarkm 127857416Smarkm default: 127957416Smarkm break; 128057416Smarkm } /* end of switch */ 128157416Smarkm 128257416Smarkm} /* end of suboption */ 128357416Smarkm 128457416Smarkmvoid 128557416Smarkmdoclientstat(void) 128657416Smarkm{ 128757416Smarkm clientstat(TELOPT_LINEMODE, WILL, 0); 128857416Smarkm} 128957416Smarkm 1290142403Snectar#undef ADD 129157416Smarkm#define ADD(c) *ncp++ = c 129257416Smarkm#define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; } 129357416Smarkm 129457416Smarkmvoid 129557416Smarkmsend_status(void) 129657416Smarkm{ 129757416Smarkm unsigned char statusbuf[256]; 129857416Smarkm unsigned char *ncp; 129957416Smarkm unsigned char i; 130057416Smarkm 130157416Smarkm ncp = statusbuf; 130257416Smarkm 130357416Smarkm netflush(); /* get rid of anything waiting to go out */ 130457416Smarkm 130557416Smarkm ADD(IAC); 130657416Smarkm ADD(SB); 130757416Smarkm ADD(TELOPT_STATUS); 130857416Smarkm ADD(TELQUAL_IS); 130957416Smarkm 131057416Smarkm /* 131157416Smarkm * We check the want_state rather than the current state, 131257416Smarkm * because if we received a DO/WILL for an option that we 131357416Smarkm * don't support, and the other side didn't send a DONT/WONT 131457416Smarkm * in response to our WONT/DONT, then the "state" will be 131557416Smarkm * WILL/DO, and the "want_state" will be WONT/DONT. We 131657416Smarkm * need to go by the latter. 131757416Smarkm */ 131857416Smarkm for (i = 0; i < (unsigned char)NTELOPTS; i++) { 131957416Smarkm if (my_want_state_is_will(i)) { 132057416Smarkm ADD(WILL); 132157416Smarkm ADD_DATA(i); 132257416Smarkm } 132357416Smarkm if (his_want_state_is_will(i)) { 132457416Smarkm ADD(DO); 132557416Smarkm ADD_DATA(i); 132657416Smarkm } 132757416Smarkm } 132857416Smarkm 132957416Smarkm if (his_want_state_is_will(TELOPT_LFLOW)) { 133057416Smarkm ADD(SB); 133157416Smarkm ADD(TELOPT_LFLOW); 133257416Smarkm if (flowmode) { 133357416Smarkm ADD(LFLOW_ON); 133457416Smarkm } else { 133557416Smarkm ADD(LFLOW_OFF); 133657416Smarkm } 133757416Smarkm ADD(SE); 133857416Smarkm 133957416Smarkm if (restartany >= 0) { 134057416Smarkm ADD(SB); 134157416Smarkm ADD(TELOPT_LFLOW); 134257416Smarkm if (restartany) { 134357416Smarkm ADD(LFLOW_RESTART_ANY); 134457416Smarkm } else { 134557416Smarkm ADD(LFLOW_RESTART_XON); 134657416Smarkm } 134757416Smarkm ADD(SE); 134857416Smarkm } 134957416Smarkm } 135057416Smarkm 135157416Smarkm 135257416Smarkm ADD(IAC); 135357416Smarkm ADD(SE); 135457416Smarkm 135557416Smarkm writenet(statusbuf, ncp - statusbuf); 135657416Smarkm netflush(); /* Send it on its way */ 135757416Smarkm 135857416Smarkm DIAG(TD_OPTIONS, 135957416Smarkm {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 136057416Smarkm} 1361