From c91409da0ac0d3fb4a225ab85e14370514e4094e Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 19 Jan 2001 10:10:37 +0000 Subject: [PATCH] Experimental Rlogin support, thanks to Delian Delchev. Local flow control is unsupported, and server-to-client comms may fail for want of working TCP Urgent. git-svn-id: svn://svn.tartarus.org/sgt/putty@875 cda61777-01e9-0310-a592-d414129be87e --- Makefile | 3 +- be_all.c | 1 + be_none.c | 1 + be_nossh.c | 1 + network.h | 2 +- putty.h | 9 +++- raw.c | 2 +- rlogin.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ settings.c | 2 + ssh.c | 2 +- telnet.c | 2 +- windlg.c | 62 +++++++++++++++++++++---- winnet.c | 63 +++++++++++++++++++------- 13 files changed, 267 insertions(+), 32 deletions(-) create mode 100644 rlogin.c diff --git a/Makefile b/Makefile index 79f3fcf4..2dedf302 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ RES=res GOBJS1 = window.$(OBJ) windlg.$(OBJ) winctrls.$(OBJ) terminal.$(OBJ) GOBJS2 = xlat.$(OBJ) sizetip.$(OBJ) ##-- objects putty puttytel plink -LOBJS1 = telnet.$(OBJ) raw.$(OBJ) ldisc.$(OBJ) winnet.$(OBJ) +LOBJS1 = telnet.$(OBJ) raw.$(OBJ) rlogin.$(OBJ) ldisc.$(OBJ) winnet.$(OBJ) ##-- objects putty plink POBJS = be_all.$(OBJ) ##-- objects puttytel @@ -224,6 +224,7 @@ terminal.$(OBJ): terminal.c putty.h puttymem.h network.h sizetip.$(OBJ): sizetip.c putty.h puttymem.h network.h winstuff.h telnet.$(OBJ): telnet.c putty.h puttymem.h network.h raw.$(OBJ): raw.c putty.h puttymem.h network.h +rlogin.$(OBJ): rlogin.c putty.h puttymem.h network.h xlat.$(OBJ): xlat.c putty.h puttymem.h network.h ldisc.$(OBJ): ldisc.c putty.h puttymem.h network.h misc.$(OBJ): misc.c putty.h puttymem.h network.h diff --git a/be_all.c b/be_all.c index 69e44b76..fc0755ae 100644 --- a/be_all.c +++ b/be_all.c @@ -10,6 +10,7 @@ struct backend_list backends[] = { {PROT_SSH, "ssh", &ssh_backend}, {PROT_TELNET, "telnet", &telnet_backend}, + {PROT_RLOGIN, "rlogin", &rlogin_backend}, {PROT_RAW, "raw", &raw_backend}, {0, NULL} }; diff --git a/be_none.c b/be_none.c index d112143e..a08a938a 100644 --- a/be_none.c +++ b/be_none.c @@ -11,6 +11,7 @@ struct backend_list backends[] = { {PROT_SSH, "ssh", NULL}, {PROT_TELNET, "telnet", NULL}, + {PROT_RLOGIN, "rlogin", NULL}, {PROT_RAW, "raw", NULL}, {0, NULL} }; diff --git a/be_nossh.c b/be_nossh.c index d9777e0b..54e0d427 100644 --- a/be_nossh.c +++ b/be_nossh.c @@ -9,6 +9,7 @@ struct backend_list backends[] = { {PROT_TELNET, "telnet", &telnet_backend}, + {PROT_RLOGIN, "rlogin", &rlogin_backend}, {PROT_RAW, "raw", &raw_backend}, {0, NULL} }; diff --git a/network.h b/network.h index dda34bc0..46088002 100644 --- a/network.h +++ b/network.h @@ -26,7 +26,7 @@ void sk_init(void); /* called once at program startup */ SockAddr sk_namelookup(char *host, char **canonicalname); void sk_addr_free(SockAddr addr); -Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver); +Socket sk_new(SockAddr addr, int port, int privport, sk_receiver_t receiver); void sk_close(Socket s); void sk_write(Socket s, char *buf, int len); void sk_write_oob(Socket s, char *buf, int len); diff --git a/putty.h b/putty.h index d8079899..34cf0696 100644 --- a/putty.h +++ b/putty.h @@ -137,7 +137,7 @@ typedef struct { /* Basic options */ char host[512]; int port; - enum { PROT_RAW, PROT_TELNET, PROT_SSH } protocol; + enum { PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH } protocol; int close_on_exit; int warn_on_close; int ping_interval; /* in seconds */ @@ -156,6 +156,7 @@ typedef struct { char termspeed[32]; char environmt[1024]; /* VAR\tvalue\0VAR\tvalue\0\0 */ char username[32]; + char localusername[32]; int rfc_environ; /* Keyboard options */ int bksp_is_delete; @@ -334,6 +335,12 @@ void term_copyall(void); extern Backend raw_backend; /* + * Exports from rlogin.c. + */ + +extern Backend rlogin_backend; + +/* * Exports from telnet.c. */ diff --git a/raw.c b/raw.c index 833f8432..61a7d668 100644 --- a/raw.c +++ b/raw.c @@ -59,7 +59,7 @@ static char *raw_init (char *host, int port, char **realhost) { /* * Open socket. */ - s = sk_new(addr, port, raw_receive); + s = sk_new(addr, port, 0, raw_receive); if ( (err = sk_socket_error(s)) ) return err; diff --git a/rlogin.c b/rlogin.c new file mode 100644 index 00000000..bf1db8d1 --- /dev/null +++ b/rlogin.c @@ -0,0 +1,149 @@ +#include +#include +#include + +#include "putty.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +static Socket s = NULL; + +static void rlogin_size(void); + +static int sb_opt, sb_len; +static char *sb_buf = NULL; +static int sb_size = 0; +#define SB_DELTA 1024 + +static void c_write (char *buf, int len) { + from_backend(0, buf, len); +} + +static int rlogin_receive (Socket s, int urgent, char *data, int len) { + if (!len) { + /* Connection has closed. */ + sk_close(s); + s = NULL; + return 0; + } + if (urgent == 2) { + char c; + int i; + + c = *data++; len--; + if (c == 0x80) + rlogin_size(); + /* + * 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... + */ + } + c_write(data, len); + return 1; +} + +/* + * Called to set up the rlogin connection. + * + * Returns an error message, or NULL on success. + * + * Also places the canonical host name into `realhost'. + */ +static char *rlogin_init (char *host, int port, char **realhost) { + SockAddr addr; + char *err; + + /* + * Try to find host. + */ + addr = sk_namelookup(host, realhost); + if ( (err = sk_addr_error(addr)) ) + return err; + + if (port < 0) + port = 513; /* default rlogin port */ + + /* + * Open socket. + */ + s = sk_new(addr, port, 1, rlogin_receive); + if ( (err = sk_socket_error(s)) ) + return err; + + sk_addr_free(addr); + + /* + * Send local username, remote username, terminal/speed + */ + + { + char z = 0; + char *p; + sk_write(s, &z, 1); + sk_write(s, cfg.localusername, strlen(cfg.localusername)); + sk_write(s, &z, 1); + sk_write(s, cfg.username, strlen(cfg.username)); + sk_write(s, &z, 1); + sk_write(s, cfg.termtype, strlen(cfg.termtype)); + sk_write(s, "/", 1); + for(p = cfg.termspeed; isdigit(*p); p++); + sk_write(s, cfg.termspeed, p - cfg.termspeed); + sk_write(s, &z, 1); + } + + begin_session(); + + return NULL; +} + +/* + * Called to send data down the rlogin connection. + */ +static void rlogin_send (char *buf, int len) { + + if (s == NULL) + return; + + sk_write(s, buf, len); +} + +/* + * Called to set the size of the window + */ +static void rlogin_size(void) { + char b[12] = { 0xFF, 0xFF, 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 }; + + b[6] = cols >> 8; b[7] = cols & 0xFF; + b[4] = rows >> 8; b[5] = rows & 0xFF; + sk_write(s, b, 12); + return; +} + +/* + * Send rlogin special codes. + */ +static void rlogin_special (Telnet_Special code) { + /* Do nothing! */ + return; +} + +static Socket rlogin_socket(void) { return s; } + +static int rlogin_sendok(void) { return 1; } + +Backend rlogin_backend = { + rlogin_init, + rlogin_send, + rlogin_size, + rlogin_special, + rlogin_socket, + rlogin_sendok, + 1 +}; diff --git a/settings.c b/settings.c index ada2b740..67fd748e 100644 --- a/settings.c +++ b/settings.c @@ -68,6 +68,7 @@ void save_settings (char *section, int do_host, Config *cfg) { write_setting_s (sesskey, "Environment", buf); } write_setting_s (sesskey, "UserName", cfg->username); + write_setting_s (sesskey, "LocalUserName", cfg->localusername); write_setting_i (sesskey, "NoPTY", cfg->nopty); write_setting_i (sesskey, "Compression", cfg->compression); write_setting_i (sesskey, "AgentFwd", cfg->agentfwd); @@ -199,6 +200,7 @@ void load_settings (char *section, int do_host, Config *cfg) { *q = '\0'; } gpps (sesskey, "UserName", "", cfg->username, sizeof(cfg->username)); + gpps (sesskey, "LocalUserName", "", cfg->localusername, sizeof(cfg->localusername)); gppi (sesskey, "NoPTY", 0, &cfg->nopty); gppi (sesskey, "Compression", 0, &cfg->compression); gppi (sesskey, "AgentFwd", 0, &cfg->agentfwd); diff --git a/ssh.c b/ssh.c index a8646146..f75041ba 100644 --- a/ssh.c +++ b/ssh.c @@ -1184,7 +1184,7 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Open socket. */ - s = sk_new(addr, port, ssh_receive); + s = sk_new(addr, port, 0, ssh_receive); if ( (err = sk_socket_error(s)) ) return err; diff --git a/telnet.c b/telnet.c index 8eb6e8a7..67979120 100644 --- a/telnet.c +++ b/telnet.c @@ -495,7 +495,7 @@ static char *telnet_init (char *host, int port, char **realhost) { /* * Open socket. */ - s = sk_new(addr, port, telnet_receive); + s = sk_new(addr, port, 0, telnet_receive); if ( (err = sk_socket_error(s)) ) return err; diff --git a/windlg.c b/windlg.c index 96335fda..62b49d0a 100644 --- a/windlg.c +++ b/windlg.c @@ -189,6 +189,7 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue, IDC_PROTSTATIC, IDC_PROTRAW, IDC_PROTTELNET, + IDC_PROTRLOGIN, IDC_PROTSSH, IDC_SESSSTATIC, IDC_SESSEDIT, @@ -317,6 +318,16 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue, IDC_EMRFC, telnetpanelend, + rloginpanelstart, + IDC_TITLE_RLOGIN, + IDC_BOX_RLOGIN1, IDC_BOXT_RLOGIN1, + IDC_BOX_RLOGIN2, IDC_BOXT_RLOGIN2, + IDC_R_TSSTATIC, + IDC_R_TSEDIT, + IDC_RLLUSERSTATIC, + IDC_RLLUSEREDIT, + rloginpanelend, + sshpanelstart, IDC_TITLE_SSH, IDC_BOX_SSH1, IDC_BOXT_SSH1, @@ -433,7 +444,8 @@ static void init_dlg_ctrls(HWND hwnd) { SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE); CheckRadioButton (hwnd, IDC_PROTRAW, IDC_PROTSSH, cfg.protocol==PROT_SSH ? IDC_PROTSSH : - cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : IDC_PROTRAW ); + cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : + cfg.protocol==PROT_RLOGIN ? IDC_PROTRLOGIN : IDC_PROTRAW ); SetDlgItemInt (hwnd, IDC_PINGEDIT, cfg.ping_interval, FALSE); CheckRadioButton (hwnd, IDC_DEL008, IDC_DEL127, @@ -487,6 +499,8 @@ static void init_dlg_ctrls(HWND hwnd) { SetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype); SetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed); + SetDlgItemText (hwnd, IDC_R_TSEDIT, cfg.termspeed); + SetDlgItemText (hwnd, IDC_RLLUSEREDIT, cfg.localusername); SetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username); SetDlgItemText (hwnd, IDC_LGFEDIT, cfg.logfilename); CheckRadioButton(hwnd, IDC_LSTATOFF, IDC_LSTATRAW, @@ -693,13 +707,15 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, "&Port", IDC_PORTSTATIC, IDC_PORT, 25, NULL); if (backends[2].backend == NULL) { /* this is PuTTYtel, so only two protocols available */ - radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3, + radioline(&cp, "Protocol:", IDC_PROTSTATIC, 4, "&Raw", IDC_PROTRAW, - "&Telnet", IDC_PROTTELNET, NULL); + "&Telnet", IDC_PROTTELNET, + "R&login", IDC_PROTRLOGIN, NULL); } else { - radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3, + radioline(&cp, "Protocol:", IDC_PROTSTATIC, 4, "&Raw", IDC_PROTRAW, "&Telnet", IDC_PROTTELNET, + "R&login", IDC_PROTRLOGIN, #ifdef FWHACK "SS&H/hack", #else @@ -993,6 +1009,23 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, } } + + /* The Rlogin Panel */ + { + struct ctlpos cp; + ctlposinit(&cp, hwnd, 80, 3, 13); + if (dlgtype == 0) { + bartitle(&cp, "Options controlling Rlogin connections", IDC_TITLE_RLOGIN); + beginbox(&cp, "Data to send to the server", + IDC_BOX_RLOGIN1, IDC_BOXT_RLOGIN1); + staticedit(&cp, "Terminal-&speed string", IDC_R_TSSTATIC, IDC_R_TSEDIT, 50); + staticedit(&cp, "&Local username:", IDC_RLLUSERSTATIC, IDC_RLLUSEREDIT, 50); + endbox(&cp); + + treeview_insert(&tvfaff, 1, "Rlogin"); + } + } + /* The SSH panel. Accelerators used: [acgo] rmakwp123bd */ if (backends[2].backend != NULL) { struct ctlpos cp; @@ -1095,6 +1128,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, hide(hwnd, FALSE, connectionpanelstart, connectionpanelend); if (!strcmp(buffer, "Telnet")) hide(hwnd, FALSE, telnetpanelstart, telnetpanelend); + if (!strcmp(buffer, "Rlogin")) + hide(hwnd, FALSE, rloginpanelstart, rloginpanelend); if (!strcmp(buffer, "SSH")) hide(hwnd, FALSE, sshpanelstart, sshpanelend); if (!strcmp(buffer, "Selection")) @@ -1123,16 +1158,19 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, EndDialog (hwnd, 0); return 0; case IDC_PROTTELNET: + case IDC_PROTRLOGIN: case IDC_PROTSSH: case IDC_PROTRAW: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int i = IsDlgButtonChecked (hwnd, IDC_PROTSSH); int j = IsDlgButtonChecked (hwnd, IDC_PROTTELNET); - cfg.protocol = i ? PROT_SSH : j ? PROT_TELNET : PROT_RAW ; - if ((cfg.protocol == PROT_SSH && cfg.port == 23) || - (cfg.protocol == PROT_TELNET && cfg.port == 22)) { - cfg.port = i ? 22 : 23; + int k = IsDlgButtonChecked (hwnd, IDC_PROTRLOGIN); + cfg.protocol = i ? PROT_SSH : j ? PROT_TELNET : k ? PROT_RLOGIN : PROT_RAW ; + if ((cfg.protocol == PROT_SSH && cfg.port != 22) || + (cfg.protocol == PROT_TELNET && cfg.port != 23) || + (cfg.protocol == PROT_RLOGIN && cfg.port != 513)) { + cfg.port = i ? 22 : j ? 23 : 513; SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE); } } @@ -1517,8 +1555,9 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, } break; case IDC_TSEDIT: + case IDC_R_TSEDIT: if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed, + GetDlgItemText (hwnd, LOWORD(wParam), cfg.termspeed, sizeof(cfg.termspeed)-1); break; case IDC_LOGEDIT: @@ -1526,6 +1565,11 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, GetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username, sizeof(cfg.username)-1); break; + case IDC_RLLUSEREDIT: + if (HIWORD(wParam) == EN_CHANGE) + GetDlgItemText (hwnd, IDC_RLLUSEREDIT, cfg.localusername, + sizeof(cfg.localusername)-1); + break; case IDC_EMBSD: case IDC_EMRFC: cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC_EMRFC); diff --git a/winnet.c b/winnet.c index 86ae41c8..53a40e81 100644 --- a/winnet.c +++ b/winnet.c @@ -288,7 +288,7 @@ void sk_addr_free(SockAddr addr) { sfree(addr); } -Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) { +Socket sk_new(SockAddr addr, int port, int privport, sk_receiver_t receiver) { SOCKET s; #ifdef IPV6 SOCKADDR_IN6 a6; @@ -298,6 +298,7 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) { char *errstr; Socket ret; extern char *do_select(SOCKET skt, int startup); + short localport; /* * Create Socket structure. @@ -325,28 +326,56 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) { /* * Bind to local address. */ -#ifdef IPV6 - if (addr->family == AF_INET6) - { - memset(&a6,0,sizeof(a6)); - a6.sin6_family = AF_INET6; - /*a6.sin6_addr = in6addr_any;*/ /* == 0 */ - a6.sin6_port = htons(0); - } + if (privport) + localport = 1023; /* count from 1023 downwards */ else - { + localport = 0; /* just use port 0 (ie winsock picks) */ + + /* Loop round trying to bind */ + while (1) { + int retcode; + +#ifdef IPV6 + if (addr->family == AF_INET6) + { + memset(&a6,0,sizeof(a6)); + a6.sin6_family = AF_INET6; + /*a6.sin6_addr = in6addr_any;*/ /* == 0 */ + a6.sin6_port = htons(localport); + } + else + { #endif - a.sin_family = AF_INET; - a.sin_addr.s_addr = htonl(INADDR_ANY); - a.sin_port = htons(0); + a.sin_family = AF_INET; + a.sin_addr.s_addr = htonl(INADDR_ANY); + a.sin_port = htons(localport); #ifdef IPV6 - } - if (bind (s, (addr->family == AF_INET6) ? (struct sockaddr *)&a6 : (struct sockaddr *)&a, (addr->family == AF_INET6) ? sizeof(a6) : sizeof(a)) == SOCKET_ERROR) + } + retcode = bind (s, (addr->family == AF_INET6 ? + (struct sockaddr *)&a6 : + (struct sockaddr *)&a), + (addr->family == AF_INET6 ? sizeof(a6) : sizeof(a))); #else - if (bind (s, (struct sockaddr *)&a, sizeof(a)) == SOCKET_ERROR) + retcode = bind (s, (struct sockaddr *)&a, sizeof(a)); #endif + if (retcode != SOCKET_ERROR) { + err = 0; + break; /* done */ + } else { + err = WSAGetLastError(); + if (err != WSAEADDRINUSE) /* failed, for a bad reason */ + break; + } + + if (localport == 0) + break; /* we're only looping once */ + localport--; + if (localport == 0) + break; /* we might have got to the end */ + } + + if (err) { - err = WSAGetLastError(); ret->error = winsock_error_string(err); return ret; } -- 2.11.0