]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - telnet.c
Revamp of EOF handling in all network connections, pipes and other
[PuTTY.git] / telnet.c
1 /*
2  * Telnet backend.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include "putty.h"
9
10 #ifndef FALSE
11 #define FALSE 0
12 #endif
13 #ifndef TRUE
14 #define TRUE 1
15 #endif
16
17 #define IAC     255                    /* interpret as command: */
18 #define DONT    254                    /* you are not to use option */
19 #define DO      253                    /* please, you use option */
20 #define WONT    252                    /* I won't use option */
21 #define WILL    251                    /* I will use option */
22 #define SB      250                    /* interpret as subnegotiation */
23 #define SE      240                    /* end sub negotiation */
24
25 #define GA      249                    /* you may reverse the line */
26 #define EL      248                    /* erase the current line */
27 #define EC      247                    /* erase the current character */
28 #define AYT     246                    /* are you there */
29 #define AO      245                    /* abort output--but let prog finish */
30 #define IP      244                    /* interrupt process--permanently */
31 #define BREAK   243                    /* break */
32 #define DM      242                    /* data mark--for connect. cleaning */
33 #define NOP     241                    /* nop */
34 #define EOR     239                    /* end of record (transparent mode) */
35 #define ABORT   238                    /* Abort process */
36 #define SUSP    237                    /* Suspend process */
37 #define xEOF    236                    /* End of file: EOF is already used... */
38
39 #define TELOPTS(X) \
40     X(BINARY, 0)                       /* 8-bit data path */ \
41     X(ECHO, 1)                         /* echo */ \
42     X(RCP, 2)                          /* prepare to reconnect */ \
43     X(SGA, 3)                          /* suppress go ahead */ \
44     X(NAMS, 4)                         /* approximate message size */ \
45     X(STATUS, 5)                       /* give status */ \
46     X(TM, 6)                           /* timing mark */ \
47     X(RCTE, 7)                         /* remote controlled transmission and echo */ \
48     X(NAOL, 8)                         /* negotiate about output line width */ \
49     X(NAOP, 9)                         /* negotiate about output page size */ \
50     X(NAOCRD, 10)                      /* negotiate about CR disposition */ \
51     X(NAOHTS, 11)                      /* negotiate about horizontal tabstops */ \
52     X(NAOHTD, 12)                      /* negotiate about horizontal tab disposition */ \
53     X(NAOFFD, 13)                      /* negotiate about formfeed disposition */ \
54     X(NAOVTS, 14)                      /* negotiate about vertical tab stops */ \
55     X(NAOVTD, 15)                      /* negotiate about vertical tab disposition */ \
56     X(NAOLFD, 16)                      /* negotiate about output LF disposition */ \
57     X(XASCII, 17)                      /* extended ascic character set */ \
58     X(LOGOUT, 18)                      /* force logout */ \
59     X(BM, 19)                          /* byte macro */ \
60     X(DET, 20)                         /* data entry terminal */ \
61     X(SUPDUP, 21)                      /* supdup protocol */ \
62     X(SUPDUPOUTPUT, 22)                /* supdup output */ \
63     X(SNDLOC, 23)                      /* send location */ \
64     X(TTYPE, 24)                       /* terminal type */ \
65     X(EOR, 25)                         /* end or record */ \
66     X(TUID, 26)                        /* TACACS user identification */ \
67     X(OUTMRK, 27)                      /* output marking */ \
68     X(TTYLOC, 28)                      /* terminal location number */ \
69     X(3270REGIME, 29)                  /* 3270 regime */ \
70     X(X3PAD, 30)                       /* X.3 PAD */ \
71     X(NAWS, 31)                        /* window size */ \
72     X(TSPEED, 32)                      /* terminal speed */ \
73     X(LFLOW, 33)                       /* remote flow control */ \
74     X(LINEMODE, 34)                    /* Linemode option */ \
75     X(XDISPLOC, 35)                    /* X Display Location */ \
76     X(OLD_ENVIRON, 36)                 /* Old - Environment variables */ \
77     X(AUTHENTICATION, 37)              /* Authenticate */ \
78     X(ENCRYPT, 38)                     /* Encryption option */ \
79     X(NEW_ENVIRON, 39)                 /* New - Environment variables */ \
80     X(TN3270E, 40)                     /* TN3270 enhancements */ \
81     X(XAUTH, 41)                       \
82     X(CHARSET, 42)                     /* Character set */ \
83     X(RSP, 43)                         /* Remote serial port */ \
84     X(COM_PORT_OPTION, 44)             /* Com port control */ \
85     X(SLE, 45)                         /* Suppress local echo */ \
86     X(STARTTLS, 46)                    /* Start TLS */ \
87     X(KERMIT, 47)                      /* Automatic Kermit file transfer */ \
88     X(SEND_URL, 48)                    \
89     X(FORWARD_X, 49)                   \
90     X(PRAGMA_LOGON, 138)               \
91     X(SSPI_LOGON, 139)                 \
92     X(PRAGMA_HEARTBEAT, 140)           \
93     X(EXOPL, 255)                      /* extended-options-list */
94
95 #define telnet_enum(x,y) TELOPT_##x = y,
96 enum { TELOPTS(telnet_enum) dummy=0 };
97 #undef telnet_enum
98
99 #define TELQUAL_IS      0              /* option is... */
100 #define TELQUAL_SEND    1              /* send option */
101 #define TELQUAL_INFO    2              /* ENVIRON: informational version of IS */
102 #define BSD_VAR 1
103 #define BSD_VALUE 0
104 #define RFC_VAR 0
105 #define RFC_VALUE 1
106
107 #define CR 13
108 #define LF 10
109 #define NUL 0
110
111 #define iswritable(x) \
112         ( (x) != IAC && \
113               (telnet->opt_states[o_we_bin.index] == ACTIVE || (x) != CR))
114
115 static char *telopt(int opt)
116 {
117 #define telnet_str(x,y) case TELOPT_##x: return #x;
118     switch (opt) {
119         TELOPTS(telnet_str)
120       default:
121         return "<unknown>";
122     }
123 #undef telnet_str
124 }
125
126 static void telnet_size(void *handle, int width, int height);
127
128 struct Opt {
129     int send;                          /* what we initially send */
130     int nsend;                         /* -ve send if requested to stop it */
131     int ack, nak;                      /* +ve and -ve acknowledgements */
132     int option;                        /* the option code */
133     int index;                         /* index into telnet->opt_states[] */
134     enum {
135         REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE
136     } initial_state;
137 };
138
139 enum {
140     OPTINDEX_NAWS,
141     OPTINDEX_TSPEED,
142     OPTINDEX_TTYPE,
143     OPTINDEX_OENV,
144     OPTINDEX_NENV,
145     OPTINDEX_ECHO,
146     OPTINDEX_WE_SGA,
147     OPTINDEX_THEY_SGA,
148     OPTINDEX_WE_BIN,
149     OPTINDEX_THEY_BIN,
150     NUM_OPTS
151 };
152
153 static const struct Opt o_naws =
154     { WILL, WONT, DO, DONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED };
155 static const struct Opt o_tspeed =
156     { WILL, WONT, DO, DONT, TELOPT_TSPEED, OPTINDEX_TSPEED, REQUESTED };
157 static const struct Opt o_ttype =
158     { WILL, WONT, DO, DONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED };
159 static const struct Opt o_oenv =
160     { WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE };
161 static const struct Opt o_nenv =
162     { WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED };
163 static const struct Opt o_echo =
164     { DO, DONT, WILL, WONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED };
165 static const struct Opt o_we_sga =
166     { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED };
167 static const struct Opt o_they_sga =
168     { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED };
169 static const struct Opt o_we_bin =
170     { WILL, WONT, DO, DONT, TELOPT_BINARY, OPTINDEX_WE_BIN, INACTIVE };
171 static const struct Opt o_they_bin =
172     { DO, DONT, WILL, WONT, TELOPT_BINARY, OPTINDEX_THEY_BIN, INACTIVE };
173
174 static const struct Opt *const opts[] = {
175     &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo,
176     &o_we_sga, &o_they_sga, &o_we_bin, &o_they_bin, NULL
177 };
178
179 typedef struct telnet_tag {
180     const struct plug_function_table *fn;
181     /* the above field _must_ be first in the structure */
182
183     Socket s;
184
185     void *frontend;
186     void *ldisc;
187     int term_width, term_height;
188
189     int opt_states[NUM_OPTS];
190
191     int echoing, editing;
192     int activated;
193     int bufsize;
194     int in_synch;
195     int sb_opt, sb_len;
196     unsigned char *sb_buf;
197     int sb_size;
198
199     enum {
200         TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
201             SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
202     } state;
203
204     Conf *conf;
205
206     Pinger pinger;
207 } *Telnet;
208
209 #define TELNET_MAX_BACKLOG 4096
210
211 #define SB_DELTA 1024
212
213 static void c_write(Telnet telnet, char *buf, int len)
214 {
215     int backlog;
216     backlog = from_backend(telnet->frontend, 0, buf, len);
217     sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
218 }
219
220 static void log_option(Telnet telnet, char *sender, int cmd, int option)
221 {
222     char *buf;
223     /*
224      * The strange-looking "<?""?>" below is there to avoid a
225      * trigraph - a double question mark followed by > maps to a
226      * closing brace character!
227      */
228     buf = dupprintf("%s:\t%s %s", sender,
229                     (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :
230                      cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<?""?>"),
231                     telopt(option));
232     logevent(telnet->frontend, buf);
233     sfree(buf);
234 }
235
236 static void send_opt(Telnet telnet, int cmd, int option)
237 {
238     unsigned char b[3];
239
240     b[0] = IAC;
241     b[1] = cmd;
242     b[2] = option;
243     telnet->bufsize = sk_write(telnet->s, (char *)b, 3);
244     log_option(telnet, "client", cmd, option);
245 }
246
247 static void deactivate_option(Telnet telnet, const struct Opt *o)
248 {
249     if (telnet->opt_states[o->index] == REQUESTED ||
250         telnet->opt_states[o->index] == ACTIVE)
251         send_opt(telnet, o->nsend, o->option);
252     telnet->opt_states[o->index] = REALLY_INACTIVE;
253 }
254
255 /*
256  * Generate side effects of enabling or disabling an option.
257  */
258 static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)
259 {
260     if (o->option == TELOPT_ECHO && o->send == DO)
261         telnet->echoing = !enabled;
262     else if (o->option == TELOPT_SGA && o->send == DO)
263         telnet->editing = !enabled;
264     if (telnet->ldisc)                 /* cause ldisc to notice the change */
265         ldisc_send(telnet->ldisc, NULL, 0, 0);
266
267     /* Ensure we get the minimum options */
268     if (!telnet->activated) {
269         if (telnet->opt_states[o_echo.index] == INACTIVE) {
270             telnet->opt_states[o_echo.index] = REQUESTED;
271             send_opt(telnet, o_echo.send, o_echo.option);
272         }
273         if (telnet->opt_states[o_we_sga.index] == INACTIVE) {
274             telnet->opt_states[o_we_sga.index] = REQUESTED;
275             send_opt(telnet, o_we_sga.send, o_we_sga.option);
276         }
277         if (telnet->opt_states[o_they_sga.index] == INACTIVE) {
278             telnet->opt_states[o_they_sga.index] = REQUESTED;
279             send_opt(telnet, o_they_sga.send, o_they_sga.option);
280         }
281         telnet->activated = TRUE;
282     }
283 }
284
285 static void activate_option(Telnet telnet, const struct Opt *o)
286 {
287     if (o->send == WILL && o->option == TELOPT_NAWS)
288         telnet_size(telnet, telnet->term_width, telnet->term_height);
289     if (o->send == WILL &&
290         (o->option == TELOPT_NEW_ENVIRON ||
291          o->option == TELOPT_OLD_ENVIRON)) {
292         /*
293          * We may only have one kind of ENVIRON going at a time.
294          * This is a hack, but who cares.
295          */
296         deactivate_option(telnet, o->option ==
297                           TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);
298     }
299     option_side_effects(telnet, o, 1);
300 }
301
302 static void refused_option(Telnet telnet, const struct Opt *o)
303 {
304     if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&
305         telnet->opt_states[o_oenv.index] == INACTIVE) {
306         send_opt(telnet, WILL, TELOPT_OLD_ENVIRON);
307         telnet->opt_states[o_oenv.index] = REQUESTED;
308     }
309     option_side_effects(telnet, o, 0);
310 }
311
312 static void proc_rec_opt(Telnet telnet, int cmd, int option)
313 {
314     const struct Opt *const *o;
315
316     log_option(telnet, "server", cmd, option);
317     for (o = opts; *o; o++) {
318         if ((*o)->option == option && (*o)->ack == cmd) {
319             switch (telnet->opt_states[(*o)->index]) {
320               case REQUESTED:
321                 telnet->opt_states[(*o)->index] = ACTIVE;
322                 activate_option(telnet, *o);
323                 break;
324               case ACTIVE:
325                 break;
326               case INACTIVE:
327                 telnet->opt_states[(*o)->index] = ACTIVE;
328                 send_opt(telnet, (*o)->send, option);
329                 activate_option(telnet, *o);
330                 break;
331               case REALLY_INACTIVE:
332                 send_opt(telnet, (*o)->nsend, option);
333                 break;
334             }
335             return;
336         } else if ((*o)->option == option && (*o)->nak == cmd) {
337             switch (telnet->opt_states[(*o)->index]) {
338               case REQUESTED:
339                 telnet->opt_states[(*o)->index] = INACTIVE;
340                 refused_option(telnet, *o);
341                 break;
342               case ACTIVE:
343                 telnet->opt_states[(*o)->index] = INACTIVE;
344                 send_opt(telnet, (*o)->nsend, option);
345                 option_side_effects(telnet, *o, 0);
346                 break;
347               case INACTIVE:
348               case REALLY_INACTIVE:
349                 break;
350             }
351             return;
352         }
353     }
354     /*
355      * If we reach here, the option was one we weren't prepared to
356      * cope with. If the request was positive (WILL or DO), we send
357      * a negative ack to indicate refusal. If the request was
358      * negative (WONT / DONT), we must do nothing.
359      */
360     if (cmd == WILL || cmd == DO)
361         send_opt(telnet, (cmd == WILL ? DONT : WONT), option);
362 }
363
364 static void process_subneg(Telnet telnet)
365 {
366     unsigned char *b, *p, *q;
367     int var, value, n, bsize;
368     char *e, *eval, *ekey, *user;
369
370     switch (telnet->sb_opt) {
371       case TELOPT_TSPEED:
372         if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
373             char *logbuf;
374             char *termspeed = conf_get_str(telnet->conf, CONF_termspeed);
375             b = snewn(20 + strlen(termspeed), unsigned char);
376             b[0] = IAC;
377             b[1] = SB;
378             b[2] = TELOPT_TSPEED;
379             b[3] = TELQUAL_IS;
380             strcpy((char *)(b + 4), termspeed);
381             n = 4 + strlen(termspeed);
382             b[n] = IAC;
383             b[n + 1] = SE;
384             telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2);
385             logevent(telnet->frontend, "server:\tSB TSPEED SEND");
386             logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed);
387             logevent(telnet->frontend, logbuf);
388             sfree(logbuf);
389             sfree(b);
390         } else
391             logevent(telnet->frontend, "server:\tSB TSPEED <something weird>");
392         break;
393       case TELOPT_TTYPE:
394         if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
395             char *logbuf;
396             char *termtype = conf_get_str(telnet->conf, CONF_termtype);
397             b = snewn(20 + strlen(termtype), unsigned char);
398             b[0] = IAC;
399             b[1] = SB;
400             b[2] = TELOPT_TTYPE;
401             b[3] = TELQUAL_IS;
402             for (n = 0; termtype[n]; n++)
403                 b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ?
404                             termtype[n] + 'A' - 'a' :
405                             termtype[n]);
406             b[n + 4] = IAC;
407             b[n + 5] = SE;
408             telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6);
409             b[n + 4] = 0;
410             logevent(telnet->frontend, "server:\tSB TTYPE SEND");
411             logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4);
412             logevent(telnet->frontend, logbuf);
413             sfree(logbuf);
414             sfree(b);
415         } else
416             logevent(telnet->frontend, "server:\tSB TTYPE <something weird>\r\n");
417         break;
418       case TELOPT_OLD_ENVIRON:
419       case TELOPT_NEW_ENVIRON:
420         p = telnet->sb_buf;
421         q = p + telnet->sb_len;
422         if (p < q && *p == TELQUAL_SEND) {
423             char *logbuf;
424             p++;
425             logbuf = dupprintf("server:\tSB %s SEND", telopt(telnet->sb_opt));
426             logevent(telnet->frontend, logbuf);
427             sfree(logbuf);
428             if (telnet->sb_opt == TELOPT_OLD_ENVIRON) {
429                 if (conf_get_int(telnet->conf, CONF_rfc_environ)) {
430                     value = RFC_VALUE;
431                     var = RFC_VAR;
432                 } else {
433                     value = BSD_VALUE;
434                     var = BSD_VAR;
435                 }
436                 /*
437                  * Try to guess the sense of VAR and VALUE.
438                  */
439                 while (p < q) {
440                     if (*p == RFC_VAR) {
441                         value = RFC_VALUE;
442                         var = RFC_VAR;
443                     } else if (*p == BSD_VAR) {
444                         value = BSD_VALUE;
445                         var = BSD_VAR;
446                     }
447                     p++;
448                 }
449             } else {
450                 /*
451                  * With NEW_ENVIRON, the sense of VAR and VALUE
452                  * isn't in doubt.
453                  */
454                 value = RFC_VALUE;
455                 var = RFC_VAR;
456             }
457             bsize = 20;
458             for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
459                                          NULL, &ekey);
460                  eval != NULL;
461                  eval = conf_get_str_strs(telnet->conf, CONF_environmt,
462                                          ekey, &ekey))
463                  bsize += strlen(ekey) + strlen(eval) + 2;
464             user = get_remote_username(telnet->conf);
465             if (user)
466                 bsize += 6 + strlen(user);
467
468             b = snewn(bsize, unsigned char);
469             b[0] = IAC;
470             b[1] = SB;
471             b[2] = telnet->sb_opt;
472             b[3] = TELQUAL_IS;
473             n = 4;
474             for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
475                                          NULL, &ekey);
476                  eval != NULL;
477                  eval = conf_get_str_strs(telnet->conf, CONF_environmt,
478                                          ekey, &ekey)) {
479                 b[n++] = var;
480                 for (e = ekey; *e; e++)
481                     b[n++] = *e;
482                 b[n++] = value;
483                 for (e = eval; *e; e++)
484                     b[n++] = *e;
485             }
486             if (user) {
487                 b[n++] = var;
488                 b[n++] = 'U';
489                 b[n++] = 'S';
490                 b[n++] = 'E';
491                 b[n++] = 'R';
492                 b[n++] = value;
493                 for (e = user; *e; e++)
494                     b[n++] = *e;
495             }
496             b[n++] = IAC;
497             b[n++] = SE;
498             telnet->bufsize = sk_write(telnet->s, (char *)b, n);
499             if (n == 6) {
500                 logbuf = dupprintf("client:\tSB %s IS <nothing>",
501                                    telopt(telnet->sb_opt));
502                 logevent(telnet->frontend, logbuf);
503                 sfree(logbuf);
504             } else {
505                 logbuf = dupprintf("client:\tSB %s IS:",
506                                    telopt(telnet->sb_opt));
507                 logevent(telnet->frontend, logbuf);
508                 sfree(logbuf);
509                 for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
510                                              NULL, &ekey);
511                      eval != NULL;
512                      eval = conf_get_str_strs(telnet->conf, CONF_environmt,
513                                              ekey, &ekey)) {
514                     logbuf = dupprintf("\t%s=%s", ekey, eval);
515                     logevent(telnet->frontend, logbuf);
516                     sfree(logbuf);
517                 }
518                 if (user) {
519                     logbuf = dupprintf("\tUSER=%s", user);
520                     logevent(telnet->frontend, logbuf);
521                     sfree(logbuf);
522                 }
523             }
524             sfree(b);
525             sfree(user);
526         }
527         break;
528     }
529 }
530
531 static void do_telnet_read(Telnet telnet, char *buf, int len)
532 {
533     char *outbuf = NULL;
534     int outbuflen = 0, outbufsize = 0;
535
536 #define ADDTOBUF(c) do { \
537     if (outbuflen >= outbufsize) { \
538         outbufsize = outbuflen + 256; \
539         outbuf = sresize(outbuf, outbufsize, char); \
540     } \
541     outbuf[outbuflen++] = (c); \
542 } while (0)
543
544     while (len--) {
545         int c = (unsigned char) *buf++;
546
547         switch (telnet->state) {
548           case TOP_LEVEL:
549           case SEENCR:
550             if (c == NUL && telnet->state == SEENCR)
551                 telnet->state = TOP_LEVEL;
552             else if (c == IAC)
553                 telnet->state = SEENIAC;
554             else {
555                 if (!telnet->in_synch)
556                     ADDTOBUF(c);
557
558 #if 1
559                 /* I can't get the F***ing winsock to insert the urgent IAC
560                  * into the right position! Even with SO_OOBINLINE it gives
561                  * it to recv too soon. And of course the DM byte (that
562                  * arrives in the same packet!) appears several K later!!
563                  *
564                  * Oh well, we do get the DM in the right place so I'll
565                  * just stop hiding on the next 0xf2 and hope for the best.
566                  */
567                 else if (c == DM)
568                     telnet->in_synch = 0;
569 #endif
570                 if (c == CR && telnet->opt_states[o_they_bin.index] != ACTIVE)
571                     telnet->state = SEENCR;
572                 else
573                     telnet->state = TOP_LEVEL;
574             }
575             break;
576           case SEENIAC:
577             if (c == DO)
578                 telnet->state = SEENDO;
579             else if (c == DONT)
580                 telnet->state = SEENDONT;
581             else if (c == WILL)
582                 telnet->state = SEENWILL;
583             else if (c == WONT)
584                 telnet->state = SEENWONT;
585             else if (c == SB)
586                 telnet->state = SEENSB;
587             else if (c == DM) {
588                 telnet->in_synch = 0;
589                 telnet->state = TOP_LEVEL;
590             } else {
591                 /* ignore everything else; print it if it's IAC */
592                 if (c == IAC) {
593                     ADDTOBUF(c);
594                 }
595                 telnet->state = TOP_LEVEL;
596             }
597             break;
598           case SEENWILL:
599             proc_rec_opt(telnet, WILL, c);
600             telnet->state = TOP_LEVEL;
601             break;
602           case SEENWONT:
603             proc_rec_opt(telnet, WONT, c);
604             telnet->state = TOP_LEVEL;
605             break;
606           case SEENDO:
607             proc_rec_opt(telnet, DO, c);
608             telnet->state = TOP_LEVEL;
609             break;
610           case SEENDONT:
611             proc_rec_opt(telnet, DONT, c);
612             telnet->state = TOP_LEVEL;
613             break;
614           case SEENSB:
615             telnet->sb_opt = c;
616             telnet->sb_len = 0;
617             telnet->state = SUBNEGOT;
618             break;
619           case SUBNEGOT:
620             if (c == IAC)
621                 telnet->state = SUBNEG_IAC;
622             else {
623               subneg_addchar:
624                 if (telnet->sb_len >= telnet->sb_size) {
625                     telnet->sb_size += SB_DELTA;
626                     telnet->sb_buf = sresize(telnet->sb_buf, telnet->sb_size,
627                                              unsigned char);
628                 }
629                 telnet->sb_buf[telnet->sb_len++] = c;
630                 telnet->state = SUBNEGOT;       /* in case we came here by goto */
631             }
632             break;
633           case SUBNEG_IAC:
634             if (c != SE)
635                 goto subneg_addchar;   /* yes, it's a hack, I know, but... */
636             else {
637                 process_subneg(telnet);
638                 telnet->state = TOP_LEVEL;
639             }
640             break;
641         }
642     }
643
644     if (outbuflen)
645         c_write(telnet, outbuf, outbuflen);
646     sfree(outbuf);
647 }
648
649 static void telnet_log(Plug plug, int type, SockAddr addr, int port,
650                        const char *error_msg, int error_code)
651 {
652     Telnet telnet = (Telnet) plug;
653     char addrbuf[256], *msg;
654
655     sk_getaddr(addr, addrbuf, lenof(addrbuf));
656
657     if (type == 0)
658         msg = dupprintf("Connecting to %s port %d", addrbuf, port);
659     else
660         msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
661
662     logevent(telnet->frontend, msg);
663 }
664
665 static int telnet_closing(Plug plug, const char *error_msg, int error_code,
666                           int calling_back)
667 {
668     Telnet telnet = (Telnet) plug;
669
670     /*
671      * We don't implement independent EOF in each direction for Telnet
672      * connections; as soon as we get word that the remote side has
673      * sent us EOF, we wind up the whole connection.
674      */
675
676     if (telnet->s) {
677         sk_close(telnet->s);
678         telnet->s = NULL;
679         notify_remote_exit(telnet->frontend);
680     }
681     if (error_msg) {
682         logevent(telnet->frontend, error_msg);
683         connection_fatal(telnet->frontend, "%s", error_msg);
684     }
685     /* Otherwise, the remote side closed the connection normally. */
686     return 0;
687 }
688
689 static int telnet_receive(Plug plug, int urgent, char *data, int len)
690 {
691     Telnet telnet = (Telnet) plug;
692     if (urgent)
693         telnet->in_synch = TRUE;
694     do_telnet_read(telnet, data, len);
695     return 1;
696 }
697
698 static void telnet_sent(Plug plug, int bufsize)
699 {
700     Telnet telnet = (Telnet) plug;
701     telnet->bufsize = bufsize;
702 }
703
704 /*
705  * Called to set up the Telnet connection.
706  *
707  * Returns an error message, or NULL on success.
708  *
709  * Also places the canonical host name into `realhost'. It must be
710  * freed by the caller.
711  */
712 static const char *telnet_init(void *frontend_handle, void **backend_handle,
713                                Conf *conf, char *host, int port,
714                                char **realhost, int nodelay, int keepalive)
715 {
716     static const struct plug_function_table fn_table = {
717         telnet_log,
718         telnet_closing,
719         telnet_receive,
720         telnet_sent
721     };
722     SockAddr addr;
723     const char *err;
724     Telnet telnet;
725     char *loghost;
726     int addressfamily;
727
728     telnet = snew(struct telnet_tag);
729     telnet->fn = &fn_table;
730     telnet->conf = conf_copy(conf);
731     telnet->s = NULL;
732     telnet->echoing = TRUE;
733     telnet->editing = TRUE;
734     telnet->activated = FALSE;
735     telnet->sb_buf = NULL;
736     telnet->sb_size = 0;
737     telnet->frontend = frontend_handle;
738     telnet->term_width = conf_get_int(telnet->conf, CONF_width);
739     telnet->term_height = conf_get_int(telnet->conf, CONF_height);
740     telnet->state = TOP_LEVEL;
741     telnet->ldisc = NULL;
742     telnet->pinger = NULL;
743     *backend_handle = telnet;
744
745     /*
746      * Try to find host.
747      */
748     {
749         char *buf;
750         addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
751         buf = dupprintf("Looking up host \"%s\"%s", host,
752                         (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
753                          (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
754                           "")));
755         logevent(telnet->frontend, buf);
756         sfree(buf);
757     }
758     addr = name_lookup(host, port, realhost, telnet->conf, addressfamily);
759     if ((err = sk_addr_error(addr)) != NULL) {
760         sk_addr_free(addr);
761         return err;
762     }
763
764     if (port < 0)
765         port = 23;                     /* default telnet port */
766
767     /*
768      * Open socket.
769      */
770     telnet->s = new_connection(addr, *realhost, port, 0, 1,
771                                nodelay, keepalive, (Plug) telnet, telnet->conf);
772     if ((err = sk_socket_error(telnet->s)) != NULL)
773         return err;
774
775     telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet);
776
777     /*
778      * Initialise option states.
779      */
780     if (conf_get_int(telnet->conf, CONF_passive_telnet)) {
781         const struct Opt *const *o;
782
783         for (o = opts; *o; o++)
784             telnet->opt_states[(*o)->index] = INACTIVE;
785     } else {
786         const struct Opt *const *o;
787
788         for (o = opts; *o; o++) {
789             telnet->opt_states[(*o)->index] = (*o)->initial_state;
790             if (telnet->opt_states[(*o)->index] == REQUESTED)
791                 send_opt(telnet, (*o)->send, (*o)->option);
792         }
793         telnet->activated = TRUE;
794     }
795
796     /*
797      * Set up SYNCH state.
798      */
799     telnet->in_synch = FALSE;
800
801     /*
802      * We can send special commands from the start.
803      */
804     update_specials_menu(telnet->frontend);
805
806     /*
807      * loghost overrides realhost, if specified.
808      */
809     loghost = conf_get_str(telnet->conf, CONF_loghost);
810     if (*loghost) {
811         char *colon;
812
813         sfree(*realhost);
814         *realhost = dupstr(loghost);
815         colon = strrchr(*realhost, ':');
816         if (colon) {
817             /*
818              * FIXME: if we ever update this aspect of ssh.c for
819              * IPv6 literal management, this should change in line
820              * with it.
821              */
822             *colon++ = '\0';
823         }
824     }
825
826     return NULL;
827 }
828
829 static void telnet_free(void *handle)
830 {
831     Telnet telnet = (Telnet) handle;
832
833     sfree(telnet->sb_buf);
834     if (telnet->s)
835         sk_close(telnet->s);
836     if (telnet->pinger)
837         pinger_free(telnet->pinger);
838     conf_free(telnet->conf);
839     sfree(telnet);
840 }
841 /*
842  * Reconfigure the Telnet backend. There's no immediate action
843  * necessary, in this backend: we just save the fresh config for
844  * any subsequent negotiations.
845  */
846 static void telnet_reconfig(void *handle, Conf *conf)
847 {
848     Telnet telnet = (Telnet) handle;
849     pinger_reconfig(telnet->pinger, telnet->conf, conf);
850     conf_free(telnet->conf);
851     telnet->conf = conf_copy(conf);
852 }
853
854 /*
855  * Called to send data down the Telnet connection.
856  */
857 static int telnet_send(void *handle, char *buf, int len)
858 {
859     Telnet telnet = (Telnet) handle;
860     unsigned char *p, *end;
861     static const unsigned char iac[2] = { IAC, IAC };
862     static const unsigned char cr[2] = { CR, NUL };
863 #if 0
864     static const unsigned char nl[2] = { CR, LF };
865 #endif
866
867     if (telnet->s == NULL)
868         return 0;
869
870     p = (unsigned char *)buf;
871     end = (unsigned char *)(buf + len);
872     while (p < end) {
873         unsigned char *q = p;
874
875         while (p < end && iswritable(*p))
876             p++;
877         telnet->bufsize = sk_write(telnet->s, (char *)q, p - q);
878
879         while (p < end && !iswritable(*p)) {
880             telnet->bufsize = 
881                 sk_write(telnet->s, (char *)(*p == IAC ? iac : cr), 2);
882             p++;
883         }
884     }
885
886     return telnet->bufsize;
887 }
888
889 /*
890  * Called to query the current socket sendability status.
891  */
892 static int telnet_sendbuffer(void *handle)
893 {
894     Telnet telnet = (Telnet) handle;
895     return telnet->bufsize;
896 }
897
898 /*
899  * Called to set the size of the window from Telnet's POV.
900  */
901 static void telnet_size(void *handle, int width, int height)
902 {
903     Telnet telnet = (Telnet) handle;
904     unsigned char b[24];
905     int n;
906     char *logbuf;
907
908     telnet->term_width = width;
909     telnet->term_height = height;
910
911     if (telnet->s == NULL || telnet->opt_states[o_naws.index] != ACTIVE)
912         return;
913     n = 0;
914     b[n++] = IAC;
915     b[n++] = SB;
916     b[n++] = TELOPT_NAWS;
917     b[n++] = telnet->term_width >> 8;
918     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
919     b[n++] = telnet->term_width & 0xFF;
920     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
921     b[n++] = telnet->term_height >> 8;
922     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
923     b[n++] = telnet->term_height & 0xFF;
924     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
925     b[n++] = IAC;
926     b[n++] = SE;
927     telnet->bufsize = sk_write(telnet->s, (char *)b, n);
928     logbuf = dupprintf("client:\tSB NAWS %d,%d",
929                        telnet->term_width, telnet->term_height);
930     logevent(telnet->frontend, logbuf);
931     sfree(logbuf);
932 }
933
934 /*
935  * Send Telnet special codes.
936  */
937 static void telnet_special(void *handle, Telnet_Special code)
938 {
939     Telnet telnet = (Telnet) handle;
940     unsigned char b[2];
941
942     if (telnet->s == NULL)
943         return;
944
945     b[0] = IAC;
946     switch (code) {
947       case TS_AYT:
948         b[1] = AYT;
949         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
950         break;
951       case TS_BRK:
952         b[1] = BREAK;
953         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
954         break;
955       case TS_EC:
956         b[1] = EC;
957         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
958         break;
959       case TS_EL:
960         b[1] = EL;
961         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
962         break;
963       case TS_GA:
964         b[1] = GA;
965         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
966         break;
967       case TS_NOP:
968         b[1] = NOP;
969         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
970         break;
971       case TS_ABORT:
972         b[1] = ABORT;
973         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
974         break;
975       case TS_AO:
976         b[1] = AO;
977         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
978         break;
979       case TS_IP:
980         b[1] = IP;
981         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
982         break;
983       case TS_SUSP:
984         b[1] = SUSP;
985         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
986         break;
987       case TS_EOR:
988         b[1] = EOR;
989         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
990         break;
991       case TS_EOF:
992         b[1] = xEOF;
993         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
994         break;
995       case TS_EOL:
996         /* In BINARY mode, CR-LF becomes just CR -
997          * and without the NUL suffix too. */
998         if (telnet->opt_states[o_we_bin.index] == ACTIVE)
999             telnet->bufsize = sk_write(telnet->s, "\r", 1);
1000         else
1001             telnet->bufsize = sk_write(telnet->s, "\r\n", 2);
1002         break;
1003       case TS_SYNCH:
1004         b[1] = DM;
1005         telnet->bufsize = sk_write(telnet->s, (char *)b, 1);
1006         telnet->bufsize = sk_write_oob(telnet->s, (char *)(b + 1), 1);
1007         break;
1008       case TS_RECHO:
1009         if (telnet->opt_states[o_echo.index] == INACTIVE ||
1010             telnet->opt_states[o_echo.index] == REALLY_INACTIVE) {
1011             telnet->opt_states[o_echo.index] = REQUESTED;
1012             send_opt(telnet, o_echo.send, o_echo.option);
1013         }
1014         break;
1015       case TS_LECHO:
1016         if (telnet->opt_states[o_echo.index] == ACTIVE) {
1017             telnet->opt_states[o_echo.index] = REQUESTED;
1018             send_opt(telnet, o_echo.nsend, o_echo.option);
1019         }
1020         break;
1021       case TS_PING:
1022         if (telnet->opt_states[o_they_sga.index] == ACTIVE) {
1023             b[1] = NOP;
1024             telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
1025         }
1026         break;
1027       default:
1028         break;  /* never heard of it */
1029     }
1030 }
1031
1032 static const struct telnet_special *telnet_get_specials(void *handle)
1033 {
1034     static const struct telnet_special specials[] = {
1035         {"Are You There", TS_AYT},
1036         {"Break", TS_BRK},
1037         {"Synch", TS_SYNCH},
1038         {"Erase Character", TS_EC},
1039         {"Erase Line", TS_EL},
1040         {"Go Ahead", TS_GA},
1041         {"No Operation", TS_NOP},
1042         {NULL, TS_SEP},
1043         {"Abort Process", TS_ABORT},
1044         {"Abort Output", TS_AO},
1045         {"Interrupt Process", TS_IP},
1046         {"Suspend Process", TS_SUSP},
1047         {NULL, TS_SEP},
1048         {"End Of Record", TS_EOR},
1049         {"End Of File", TS_EOF},
1050         {NULL, TS_EXITMENU}
1051     };
1052     return specials;
1053 }
1054
1055 static int telnet_connected(void *handle)
1056 {
1057     Telnet telnet = (Telnet) handle;
1058     return telnet->s != NULL;
1059 }
1060
1061 static int telnet_sendok(void *handle)
1062 {
1063     /* Telnet telnet = (Telnet) handle; */
1064     return 1;
1065 }
1066
1067 static void telnet_unthrottle(void *handle, int backlog)
1068 {
1069     Telnet telnet = (Telnet) handle;
1070     sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
1071 }
1072
1073 static int telnet_ldisc(void *handle, int option)
1074 {
1075     Telnet telnet = (Telnet) handle;
1076     if (option == LD_ECHO)
1077         return telnet->echoing;
1078     if (option == LD_EDIT)
1079         return telnet->editing;
1080     return FALSE;
1081 }
1082
1083 static void telnet_provide_ldisc(void *handle, void *ldisc)
1084 {
1085     Telnet telnet = (Telnet) handle;
1086     telnet->ldisc = ldisc;
1087 }
1088
1089 static void telnet_provide_logctx(void *handle, void *logctx)
1090 {
1091     /* This is a stub. */
1092 }
1093
1094 static int telnet_exitcode(void *handle)
1095 {
1096     Telnet telnet = (Telnet) handle;
1097     if (telnet->s != NULL)
1098         return -1;                     /* still connected */
1099     else
1100         /* Telnet doesn't transmit exit codes back to the client */
1101         return 0;
1102 }
1103
1104 /*
1105  * cfg_info for Telnet does nothing at all.
1106  */
1107 static int telnet_cfg_info(void *handle)
1108 {
1109     return 0;
1110 }
1111
1112 Backend telnet_backend = {
1113     telnet_init,
1114     telnet_free,
1115     telnet_reconfig,
1116     telnet_send,
1117     telnet_sendbuffer,
1118     telnet_size,
1119     telnet_special,
1120     telnet_get_specials,
1121     telnet_connected,
1122     telnet_exitcode,
1123     telnet_sendok,
1124     telnet_ldisc,
1125     telnet_provide_ldisc,
1126     telnet_provide_logctx,
1127     telnet_unthrottle,
1128     telnet_cfg_info,
1129     "telnet",
1130     PROT_TELNET,
1131     23
1132 };