mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-15 03:23:02 -05:00

This removes a lot of pointless duplications of those constants. Of course, _ideally_, I should upgrade to C99 bool throughout the code base, replacing TRUE and FALSE with true and false and tagging variables explicitly as bool when that's what they semantically are. But that's a much bigger piece of work, and shouldn't block this trivial cleanup!
421 lines
10 KiB
C
421 lines
10 KiB
C
/*
|
|
* Rlogin backend.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
|
|
#include "putty.h"
|
|
|
|
#define RLOGIN_MAX_BACKLOG 4096
|
|
|
|
typedef struct rlogin_tag {
|
|
const struct plug_function_table *fn;
|
|
/* the above field _must_ be first in the structure */
|
|
|
|
Socket s;
|
|
int closed_on_socket_error;
|
|
int bufsize;
|
|
int firstbyte;
|
|
int cansize;
|
|
int term_width, term_height;
|
|
void *frontend;
|
|
|
|
Conf *conf;
|
|
|
|
/* In case we need to read a username from the terminal before starting */
|
|
prompts_t *prompt;
|
|
} *Rlogin;
|
|
|
|
static void rlogin_size(void *handle, int width, int height);
|
|
|
|
static void c_write(Rlogin rlogin, char *buf, int len)
|
|
{
|
|
int backlog = from_backend(rlogin->frontend, 0, buf, len);
|
|
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
|
}
|
|
|
|
static void rlogin_log(Plug plug, int type, SockAddr addr, int port,
|
|
const char *error_msg, int error_code)
|
|
{
|
|
Rlogin rlogin = (Rlogin) plug;
|
|
backend_socket_log(rlogin->frontend, type, addr, port,
|
|
error_msg, error_code,
|
|
rlogin->conf, !rlogin->firstbyte);
|
|
}
|
|
|
|
static void rlogin_closing(Plug plug, const char *error_msg, int error_code,
|
|
int calling_back)
|
|
{
|
|
Rlogin rlogin = (Rlogin) 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 (rlogin->s) {
|
|
sk_close(rlogin->s);
|
|
rlogin->s = NULL;
|
|
if (error_msg)
|
|
rlogin->closed_on_socket_error = TRUE;
|
|
notify_remote_exit(rlogin->frontend);
|
|
}
|
|
if (error_msg) {
|
|
/* A socket error has occurred. */
|
|
logevent(rlogin->frontend, error_msg);
|
|
connection_fatal(rlogin->frontend, "%s", error_msg);
|
|
} /* Otherwise, the remote side closed the connection normally. */
|
|
}
|
|
|
|
static void rlogin_receive(Plug plug, int urgent, char *data, int len)
|
|
{
|
|
Rlogin rlogin = (Rlogin) plug;
|
|
if (urgent == 2) {
|
|
char c;
|
|
|
|
c = *data++;
|
|
len--;
|
|
if (c == '\x80') {
|
|
rlogin->cansize = 1;
|
|
rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);
|
|
}
|
|
/*
|
|
* We should flush everything (aka Telnet SYNCH) if we see
|
|
* 0x02, and we should turn off and on _local_ flow control
|
|
* on 0x10 and 0x20 respectively. I'm not convinced it's
|
|
* worth it...
|
|
*/
|
|
} else {
|
|
/*
|
|
* Main rlogin protocol. This is really simple: the first
|
|
* byte is expected to be NULL and is ignored, and the rest
|
|
* is printed.
|
|
*/
|
|
if (rlogin->firstbyte) {
|
|
if (data[0] == '\0') {
|
|
data++;
|
|
len--;
|
|
}
|
|
rlogin->firstbyte = 0;
|
|
}
|
|
if (len > 0)
|
|
c_write(rlogin, data, len);
|
|
}
|
|
}
|
|
|
|
static void rlogin_sent(Plug plug, int bufsize)
|
|
{
|
|
Rlogin rlogin = (Rlogin) plug;
|
|
rlogin->bufsize = bufsize;
|
|
}
|
|
|
|
static void rlogin_startup(Rlogin rlogin, const char *ruser)
|
|
{
|
|
char z = 0;
|
|
char *p;
|
|
|
|
sk_write(rlogin->s, &z, 1);
|
|
p = conf_get_str(rlogin->conf, CONF_localusername);
|
|
sk_write(rlogin->s, p, strlen(p));
|
|
sk_write(rlogin->s, &z, 1);
|
|
sk_write(rlogin->s, ruser, strlen(ruser));
|
|
sk_write(rlogin->s, &z, 1);
|
|
p = conf_get_str(rlogin->conf, CONF_termtype);
|
|
sk_write(rlogin->s, p, strlen(p));
|
|
sk_write(rlogin->s, "/", 1);
|
|
p = conf_get_str(rlogin->conf, CONF_termspeed);
|
|
sk_write(rlogin->s, p, strspn(p, "0123456789"));
|
|
rlogin->bufsize = sk_write(rlogin->s, &z, 1);
|
|
|
|
rlogin->prompt = NULL;
|
|
}
|
|
|
|
/*
|
|
* Called to set up the rlogin connection.
|
|
*
|
|
* Returns an error message, or NULL on success.
|
|
*
|
|
* Also places the canonical host name into `realhost'. It must be
|
|
* freed by the caller.
|
|
*/
|
|
static const char *rlogin_init(void *frontend_handle, void **backend_handle,
|
|
Conf *conf,
|
|
const char *host, int port, char **realhost,
|
|
int nodelay, int keepalive)
|
|
{
|
|
static const struct plug_function_table fn_table = {
|
|
rlogin_log,
|
|
rlogin_closing,
|
|
rlogin_receive,
|
|
rlogin_sent
|
|
};
|
|
SockAddr addr;
|
|
const char *err;
|
|
Rlogin rlogin;
|
|
char *ruser;
|
|
int addressfamily;
|
|
char *loghost;
|
|
|
|
rlogin = snew(struct rlogin_tag);
|
|
rlogin->fn = &fn_table;
|
|
rlogin->s = NULL;
|
|
rlogin->closed_on_socket_error = FALSE;
|
|
rlogin->frontend = frontend_handle;
|
|
rlogin->term_width = conf_get_int(conf, CONF_width);
|
|
rlogin->term_height = conf_get_int(conf, CONF_height);
|
|
rlogin->firstbyte = 1;
|
|
rlogin->cansize = 0;
|
|
rlogin->prompt = NULL;
|
|
rlogin->conf = conf_copy(conf);
|
|
*backend_handle = rlogin;
|
|
|
|
addressfamily = conf_get_int(conf, CONF_addressfamily);
|
|
/*
|
|
* Try to find host.
|
|
*/
|
|
addr = name_lookup(host, port, realhost, conf, addressfamily,
|
|
rlogin->frontend, "rlogin connection");
|
|
if ((err = sk_addr_error(addr)) != NULL) {
|
|
sk_addr_free(addr);
|
|
return err;
|
|
}
|
|
|
|
if (port < 0)
|
|
port = 513; /* default rlogin port */
|
|
|
|
/*
|
|
* Open socket.
|
|
*/
|
|
rlogin->s = new_connection(addr, *realhost, port, 1, 0,
|
|
nodelay, keepalive, (Plug) rlogin, conf);
|
|
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
|
return err;
|
|
|
|
loghost = conf_get_str(conf, CONF_loghost);
|
|
if (*loghost) {
|
|
char *colon;
|
|
|
|
sfree(*realhost);
|
|
*realhost = dupstr(loghost);
|
|
|
|
colon = host_strrchr(*realhost, ':');
|
|
if (colon)
|
|
*colon++ = '\0';
|
|
}
|
|
|
|
/*
|
|
* Send local username, remote username, terminal type and
|
|
* terminal speed - unless we don't have the remote username yet,
|
|
* in which case we prompt for it and may end up deferring doing
|
|
* anything else until the local prompt mechanism returns.
|
|
*/
|
|
if ((ruser = get_remote_username(conf)) != NULL) {
|
|
rlogin_startup(rlogin, ruser);
|
|
sfree(ruser);
|
|
} else {
|
|
int ret;
|
|
|
|
rlogin->prompt = new_prompts(rlogin->frontend);
|
|
rlogin->prompt->to_server = TRUE;
|
|
rlogin->prompt->name = dupstr("Rlogin login name");
|
|
add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE);
|
|
ret = get_userpass_input(rlogin->prompt, NULL);
|
|
if (ret >= 0) {
|
|
rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void rlogin_free(void *handle)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
|
|
if (rlogin->prompt)
|
|
free_prompts(rlogin->prompt);
|
|
if (rlogin->s)
|
|
sk_close(rlogin->s);
|
|
conf_free(rlogin->conf);
|
|
sfree(rlogin);
|
|
}
|
|
|
|
/*
|
|
* Stub routine (we don't have any need to reconfigure this backend).
|
|
*/
|
|
static void rlogin_reconfig(void *handle, Conf *conf)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Called to send data down the rlogin connection.
|
|
*/
|
|
static int rlogin_send(void *handle, const char *buf, int len)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
bufchain bc;
|
|
|
|
if (rlogin->s == NULL)
|
|
return 0;
|
|
|
|
bufchain_init(&bc);
|
|
bufchain_add(&bc, buf, len);
|
|
|
|
if (rlogin->prompt) {
|
|
/*
|
|
* We're still prompting for a username, and aren't talking
|
|
* directly to the network connection yet.
|
|
*/
|
|
int ret = get_userpass_input(rlogin->prompt, &bc);
|
|
if (ret >= 0) {
|
|
rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
|
|
/* that nulls out rlogin->prompt, so then we'll start sending
|
|
* data down the wire in the obvious way */
|
|
}
|
|
}
|
|
|
|
if (!rlogin->prompt) {
|
|
while (bufchain_size(&bc) > 0) {
|
|
void *data;
|
|
int len;
|
|
bufchain_prefix(&bc, &data, &len);
|
|
rlogin->bufsize = sk_write(rlogin->s, data, len);
|
|
bufchain_consume(&bc, len);
|
|
}
|
|
}
|
|
|
|
bufchain_clear(&bc);
|
|
|
|
return rlogin->bufsize;
|
|
}
|
|
|
|
/*
|
|
* Called to query the current socket sendability status.
|
|
*/
|
|
static int rlogin_sendbuffer(void *handle)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
return rlogin->bufsize;
|
|
}
|
|
|
|
/*
|
|
* Called to set the size of the window
|
|
*/
|
|
static void rlogin_size(void *handle, int width, int height)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
rlogin->term_width = width;
|
|
rlogin->term_height = height;
|
|
|
|
if (rlogin->s == NULL || !rlogin->cansize)
|
|
return;
|
|
|
|
b[6] = rlogin->term_width >> 8;
|
|
b[7] = rlogin->term_width & 0xFF;
|
|
b[4] = rlogin->term_height >> 8;
|
|
b[5] = rlogin->term_height & 0xFF;
|
|
rlogin->bufsize = sk_write(rlogin->s, b, 12);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Send rlogin special codes.
|
|
*/
|
|
static void rlogin_special(void *handle, Telnet_Special code)
|
|
{
|
|
/* Do nothing! */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Return a list of the special codes that make sense in this
|
|
* protocol.
|
|
*/
|
|
static const struct telnet_special *rlogin_get_specials(void *handle)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static int rlogin_connected(void *handle)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
return rlogin->s != NULL;
|
|
}
|
|
|
|
static int rlogin_sendok(void *handle)
|
|
{
|
|
/* Rlogin rlogin = (Rlogin) handle; */
|
|
return 1;
|
|
}
|
|
|
|
static void rlogin_unthrottle(void *handle, int backlog)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
|
}
|
|
|
|
static int rlogin_ldisc(void *handle, int option)
|
|
{
|
|
/* Rlogin rlogin = (Rlogin) handle; */
|
|
return 0;
|
|
}
|
|
|
|
static void rlogin_provide_ldisc(void *handle, void *ldisc)
|
|
{
|
|
/* This is a stub. */
|
|
}
|
|
|
|
static void rlogin_provide_logctx(void *handle, void *logctx)
|
|
{
|
|
/* This is a stub. */
|
|
}
|
|
|
|
static int rlogin_exitcode(void *handle)
|
|
{
|
|
Rlogin rlogin = (Rlogin) handle;
|
|
if (rlogin->s != NULL)
|
|
return -1; /* still connected */
|
|
else if (rlogin->closed_on_socket_error)
|
|
return INT_MAX; /* a socket error counts as an unclean exit */
|
|
else
|
|
/* If we ever implement RSH, we'll probably need to do this properly */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cfg_info for rlogin does nothing at all.
|
|
*/
|
|
static int rlogin_cfg_info(void *handle)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Backend rlogin_backend = {
|
|
rlogin_init,
|
|
rlogin_free,
|
|
rlogin_reconfig,
|
|
rlogin_send,
|
|
rlogin_sendbuffer,
|
|
rlogin_size,
|
|
rlogin_special,
|
|
rlogin_get_specials,
|
|
rlogin_connected,
|
|
rlogin_exitcode,
|
|
rlogin_sendok,
|
|
rlogin_ldisc,
|
|
rlogin_provide_ldisc,
|
|
rlogin_provide_logctx,
|
|
rlogin_unthrottle,
|
|
rlogin_cfg_info,
|
|
NULL /* test_for_upstream */,
|
|
"rlogin",
|
|
PROT_RLOGIN,
|
|
513
|
|
};
|