X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/6196fb7ebed0b7e53b706faacfcc5fb13aa24738..e99bb8bfc8d2c1a47b6ae90ef43683d191c30f66:/telnet.c diff --git a/telnet.c b/telnet.c index 4f0a36fe..3536414c 100644 --- a/telnet.c +++ b/telnet.c @@ -4,6 +4,7 @@ #include #include +#include #include "putty.h" @@ -181,6 +182,7 @@ typedef struct telnet_tag { /* the above field _must_ be first in the structure */ Socket s; + int closed_on_socket_error; void *frontend; void *ldisc; @@ -201,7 +203,7 @@ typedef struct telnet_tag { SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; - Config cfg; + Conf *conf; Pinger pinger; } *Telnet; @@ -363,42 +365,46 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option) static void process_subneg(Telnet telnet) { - unsigned char b[2048], *p, *q; - int var, value, n; - char *e; + unsigned char *b, *p, *q; + int var, value, n, bsize; + char *e, *eval, *ekey, *user; switch (telnet->sb_opt) { case TELOPT_TSPEED: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termspeed = conf_get_str(telnet->conf, CONF_termspeed); + b = snewn(20 + strlen(termspeed), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED; b[3] = TELQUAL_IS; - strcpy((char *)(b + 4), telnet->cfg.termspeed); - n = 4 + strlen(telnet->cfg.termspeed); + strcpy((char *)(b + 4), termspeed); + n = 4 + strlen(termspeed); b[n] = IAC; b[n + 1] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2); logevent(telnet->frontend, "server:\tSB TSPEED SEND"); - logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed); + logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TSPEED "); break; case TELOPT_TTYPE: if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) { char *logbuf; + char *termtype = conf_get_str(telnet->conf, CONF_termtype); + b = snewn(20 + strlen(termtype), unsigned char); b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE; b[3] = TELQUAL_IS; - for (n = 0; telnet->cfg.termtype[n]; n++) - b[n + 4] = (telnet->cfg.termtype[n] >= 'a' - && telnet->cfg.termtype[n] <= - 'z' ? telnet->cfg.termtype[n] + 'A' - - 'a' : telnet->cfg.termtype[n]); + for (n = 0; termtype[n]; n++) + b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ? + termtype[n] + 'A' - 'a' : + termtype[n]); b[n + 4] = IAC; b[n + 5] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6); @@ -407,6 +413,7 @@ static void process_subneg(Telnet telnet) logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4); logevent(telnet->frontend, logbuf); sfree(logbuf); + sfree(b); } else logevent(telnet->frontend, "server:\tSB TTYPE \r\n"); break; @@ -421,7 +428,7 @@ static void process_subneg(Telnet telnet) logevent(telnet->frontend, logbuf); sfree(logbuf); if (telnet->sb_opt == TELOPT_OLD_ENVIRON) { - if (telnet->cfg.rfc_environ) { + if (conf_get_int(telnet->conf, CONF_rfc_environ)) { value = RFC_VALUE; var = RFC_VAR; } else { @@ -449,46 +456,75 @@ static void process_subneg(Telnet telnet) value = RFC_VALUE; var = RFC_VAR; } + bsize = 20; + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) + bsize += strlen(ekey) + strlen(eval) + 2; + user = get_remote_username(telnet->conf); + if (user) + bsize += 6 + strlen(user); + + b = snewn(bsize, unsigned char); b[0] = IAC; b[1] = SB; b[2] = telnet->sb_opt; b[3] = TELQUAL_IS; n = 4; - e = telnet->cfg.environmt; - while (*e) { + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { b[n++] = var; - while (*e && *e != '\t') - b[n++] = *e++; - if (*e == '\t') - e++; + for (e = ekey; *e; e++) + b[n++] = *e; b[n++] = value; - while (*e) - b[n++] = *e++; - e++; + for (e = eval; *e; e++) + b[n++] = *e; } - if (*telnet->cfg.username) { + if (user) { b[n++] = var; b[n++] = 'U'; b[n++] = 'S'; b[n++] = 'E'; b[n++] = 'R'; b[n++] = value; - e = telnet->cfg.username; - while (*e) - b[n++] = *e++; + for (e = user; *e; e++) + b[n++] = *e; } b[n++] = IAC; b[n++] = SE; telnet->bufsize = sk_write(telnet->s, (char *)b, n); - logbuf = dupprintf("client:\tSB %s IS %s%s%s%s", - telopt(telnet->sb_opt), - *telnet->cfg.username ? "USER=" : "", - telnet->cfg.username, - *telnet->cfg.username ? " " : "", - n == 6 ? "" : - (*telnet->cfg.environmt ? "" : "")); - logevent(telnet->frontend, logbuf); - sfree(logbuf); + if (n == 6) { + logbuf = dupprintf("client:\tSB %s IS ", + telopt(telnet->sb_opt)); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } else { + logbuf = dupprintf("client:\tSB %s IS:", + telopt(telnet->sb_opt)); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + for (eval = conf_get_str_strs(telnet->conf, CONF_environmt, + NULL, &ekey); + eval != NULL; + eval = conf_get_str_strs(telnet->conf, CONF_environmt, + ekey, &ekey)) { + logbuf = dupprintf("\t%s=%s", ekey, eval); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } + if (user) { + logbuf = dupprintf("\tUSER=%s", user); + logevent(telnet->frontend, logbuf); + sfree(logbuf); + } + } + sfree(b); + sfree(user); } break; } @@ -633,9 +669,17 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code, { Telnet telnet = (Telnet) plug; + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + if (telnet->s) { sk_close(telnet->s); telnet->s = NULL; + if (error_msg) + telnet->closed_on_socket_error = TRUE; notify_remote_exit(telnet->frontend); } if (error_msg) { @@ -670,9 +714,8 @@ static void telnet_sent(Plug plug, int bufsize) * freed by the caller. */ static const char *telnet_init(void *frontend_handle, void **backend_handle, - Config *cfg, - char *host, int port, char **realhost, - int nodelay, int keepalive) + Conf *conf, char *host, int port, + char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { telnet_log, @@ -683,19 +726,22 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, SockAddr addr; const char *err; Telnet telnet; + char *loghost; + int addressfamily; telnet = snew(struct telnet_tag); telnet->fn = &fn_table; - telnet->cfg = *cfg; /* STRUCTURE COPY */ + telnet->conf = conf_copy(conf); telnet->s = NULL; + telnet->closed_on_socket_error = FALSE; telnet->echoing = TRUE; telnet->editing = TRUE; telnet->activated = FALSE; telnet->sb_buf = NULL; telnet->sb_size = 0; telnet->frontend = frontend_handle; - telnet->term_width = telnet->cfg.width; - telnet->term_height = telnet->cfg.height; + telnet->term_width = conf_get_int(telnet->conf, CONF_width); + telnet->term_height = conf_get_int(telnet->conf, CONF_height); telnet->state = TOP_LEVEL; telnet->ldisc = NULL; telnet->pinger = NULL; @@ -706,14 +752,15 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, */ { char *buf; + addressfamily = conf_get_int(telnet->conf, CONF_addressfamily); buf = dupprintf("Looking up host \"%s\"%s", host, - (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : - (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(telnet->frontend, buf); sfree(buf); } - addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily); + addr = name_lookup(host, port, realhost, telnet->conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; @@ -726,16 +773,16 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, * Open socket. */ telnet->s = new_connection(addr, *realhost, port, 0, 1, - nodelay, keepalive, (Plug) telnet, &telnet->cfg); + nodelay, keepalive, (Plug) telnet, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) return err; - telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet); + telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet); /* * Initialise option states. */ - if (telnet->cfg.passive_telnet) { + if (conf_get_int(telnet->conf, CONF_passive_telnet)) { const struct Opt *const *o; for (o = opts; *o; o++) @@ -764,11 +811,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle, /* * loghost overrides realhost, if specified. */ - if (*telnet->cfg.loghost) { + loghost = conf_get_str(telnet->conf, CONF_loghost); + if (*loghost) { char *colon; sfree(*realhost); - *realhost = dupstr(telnet->cfg.loghost); + *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* @@ -792,6 +840,7 @@ static void telnet_free(void *handle) sk_close(telnet->s); if (telnet->pinger) pinger_free(telnet->pinger); + conf_free(telnet->conf); sfree(telnet); } /* @@ -799,11 +848,12 @@ static void telnet_free(void *handle) * necessary, in this backend: we just save the fresh config for * any subsequent negotiations. */ -static void telnet_reconfig(void *handle, Config *cfg) +static void telnet_reconfig(void *handle, Conf *conf) { Telnet telnet = (Telnet) handle; - pinger_reconfig(telnet->pinger, &telnet->cfg, cfg); - telnet->cfg = *cfg; /* STRUCTURE COPY */ + pinger_reconfig(telnet->pinger, telnet->conf, conf); + conf_free(telnet->conf); + telnet->conf = conf_copy(conf); } /* @@ -1051,6 +1101,8 @@ static int telnet_exitcode(void *handle) Telnet telnet = (Telnet) handle; if (telnet->s != NULL) return -1; /* still connected */ + else if (telnet->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ else /* Telnet doesn't transmit exit codes back to the client */ return 0;