Path: blob/main/crypto/heimdal/appl/telnet/telnetd/state.c
34878 views
/*1* Copyright (c) 1989, 19932* The Regents of the University of California. All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. All advertising materials mentioning features or use of this software13* must display the following acknowledgement:14* This product includes software developed by the University of15* California, Berkeley and its contributors.16* 4. Neither the name of the University nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "telnetd.h"3435RCSID("$Id$");3637unsigned char doopt[] = { IAC, DO, '%', 'c', 0 };38unsigned char dont[] = { IAC, DONT, '%', 'c', 0 };39unsigned char will[] = { IAC, WILL, '%', 'c', 0 };40unsigned char wont[] = { IAC, WONT, '%', 'c', 0 };41int not42 = 1;4243/*44* Buffer for sub-options, and macros45* for suboptions buffer manipulations46*/47unsigned char subbuffer[1024*64], *subpointer= subbuffer, *subend= subbuffer;4849#define SB_CLEAR() subpointer = subbuffer50#define SB_TERM() { subend = subpointer; SB_CLEAR(); }51#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \52*subpointer++ = (c); \53}54#define SB_GET() ((*subpointer++)&0xff)55#define SB_EOF() (subpointer >= subend)56#define SB_LEN() (subend - subpointer)5758#ifdef ENV_HACK59unsigned char *subsave;60#define SB_SAVE() subsave = subpointer;61#define SB_RESTORE() subpointer = subsave;62#endif636465/*66* State for recv fsm67*/68#define TS_DATA 0 /* base state */69#define TS_IAC 1 /* look for double IAC's */70#define TS_CR 2 /* CR-LF ->'s CR */71#define TS_SB 3 /* throw away begin's... */72#define TS_SE 4 /* ...end's (suboption negotiation) */73#define TS_WILL 5 /* will option negotiation */74#define TS_WONT 6 /* wont -''- */75#define TS_DO 7 /* do -''- */76#define TS_DONT 8 /* dont -''- */7778void79telrcv(void)80{81int c;82static int state = TS_DATA;8384while (ncc > 0) {85if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)86break;87c = *netip++ & 0377, ncc--;88#ifdef ENCRYPTION89if (decrypt_input)90c = (*decrypt_input)(c);91#endif92switch (state) {9394case TS_CR:95state = TS_DATA;96/* Strip off \n or \0 after a \r */97if ((c == 0) || (c == '\n')) {98break;99}100/* FALL THROUGH */101102case TS_DATA:103if (c == IAC) {104state = TS_IAC;105break;106}107/*108* We now map \r\n ==> \r for pragmatic reasons.109* Many client implementations send \r\n when110* the user hits the CarriageReturn key.111*112* We USED to map \r\n ==> \n, since \r\n says113* that we want to be in column 1 of the next114* printable line, and \n is the standard115* unix way of saying that (\r is only good116* if CRMOD is set, which it normally is).117*/118if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {119int nc = *netip;120#ifdef ENCRYPTION121if (decrypt_input)122nc = (*decrypt_input)(nc & 0xff);123#endif124{125#ifdef ENCRYPTION126if (decrypt_input)127(void)(*decrypt_input)(-1);128#endif129state = TS_CR;130}131}132*pfrontp++ = c;133break;134135case TS_IAC:136gotiac: switch (c) {137138/*139* Send the process on the pty side an140* interrupt. Do this with a NULL or141* interrupt char; depending on the tty mode.142*/143case IP:144DIAG(TD_OPTIONS,145printoption("td: recv IAC", c));146interrupt();147break;148149case BREAK:150DIAG(TD_OPTIONS,151printoption("td: recv IAC", c));152sendbrk();153break;154155/*156* Are You There?157*/158case AYT:159DIAG(TD_OPTIONS,160printoption("td: recv IAC", c));161recv_ayt();162break;163164/*165* Abort Output166*/167case AO:168{169DIAG(TD_OPTIONS,170printoption("td: recv IAC", c));171ptyflush(); /* half-hearted */172init_termbuf();173174if (slctab[SLC_AO].sptr &&175*slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {176*pfrontp++ =177(unsigned char)*slctab[SLC_AO].sptr;178}179180netclear(); /* clear buffer back */181output_data ("%c%c", IAC, DM);182neturg = nfrontp-1; /* off by one XXX */183DIAG(TD_OPTIONS,184printoption("td: send IAC", DM));185break;186}187188/*189* Erase Character and190* Erase Line191*/192case EC:193case EL:194{195cc_t ch;196197DIAG(TD_OPTIONS,198printoption("td: recv IAC", c));199ptyflush(); /* half-hearted */200init_termbuf();201if (c == EC)202ch = *slctab[SLC_EC].sptr;203else204ch = *slctab[SLC_EL].sptr;205if (ch != (cc_t)(_POSIX_VDISABLE))206*pfrontp++ = (unsigned char)ch;207break;208}209210/*211* Check for urgent data...212*/213case DM:214DIAG(TD_OPTIONS,215printoption("td: recv IAC", c));216SYNCHing = stilloob(net);217settimer(gotDM);218break;219220221/*222* Begin option subnegotiation...223*/224case SB:225state = TS_SB;226SB_CLEAR();227continue;228229case WILL:230state = TS_WILL;231continue;232233case WONT:234state = TS_WONT;235continue;236237case DO:238state = TS_DO;239continue;240241case DONT:242state = TS_DONT;243continue;244case EOR:245if (his_state_is_will(TELOPT_EOR))246doeof();247break;248249/*250* Handle RFC 10xx Telnet linemode option additions251* to command stream (EOF, SUSP, ABORT).252*/253case xEOF:254doeof();255break;256257case SUSP:258sendsusp();259break;260261case ABORT:262sendbrk();263break;264265case IAC:266*pfrontp++ = c;267break;268}269state = TS_DATA;270break;271272case TS_SB:273if (c == IAC) {274state = TS_SE;275} else {276SB_ACCUM(c);277}278break;279280case TS_SE:281if (c != SE) {282if (c != IAC) {283/*284* bad form of suboption negotiation.285* handle it in such a way as to avoid286* damage to local state. Parse287* suboption buffer found so far,288* then treat remaining stream as289* another command sequence.290*/291292/* for DIAGNOSTICS */293SB_ACCUM(IAC);294SB_ACCUM(c);295subpointer -= 2;296297SB_TERM();298suboption();299state = TS_IAC;300goto gotiac;301}302SB_ACCUM(c);303state = TS_SB;304} else {305/* for DIAGNOSTICS */306SB_ACCUM(IAC);307SB_ACCUM(SE);308subpointer -= 2;309310SB_TERM();311suboption(); /* handle sub-option */312state = TS_DATA;313}314break;315316case TS_WILL:317willoption(c);318state = TS_DATA;319continue;320321case TS_WONT:322wontoption(c);323if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) )324dontoption(c);325state = TS_DATA;326continue;327328case TS_DO:329dooption(c);330state = TS_DATA;331continue;332333case TS_DONT:334dontoption(c);335state = TS_DATA;336continue;337338default:339syslog(LOG_ERR, "telnetd: panic state=%d\n", state);340printf("telnetd: panic state=%d\n", state);341exit(1);342}343}344} /* end of telrcv */345346/*347* The will/wont/do/dont state machines are based on Dave Borman's348* Telnet option processing state machine.349*350* These correspond to the following states:351* my_state = the last negotiated state352* want_state = what I want the state to go to353* want_resp = how many requests I have sent354* All state defaults are negative, and resp defaults to 0.355*356* When initiating a request to change state to new_state:357*358* if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {359* do nothing;360* } else {361* want_state = new_state;362* send new_state;363* want_resp++;364* }365*366* When receiving new_state:367*368* if (want_resp) {369* want_resp--;370* if (want_resp && (new_state == my_state))371* want_resp--;372* }373* if ((want_resp == 0) && (new_state != want_state)) {374* if (ok_to_switch_to new_state)375* want_state = new_state;376* else377* want_resp++;378* send want_state;379* }380* my_state = new_state;381*382* Note that new_state is implied in these functions by the function itself.383* will and do imply positive new_state, wont and dont imply negative.384*385* Finally, there is one catch. If we send a negative response to a386* positive request, my_state will be the positive while want_state will387* remain negative. my_state will revert to negative when the negative388* acknowlegment arrives from the peer. Thus, my_state generally tells389* us not only the last negotiated state, but also tells us what the peer390* wants to be doing as well. It is important to understand this difference391* as we may wish to be processing data streams based on our desired state392* (want_state) or based on what the peer thinks the state is (my_state).393*394* This all works fine because if the peer sends a positive request, the data395* that we receive prior to negative acknowlegment will probably be affected396* by the positive state, and we can process it as such (if we can; if we397* can't then it really doesn't matter). If it is that important, then the398* peer probably should be buffering until this option state negotiation399* is complete.400*401*/402void403send_do(int option, int init)404{405if (init) {406if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||407his_want_state_is_will(option))408return;409/*410* Special case for TELOPT_TM: We send a DO, but pretend411* that we sent a DONT, so that we can send more DOs if412* we want to.413*/414if (option == TELOPT_TM)415set_his_want_state_wont(option);416else417set_his_want_state_will(option);418do_dont_resp[option]++;419}420output_data((const char *)doopt, option);421422DIAG(TD_OPTIONS, printoption("td: send do", option));423}424425#ifdef AUTHENTICATION426extern void auth_request(void);427#endif428#ifdef ENCRYPTION429extern void encrypt_send_support(void);430#endif431432void433willoption(int option)434{435int changeok = 0;436void (*func)(void) = NULL;437438/*439* process input from peer.440*/441442DIAG(TD_OPTIONS, printoption("td: recv will", option));443444if (do_dont_resp[option]) {445do_dont_resp[option]--;446if (do_dont_resp[option] && his_state_is_will(option))447do_dont_resp[option]--;448}449if (do_dont_resp[option] == 0) {450if (his_want_state_is_wont(option)) {451switch (option) {452453case TELOPT_BINARY:454init_termbuf();455tty_binaryin(1);456set_termbuf();457changeok++;458break;459460case TELOPT_ECHO:461/*462* See comments below for more info.463*/464not42 = 0; /* looks like a 4.2 system */465break;466467case TELOPT_TM:468/*469* We never respond to a WILL TM, and470* we leave the state WONT.471*/472return;473474case TELOPT_LFLOW:475/*476* If we are going to support flow control477* option, then don't worry peer that we can't478* change the flow control characters.479*/480slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;481slctab[SLC_XON].defset.flag |= SLC_DEFAULT;482slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;483slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;484case TELOPT_TTYPE:485case TELOPT_SGA:486case TELOPT_NAWS:487case TELOPT_TSPEED:488case TELOPT_XDISPLOC:489case TELOPT_NEW_ENVIRON:490case TELOPT_OLD_ENVIRON:491changeok++;492break;493494495#ifdef AUTHENTICATION496case TELOPT_AUTHENTICATION:497func = auth_request;498changeok++;499break;500#endif501502#ifdef ENCRYPTION503case TELOPT_ENCRYPT:504func = encrypt_send_support;505changeok++;506break;507#endif508509default:510break;511}512if (changeok) {513set_his_want_state_will(option);514send_do(option, 0);515} else {516do_dont_resp[option]++;517send_dont(option, 0);518}519} else {520/*521* Option processing that should happen when522* we receive conformation of a change in523* state that we had requested.524*/525switch (option) {526case TELOPT_ECHO:527not42 = 0; /* looks like a 4.2 system */528/*529* Egads, he responded "WILL ECHO". Turn530* it off right now!531*/532send_dont(option, 1);533/*534* "WILL ECHO". Kludge upon kludge!535* A 4.2 client is now echoing user input at536* the tty. This is probably undesireable and537* it should be stopped. The client will538* respond WONT TM to the DO TM that we send to539* check for kludge linemode. When the WONT TM540* arrives, linemode will be turned off and a541* change propogated to the pty. This change542* will cause us to process the new pty state543* in localstat(), which will notice that544* linemode is off and send a WILL ECHO545* so that we are properly in character mode and546* all is well.547*/548break;549550#ifdef AUTHENTICATION551case TELOPT_AUTHENTICATION:552func = auth_request;553break;554#endif555556#ifdef ENCRYPTION557case TELOPT_ENCRYPT:558func = encrypt_send_support;559break;560#endif561562case TELOPT_LFLOW:563func = flowstat;564break;565}566}567}568set_his_state_will(option);569if (func)570(*func)();571} /* end of willoption */572573void574send_dont(int option, int init)575{576if (init) {577if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||578his_want_state_is_wont(option))579return;580set_his_want_state_wont(option);581do_dont_resp[option]++;582}583output_data((const char *)dont, option);584585DIAG(TD_OPTIONS, printoption("td: send dont", option));586}587588void589wontoption(int option)590{591/*592* Process client input.593*/594595DIAG(TD_OPTIONS, printoption("td: recv wont", option));596597if (do_dont_resp[option]) {598do_dont_resp[option]--;599if (do_dont_resp[option] && his_state_is_wont(option))600do_dont_resp[option]--;601}602if (do_dont_resp[option] == 0) {603if (his_want_state_is_will(option)) {604/* it is always ok to change to negative state */605switch (option) {606case TELOPT_ECHO:607not42 = 1; /* doesn't seem to be a 4.2 system */608break;609610case TELOPT_BINARY:611init_termbuf();612tty_binaryin(0);613set_termbuf();614break;615616case TELOPT_TM:617/*618* If we get a WONT TM, and had sent a DO TM,619* don't respond with a DONT TM, just leave it620* as is. Short circut the state machine to621* achive this.622*/623set_his_want_state_wont(TELOPT_TM);624return;625626case TELOPT_LFLOW:627/*628* If we are not going to support flow control629* option, then let peer know that we can't630* change the flow control characters.631*/632slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;633slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;634slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;635slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;636break;637638#ifdef AUTHENTICATION639case TELOPT_AUTHENTICATION:640auth_finished(0, AUTH_REJECT);641break;642#endif643644/*645* For options that we might spin waiting for646* sub-negotiation, if the client turns off the647* option rather than responding to the request,648* we have to treat it here as if we got a response649* to the sub-negotiation, (by updating the timers)650* so that we'll break out of the loop.651*/652case TELOPT_TTYPE:653settimer(ttypesubopt);654break;655656case TELOPT_TSPEED:657settimer(tspeedsubopt);658break;659660case TELOPT_XDISPLOC:661settimer(xdisplocsubopt);662break;663664case TELOPT_OLD_ENVIRON:665settimer(oenvironsubopt);666break;667668case TELOPT_NEW_ENVIRON:669settimer(environsubopt);670break;671672default:673break;674}675set_his_want_state_wont(option);676if (his_state_is_will(option))677send_dont(option, 0);678} else {679switch (option) {680case TELOPT_TM:681break;682683#ifdef AUTHENTICATION684case TELOPT_AUTHENTICATION:685auth_finished(0, AUTH_REJECT);686break;687#endif688default:689break;690}691}692}693set_his_state_wont(option);694695} /* end of wontoption */696697void698send_will(int option, int init)699{700if (init) {701if ((will_wont_resp[option] == 0 && my_state_is_will(option))||702my_want_state_is_will(option))703return;704set_my_want_state_will(option);705will_wont_resp[option]++;706}707output_data ((const char *)will, option);708709DIAG(TD_OPTIONS, printoption("td: send will", option));710}711712/*713* When we get a DONT SGA, we will try once to turn it714* back on. If the other side responds DONT SGA, we715* leave it at that. This is so that when we talk to716* clients that understand KLUDGELINEMODE but not LINEMODE,717* we'll keep them in char-at-a-time mode.718*/719int turn_on_sga = 0;720721void722dooption(int option)723{724int changeok = 0;725726/*727* Process client input.728*/729730DIAG(TD_OPTIONS, printoption("td: recv do", option));731732if (will_wont_resp[option]) {733will_wont_resp[option]--;734if (will_wont_resp[option] && my_state_is_will(option))735will_wont_resp[option]--;736}737if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {738switch (option) {739case TELOPT_ECHO:740{741init_termbuf();742tty_setecho(1);743set_termbuf();744}745changeok++;746break;747748case TELOPT_BINARY:749init_termbuf();750tty_binaryout(1);751set_termbuf();752changeok++;753break;754755case TELOPT_SGA:756turn_on_sga = 0;757changeok++;758break;759760case TELOPT_STATUS:761changeok++;762break;763764case TELOPT_TM:765/*766* Special case for TM. We send a WILL, but767* pretend we sent a WONT.768*/769send_will(option, 0);770set_my_want_state_wont(option);771set_my_state_wont(option);772return;773774case TELOPT_LOGOUT:775/*776* When we get a LOGOUT option, respond777* with a WILL LOGOUT, make sure that778* it gets written out to the network,779* and then just go away...780*/781set_my_want_state_will(TELOPT_LOGOUT);782send_will(TELOPT_LOGOUT, 0);783set_my_state_will(TELOPT_LOGOUT);784netflush();785cleanup(0);786/* NOT REACHED */787break;788789#ifdef ENCRYPTION790case TELOPT_ENCRYPT:791changeok++;792break;793#endif794case TELOPT_LINEMODE:795case TELOPT_TTYPE:796case TELOPT_NAWS:797case TELOPT_TSPEED:798case TELOPT_LFLOW:799case TELOPT_XDISPLOC:800#ifdef TELOPT_ENVIRON801case TELOPT_NEW_ENVIRON:802#endif803case TELOPT_OLD_ENVIRON:804default:805break;806}807if (changeok) {808set_my_want_state_will(option);809send_will(option, 0);810} else {811will_wont_resp[option]++;812send_wont(option, 0);813}814}815set_my_state_will(option);816817} /* end of dooption */818819void820send_wont(int option, int init)821{822if (init) {823if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||824my_want_state_is_wont(option))825return;826set_my_want_state_wont(option);827will_wont_resp[option]++;828}829output_data ((const char *)wont, option);830831DIAG(TD_OPTIONS, printoption("td: send wont", option));832}833834void835dontoption(int option)836{837/*838* Process client input.839*/840841842DIAG(TD_OPTIONS, printoption("td: recv dont", option));843844if (will_wont_resp[option]) {845will_wont_resp[option]--;846if (will_wont_resp[option] && my_state_is_wont(option))847will_wont_resp[option]--;848}849if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {850switch (option) {851case TELOPT_BINARY:852init_termbuf();853tty_binaryout(0);854set_termbuf();855break;856857case TELOPT_ECHO: /* we should stop echoing */858{859init_termbuf();860tty_setecho(0);861set_termbuf();862}863break;864865case TELOPT_SGA:866set_my_want_state_wont(option);867if (my_state_is_will(option))868send_wont(option, 0);869set_my_state_wont(option);870if (turn_on_sga ^= 1)871send_will(option, 1);872return;873874default:875break;876}877878set_my_want_state_wont(option);879if (my_state_is_will(option))880send_wont(option, 0);881}882set_my_state_wont(option);883884} /* end of dontoption */885886#ifdef ENV_HACK887int env_ovar = -1;888int env_ovalue = -1;889#else /* ENV_HACK */890# define env_ovar OLD_ENV_VAR891# define env_ovalue OLD_ENV_VALUE892#endif /* ENV_HACK */893894/*895* suboption()896*897* Look at the sub-option buffer, and try to be helpful to the other898* side.899*900* Currently we recognize:901*902* Terminal type is903* Linemode904* Window size905* Terminal speed906*/907void908suboption(void)909{910int subchar;911912DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});913914subchar = SB_GET();915switch (subchar) {916case TELOPT_TSPEED: {917int xspeed, rspeed;918919if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */920break;921922settimer(tspeedsubopt);923924if (SB_EOF() || SB_GET() != TELQUAL_IS)925return;926927xspeed = atoi((char *)subpointer);928929while (SB_GET() != ',' && !SB_EOF());930if (SB_EOF())931return;932933rspeed = atoi((char *)subpointer);934clientstat(TELOPT_TSPEED, xspeed, rspeed);935936break;937938} /* end of case TELOPT_TSPEED */939940case TELOPT_TTYPE: { /* Yaaaay! */941char *p;942943if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */944break;945settimer(ttypesubopt);946947if (SB_EOF() || SB_GET() != TELQUAL_IS) {948return; /* ??? XXX but, this is the most robust */949}950951p = terminaltype;952953while ((p < (terminaltype + sizeof terminaltype-1)) &&954!SB_EOF()) {955int c;956957c = SB_GET();958if (isupper(c)) {959c = tolower(c);960}961*p++ = c; /* accumulate name */962}963*p = 0;964break;965} /* end of case TELOPT_TTYPE */966967case TELOPT_NAWS: {968int xwinsize, ywinsize;969970if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */971break;972973if (SB_EOF())974return;975xwinsize = SB_GET() << 8;976if (SB_EOF())977return;978xwinsize |= SB_GET();979if (SB_EOF())980return;981ywinsize = SB_GET() << 8;982if (SB_EOF())983return;984ywinsize |= SB_GET();985clientstat(TELOPT_NAWS, xwinsize, ywinsize);986987break;988989} /* end of case TELOPT_NAWS */990991case TELOPT_STATUS: {992int mode;993994if (SB_EOF())995break;996mode = SB_GET();997switch (mode) {998case TELQUAL_SEND:999if (my_state_is_will(TELOPT_STATUS))1000send_status();1001break;10021003case TELQUAL_IS:1004break;10051006default:1007break;1008}1009break;1010} /* end of case TELOPT_STATUS */10111012case TELOPT_XDISPLOC: {1013if (SB_EOF() || SB_GET() != TELQUAL_IS)1014return;1015settimer(xdisplocsubopt);1016subpointer[SB_LEN()] = '\0';1017esetenv("DISPLAY", (char *)subpointer, 1);1018break;1019} /* end of case TELOPT_XDISPLOC */10201021#ifdef TELOPT_NEW_ENVIRON1022case TELOPT_NEW_ENVIRON:1023#endif1024case TELOPT_OLD_ENVIRON: {1025int c;1026char *cp, *varp, *valp;10271028if (SB_EOF())1029return;1030c = SB_GET();1031if (c == TELQUAL_IS) {1032if (subchar == TELOPT_OLD_ENVIRON)1033settimer(oenvironsubopt);1034else1035settimer(environsubopt);1036} else if (c != TELQUAL_INFO) {1037return;1038}10391040#ifdef TELOPT_NEW_ENVIRON1041if (subchar == TELOPT_NEW_ENVIRON) {1042while (!SB_EOF()) {1043c = SB_GET();1044if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))1045break;1046}1047} else1048#endif1049{1050#ifdef ENV_HACK1051/*1052* We only want to do this if we haven't already decided1053* whether or not the other side has its VALUE and VAR1054* reversed.1055*/1056if (env_ovar < 0) {1057int last = -1; /* invalid value */1058int empty = 0;1059int got_var = 0, got_value = 0, got_uservar = 0;10601061/*1062* The other side might have its VALUE and VAR values1063* reversed. To be interoperable, we need to determine1064* which way it is. If the first recognized character1065* is a VAR or VALUE, then that will tell us what1066* type of client it is. If the fist recognized1067* character is a USERVAR, then we continue scanning1068* the suboption looking for two consecutive1069* VAR or VALUE fields. We should not get two1070* consecutive VALUE fields, so finding two1071* consecutive VALUE or VAR fields will tell us1072* what the client is.1073*/1074SB_SAVE();1075while (!SB_EOF()) {1076c = SB_GET();1077switch(c) {1078case OLD_ENV_VAR:1079if (last < 0 || last == OLD_ENV_VAR1080|| (empty && (last == OLD_ENV_VALUE)))1081goto env_ovar_ok;1082got_var++;1083last = OLD_ENV_VAR;1084break;1085case OLD_ENV_VALUE:1086if (last < 0 || last == OLD_ENV_VALUE1087|| (empty && (last == OLD_ENV_VAR)))1088goto env_ovar_wrong;1089got_value++;1090last = OLD_ENV_VALUE;1091break;1092case ENV_USERVAR:1093/* count strings of USERVAR as one */1094if (last != ENV_USERVAR)1095got_uservar++;1096if (empty) {1097if (last == OLD_ENV_VALUE)1098goto env_ovar_ok;1099if (last == OLD_ENV_VAR)1100goto env_ovar_wrong;1101}1102last = ENV_USERVAR;1103break;1104case ENV_ESC:1105if (!SB_EOF())1106c = SB_GET();1107/* FALL THROUGH */1108default:1109empty = 0;1110continue;1111}1112empty = 1;1113}1114if (empty) {1115if (last == OLD_ENV_VALUE)1116goto env_ovar_ok;1117if (last == OLD_ENV_VAR)1118goto env_ovar_wrong;1119}1120/*1121* Ok, the first thing was a USERVAR, and there1122* are not two consecutive VAR or VALUE commands,1123* and none of the VAR or VALUE commands are empty.1124* If the client has sent us a well-formed option,1125* then the number of VALUEs received should always1126* be less than or equal to the number of VARs and1127* USERVARs received.1128*1129* If we got exactly as many VALUEs as VARs and1130* USERVARs, the client has the same definitions.1131*1132* If we got exactly as many VARs as VALUEs and1133* USERVARS, the client has reversed definitions.1134*/1135if (got_uservar + got_var == got_value) {1136env_ovar_ok:1137env_ovar = OLD_ENV_VAR;1138env_ovalue = OLD_ENV_VALUE;1139} else if (got_uservar + got_value == got_var) {1140env_ovar_wrong:1141env_ovar = OLD_ENV_VALUE;1142env_ovalue = OLD_ENV_VAR;1143DIAG(TD_OPTIONS, {1144output_data("ENVIRON VALUE and VAR are reversed!\r\n");1145});11461147}1148}1149SB_RESTORE();1150#endif11511152while (!SB_EOF()) {1153c = SB_GET();1154if ((c == env_ovar) || (c == ENV_USERVAR))1155break;1156}1157}11581159if (SB_EOF())1160return;11611162cp = varp = (char *)subpointer;1163valp = 0;11641165while (!SB_EOF()) {1166c = SB_GET();1167if (subchar == TELOPT_OLD_ENVIRON) {1168if (c == env_ovar)1169c = NEW_ENV_VAR;1170else if (c == env_ovalue)1171c = NEW_ENV_VALUE;1172}1173switch (c) {11741175case NEW_ENV_VALUE:1176*cp = '\0';1177cp = valp = (char *)subpointer;1178break;11791180case NEW_ENV_VAR:1181case ENV_USERVAR:1182*cp = '\0';1183if (valp)1184esetenv(varp, valp, 1);1185else1186unsetenv(varp);1187cp = varp = (char *)subpointer;1188valp = 0;1189break;11901191case ENV_ESC:1192if (SB_EOF())1193break;1194c = SB_GET();1195/* FALL THROUGH */1196default:1197*cp++ = c;1198break;1199}1200}1201*cp = '\0';1202if (valp)1203esetenv(varp, valp, 1);1204else1205unsetenv(varp);1206break;1207} /* end of case TELOPT_NEW_ENVIRON */1208#ifdef AUTHENTICATION1209case TELOPT_AUTHENTICATION:1210if (SB_EOF())1211break;1212switch(SB_GET()) {1213case TELQUAL_SEND:1214case TELQUAL_REPLY:1215/*1216* These are sent by us and cannot be sent by1217* the client.1218*/1219break;1220case TELQUAL_IS:1221auth_is(subpointer, SB_LEN());1222break;1223case TELQUAL_NAME:1224auth_name(subpointer, SB_LEN());1225break;1226}1227break;1228#endif1229#ifdef ENCRYPTION1230case TELOPT_ENCRYPT:1231if (SB_EOF())1232break;1233switch(SB_GET()) {1234case ENCRYPT_SUPPORT:1235encrypt_support(subpointer, SB_LEN());1236break;1237case ENCRYPT_IS:1238encrypt_is(subpointer, SB_LEN());1239break;1240case ENCRYPT_REPLY:1241encrypt_reply(subpointer, SB_LEN());1242break;1243case ENCRYPT_START:1244encrypt_start(subpointer, SB_LEN());1245break;1246case ENCRYPT_END:1247if (require_encryption)1248fatal(net, "Output encryption is not possible to turn off");1249encrypt_end();1250break;1251case ENCRYPT_REQSTART:1252encrypt_request_start(subpointer, SB_LEN());1253break;1254case ENCRYPT_REQEND:1255/*1256* We can always send an REQEND so that we cannot1257* get stuck encrypting. We should only get this1258* if we have been able to get in the correct mode1259* anyhow.1260*/1261if (require_encryption)1262fatal(net, "Input encryption is not possible to turn off");1263encrypt_request_end();1264break;1265case ENCRYPT_ENC_KEYID:1266encrypt_enc_keyid(subpointer, SB_LEN());1267break;1268case ENCRYPT_DEC_KEYID:1269encrypt_dec_keyid(subpointer, SB_LEN());1270break;1271default:1272break;1273}1274break;1275#endif12761277default:1278break;1279} /* end of switch */12801281} /* end of suboption */12821283void1284doclientstat(void)1285{1286clientstat(TELOPT_LINEMODE, WILL, 0);1287}12881289#undef ADD1290#define ADD(c) *ncp++ = c1291#define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }12921293void1294send_status(void)1295{1296unsigned char statusbuf[256];1297unsigned char *ncp;1298unsigned char i;12991300ncp = statusbuf;13011302netflush(); /* get rid of anything waiting to go out */13031304ADD(IAC);1305ADD(SB);1306ADD(TELOPT_STATUS);1307ADD(TELQUAL_IS);13081309/*1310* We check the want_state rather than the current state,1311* because if we received a DO/WILL for an option that we1312* don't support, and the other side didn't send a DONT/WONT1313* in response to our WONT/DONT, then the "state" will be1314* WILL/DO, and the "want_state" will be WONT/DONT. We1315* need to go by the latter.1316*/1317for (i = 0; i < (unsigned char)NTELOPTS; i++) {1318if (my_want_state_is_will(i)) {1319ADD(WILL);1320ADD_DATA(i);1321}1322if (his_want_state_is_will(i)) {1323ADD(DO);1324ADD_DATA(i);1325}1326}13271328if (his_want_state_is_will(TELOPT_LFLOW)) {1329ADD(SB);1330ADD(TELOPT_LFLOW);1331if (flowmode) {1332ADD(LFLOW_ON);1333} else {1334ADD(LFLOW_OFF);1335}1336ADD(SE);13371338if (restartany >= 0) {1339ADD(SB);1340ADD(TELOPT_LFLOW);1341if (restartany) {1342ADD(LFLOW_RESTART_ANY);1343} else {1344ADD(LFLOW_RESTART_XON);1345}1346ADD(SE);1347}1348}134913501351ADD(IAC);1352ADD(SE);13531354writenet(statusbuf, ncp - statusbuf);1355netflush(); /* Send it on its way */13561357DIAG(TD_OPTIONS,1358{printsub('>', statusbuf, ncp - statusbuf); netflush();});1359}136013611362