]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - telnet.c
Post-release destabilisation! Completely remove the struct type
[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_str(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     if (telnet->s) {
671         sk_close(telnet->s);
672         telnet->s = NULL;
673         notify_remote_exit(telnet->frontend);
674     }
675     if (error_msg) {
676         logevent(telnet->frontend, error_msg);
677         connection_fatal(telnet->frontend, "%s", error_msg);
678     }
679     /* Otherwise, the remote side closed the connection normally. */
680     return 0;
681 }
682
683 static int telnet_receive(Plug plug, int urgent, char *data, int len)
684 {
685     Telnet telnet = (Telnet) plug;
686     if (urgent)
687         telnet->in_synch = TRUE;
688     do_telnet_read(telnet, data, len);
689     return 1;
690 }
691
692 static void telnet_sent(Plug plug, int bufsize)
693 {
694     Telnet telnet = (Telnet) plug;
695     telnet->bufsize = bufsize;
696 }
697
698 /*
699  * Called to set up the Telnet connection.
700  *
701  * Returns an error message, or NULL on success.
702  *
703  * Also places the canonical host name into `realhost'. It must be
704  * freed by the caller.
705  */
706 static const char *telnet_init(void *frontend_handle, void **backend_handle,
707                                Conf *conf, char *host, int port,
708                                char **realhost, int nodelay, int keepalive)
709 {
710     static const struct plug_function_table fn_table = {
711         telnet_log,
712         telnet_closing,
713         telnet_receive,
714         telnet_sent
715     };
716     SockAddr addr;
717     const char *err;
718     Telnet telnet;
719     char *loghost;
720     int addressfamily;
721
722     telnet = snew(struct telnet_tag);
723     telnet->fn = &fn_table;
724     telnet->conf = conf_copy(conf);
725     telnet->s = NULL;
726     telnet->echoing = TRUE;
727     telnet->editing = TRUE;
728     telnet->activated = FALSE;
729     telnet->sb_buf = NULL;
730     telnet->sb_size = 0;
731     telnet->frontend = frontend_handle;
732     telnet->term_width = conf_get_int(telnet->conf, CONF_width);
733     telnet->term_height = conf_get_int(telnet->conf, CONF_height);
734     telnet->state = TOP_LEVEL;
735     telnet->ldisc = NULL;
736     telnet->pinger = NULL;
737     *backend_handle = telnet;
738
739     /*
740      * Try to find host.
741      */
742     {
743         char *buf;
744         addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
745         buf = dupprintf("Looking up host \"%s\"%s", host,
746                         (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
747                          (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
748                           "")));
749         logevent(telnet->frontend, buf);
750         sfree(buf);
751     }
752     addr = name_lookup(host, port, realhost, telnet->conf, addressfamily);
753     if ((err = sk_addr_error(addr)) != NULL) {
754         sk_addr_free(addr);
755         return err;
756     }
757
758     if (port < 0)
759         port = 23;                     /* default telnet port */
760
761     /*
762      * Open socket.
763      */
764     telnet->s = new_connection(addr, *realhost, port, 0, 1,
765                                nodelay, keepalive, (Plug) telnet, telnet->conf);
766     if ((err = sk_socket_error(telnet->s)) != NULL)
767         return err;
768
769     telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet);
770
771     /*
772      * Initialise option states.
773      */
774     if (conf_get_int(telnet->conf, CONF_passive_telnet)) {
775         const struct Opt *const *o;
776
777         for (o = opts; *o; o++)
778             telnet->opt_states[(*o)->index] = INACTIVE;
779     } else {
780         const struct Opt *const *o;
781
782         for (o = opts; *o; o++) {
783             telnet->opt_states[(*o)->index] = (*o)->initial_state;
784             if (telnet->opt_states[(*o)->index] == REQUESTED)
785                 send_opt(telnet, (*o)->send, (*o)->option);
786         }
787         telnet->activated = TRUE;
788     }
789
790     /*
791      * Set up SYNCH state.
792      */
793     telnet->in_synch = FALSE;
794
795     /*
796      * We can send special commands from the start.
797      */
798     update_specials_menu(telnet->frontend);
799
800     /*
801      * loghost overrides realhost, if specified.
802      */
803     loghost = conf_get_str(telnet->conf, CONF_loghost);
804     if (*loghost) {
805         char *colon;
806
807         sfree(*realhost);
808         *realhost = dupstr(loghost);
809         colon = strrchr(*realhost, ':');
810         if (colon) {
811             /*
812              * FIXME: if we ever update this aspect of ssh.c for
813              * IPv6 literal management, this should change in line
814              * with it.
815              */
816             *colon++ = '\0';
817         }
818     }
819
820     return NULL;
821 }
822
823 static void telnet_free(void *handle)
824 {
825     Telnet telnet = (Telnet) handle;
826
827     sfree(telnet->sb_buf);
828     if (telnet->s)
829         sk_close(telnet->s);
830     if (telnet->pinger)
831         pinger_free(telnet->pinger);
832     conf_free(telnet->conf);
833     sfree(telnet);
834 }
835 /*
836  * Reconfigure the Telnet backend. There's no immediate action
837  * necessary, in this backend: we just save the fresh config for
838  * any subsequent negotiations.
839  */
840 static void telnet_reconfig(void *handle, Conf *conf)
841 {
842     Telnet telnet = (Telnet) handle;
843     pinger_reconfig(telnet->pinger, telnet->conf, conf);
844     conf_free(telnet->conf);
845     telnet->conf = conf_copy(conf);
846 }
847
848 /*
849  * Called to send data down the Telnet connection.
850  */
851 static int telnet_send(void *handle, char *buf, int len)
852 {
853     Telnet telnet = (Telnet) handle;
854     unsigned char *p, *end;
855     static const unsigned char iac[2] = { IAC, IAC };
856     static const unsigned char cr[2] = { CR, NUL };
857 #if 0
858     static const unsigned char nl[2] = { CR, LF };
859 #endif
860
861     if (telnet->s == NULL)
862         return 0;
863
864     p = (unsigned char *)buf;
865     end = (unsigned char *)(buf + len);
866     while (p < end) {
867         unsigned char *q = p;
868
869         while (p < end && iswritable(*p))
870             p++;
871         telnet->bufsize = sk_write(telnet->s, (char *)q, p - q);
872
873         while (p < end && !iswritable(*p)) {
874             telnet->bufsize = 
875                 sk_write(telnet->s, (char *)(*p == IAC ? iac : cr), 2);
876             p++;
877         }
878     }
879
880     return telnet->bufsize;
881 }
882
883 /*
884  * Called to query the current socket sendability status.
885  */
886 static int telnet_sendbuffer(void *handle)
887 {
888     Telnet telnet = (Telnet) handle;
889     return telnet->bufsize;
890 }
891
892 /*
893  * Called to set the size of the window from Telnet's POV.
894  */
895 static void telnet_size(void *handle, int width, int height)
896 {
897     Telnet telnet = (Telnet) handle;
898     unsigned char b[24];
899     int n;
900     char *logbuf;
901
902     telnet->term_width = width;
903     telnet->term_height = height;
904
905     if (telnet->s == NULL || telnet->opt_states[o_naws.index] != ACTIVE)
906         return;
907     n = 0;
908     b[n++] = IAC;
909     b[n++] = SB;
910     b[n++] = TELOPT_NAWS;
911     b[n++] = telnet->term_width >> 8;
912     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
913     b[n++] = telnet->term_width & 0xFF;
914     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
915     b[n++] = telnet->term_height >> 8;
916     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
917     b[n++] = telnet->term_height & 0xFF;
918     if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */
919     b[n++] = IAC;
920     b[n++] = SE;
921     telnet->bufsize = sk_write(telnet->s, (char *)b, n);
922     logbuf = dupprintf("client:\tSB NAWS %d,%d",
923                        telnet->term_width, telnet->term_height);
924     logevent(telnet->frontend, logbuf);
925     sfree(logbuf);
926 }
927
928 /*
929  * Send Telnet special codes.
930  */
931 static void telnet_special(void *handle, Telnet_Special code)
932 {
933     Telnet telnet = (Telnet) handle;
934     unsigned char b[2];
935
936     if (telnet->s == NULL)
937         return;
938
939     b[0] = IAC;
940     switch (code) {
941       case TS_AYT:
942         b[1] = AYT;
943         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
944         break;
945       case TS_BRK:
946         b[1] = BREAK;
947         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
948         break;
949       case TS_EC:
950         b[1] = EC;
951         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
952         break;
953       case TS_EL:
954         b[1] = EL;
955         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
956         break;
957       case TS_GA:
958         b[1] = GA;
959         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
960         break;
961       case TS_NOP:
962         b[1] = NOP;
963         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
964         break;
965       case TS_ABORT:
966         b[1] = ABORT;
967         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
968         break;
969       case TS_AO:
970         b[1] = AO;
971         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
972         break;
973       case TS_IP:
974         b[1] = IP;
975         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
976         break;
977       case TS_SUSP:
978         b[1] = SUSP;
979         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
980         break;
981       case TS_EOR:
982         b[1] = EOR;
983         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
984         break;
985       case TS_EOF:
986         b[1] = xEOF;
987         telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
988         break;
989       case TS_EOL:
990         /* In BINARY mode, CR-LF becomes just CR -
991          * and without the NUL suffix too. */
992         if (telnet->opt_states[o_we_bin.index] == ACTIVE)
993             telnet->bufsize = sk_write(telnet->s, "\r", 1);
994         else
995             telnet->bufsize = sk_write(telnet->s, "\r\n", 2);
996         break;
997       case TS_SYNCH:
998         b[1] = DM;
999         telnet->bufsize = sk_write(telnet->s, (char *)b, 1);
1000         telnet->bufsize = sk_write_oob(telnet->s, (char *)(b + 1), 1);
1001         break;
1002       case TS_RECHO:
1003         if (telnet->opt_states[o_echo.index] == INACTIVE ||
1004             telnet->opt_states[o_echo.index] == REALLY_INACTIVE) {
1005             telnet->opt_states[o_echo.index] = REQUESTED;
1006             send_opt(telnet, o_echo.send, o_echo.option);
1007         }
1008         break;
1009       case TS_LECHO:
1010         if (telnet->opt_states[o_echo.index] == ACTIVE) {
1011             telnet->opt_states[o_echo.index] = REQUESTED;
1012             send_opt(telnet, o_echo.nsend, o_echo.option);
1013         }
1014         break;
1015       case TS_PING:
1016         if (telnet->opt_states[o_they_sga.index] == ACTIVE) {
1017             b[1] = NOP;
1018             telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
1019         }
1020         break;
1021       default:
1022         break;  /* never heard of it */
1023     }
1024 }
1025
1026 static const struct telnet_special *telnet_get_specials(void *handle)
1027 {
1028     static const struct telnet_special specials[] = {
1029         {"Are You There", TS_AYT},
1030         {"Break", TS_BRK},
1031         {"Synch", TS_SYNCH},
1032         {"Erase Character", TS_EC},
1033         {"Erase Line", TS_EL},
1034         {"Go Ahead", TS_GA},
1035         {"No Operation", TS_NOP},
1036         {NULL, TS_SEP},
1037         {"Abort Process", TS_ABORT},
1038         {"Abort Output", TS_AO},
1039         {"Interrupt Process", TS_IP},
1040         {"Suspend Process", TS_SUSP},
1041         {NULL, TS_SEP},
1042         {"End Of Record", TS_EOR},
1043         {"End Of File", TS_EOF},
1044         {NULL, TS_EXITMENU}
1045     };
1046     return specials;
1047 }
1048
1049 static int telnet_connected(void *handle)
1050 {
1051     Telnet telnet = (Telnet) handle;
1052     return telnet->s != NULL;
1053 }
1054
1055 static int telnet_sendok(void *handle)
1056 {
1057     /* Telnet telnet = (Telnet) handle; */
1058     return 1;
1059 }
1060
1061 static void telnet_unthrottle(void *handle, int backlog)
1062 {
1063     Telnet telnet = (Telnet) handle;
1064     sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
1065 }
1066
1067 static int telnet_ldisc(void *handle, int option)
1068 {
1069     Telnet telnet = (Telnet) handle;
1070     if (option == LD_ECHO)
1071         return telnet->echoing;
1072     if (option == LD_EDIT)
1073         return telnet->editing;
1074     return FALSE;
1075 }
1076
1077 static void telnet_provide_ldisc(void *handle, void *ldisc)
1078 {
1079     Telnet telnet = (Telnet) handle;
1080     telnet->ldisc = ldisc;
1081 }
1082
1083 static void telnet_provide_logctx(void *handle, void *logctx)
1084 {
1085     /* This is a stub. */
1086 }
1087
1088 static int telnet_exitcode(void *handle)
1089 {
1090     Telnet telnet = (Telnet) handle;
1091     if (telnet->s != NULL)
1092         return -1;                     /* still connected */
1093     else
1094         /* Telnet doesn't transmit exit codes back to the client */
1095         return 0;
1096 }
1097
1098 /*
1099  * cfg_info for Telnet does nothing at all.
1100  */
1101 static int telnet_cfg_info(void *handle)
1102 {
1103     return 0;
1104 }
1105
1106 Backend telnet_backend = {
1107     telnet_init,
1108     telnet_free,
1109     telnet_reconfig,
1110     telnet_send,
1111     telnet_sendbuffer,
1112     telnet_size,
1113     telnet_special,
1114     telnet_get_specials,
1115     telnet_connected,
1116     telnet_exitcode,
1117     telnet_sendok,
1118     telnet_ldisc,
1119     telnet_provide_ldisc,
1120     telnet_provide_logctx,
1121     telnet_unthrottle,
1122     telnet_cfg_info,
1123     "telnet",
1124     PROT_TELNET,
1125     23
1126 };