From 374330e25a6b51c40436fa869a381dd510790f6e Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 8 Jan 1999 13:02:13 +0000 Subject: [PATCH] Initial checkin: beta 0.43 git-svn-id: svn://svn.tartarus.org/sgt/putty@11 cda61777-01e9-0310-a592-d414129be87e --- Makefile | 56 +++ misc.c | 67 +++ noise.c | 148 ++++++ putty.h | 252 ++++++++++ putty.ico | Bin 0 -> 3318 bytes resource.h | 15 + ssh.c | 694 ++++++++++++++++++++++++++++ ssh.h | 39 ++ sshcrc.c | 111 +++++ sshdes.c | 768 +++++++++++++++++++++++++++++++ sshmd5.c | 249 ++++++++++ sshrand.c | 169 +++++++ sshrsa.c | 412 +++++++++++++++++ sshsha.c | 141 ++++++ telnet.c | 720 +++++++++++++++++++++++++++++ terminal.c | 1447 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ win_res.h | 116 +++++ win_res.rc | 235 ++++++++++ windlg.c | 1336 +++++++++++++++++++++++++++++++++++++++++++++++++++++ window.c | 1499 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 8474 insertions(+) create mode 100644 Makefile create mode 100644 misc.c create mode 100644 noise.c create mode 100644 putty.h create mode 100644 putty.ico create mode 100644 resource.h create mode 100644 ssh.c create mode 100644 ssh.h create mode 100644 sshcrc.c create mode 100644 sshdes.c create mode 100644 sshmd5.c create mode 100644 sshrand.c create mode 100644 sshrsa.c create mode 100644 sshsha.c create mode 100644 telnet.c create mode 100644 terminal.c create mode 100644 win_res.h create mode 100644 win_res.rc create mode 100644 windlg.c create mode 100644 window.c diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..86611bee --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +# Makefile for PuTTY. Use `FWHACK=/DFWHACK' to cause the firewall hack +# to be built in. (requires rebuild of ssh.obj only) + +CFLAGS = /nologo /W3 /YX /O2 /Yd /D_X86_ /D_WINDOWS /DDEBUG /ML /Fd + +.c.obj: + cl $(FWHACK) $(CFLAGS) /c $*.c + +OBJS1 = window.obj windlg.obj terminal.obj telnet.obj misc.obj noise.obj +OBJS2 = ssh.obj sshcrc.obj sshdes.obj sshmd5.obj sshrsa.obj sshrand.obj +OBJS3 = sshsha.obj +RESRC = win_res.res +LIBS1 = advapi32.lib user32.lib gdi32.lib +LIBS2 = wsock32.lib comctl32.lib comdlg32.lib + +putty.exe: $(OBJS1) $(OBJS2) $(OBJS3) $(RESRC) link.rsp + link /debug -out:putty.exe @link.rsp + +puttyd.exe: $(OBJS1) $(OBJS2) $(OBJS3) $(RESRC) link.rsp + link /debug -out:puttyd.exe @link.rsp + +link.rsp: makefile + echo /nologo /subsystem:windows > link.rsp + echo $(OBJS1) >> link.rsp + echo $(OBJS2) >> link.rsp + echo $(OBJS3) >> link.rsp + echo $(RESRC) >> link.rsp + echo $(LIBS1) >> link.rsp + echo $(LIBS2) >> link.rsp + +window.obj: window.c putty.h win_res.h +windlg.obj: windlg.c putty.h ssh.h win_res.h +terminal.obj: terminal.c putty.h +telnet.obj: telnet.c putty.h +misc.obj: misc.c putty.h +noise.obj: noise.c putty.h ssh.h +ssh.obj: ssh.c ssh.h putty.h +sshcrc.obj: sshcrc.c ssh.h +sshdes.obj: sshdes.c ssh.h +sshmd5.obj: sshmd5.c ssh.h +sshrsa.obj: sshrsa.c ssh.h +sshsha.obj: sshsha.c ssh.h +sshrand.obj: sshrand.c ssh.h + +win_res.res: win_res.rc win_res.h putty.ico + rc $(FWHACK) -r win_res.rc + +clean: + del *.obj + del *.exe + del *.res + del *.pch + del *.aps + del *.ilk + del *.pdb + del link.rsp diff --git a/misc.c b/misc.c new file mode 100644 index 00000000..370eea07 --- /dev/null +++ b/misc.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include "putty.h" + +/* My own versions of malloc, realloc and free. Because I want malloc and + * realloc to bomb out and exit the program if they run out of memory, + * realloc to reliably call malloc if passed a NULL pointer, and free + * to reliably do nothing if passed a NULL pointer. Of course we can also + * put trace printouts in, if we need to. */ + +#ifdef MALLOC_LOG +static FILE *fp = NULL; + +void mlog(char *file, int line) { + if (!fp) + fp = fopen("putty_mem.log", "w"); + if (fp) + fprintf (fp, "%s:%d: ", file, line); +} +#endif + +void *safemalloc(size_t size) { + void *p = malloc (size); + if (!p) { + MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error", + MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + exit(1); + } +#ifdef MALLOC_LOG + if (fp) + fprintf(fp, "malloc(%d) returns %p\n", size, p); +#endif + return p; +} + +void *saferealloc(void *ptr, size_t size) { + void *p; + if (!ptr) + p = malloc (size); + else + p = realloc (ptr, size); + if (!p) { + MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error", + MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + exit(1); + } +#ifdef MALLOC_LOG + if (fp) + fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p); +#endif + return p; +} + +void safefree(void *ptr) { + if (ptr) { +#ifdef MALLOC_LOG + if (fp) + fprintf(fp, "free(%p)\n", ptr); +#endif + free (ptr); + } +#ifdef MALLOC_LOG + else if (fp) + fprintf(fp, "freeing null pointer - no action taken\n"); +#endif +} diff --git a/noise.c b/noise.c new file mode 100644 index 00000000..cf9bba57 --- /dev/null +++ b/noise.c @@ -0,0 +1,148 @@ +/* + * Noise generation for PuTTY's cryptographic random number + * generator. + */ + +#include +#include + +#include "putty.h" +#include "ssh.h" + +static char seedpath[2*MAX_PATH+10] = "\0"; + +/* + * Find the random seed file path and store it in `seedpath'. + */ +static void get_seedpath(void) { + HKEY rkey; + DWORD type, size; + + size = sizeof(seedpath); + + if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey)==ERROR_SUCCESS) { + int ret = RegQueryValueEx(rkey, "RandSeedFile", + 0, &type, seedpath, &size); + if (ret != ERROR_SUCCESS || type != REG_SZ) + seedpath[0] = '\0'; + RegCloseKey(rkey); + } else + seedpath[0] = '\0'; + + if (!seedpath[0]) { + int len, ret; + + len = GetEnvironmentVariable("HOMEDRIVE", seedpath, sizeof(seedpath)); + ret = GetEnvironmentVariable("HOMEPATH", seedpath+len, + sizeof(seedpath)-len); + if (ret == 0) { /* probably win95; store in \WINDOWS */ + GetWindowsDirectory(seedpath, sizeof(seedpath)); + len = strlen(seedpath); + } else + len += ret; + strcpy(seedpath+len, "\\PUTTY.RND"); + } +} + +/* + * This function is called once, at PuTTY startup, and will do some + * seriously silly things like listing directories and getting disk + * free space and a process snapshot. + */ + +void noise_get_heavy(void (*func) (void *, int)) { + HANDLE srch; + HANDLE seedf; + WIN32_FIND_DATA finddata; + char winpath[MAX_PATH+3]; + + GetWindowsDirectory(winpath, sizeof(winpath)); + strcat(winpath, "\\*"); + srch = FindFirstFile(winpath, &finddata); + if (srch != INVALID_HANDLE_VALUE) { + do { + func(&finddata, sizeof(finddata)); + } while (FindNextFile(srch, &finddata)); + FindClose(srch); + } + + if (!seedpath[0]) + get_seedpath(); + + seedf = CreateFile(seedpath, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + + if (seedf) { + while (1) { + char buf[1024]; + DWORD len; + + if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len) + func(buf, len); + else + break; + } + CloseHandle(seedf); + } +} + +void random_save_seed(void) { + HANDLE seedf; + + if (!seedpath[0]) + get_seedpath(); + + seedf = CreateFile(seedpath, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (seedf) { + int len; + DWORD lenwritten; + void *data; + + random_get_savedata(&data, &len); + WriteFile(seedf, data, len, &lenwritten, NULL); + CloseHandle(seedf); + } +} + +/* + * This function is called every time the random pool needs + * stirring, and will acquire the system time in all available + * forms and the battery status. + */ +void noise_get_light(void (*func) (void *, int)) { + SYSTEMTIME systime; + DWORD adjust[2]; + BOOL rubbish; + SYSTEM_POWER_STATUS pwrstat; + + GetSystemTime(&systime); + func(&systime, sizeof(systime)); + + GetSystemTimeAdjustment(&adjust[0], &adjust[1], &rubbish); + func(&adjust, sizeof(adjust)); + + if (GetSystemPowerStatus(&pwrstat)) + func(&pwrstat, sizeof(pwrstat)); +} + +/* + * This function is called on every keypress or mouse move, and + * will add the current Windows time and performance monitor + * counter to the noise pool. It gets the scan code or mouse + * position passed in. + */ +void noise_ultralight(DWORD data) { + DWORD wintime; + LARGE_INTEGER perftime; + + random_add_noise(&data, sizeof(DWORD)); + + wintime = GetTickCount(); + random_add_noise(&wintime, sizeof(DWORD)); + + if (QueryPerformanceCounter(&perftime)) + random_add_noise(&perftime, sizeof(perftime)); +} diff --git a/putty.h b/putty.h new file mode 100644 index 00000000..c570392a --- /dev/null +++ b/putty.h @@ -0,0 +1,252 @@ +#ifndef PUTTY_PUTTY_H +#define PUTTY_PUTTY_H + +#define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY" + +/* + * Global variables. Most modules declare these `extern', but + * window.c will do `#define PUTTY_DO_GLOBALS' before including this + * module, and so will get them properly defined. + */ +#ifdef PUTTY_DO_GLOBALS +#define GLOBAL +#else +#define GLOBAL extern +#endif + +#define ATTR_ACTCURS 0x80000000UL /* active cursor (block) */ +#define ATTR_PASCURS 0x40000000UL /* passive cursor (box) */ +#define ATTR_INVALID 0x20000000UL +#define ATTR_WRAPPED 0x10000000UL + +#define ATTR_ASCII 0x00000000UL /* normal ASCII charset ESC ( B */ +#define ATTR_GBCHR 0x00100000UL /* UK variant charset ESC ( A */ +#define ATTR_LINEDRW 0x00200000UL /* line drawing charset ESC ( 0 */ + +#define ATTR_BOLD 0x00000100UL +#define ATTR_UNDER 0x00000200UL +#define ATTR_REVERSE 0x00000400UL +#define ATTR_FGMASK 0x0000F000UL +#define ATTR_BGMASK 0x000F0000UL +#define ATTR_FGSHIFT 12 +#define ATTR_BGSHIFT 16 + +#define ATTR_DEFAULT 0x00098000UL +#define ATTR_DEFFG 0x00008000UL +#define ATTR_DEFBG 0x00090000UL +#define ATTR_CUR_XOR 0x000BA000UL +#define ERASE_CHAR (ATTR_DEFAULT | ' ') +#define ATTR_MASK 0xFFFFFF00UL +#define CHAR_MASK 0x000000FFUL + +typedef HDC Context; +#define SEL_NL { 13, 10 } + +GLOBAL int rows, cols, savelines; + +GLOBAL int font_width, font_height; + +#define INBUF_SIZE 2048 +#define INBUF_MASK (INBUF_SIZE-1) +GLOBAL unsigned char inbuf[INBUF_SIZE]; +GLOBAL int inbuf_head, inbuf_reap; + +#define OUTBUF_SIZE 2048 +#define OUTBUF_MASK (OUTBUF_SIZE-1) +GLOBAL unsigned char outbuf[OUTBUF_SIZE]; +GLOBAL int outbuf_head, outbuf_reap; + +GLOBAL int has_focus; + +GLOBAL int app_cursor_keys, app_keypad_keys; + +#define WM_NETEVENT (WM_USER + 1) + +typedef enum { + TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT, + TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF +} Telnet_Special; + +typedef enum { + MB_NOTHING, MB_SELECT, MB_EXTEND, MB_PASTE +} Mouse_Button; + +typedef enum { + MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE +} Mouse_Action; + +typedef enum { + VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN +} VT_Mode; + +typedef struct { + char *(*init) (HWND hwnd, char *host, int port, char **realhost); + int (*msg) (WPARAM wParam, LPARAM lParam); + void (*send) (char *buf, int len); + void (*size) (void); + void (*special) (Telnet_Special code); +} Backend; + +GLOBAL Backend *back; + +typedef struct { + /* Basic options */ + char host[512]; + int port; + enum { PROT_TELNET, PROT_SSH } protocol; + int close_on_exit; + /* Telnet options */ + char termtype[32]; + char termspeed[32]; + char environ[1024]; /* VAR\tvalue\0VAR\tvalue\0\0 */ + char username[32]; + int rfc_environ; + /* Keyboard options */ + int bksp_is_delete; + int rxvt_homeend; + int linux_funkeys; + int app_cursor; + int app_keypad; + /* Terminal options */ + int savelines; + int dec_om; + int wrap_mode; + int win_name_always; + int width, height; + char font[64]; + int fontisbold; + int fontheight; + VT_Mode vtmode; + /* Colour options */ + int try_palette; + int bold_colour; + unsigned char colours[22][3]; + /* Selection options */ + int mouse_is_xterm; + short wordness[256]; +} Config; + +GLOBAL Config cfg; + +/* + * Exports from window.c. + */ +void request_resize (int, int); +void do_text (Context, int, int, char *, int, unsigned long); +void set_title (char *); +void set_icon (char *); +void set_sbar (int, int, int); +Context get_ctx(); +void free_ctx (Context); +void palette_set (int, int, int, int); +void palette_reset (void); +void write_clip (void *, int); +void get_clip (void **, int *); +void optimised_move (int, int, int); +void fatalbox (char *, ...); +void beep (void); +#define OPTIMISE_IS_SCROLL 1 + +/* + * Exports from noise.c. + */ +void noise_get_heavy(void (*func) (void *, int)); +void noise_get_light(void (*func) (void *, int)); +void noise_ultralight(DWORD data); +void random_save_seed(void); + +/* + * Exports from windlg.c. + */ +int do_config (void); +int do_reconfig (HWND); +void do_defaults (char *); +void lognegot (char *); +void shownegot (HWND); +void showabout (HWND); +void verify_ssh_host_key(char *host, struct RSAKey *key); + +/* + * Exports from terminal.c. + */ + +void term_init (void); +void term_size (int, int, int); +void term_out (void); +void term_paint (Context, int, int, int, int); +void term_scroll (int, int); +void term_pwron (void); +void term_clrsb (void); +void term_mouse (Mouse_Button, Mouse_Action, int, int); +void term_deselect (void); +void term_update (void); +void term_invalidate(void); + +/* + * Exports from telnet.c. + */ + +Backend telnet_backend; + +/* + * Exports from ssh.c. + */ + +Backend ssh_backend; + +/* + * Exports from sshrand.c. + */ + +void random_add_noise(void *noise, int length); +void random_init(void); +int random_byte(void); +void random_get_savedata(void **data, int *len); + +/* + * Exports from misc.c. + */ + +/* #define MALLOC_LOG do this if you suspect putty of leaking memory */ +#ifdef MALLOC_LOG +#define smalloc(z) (mlog(__FILE__,__LINE__), safemalloc(z)) +#define srealloc(y,z) (mlog(__FILE__,__LINE__), saferealloc(y,z)) +#define sfree(z) (mlog(__FILE__,__LINE__), safefree(z)) +void mlog(char *, int); +#else +#define smalloc safemalloc +#define srealloc saferealloc +#define sfree safefree +#endif + +void *safemalloc(size_t); +void *saferealloc(void *, size_t); +void safefree(void *); + +/* + * A debug system. + */ +#ifdef DEBUG +#include +#define debug(x) (dprintf x) +static void dprintf(char *fmt, ...) { + char buf[2048]; + DWORD dw; + va_list ap; + static int gotconsole = 0; + + if (!gotconsole) { + AllocConsole(); + gotconsole = 1; + } + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &dw, NULL); + va_end(ap); +} +#else +#define debug(x) +#endif + +#endif diff --git a/putty.ico b/putty.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0c7cfc67d385b242d93ae9e13e76dd9579beb25 GIT binary patch literal 3318 zcmd6pF=%5)5Qcxnf+vtbabr@Li{k3s9V`S~opEot#gk`YV(Gd>4>{xm|hVC)C z*pE*0@?0`-Zjz?iOZLWayst7|{$`@9*Pf|~)NXb|_+?rXwbg4?^p?6+Jv3Xh{;klT z)92y=l+DDqTgFFS*2W{QI6rc|}2iZkxYB2K}*VD3K%Gt!sJDnD09@Bf?|E;gVuRo|>udC5$sMTs| zeSKXU8yni(+|<_AmbSOIb#QQ?{r!FI?d@r2XGi&0QLkP_eXC2wKZD*~{-w+J@Ab`3 zKat0#fgFb%pWBq4$-&@Ya4qNROBx3OQj}v0aHQJf&>Lj1-T10 z3vw6aF36qQk#b`ve?k6&{MMp^`~|fsU?^ZHsDrhQs-)XvVP&>9QiDLg09$|~kT12? zIH*B@BNzu4OYP=RfF-~ZfCwZ^uK+B8g8?VL6A|q8x)=*R39tmKM`E$J!McDAh6qE1 zA;J)0h%ltr35EbegdxHZU?2>FKMWCu2t$N{&Ppww*SfvE)!p5l?(gq491b-YMSb`7 zEuZ6@>e^?(7UCt%;+ksO2L{0^Bb)P&*?8Eum4+F)D`UDpahr2UU#K(FlK|C(!#H=| zlFkg_P|1(-82w4`~gEL+%0w#vbOXXT*mm2?w_(YWGIZiBpd7yp+o0%-yHTE>9+3$9(+MQJccsFP$X2tR3VNq=oRZw?{d#`_juBQwGX#UCZmz Z@40GUu=fJtbPba+OeJ!t9RGE_{soj)^Dh7Z literal 0 HcmV?d00001 diff --git a/resource.h b/resource.h new file mode 100644 index 00000000..07d5008a --- /dev/null +++ b/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by win_res.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ssh.c b/ssh.c new file mode 100644 index 00000000..f80055c9 --- /dev/null +++ b/ssh.c @@ -0,0 +1,694 @@ +#include +#include +#include + +#include "putty.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#include "ssh.h" + +/* Coroutine mechanics for the sillier bits of the code */ +#define crBegin1 static int crLine = 0; +#define crBegin2 switch(crLine) { case 0:; +#define crBegin crBegin1; crBegin2; +#define crFinish(z) } crLine = 0; return (z) +#define crFinishV } crLine = 0; return +#define crReturn(z) \ + do {\ + crLine=__LINE__; return (z); case __LINE__:;\ + } while (0) +#define crReturnV \ + do {\ + crLine=__LINE__; return; case __LINE__:;\ + } while (0) +#define crStop(z) do{ crLine = 0; return (z); }while(0) +#define crStopV do{ crLine = 0; return; }while(0) + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +static SOCKET s = INVALID_SOCKET; + +static unsigned char session_key[32]; +static struct ssh_cipher *cipher = NULL; + +static char *savedhost; + +static enum { + SSH_STATE_BEFORE_SIZE, + SSH_STATE_INTERMED, + SSH_STATE_SESSION +} ssh_state = SSH_STATE_BEFORE_SIZE; + +static int size_needed = FALSE; + +static void s_write (char *buf, int len) { + while (len > 0) { + int i = send (s, buf, len, 0); + if (i > 0) + len -= i, buf += i; + } +} + +static int s_read (char *buf, int len) { + int ret = 0; + while (len > 0) { + int i = recv (s, buf, len, 0); + if (i > 0) + len -= i, buf += i, ret += i; + else + return i; + } + return ret; +} + +static void c_write (char *buf, int len) { + while (len--) { + int new_head = (inbuf_head + 1) & INBUF_MASK; + int c = (unsigned char) *buf; + if (new_head != inbuf_reap) { + inbuf[inbuf_head] = *buf++; + inbuf_head = new_head; + } + } +} + +struct Packet { + long length; + int type; + unsigned long crc; + unsigned char *data; + unsigned char *body; + long maxlen; +}; + +static struct Packet pktin = { 0, 0, 0, NULL, 0 }; +static struct Packet pktout = { 0, 0, 0, NULL, 0 }; + +static void ssh_protocol(unsigned char *in, int inlen, int ispkt); +static void ssh_size(void); + +static void ssh_gotdata(unsigned char *data, int datalen) { + static long len, biglen, to_read; + static unsigned char c, *p; + static int i, pad; + static char padding[8]; + static unsigned char word[4]; + + crBegin; + while (1) { + for (i = len = 0; i < 4; i++) { + while (datalen == 0) + crReturnV; + len = (len << 8) + *data; + data++, datalen--; + } + + pad = 8 - (len%8); + + biglen = len + pad; + + len -= 5; /* type and CRC */ + + pktin.length = len; + if (pktin.maxlen < biglen) { + pktin.maxlen = biglen; + pktin.data = (pktin.data == NULL ? malloc(biglen) : + realloc(pktin.data, biglen)); + if (!pktin.data) + fatalbox("Out of memory"); + } + + p = pktin.data, to_read = biglen; + while (to_read > 0) { + static int chunk; + chunk = to_read; + while (datalen == 0) + crReturnV; + if (chunk > datalen) + chunk = datalen; + memcpy(p, data, chunk); + data += chunk; + datalen -= chunk; + p += chunk; + to_read -= chunk; + } + + if (cipher) + cipher->decrypt(pktin.data, biglen); + + pktin.type = pktin.data[pad]; + pktin.body = pktin.data+pad+1; + + if (pktin.type == 36) { /* SSH_MSG_DEBUG */ + /* FIXME: log it */ + } else + ssh_protocol(NULL, 0, 1); + } + crFinishV; +} + +static void s_wrpkt_start(int type, int len) { + int pad, biglen; + + len += 5; /* type and CRC */ + pad = 8 - (len%8); + biglen = len + pad; + + pktout.length = len-5; + if (pktout.maxlen < biglen) { + pktout.maxlen = biglen; + pktout.data = (pktout.data == NULL ? malloc(biglen) : + realloc(pktout.data, biglen)); + if (!pktout.data) + fatalbox("Out of memory"); + } + + pktout.type = type; + pktout.body = pktout.data+4+pad+1; +} + +static void s_wrpkt(void) { + int pad, len, biglen, i; + unsigned long crc; + + len = pktout.length + 5; /* type and CRC */ + pad = 8 - (len%8); + biglen = len + pad; + + pktout.body[-1] = pktout.type; + for (i=0; i> 24) & 0xFF); + pktout.data[biglen+1] = (unsigned char) ((crc >> 16) & 0xFF); + pktout.data[biglen+2] = (unsigned char) ((crc >> 8) & 0xFF); + pktout.data[biglen+3] = (unsigned char) (crc & 0xFF); + + pktout.data[0] = (len >> 24) & 0xFF; + pktout.data[1] = (len >> 16) & 0xFF; + pktout.data[2] = (len >> 8) & 0xFF; + pktout.data[3] = len & 0xFF; + + if (cipher) + cipher->encrypt(pktout.data+4, biglen); + + s_write(pktout.data, biglen+4); +} + +static int do_ssh_init(void) { + char c; + char version[10]; + char vstring[40]; + int i; + +#ifdef FWHACK + i = 0; + while (s_read(&c, 1) == 1) { + if (c == 'S' && i < 2) i++; + else if (c == 'S' && i == 2) i = 2; + else if (c == 'H' && i == 2) break; + else i = 0; + } +#else + if (s_read(&c,1) != 1 || c != 'S') return 0; + if (s_read(&c,1) != 1 || c != 'S') return 0; + if (s_read(&c,1) != 1 || c != 'H') return 0; +#endif + if (s_read(&c,1) != 1 || c != '-') return 0; + i = 0; + while (1) { + if (s_read(&c,1) != 1) + return 0; + if (i >= 0) { + if (c == '-') { + version[i] = '\0'; + i = -1; + } else if (i < sizeof(version)-1) + version[i++] = c; + } + else if (c == '\n') + break; + } + + sprintf(vstring, "SSH-%s-7.7.7\n", + (strcmp(version, "1.5") <= 0 ? version : "1.5")); + s_write(vstring, strlen(vstring)); +} + +static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { + int i, j, len; + unsigned char session_id[16]; + unsigned char *rsabuf, *keystr1, *keystr2; + unsigned char cookie[8]; + struct RSAKey servkey, hostkey; + struct MD5Context md5c; + + extern struct ssh_cipher ssh_3des; + + crBegin; + + random_init(); + + while (!ispkt) + crReturnV; + + if (pktin.type != 2) + fatalbox("Public key packet not received"); + + memcpy(cookie, pktin.body, 8); + + MD5Init(&md5c); + + i = makekey(pktin.body+8, &servkey, &keystr1); + + j = makekey(pktin.body+8+i, &hostkey, &keystr2); + + MD5Update(&md5c, keystr2, hostkey.bytes); + MD5Update(&md5c, keystr1, servkey.bytes); + MD5Update(&md5c, pktin.body, 8); + + MD5Final(session_id, &md5c); + + for (i=0; i<32; i++) + session_key[i] = random_byte(); + + len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes); + + rsabuf = malloc(len); + if (!rsabuf) + fatalbox("Out of memory"); + + verify_ssh_host_key(savedhost, &hostkey); + + for (i=0; i<32; i++) { + rsabuf[i] = session_key[i]; + if (i < 16) + rsabuf[i] ^= session_id[i]; + } + + if (hostkey.bytes > servkey.bytes) { + rsaencrypt(rsabuf, 32, &servkey); + rsaencrypt(rsabuf, servkey.bytes, &hostkey); + } else { + rsaencrypt(rsabuf, 32, &hostkey); + rsaencrypt(rsabuf, hostkey.bytes, &servkey); + } + + s_wrpkt_start(3, len+15); + pktout.body[0] = 3; /* SSH_CIPHER_3DES */ + memcpy(pktout.body+1, cookie, 8); + pktout.body[9] = (len*8) >> 8; + pktout.body[10] = (len*8) & 0xFF; + memcpy(pktout.body+11, rsabuf, len); + pktout.body[len+11] = pktout.body[len+12] = 0; /* protocol flags */ + pktout.body[len+13] = pktout.body[len+14] = 0; + s_wrpkt(); + + free(rsabuf); + + cipher = &ssh_3des; + cipher->sesskey(session_key); + + do { crReturnV; } while (!ispkt); + + if (pktin.type != 14) + fatalbox("Encryption not successfully enabled"); + + fflush(stdout); + { + static char username[100]; + static int pos = 0; + static char c; + if (!*cfg.username) { + c_write("login as: ", 10); + while (pos >= 0) { + do { crReturnV; } while (ispkt); + while (inlen--) switch (c = *in++) { + case 10: case 13: + username[pos] = 0; + pos = -1; + break; + case 8: case 127: + if (pos > 0) { + c_write("\b \b", 3); + pos--; + } + break; + case 21: case 27: + while (pos > 0) { + c_write("\b \b", 3); + pos--; + } + break; + case 3: case 4: + random_save_seed(); + exit(0); + break; + default: + if (c >= ' ' && c <= '~' && pos < 40) { + username[pos++] = c; + c_write(&c, 1); + } + break; + } + } + c_write("\r\n", 2); + username[strcspn(username, "\n\r")] = '\0'; + } else { + char stuff[200]; + strncpy(username, cfg.username, 99); + username[99] = '\0'; + sprintf(stuff, "Sent username \"%s\".\r\n", username); + c_write(stuff, strlen(stuff)); + } + s_wrpkt_start(4, 4+strlen(username)); + pktout.body[0] = pktout.body[1] = pktout.body[2] = 0; + pktout.body[3] = strlen(username); + memcpy(pktout.body+4, username, strlen(username)); + s_wrpkt(); + } + + do { crReturnV; } while (!ispkt); + + while (pktin.type == 15) { + static char password[100]; + static int pos; + static char c; + c_write("password: ", 10); + pos = 0; + while (pos >= 0) { + do { crReturnV; } while (ispkt); + while (inlen--) switch (c = *in++) { + case 10: case 13: + password[pos] = 0; + pos = -1; + break; + case 8: case 127: + if (pos > 0) + pos--; + break; + case 21: case 27: + pos = 0; + break; + case 3: case 4: + random_save_seed(); + exit(0); + break; + default: + if (c >= ' ' && c <= '~' && pos < 40) + password[pos++] = c; + break; + } + } + c_write("\r\n", 2); + s_wrpkt_start(9, 4+strlen(password)); + pktout.body[0] = pktout.body[1] = pktout.body[2] = 0; + pktout.body[3] = strlen(password); + memcpy(pktout.body+4, password, strlen(password)); + s_wrpkt(); + memset(password, 0, strlen(password)); + do { crReturnV; } while (!ispkt); + if (pktin.type == 15) { + c_write("Access denied\r\n", 15); + } else if (pktin.type != 14) { + fatalbox("Strange packet received, type %d", pktin.type); + } + } + + i = strlen(cfg.termtype); + s_wrpkt_start(10, i+5*4+1); + pktout.body[0] = (i >> 24) & 0xFF; + pktout.body[1] = (i >> 16) & 0xFF; + pktout.body[2] = (i >> 8) & 0xFF; + pktout.body[3] = i & 0xFF; + memcpy(pktout.body+4, cfg.termtype, i); + i += 4; + pktout.body[i++] = (rows >> 24) & 0xFF; + pktout.body[i++] = (rows >> 16) & 0xFF; + pktout.body[i++] = (rows >> 8) & 0xFF; + pktout.body[i++] = rows & 0xFF; + pktout.body[i++] = (cols >> 24) & 0xFF; + pktout.body[i++] = (cols >> 16) & 0xFF; + pktout.body[i++] = (cols >> 8) & 0xFF; + pktout.body[i++] = cols & 0xFF; + memset(pktout.body+i, 0, 9); /* 0 pixwidth, 0 pixheight, 0.b endofopt */ + s_wrpkt(); + ssh_state = SSH_STATE_INTERMED; + do { crReturnV; } while (!ispkt); + if (pktin.type != 14 && pktin.type != 15) { + fatalbox("Protocol confusion"); + } else if (pktin.type == 15) { + c_write("Server refused to allocate pty\r\n", 32); + } + + s_wrpkt_start(12, 0); + s_wrpkt(); + + ssh_state = SSH_STATE_SESSION; + if (size_needed) + ssh_size(); + + while (1) { + crReturnV; + if (ispkt) { + if (pktin.type == 17 || pktin.type == 18) { + long len = 0; + for (i = 0; i < 4; i++) + len = (len << 8) + pktin.body[i]; + c_write(pktin.body+4, len); + } else if (pktin.type == 1) { + /* SSH_MSG_DISCONNECT: do nothing */ + } else if (pktin.type == 14) { + /* SSH_MSG_SUCCESS: may be from EXEC_SHELL on some servers */ + } else if (pktin.type == 15) { + /* SSH_MSG_FAILURE: may be from EXEC_SHELL on some servers + * if no pty is available or in other odd cases. Ignore */ + } else if (pktin.type == 20) { + /* EXITSTATUS */ + s_wrpkt_start(33, 0); + s_wrpkt(); + } else { + fatalbox("Strange packet received: type %d", pktin.type); + } + } else { + s_wrpkt_start(16, 4+inlen); + pktout.body[0] = (inlen >> 24) & 0xFF; + pktout.body[1] = (inlen >> 16) & 0xFF; + pktout.body[2] = (inlen >> 8) & 0xFF; + pktout.body[3] = inlen & 0xFF; + memcpy(pktout.body+4, in, inlen); + s_wrpkt(); + } + } + + crFinishV; +} + +/* + * Called to set up the connection. Will arrange for WM_NETEVENT + * messages to be passed to the specified window, whose window + * procedure should then call telnet_msg(). + * + * Returns an error message, or NULL on success. + * + * Also places the canonical host name into `realhost'. + */ +static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) { + SOCKADDR_IN addr; + struct hostent *h; + unsigned long a; +#ifdef FWHACK + char *FWhost; + int FWport; +#endif + + savedhost = malloc(1+strlen(host)); + if (!savedhost) + fatalbox("Out of memory"); + strcpy(savedhost, host); + +#ifdef FWHACK + FWhost = host; + FWport = port; + host = FWSTR; + port = 23; +#endif + + /* + * Try to find host. + */ + if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) { + if ( (h = gethostbyname(host)) == NULL) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAHOST_NOT_FOUND: case WSANO_DATA: + return "Host does not exist"; + case WSATRY_AGAIN: return "Host not found"; + default: return "gethostbyname: unknown error"; + } + memcpy (&a, h->h_addr, sizeof(a)); + *realhost = h->h_name; + } else + *realhost = host; +#ifdef FWHACK + *realhost = FWhost; +#endif + a = ntohl(a); + + if (port < 0) + port = 22; /* default ssh port */ + + /* + * Open socket. + */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAEAFNOSUPPORT: return "TCP/IP support not present"; + default: return "socket(): unknown error"; + } + + /* + * Bind to local address. + */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + default: return "bind(): unknown error"; + } + + /* + * Connect to remote address. + */ + addr.sin_addr.s_addr = htonl(a); + addr.sin_port = htons((short)port); + if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAEHOSTUNREACH: return "No route to host"; + default: return "connect(): unknown error"; + } + +#ifdef FWHACK + send(s, "connect ", 8, 0); + send(s, FWhost, strlen(FWhost), 0); + { + char buf[20]; + sprintf(buf, " %d\n", FWport); + send (s, buf, strlen(buf), 0); + } +#endif + + if (!do_ssh_init()) + return "Protocol initialisation error"; + + if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | FD_CLOSE) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + default: return "WSAAsyncSelect(): unknown error"; + } + + return NULL; +} + +/* + * Process a WM_NETEVENT message. Will return 0 if the connection + * has closed, or <0 for a socket error. + */ +static int ssh_msg (WPARAM wParam, LPARAM lParam) { + int ret; + char buf[256]; + + if (s == INVALID_SOCKET) /* how the hell did we get here?! */ + return -5000; + + if (WSAGETSELECTERROR(lParam) != 0) + return -WSAGETSELECTERROR(lParam); + + switch (WSAGETSELECTEVENT(lParam)) { + case FD_READ: + ret = recv(s, buf, sizeof(buf), 0); + if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK) + return 1; + if (ret < 0) /* any _other_ error */ + return -10000-WSAGetLastError(); + if (ret == 0) { + s = INVALID_SOCKET; + return 0; /* can't happen, in theory */ + } + ssh_gotdata (buf, ret); + return 1; + case FD_CLOSE: + s = INVALID_SOCKET; + return 0; + } + return 1; /* shouldn't happen, but WTF */ +} + +/* + * Called to send data down the Telnet connection. + */ +static void ssh_send (char *buf, int len) { + if (s == INVALID_SOCKET) + return; + + ssh_protocol(buf, len, 0); +} + +/* + * Called to set the size of the window from Telnet's POV. + */ +static void ssh_size(void) { + switch (ssh_state) { + case SSH_STATE_BEFORE_SIZE: + break; /* do nothing */ + case SSH_STATE_INTERMED: + size_needed = TRUE; /* buffer for later */ + break; + case SSH_STATE_SESSION: + s_wrpkt_start(11, 16); + pktout.body[0] = (rows >> 24) & 0xFF; + pktout.body[1] = (rows >> 16) & 0xFF; + pktout.body[2] = (rows >> 8) & 0xFF; + pktout.body[3] = rows & 0xFF; + pktout.body[4] = (cols >> 24) & 0xFF; + pktout.body[5] = (cols >> 16) & 0xFF; + pktout.body[6] = (cols >> 8) & 0xFF; + pktout.body[7] = cols & 0xFF; + memset(pktout.body+8, 0, 8); + s_wrpkt(); + } +} + +/* + * (Send Telnet special codes) + */ +static void ssh_special (Telnet_Special code) { + /* do nothing */ +} + +Backend ssh_backend = { + ssh_init, + ssh_msg, + ssh_send, + ssh_size, + ssh_special +}; diff --git a/ssh.h b/ssh.h new file mode 100644 index 00000000..260d0629 --- /dev/null +++ b/ssh.h @@ -0,0 +1,39 @@ +struct RSAKey { + int bits; + int bytes; + void *modulus; + void *exponent; +}; + +int makekey(unsigned char *data, struct RSAKey *result, + unsigned char **keystr); +void rsaencrypt(unsigned char *data, int length, struct RSAKey *key); +int rsastr_len(struct RSAKey *key); +void rsastr_fmt(char *str, struct RSAKey *key); + +typedef unsigned int word32; +typedef unsigned int uint32; + +unsigned long crc32(const unsigned char *s, unsigned int len); + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); + +struct ssh_cipher { + void (*sesskey)(unsigned char *key); + void (*encrypt)(unsigned char *blk, int len); + void (*decrypt)(unsigned char *blk, int len); +}; + +void SHATransform(word32 *digest, word32 *data); + +int random_byte(void); +void random_add_noise(void *noise, int length); diff --git a/sshcrc.c b/sshcrc.c new file mode 100644 index 00000000..946da7b8 --- /dev/null +++ b/sshcrc.c @@ -0,0 +1,111 @@ + /* ============================================================= */ + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ + /* code or tables extracted from it, as desired without restriction. */ + /* */ + /* First, the polynomial itself and its table of feedback terms. The */ + /* polynomial is */ + /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ + /* */ + /* Note that we take it "backwards" and put the highest-order term in */ + /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ + /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ + /* the MSB being 1. */ + /* */ + /* Note that the usual hardware shift register implementation, which */ + /* is what we're using (we're merely optimizing it by doing eight-bit */ + /* chunks at a time) shifts bits into the lowest-order term. In our */ + /* implementation, that means shifting towards the right. Why do we */ + /* do it this way? Because the calculated CRC must be transmitted in */ + /* order from highest-order term to lowest-order term. UARTs transmit */ + /* characters in order from LSB to MSB. By storing the CRC this way, */ + /* we hand it to the UART in the order low-byte to high-byte; the UART */ + /* sends each low-bit to hight-bit; and the result is transmission bit */ + /* by bit from highest- to lowest-order term without requiring any bit */ + /* shuffling on our part. Reception works similarly. */ + /* */ + /* The feedback terms table consists of 256, 32-bit entries. Notes: */ + /* */ + /* The table can be generated at runtime if desired; code to do so */ + /* is shown later. It might not be obvious, but the feedback */ + /* terms simply represent the results of eight shift/xor opera- */ + /* tions for all combinations of data and CRC register values. */ + /* */ + /* The values must be right-shifted by eight bits by the "updcrc" */ + /* logic; the shift must be unsigned (bring in zeroes). On some */ + /* hardware you could probably optimize the shift in assembler by */ + /* using byte-swap instructions. */ + /* polynomial $edb88320 */ + /* */ + /* -------------------------------------------------------------------- */ + +static unsigned long crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +unsigned long crc32(const unsigned char *s, unsigned int len) +{ + unsigned int i; + unsigned long crc32val; + + crc32val = 0; + for (i = 0; i < len; i ++) + { + crc32val = + crc32_tab[(crc32val ^ s[i]) & 0xff] ^ + (crc32val >> 8); + } + return crc32val; +} diff --git a/sshdes.c b/sshdes.c new file mode 100644 index 00000000..897457a0 --- /dev/null +++ b/sshdes.c @@ -0,0 +1,768 @@ +#include +#include "ssh.h" + +/* + +DES implementation; 1995 Tatu Ylonen + +This implementation is derived from libdes-3.06, which is copyright +(c) 1993 Eric Young, and distributed under the GNU GPL or the ARTISTIC licence +(at the user's option). The original distribution can be found e.g. from +ftp://ftp.dsi.unimi.it/pub/security/crypt/libdes/libdes-3.06.tar.gz. + +This implementation is distributed under the same terms. See +libdes-README, libdes-ARTISTIC, and libdes-COPYING for more +information. + +*/ + +/* + * $Id: sshdes.c,v 1.1 1999/01/08 13:02:12 simon Exp $ + * $Log: sshdes.c,v $ + * Revision 1.1 1999/01/08 13:02:12 simon + * Initial checkin: beta 0.43 + * + * Revision 1.1.1.1 1996/02/18 21:38:11 ylo + * Imported ssh-1.2.13. + * + * Revision 1.2 1995/07/13 01:22:57 ylo + * Added cvs log. + * + * $Endlog$ + */ + +typedef struct +{ + word32 key_schedule[32]; +} DESContext; + +/* Sets the des key for the context. Initializes the context. The least + significant bit of each byte of the key is ignored as parity. */ +static void des_set_key(unsigned char *key, DESContext *ks); + +/* Encrypts 32 bits in l,r, and stores the result in output[0] and output[1]. + Performs encryption if encrypt is non-zero, and decryption if it is zero. + The key context must have been initialized previously with des_set_key. */ +static void des_encrypt(word32 l, word32 r, word32 *output, DESContext *ks, + int encrypt); + +/* Encrypts len bytes from src to dest in CBC modes. Len must be a multiple + of 8. iv will be modified at end to a value suitable for continuing + encryption. */ +static void des_cbc_encrypt(DESContext *ks, unsigned char *iv, unsigned char *dest, + const unsigned char *src, unsigned int len); + +/* Decrypts len bytes from src to dest in CBC modes. Len must be a multiple + of 8. iv will be modified at end to a value suitable for continuing + decryption. */ +static void des_cbc_decrypt(DESContext *ks, unsigned char *iv, unsigned char *dest, + const unsigned char *src, unsigned int len); + +/* Encrypts in CBC mode using triple-DES. */ +static void des_3cbc_encrypt(DESContext *ks1, unsigned char *iv1, + DESContext *ks2, unsigned char *iv2, + DESContext *ks3, unsigned char *iv3, + unsigned char *dest, const unsigned char *src, + unsigned int len); + +/* Decrypts in CBC mode using triple-DES. */ +static void des_3cbc_decrypt(DESContext *ks1, unsigned char *iv1, + DESContext *ks2, unsigned char *iv2, + DESContext *ks3, unsigned char *iv3, + unsigned char *dest, const unsigned char *src, + unsigned int len); + +#define GET_32BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8) | \ + ((unsigned long)(unsigned char)(cp)[2] << 16) | \ + ((unsigned long)(unsigned char)(cp)[3] << 24)) + +#define PUT_32BIT_LSB_FIRST(cp, value) do { \ + (cp)[0] = (value); \ + (cp)[1] = (value) >> 8; \ + (cp)[2] = (value) >> 16; \ + (cp)[3] = (value) >> 24; } while (0) + +/* + +DES implementation; 1995 Tatu Ylonen + +This implementation is derived from libdes-3.06, which is copyright +(c) 1993 Eric Young, and distributed under the GNU GPL or the ARTISTIC licence +(at the user's option). The original distribution can be found e.g. from +ftp://ftp.dsi.unimi.it/pub/security/crypt/libdes/libdes-3.06.tar.gz. + +This implementation is distributed under the same terms. See +libdes-README, libdes-ARTISTIC, and libdes-COPYING for more +information. + +A description of the DES algorithm can be found in every modern book on +cryptography and data security, including the following: + + Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. + + Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to + Computer Security. Prentice-Hall, 1989. + + Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, + 1994. + +*/ + +/* + * $Id: sshdes.c,v 1.1 1999/01/08 13:02:12 simon Exp $ + * $Log: sshdes.c,v $ + * Revision 1.1 1999/01/08 13:02:12 simon + * Initial checkin: beta 0.43 + * + * Revision 1.1.1.1 1996/02/18 21:38:11 ylo + * Imported ssh-1.2.13. + * + * Revision 1.2 1995/07/13 01:22:25 ylo + * Added cvs log. + * + * $Endlog$ + */ + +/* Table for key generation. This used to be in sk.h. */ +/* Copyright (C) 1993 Eric Young - see README for more details */ +static const word32 des_skb[8][64]={ +/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{ 0x00000000,0x00000010,0x20000000,0x20000010, +0x00010000,0x00010010,0x20010000,0x20010010, +0x00000800,0x00000810,0x20000800,0x20000810, +0x00010800,0x00010810,0x20010800,0x20010810, +0x00000020,0x00000030,0x20000020,0x20000030, +0x00010020,0x00010030,0x20010020,0x20010030, +0x00000820,0x00000830,0x20000820,0x20000830, +0x00010820,0x00010830,0x20010820,0x20010830, +0x00080000,0x00080010,0x20080000,0x20080010, +0x00090000,0x00090010,0x20090000,0x20090010, +0x00080800,0x00080810,0x20080800,0x20080810, +0x00090800,0x00090810,0x20090800,0x20090810, +0x00080020,0x00080030,0x20080020,0x20080030, +0x00090020,0x00090030,0x20090020,0x20090030, +0x00080820,0x00080830,0x20080820,0x20080830, +0x00090820,0x00090830,0x20090820,0x20090830 }, +/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ +{ 0x00000000,0x02000000,0x00002000,0x02002000, +0x00200000,0x02200000,0x00202000,0x02202000, +0x00000004,0x02000004,0x00002004,0x02002004, +0x00200004,0x02200004,0x00202004,0x02202004, +0x00000400,0x02000400,0x00002400,0x02002400, +0x00200400,0x02200400,0x00202400,0x02202400, +0x00000404,0x02000404,0x00002404,0x02002404, +0x00200404,0x02200404,0x00202404,0x02202404, +0x10000000,0x12000000,0x10002000,0x12002000, +0x10200000,0x12200000,0x10202000,0x12202000, +0x10000004,0x12000004,0x10002004,0x12002004, +0x10200004,0x12200004,0x10202004,0x12202004, +0x10000400,0x12000400,0x10002400,0x12002400, +0x10200400,0x12200400,0x10202400,0x12202400, +0x10000404,0x12000404,0x10002404,0x12002404, +0x10200404,0x12200404,0x10202404,0x12202404 }, +/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ +{ 0x00000000,0x00000001,0x00040000,0x00040001, +0x01000000,0x01000001,0x01040000,0x01040001, +0x00000002,0x00000003,0x00040002,0x00040003, +0x01000002,0x01000003,0x01040002,0x01040003, +0x00000200,0x00000201,0x00040200,0x00040201, +0x01000200,0x01000201,0x01040200,0x01040201, +0x00000202,0x00000203,0x00040202,0x00040203, +0x01000202,0x01000203,0x01040202,0x01040203, +0x08000000,0x08000001,0x08040000,0x08040001, +0x09000000,0x09000001,0x09040000,0x09040001, +0x08000002,0x08000003,0x08040002,0x08040003, +0x09000002,0x09000003,0x09040002,0x09040003, +0x08000200,0x08000201,0x08040200,0x08040201, +0x09000200,0x09000201,0x09040200,0x09040201, +0x08000202,0x08000203,0x08040202,0x08040203, +0x09000202,0x09000203,0x09040202,0x09040203 }, +/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ +{ 0x00000000,0x00100000,0x00000100,0x00100100, +0x00000008,0x00100008,0x00000108,0x00100108, +0x00001000,0x00101000,0x00001100,0x00101100, +0x00001008,0x00101008,0x00001108,0x00101108, +0x04000000,0x04100000,0x04000100,0x04100100, +0x04000008,0x04100008,0x04000108,0x04100108, +0x04001000,0x04101000,0x04001100,0x04101100, +0x04001008,0x04101008,0x04001108,0x04101108, +0x00020000,0x00120000,0x00020100,0x00120100, +0x00020008,0x00120008,0x00020108,0x00120108, +0x00021000,0x00121000,0x00021100,0x00121100, +0x00021008,0x00121008,0x00021108,0x00121108, +0x04020000,0x04120000,0x04020100,0x04120100, +0x04020008,0x04120008,0x04020108,0x04120108, +0x04021000,0x04121000,0x04021100,0x04121100, +0x04021008,0x04121008,0x04021108,0x04121108 }, +/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{ 0x00000000,0x10000000,0x00010000,0x10010000, +0x00000004,0x10000004,0x00010004,0x10010004, +0x20000000,0x30000000,0x20010000,0x30010000, +0x20000004,0x30000004,0x20010004,0x30010004, +0x00100000,0x10100000,0x00110000,0x10110000, +0x00100004,0x10100004,0x00110004,0x10110004, +0x20100000,0x30100000,0x20110000,0x30110000, +0x20100004,0x30100004,0x20110004,0x30110004, +0x00001000,0x10001000,0x00011000,0x10011000, +0x00001004,0x10001004,0x00011004,0x10011004, +0x20001000,0x30001000,0x20011000,0x30011000, +0x20001004,0x30001004,0x20011004,0x30011004, +0x00101000,0x10101000,0x00111000,0x10111000, +0x00101004,0x10101004,0x00111004,0x10111004, +0x20101000,0x30101000,0x20111000,0x30111000, +0x20101004,0x30101004,0x20111004,0x30111004 }, +/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ +{ 0x00000000,0x08000000,0x00000008,0x08000008, +0x00000400,0x08000400,0x00000408,0x08000408, +0x00020000,0x08020000,0x00020008,0x08020008, +0x00020400,0x08020400,0x00020408,0x08020408, +0x00000001,0x08000001,0x00000009,0x08000009, +0x00000401,0x08000401,0x00000409,0x08000409, +0x00020001,0x08020001,0x00020009,0x08020009, +0x00020401,0x08020401,0x00020409,0x08020409, +0x02000000,0x0A000000,0x02000008,0x0A000008, +0x02000400,0x0A000400,0x02000408,0x0A000408, +0x02020000,0x0A020000,0x02020008,0x0A020008, +0x02020400,0x0A020400,0x02020408,0x0A020408, +0x02000001,0x0A000001,0x02000009,0x0A000009, +0x02000401,0x0A000401,0x02000409,0x0A000409, +0x02020001,0x0A020001,0x02020009,0x0A020009, +0x02020401,0x0A020401,0x02020409,0x0A020409 }, +/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ +{ 0x00000000,0x00000100,0x00080000,0x00080100, +0x01000000,0x01000100,0x01080000,0x01080100, +0x00000010,0x00000110,0x00080010,0x00080110, +0x01000010,0x01000110,0x01080010,0x01080110, +0x00200000,0x00200100,0x00280000,0x00280100, +0x01200000,0x01200100,0x01280000,0x01280100, +0x00200010,0x00200110,0x00280010,0x00280110, +0x01200010,0x01200110,0x01280010,0x01280110, +0x00000200,0x00000300,0x00080200,0x00080300, +0x01000200,0x01000300,0x01080200,0x01080300, +0x00000210,0x00000310,0x00080210,0x00080310, +0x01000210,0x01000310,0x01080210,0x01080310, +0x00200200,0x00200300,0x00280200,0x00280300, +0x01200200,0x01200300,0x01280200,0x01280300, +0x00200210,0x00200310,0x00280210,0x00280310, +0x01200210,0x01200310,0x01280210,0x01280310 }, +/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ +{ 0x00000000,0x04000000,0x00040000,0x04040000, +0x00000002,0x04000002,0x00040002,0x04040002, +0x00002000,0x04002000,0x00042000,0x04042000, +0x00002002,0x04002002,0x00042002,0x04042002, +0x00000020,0x04000020,0x00040020,0x04040020, +0x00000022,0x04000022,0x00040022,0x04040022, +0x00002020,0x04002020,0x00042020,0x04042020, +0x00002022,0x04002022,0x00042022,0x04042022, +0x00000800,0x04000800,0x00040800,0x04040800, +0x00000802,0x04000802,0x00040802,0x04040802, +0x00002800,0x04002800,0x00042800,0x04042800, +0x00002802,0x04002802,0x00042802,0x04042802, +0x00000820,0x04000820,0x00040820,0x04040820, +0x00000822,0x04000822,0x00040822,0x04040822, +0x00002820,0x04002820,0x00042820,0x04042820, +0x00002822,0x04002822,0x00042822,0x04042822 } +}; + +/* Tables used for executing des. This used to be in spr.h. */ +/* Copyright (C) 1993 Eric Young - see README for more details */ +static const word32 des_SPtrans[8][64]={ +/* nibble 0 */ +{ 0x00820200, 0x00020000, 0x80800000, 0x80820200, +0x00800000, 0x80020200, 0x80020000, 0x80800000, +0x80020200, 0x00820200, 0x00820000, 0x80000200, +0x80800200, 0x00800000, 0x00000000, 0x80020000, +0x00020000, 0x80000000, 0x00800200, 0x00020200, +0x80820200, 0x00820000, 0x80000200, 0x00800200, +0x80000000, 0x00000200, 0x00020200, 0x80820000, +0x00000200, 0x80800200, 0x80820000, 0x00000000, +0x00000000, 0x80820200, 0x00800200, 0x80020000, +0x00820200, 0x00020000, 0x80000200, 0x00800200, +0x80820000, 0x00000200, 0x00020200, 0x80800000, +0x80020200, 0x80000000, 0x80800000, 0x00820000, +0x80820200, 0x00020200, 0x00820000, 0x80800200, +0x00800000, 0x80000200, 0x80020000, 0x00000000, +0x00020000, 0x00800000, 0x80800200, 0x00820200, +0x80000000, 0x80820000, 0x00000200, 0x80020200 }, + +/* nibble 1 */ +{ 0x10042004, 0x00000000, 0x00042000, 0x10040000, +0x10000004, 0x00002004, 0x10002000, 0x00042000, +0x00002000, 0x10040004, 0x00000004, 0x10002000, +0x00040004, 0x10042000, 0x10040000, 0x00000004, +0x00040000, 0x10002004, 0x10040004, 0x00002000, +0x00042004, 0x10000000, 0x00000000, 0x00040004, +0x10002004, 0x00042004, 0x10042000, 0x10000004, +0x10000000, 0x00040000, 0x00002004, 0x10042004, +0x00040004, 0x10042000, 0x10002000, 0x00042004, +0x10042004, 0x00040004, 0x10000004, 0x00000000, +0x10000000, 0x00002004, 0x00040000, 0x10040004, +0x00002000, 0x10000000, 0x00042004, 0x10002004, +0x10042000, 0x00002000, 0x00000000, 0x10000004, +0x00000004, 0x10042004, 0x00042000, 0x10040000, +0x10040004, 0x00040000, 0x00002004, 0x10002000, +0x10002004, 0x00000004, 0x10040000, 0x00042000 }, + +/* nibble 2 */ +{ 0x41000000, 0x01010040, 0x00000040, 0x41000040, +0x40010000, 0x01000000, 0x41000040, 0x00010040, +0x01000040, 0x00010000, 0x01010000, 0x40000000, +0x41010040, 0x40000040, 0x40000000, 0x41010000, +0x00000000, 0x40010000, 0x01010040, 0x00000040, +0x40000040, 0x41010040, 0x00010000, 0x41000000, +0x41010000, 0x01000040, 0x40010040, 0x01010000, +0x00010040, 0x00000000, 0x01000000, 0x40010040, +0x01010040, 0x00000040, 0x40000000, 0x00010000, +0x40000040, 0x40010000, 0x01010000, 0x41000040, +0x00000000, 0x01010040, 0x00010040, 0x41010000, +0x40010000, 0x01000000, 0x41010040, 0x40000000, +0x40010040, 0x41000000, 0x01000000, 0x41010040, +0x00010000, 0x01000040, 0x41000040, 0x00010040, +0x01000040, 0x00000000, 0x41010000, 0x40000040, +0x41000000, 0x40010040, 0x00000040, 0x01010000 }, + +/* nibble 3 */ +{ 0x00100402, 0x04000400, 0x00000002, 0x04100402, +0x00000000, 0x04100000, 0x04000402, 0x00100002, +0x04100400, 0x04000002, 0x04000000, 0x00000402, +0x04000002, 0x00100402, 0x00100000, 0x04000000, +0x04100002, 0x00100400, 0x00000400, 0x00000002, +0x00100400, 0x04000402, 0x04100000, 0x00000400, +0x00000402, 0x00000000, 0x00100002, 0x04100400, +0x04000400, 0x04100002, 0x04100402, 0x00100000, +0x04100002, 0x00000402, 0x00100000, 0x04000002, +0x00100400, 0x04000400, 0x00000002, 0x04100000, +0x04000402, 0x00000000, 0x00000400, 0x00100002, +0x00000000, 0x04100002, 0x04100400, 0x00000400, +0x04000000, 0x04100402, 0x00100402, 0x00100000, +0x04100402, 0x00000002, 0x04000400, 0x00100402, +0x00100002, 0x00100400, 0x04100000, 0x04000402, +0x00000402, 0x04000000, 0x04000002, 0x04100400 }, + +/* nibble 4 */ +{ 0x02000000, 0x00004000, 0x00000100, 0x02004108, +0x02004008, 0x02000100, 0x00004108, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x00004100, +0x02000108, 0x02004008, 0x02004100, 0x00000000, +0x00004100, 0x02000000, 0x00004008, 0x00000108, +0x02000100, 0x00004108, 0x00000000, 0x02000008, +0x00000008, 0x02000108, 0x02004108, 0x00004008, +0x02004000, 0x00000100, 0x00000108, 0x02004100, +0x02004100, 0x02000108, 0x00004008, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x02000100, +0x02000000, 0x00004100, 0x02004108, 0x00000000, +0x00004108, 0x02000000, 0x00000100, 0x00004008, +0x02000108, 0x00000100, 0x00000000, 0x02004108, +0x02004008, 0x02004100, 0x00000108, 0x00004000, +0x00004100, 0x02004008, 0x02000100, 0x00000108, +0x00000008, 0x00004108, 0x02004000, 0x02000008 }, + +/* nibble 5 */ +{ 0x20000010, 0x00080010, 0x00000000, 0x20080800, +0x00080010, 0x00000800, 0x20000810, 0x00080000, +0x00000810, 0x20080810, 0x00080800, 0x20000000, +0x20000800, 0x20000010, 0x20080000, 0x00080810, +0x00080000, 0x20000810, 0x20080010, 0x00000000, +0x00000800, 0x00000010, 0x20080800, 0x20080010, +0x20080810, 0x20080000, 0x20000000, 0x00000810, +0x00000010, 0x00080800, 0x00080810, 0x20000800, +0x00000810, 0x20000000, 0x20000800, 0x00080810, +0x20080800, 0x00080010, 0x00000000, 0x20000800, +0x20000000, 0x00000800, 0x20080010, 0x00080000, +0x00080010, 0x20080810, 0x00080800, 0x00000010, +0x20080810, 0x00080800, 0x00080000, 0x20000810, +0x20000010, 0x20080000, 0x00080810, 0x00000000, +0x00000800, 0x20000010, 0x20000810, 0x20080800, +0x20080000, 0x00000810, 0x00000010, 0x20080010 }, + +/* nibble 6 */ +{ 0x00001000, 0x00000080, 0x00400080, 0x00400001, +0x00401081, 0x00001001, 0x00001080, 0x00000000, +0x00400000, 0x00400081, 0x00000081, 0x00401000, +0x00000001, 0x00401080, 0x00401000, 0x00000081, +0x00400081, 0x00001000, 0x00001001, 0x00401081, +0x00000000, 0x00400080, 0x00400001, 0x00001080, +0x00401001, 0x00001081, 0x00401080, 0x00000001, +0x00001081, 0x00401001, 0x00000080, 0x00400000, +0x00001081, 0x00401000, 0x00401001, 0x00000081, +0x00001000, 0x00000080, 0x00400000, 0x00401001, +0x00400081, 0x00001081, 0x00001080, 0x00000000, +0x00000080, 0x00400001, 0x00000001, 0x00400080, +0x00000000, 0x00400081, 0x00400080, 0x00001080, +0x00000081, 0x00001000, 0x00401081, 0x00400000, +0x00401080, 0x00000001, 0x00001001, 0x00401081, +0x00400001, 0x00401080, 0x00401000, 0x00001001 }, + +/* nibble 7 */ +{ 0x08200020, 0x08208000, 0x00008020, 0x00000000, +0x08008000, 0x00200020, 0x08200000, 0x08208020, +0x00000020, 0x08000000, 0x00208000, 0x00008020, +0x00208020, 0x08008020, 0x08000020, 0x08200000, +0x00008000, 0x00208020, 0x00200020, 0x08008000, +0x08208020, 0x08000020, 0x00000000, 0x00208000, +0x08000000, 0x00200000, 0x08008020, 0x08200020, +0x00200000, 0x00008000, 0x08208000, 0x00000020, +0x00200000, 0x00008000, 0x08000020, 0x08208020, +0x00008020, 0x08000000, 0x00000000, 0x00208000, +0x08200020, 0x08008020, 0x08008000, 0x00200020, +0x08208000, 0x00000020, 0x00200020, 0x08008000, +0x08208020, 0x00200000, 0x08200000, 0x08000020, +0x00208000, 0x00008020, 0x08008020, 0x08200000, +0x00000020, 0x08208000, 0x00208020, 0x00000000, +0x08000000, 0x08200020, 0x00008000, 0x00208020 }}; + +/* Some stuff that used to be in des_locl.h. Heavily modified. */ + /* IP and FP + * The problem is more of a geometric problem that random bit fiddling. + 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 + 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 + 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 + 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 + + 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 + 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 + 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 + 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 + + The output has been subject to swaps of the form + 0 1 -> 3 1 but the odd and even bits have been put into + 2 3 2 0 + different words. The main trick is to remember that + t=((l>>size)^r)&(mask); + r^=t; + l^=(t<>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + +#define IP(l,r,t) \ + PERM_OP(r,l,t, 4,0x0f0f0f0f); \ + PERM_OP(l,r,t,16,0x0000ffff); \ + PERM_OP(r,l,t, 2,0x33333333); \ + PERM_OP(l,r,t, 8,0x00ff00ff); \ + PERM_OP(r,l,t, 1,0x55555555); + +#define FP(l,r,t) \ + PERM_OP(l,r,t, 1,0x55555555); \ + PERM_OP(r,l,t, 8,0x00ff00ff); \ + PERM_OP(l,r,t, 2,0x33333333); \ + PERM_OP(r,l,t,16,0x0000ffff); \ + PERM_OP(l,r,t, 4,0x0f0f0f0f); + +#define D_ENCRYPT(L,R,S) \ + u=(R^s[S ]); \ + t=R^s[S+1]; \ + t=((t>>4)+(t<<28)); \ + L^= des_SPtrans[1][(t )&0x3f]| \ + des_SPtrans[3][(t>> 8)&0x3f]| \ + des_SPtrans[5][(t>>16)&0x3f]| \ + des_SPtrans[7][(t>>24)&0x3f]| \ + des_SPtrans[0][(u )&0x3f]| \ + des_SPtrans[2][(u>> 8)&0x3f]| \ + des_SPtrans[4][(u>>16)&0x3f]| \ + des_SPtrans[6][(u>>24)&0x3f]; + +/* This part is based on code that used to be in ecb_enc.c. */ +/* Copyright (C) 1993 Eric Young - see README for more details */ + +static void des_encrypt(word32 l, word32 r, word32 *output, DESContext *ks, + int encrypt) +{ + register word32 t,u; + register int i; + register word32 *s; + + s = ks->key_schedule; + + IP(l,r,t); + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + t=(r<<1)|(r>>31); + r=(l<<1)|(l>>31); + l=t; + + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (encrypt) + { + for (i=0; i<32; i+=4) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + } + } + else + { + for (i=30; i>0; i-=4) + { + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + } + } + l=(l>>1)|(l<<31); + r=(r>>1)|(r<<31); + + FP(r,l,t); + output[0]=l; + output[1]=r; +} + +/* Code based on set_key.c. */ +/* Copyright (C) 1993 Eric Young - see README for more details */ + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +static void des_set_key(unsigned char *key, DESContext *ks) +{ + register word32 c, d, t, s, shifts; + register int i; + register word32 *schedule; + + schedule = ks->key_schedule; + + c = GET_32BIT_LSB_FIRST(key); + d = GET_32BIT_LSB_FIRST(key + 4); + + /* I now do it in 47 simple operations :-) + * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) + * for the inspiration. :-) */ + PERM_OP(d,c,t,4,0x0f0f0f0f); + HPERM_OP(c,t,-2,0xcccc0000); + HPERM_OP(d,t,-2,0xcccc0000); + PERM_OP(d,c,t,1,0x55555555); + PERM_OP(c,d,t,8,0x00ff00ff); + PERM_OP(d,c,t,1,0x55555555); + d = ((d & 0xff) << 16) | (d & 0xff00) | + ((d >> 16) & 0xff) | ((c >> 4) & 0xf000000); + c&=0x0fffffff; + + shifts = 0x7efc; + for (i=0; i < 16; i++) + { + if (shifts & 1) + { c=((c>>2)|(c<<26)); d=((d>>2)|(d<<26)); } + else + { c=((c>>1)|(c<<27)); d=((d>>1)|(d<<27)); } + shifts >>= 1; + c&=0x0fffffff; + d&=0x0fffffff; + + /* could be a few less shifts but I am to lazy at this + * point in time to investigate */ + + s = des_skb[0][ (c )&0x3f ] | + des_skb[1][((c>> 6)&0x03)|((c>> 7)&0x3c)] | + des_skb[2][((c>>13)&0x0f)|((c>>14)&0x30)] | + des_skb[3][((c>>20)&0x01)|((c>>21)&0x06)|((c>>22)&0x38)]; + + t = des_skb[4][ (d )&0x3f ] | + des_skb[5][((d>> 7)&0x03)|((d>> 8)&0x3c)] | + des_skb[6][ (d>>15)&0x3f ] | + des_skb[7][((d>>21)&0x0f)|((d>>22)&0x30)]; + + /* table contained 0213 4657 */ + *schedule++ = ((t << 16) | (s & 0xffff)); + s = ((s >> 16) | (t & 0xffff0000)); + *schedule++ = (s << 4) | (s >> 28); + } +} + +static void des_cbc_encrypt(DESContext *ks, unsigned char *iv, + unsigned char *dest, const unsigned char *src, + unsigned int len) +{ + word32 iv0, iv1, out[2]; + unsigned int i; + + assert((len & 7) == 0); + + iv0 = GET_32BIT_LSB_FIRST(iv); + iv1 = GET_32BIT_LSB_FIRST(iv + 4); + + for (i = 0; i < len; i += 8) + { + iv0 ^= GET_32BIT_LSB_FIRST(src + i); + iv1 ^= GET_32BIT_LSB_FIRST(src + i + 4); + des_encrypt(iv0, iv1, out, ks, 1); + iv0 = out[0]; + iv1 = out[1]; + PUT_32BIT_LSB_FIRST(dest + i, iv0); + PUT_32BIT_LSB_FIRST(dest + i + 4, iv1); + } + PUT_32BIT_LSB_FIRST(iv, iv0); + PUT_32BIT_LSB_FIRST(iv + 4, iv1); +} + +static void des_cbc_decrypt(DESContext *ks, unsigned char *iv, + unsigned char *dest, const unsigned char *src, + unsigned int len) +{ + word32 iv0, iv1, d0, d1, out[2]; + unsigned int i; + + assert((len & 7) == 0); + + iv0 = GET_32BIT_LSB_FIRST(iv); + iv1 = GET_32BIT_LSB_FIRST(iv + 4); + + for (i = 0; i < len; i += 8) + { + d0 = GET_32BIT_LSB_FIRST(src + i); + d1 = GET_32BIT_LSB_FIRST(src + i + 4); + des_encrypt(d0, d1, out, ks, 0); + iv0 ^= out[0]; + iv1 ^= out[1]; + PUT_32BIT_LSB_FIRST(dest + i, iv0); + PUT_32BIT_LSB_FIRST(dest + i + 4, iv1); + iv0 = d0; + iv1 = d1; + } + PUT_32BIT_LSB_FIRST(iv, iv0); + PUT_32BIT_LSB_FIRST(iv + 4, iv1); +} + +static void des_3cbc_encrypt(DESContext *ks1, unsigned char *iv1, + DESContext *ks2, unsigned char *iv2, + DESContext *ks3, unsigned char *iv3, + unsigned char *dest, const unsigned char *src, + unsigned int len) +{ + des_cbc_encrypt(ks1, iv1, dest, src, len); + des_cbc_decrypt(ks2, iv2, dest, dest, len); + des_cbc_encrypt(ks3, iv3, dest, dest, len); +} + +static void des_3cbc_decrypt(DESContext *ks1, unsigned char *iv1, + DESContext *ks2, unsigned char *iv2, + DESContext *ks3, unsigned char *iv3, + unsigned char *dest, const unsigned char *src, + unsigned int len) +{ + des_cbc_decrypt(ks3, iv3, dest, src, len); + des_cbc_encrypt(ks2, iv2, dest, dest, len); + des_cbc_decrypt(ks1, iv1, dest, dest, len); +} + +DESContext ekey1, ekey2, ekey3; +unsigned char eiv1[8], eiv2[8], eiv3[8]; + +DESContext dkey1, dkey2, dkey3; +unsigned char div1[8], div2[8], div3[8]; + +static void des3_sesskey(unsigned char *key) { + des_set_key(key, &ekey1); + des_set_key(key+8, &ekey2); + des_set_key(key+16, &ekey3); + memset(eiv1, 0, sizeof(eiv1)); + memset(eiv2, 0, sizeof(eiv2)); + memset(eiv3, 0, sizeof(eiv3)); + des_set_key(key, &dkey1); + des_set_key(key+8, &dkey2); + des_set_key(key+16, &dkey3); + memset(div1, 0, sizeof(div1)); + memset(div2, 0, sizeof(div2)); + memset(div3, 0, sizeof(div3)); +} + +static void des3_encrypt_blk(unsigned char *blk, int len) { + des_3cbc_encrypt(&ekey1, eiv1, &ekey2, eiv2, &ekey3, eiv3, blk, blk, len); +} + +static void des3_decrypt_blk(unsigned char *blk, int len) { + des_3cbc_decrypt(&dkey1, div1, &dkey2, div2, &dkey3, div3, blk, blk, len); +} + +struct ssh_cipher ssh_3des = { + des3_sesskey, + des3_encrypt_blk, + des3_decrypt_blk +}; + +#ifdef DES_TEST + +void des_encrypt_buf(DESContext *ks, unsigned char *out, + const unsigned char *in, int encrypt) +{ + word32 in0, in1, output[0]; + + in0 = GET_32BIT_LSB_FIRST(in); + in1 = GET_32BIT_LSB_FIRST(in + 4); + des_encrypt(in0, in1, output, ks, encrypt); + PUT_32BIT_LSB_FIRST(out, output[0]); + PUT_32BIT_LSB_FIRST(out + 4, output[1]); +} + +int main(int ac, char **av) +{ + FILE *f; + char line[1024], *cp; + int i, value; + unsigned char key[8], data[8], result[8], output[8]; + DESContext ks; + + while (fgets(line, sizeof(line), stdin)) + { + for (i = 0; i < 8; i++) + { + if (sscanf(line + 2 * i, "%02x", &value) != 1) + { + fprintf(stderr, "1st col, i = %d, line: %s", i, line); + exit(1); + } + key[i] = value; + } + for (i = 0; i < 8; i++) + { + if (sscanf(line + 2 * i + 17, "%02x", &value) != 1) + { + fprintf(stderr, "2nd col, i = %d, line: %s", i, line); + exit(1); + } + data[i] = value; + } + for (i = 0; i < 8; i++) + { + if (sscanf(line + 2 * i + 2*17, "%02x", &value) != 1) + { + fprintf(stderr, "3rd col, i = %d, line: %s", i, line); + exit(1); + } + result[i] = value; + } + des_set_key(key, &ks); + des_encrypt_buf(&ks, output, data, 1); + if (memcmp(output, result, 8) != 0) + fprintf(stderr, "Encrypt failed: %s", line); + des_encrypt_buf(&ks, output, result, 0); + if (memcmp(output, data, 8) != 0) + fprintf(stderr, "Decrypt failed: %s", line); + } + exit(0); +} +#endif /* DES_TEST */ + diff --git a/sshmd5.c b/sshmd5.c new file mode 100644 index 00000000..c242b839 --- /dev/null +++ b/sshmd5.c @@ -0,0 +1,249 @@ +/* This code has been heavily hacked by Tatu Ylonen to + make it compile on machines like Cray that don't have a 32 bit integer + type. */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "ssh.h" + +#define GET_32BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8) | \ + ((unsigned long)(unsigned char)(cp)[2] << 16) | \ + ((unsigned long)(unsigned char)(cp)[3] << 24)) + +#define PUT_32BIT_LSB_FIRST(cp, value) do { \ + (cp)[0] = (value); \ + (cp)[1] = (value) >> 8; \ + (cp)[2] = (value) >> 16; \ + (cp)[3] = (value) >> 24; } while (0) + +void MD5Transform(uint32 buf[4], const unsigned char in[64]); + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + MD5Transform(ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + + /* Append length in bits and transform */ + PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]); + PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]); + + MD5Transform(ctx->buf, ctx->in); + PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]); + PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]); + PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]); + PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32 buf[4], const unsigned char inext[64]) +{ + register word32 a, b, c, d, i; + word32 in[16]; + + for (i = 0; i < 16; i++) + in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/sshrand.c b/sshrand.c new file mode 100644 index 00000000..17ef6e34 --- /dev/null +++ b/sshrand.c @@ -0,0 +1,169 @@ +/* + * cryptographic random number generator for PuTTY's ssh client + */ + +#include "ssh.h" + +void noise_get_heavy(void (*func) (void *, int)); +void noise_get_light(void (*func) (void *, int)); + +/* + * `pool' itself is a pool of random data which we actually use: we + * return bytes from `pool', at position `poolpos', until `poolpos' + * reaches the end of the pool. At this point we generate more + * random data, by adding noise, stirring well, and resetting + * `poolpos' to point to just past the beginning of the pool (not + * _the_ beginning, since otherwise we'd give away the whole + * contents of our pool, and attackers would just have to guess the + * next lot of noise). + * + * `incomingb' buffers acquired noise data, until it gets full, at + * which point the acquired noise is SHA'ed into `incoming' and + * `incomingb' is cleared. The noise in `incoming' is used as part + * of the noise for each stirring of the pool, in addition to local + * time, process listings, and other such stuff. + */ + +#define HASHINPUT 64 /* 64 bytes SHA input */ +#define HASHSIZE 20 /* 160 bits SHA output */ +#define POOLSIZE 1200 /* size of random pool */ + +struct RandPool { + unsigned char pool[POOLSIZE]; + int poolpos; + + unsigned char incoming[HASHSIZE]; + + unsigned char incomingb[HASHINPUT]; + int incomingpos; +}; + +static struct RandPool pool; + +void random_add_noise(void *noise, int length) { + unsigned char *p = noise; + + while (length >= (HASHINPUT - pool.incomingpos)) { + memcpy(pool.incomingb + pool.incomingpos, p, + HASHINPUT - pool.incomingpos); + p += HASHINPUT - pool.incomingpos; + length -= HASHINPUT - pool.incomingpos; + SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb); + pool.incomingpos = 0; + } + + memcpy(pool.incomingb, p, length); + pool.incomingpos = length; +} + +void random_stir(void) { + word32 block[HASHINPUT/sizeof(word32)]; + word32 digest[HASHSIZE/sizeof(word32)]; + int i, j, k; + + noise_get_light(random_add_noise); + + SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb); + pool.incomingpos = 0; + + /* + * Chunks of this code are blatantly endianness-dependent, but + * as it's all random bits anyway, WHO CARES? + */ + memcpy(digest, pool.incoming, sizeof(digest)); + + /* + * Make two passes over the pool. + */ + for (i = 0; i < 2; i++) { + + /* + * We operate SHA in CFB mode, repeatedly adding the same + * block of data to the digest. But we're also fiddling + * with the digest-so-far, so this shouldn't be Bad or + * anything. + */ + memcpy(block, pool.pool, sizeof(block)); + + /* + * Each pass processes the pool backwards in blocks of + * HASHSIZE, just so that in general we get the output of + * SHA before the corresponding input, in the hope that + * things will be that much less predictable that way + * round, when we subsequently return bytes ... + */ + for (j = POOLSIZE; (j -= HASHSIZE) >= 0 ;) { + /* + * XOR the bit of the pool we're processing into the + * digest. + */ + + for (k = 0; k < sizeof(digest)/sizeof(*digest); k++) + digest[k] ^= ((word32 *)(pool.pool+j))[k]; + + /* + * Munge our unrevealed first block of the pool into + * it. + */ + SHATransform(digest, block); + + /* + * Stick the result back into the pool. + */ + + for (k = 0; k < sizeof(digest)/sizeof(*digest); k++) + ((word32 *)(pool.pool+j))[k] = digest[k]; + } + } + + /* + * Might as well save this value back into `incoming', just so + * there'll be some extra bizarreness there. + */ + SHATransform(digest, block); + memcpy(digest, pool.incoming, sizeof(digest)); + + pool.poolpos = sizeof(pool.incoming); +} + +static void random_add_heavynoise(void *noise, int length) { + unsigned char *p = noise; + + while (length >= (POOLSIZE - pool.poolpos)) { + memcpy(pool.pool + pool.poolpos, p, POOLSIZE - pool.poolpos); + p += POOLSIZE - pool.poolpos; + length -= POOLSIZE - pool.poolpos; + random_stir(); + pool.poolpos = 0; + } + + memcpy(pool.pool, p, length); + pool.poolpos = length; +} + +void random_init(void) { + memset(&pool, 0, sizeof(pool)); /* just to start with */ + + /* + * For noise_get_heavy, we temporarily use `poolpos' as the + * pointer for addition of noise, rather than extraction of + * random numbers. + */ + pool.poolpos = 0; + noise_get_heavy(random_add_heavynoise); + + random_stir(); +} + +int random_byte(void) { + if (pool.poolpos >= POOLSIZE) + random_stir(); + + return pool.pool[pool.poolpos++]; +} + +void random_get_savedata(void **data, int *len) { + random_stir(); + *data = pool.pool+pool.poolpos; + *len = POOLSIZE/2; +} diff --git a/sshrsa.c b/sshrsa.c new file mode 100644 index 00000000..70cf9bbe --- /dev/null +++ b/sshrsa.c @@ -0,0 +1,412 @@ +/* + * RSA implementation just sufficient for ssh client-side + * initialisation step + */ + +/*#include +#define RSADEBUG +#define DLVL 2 +#include "stel.h"*/ + +#include +#include +#include + +#include "ssh.h" + +typedef unsigned short *Bignum; + +static unsigned short Zero[1] = { 0 }; + +#if defined TESTMODE || defined RSADEBUG +#ifndef DLVL +#define DLVL 10000 +#endif +#define debug(x) bndebug(#x,x) +static int level = 0; +static void bndebug(char *name, Bignum b) { + int i; + int w = 50-level-strlen(name)-5*b[0]; + if (level >= DLVL) + return; + if (w < 0) w = 0; + dprintf("%*s%s%*s", level, "", name, w, ""); + for (i=b[0]; i>0; i--) + dprintf(" %04x", b[i]); + dprintf("\n"); +} +#define dmsg(x) do {if(level0; i--) + if (r[i]) + break; + + j = (i-1)*16; + n = r[i]; + if (n & 0xFF00) j += 8, n >>= 8; + if (n & 0x00F0) j += 4, n >>= 4; + if (n & 0x000C) j += 2, n >>= 2; + if (n & 0x0002) j += 1, n >>= 1; + + return j; +} + +static void add(Bignum r1, Bignum r2, Bignum result) { + int i; + long stuff = 0; + + enter((">add\n")); + debug(r1); + debug(r2); + + for (i = 1 ;; i++) { + if (i <= r1[0]) + stuff += r1[i]; + if (i <= r2[0]) + stuff += r2[i]; + if (i <= result[0]) + result[i] = stuff & 0xFFFFU; + if (i > r1[0] && i > r2[0] && i >= result[0]) + break; + stuff >>= 16; + } + + debug(result); + leave(("sub\n")); + debug(r1); + debug(r2); + + for (i = 1 ;; i++) { + if (i <= r1[0]) + stuff += r1[i]; + if (i <= r2[0]) + stuff -= r2[i]; + if (i <= result[0]) + result[i] = stuff & 0xFFFFU; + if (i > r1[0] && i > r2[0] && i >= result[0]) + break; + stuff = stuff<0 ? -1 : 0; + } + + debug(result); + leave(("ge\n")); + debug(r1); + debug(r2); + + if (r1[0] < r2[0]) + i = r2[0]; + else + i = r1[0]; + + while (i > 0) { + unsigned short n1 = (i > r1[0] ? 0 : r1[i]); + unsigned short n2 = (i > r2[0] ? 0 : r2[i]); + + if (n1 > n2) { + dmsg(("greater\n")); + leave((" r2 */ + } else if (n1 < n2) { + dmsg(("less\n")); + leave(("modmult\n")); + debug(r1); + debug(r2); + debug(modulus); + + for (i=1; i<=result[0]; i++) + result[i] = 0; /* result := 0 */ + for (i=1; i<=temp[0]; i++) + temp[i] = (i > r2[0] ? 0 : r2[i]); /* temp := r2 */ + + bits = 1+msb(r1); + + for (bit = 0; bit < bits; bit++) { + digit = 1 + bit / 16; + smallbit = bit % 16; + + debug(temp); + if (digit <= r1[0] && (r1[digit] & (1<modpow\n")); + debug(r1); + debug(r2); + debug(modulus); + + for (i=1; i<=result[0]; i++) + result[i] = (i==1); /* result := 1 */ + for (i=1; i<=temp[0]; i++) + temp[i] = (i > r1[0] ? 0 : r1[i]); /* temp := r1 */ + + bits = 1+msb(r2); + + for (bit = 0; bit < bits; bit++) { + digit = 1 + bit / 16; + smallbit = bit % 16; + + debug(temp); + if (digit <= r2[0] && (r2[digit] & (1<bits = 0; + for (i=0; i<4; i++) + result->bits = (result->bits << 8) + *p++; + + for (j=0; j<2; j++) { + + w = 0; + for (i=0; i<2; i++) + w = (w << 8) + *p++; + + result->bytes = b = (w+7)/8; /* bits -> bytes */ + w = (w+15)/16; /* bits -> words */ + + bn[j] = newbn(w); + + if (keystr) *keystr = p; /* point at key string, second time */ + + for (i=1; i<=w; i++) + bn[j][i] = 0; + for (i=0; iexponent = bn[0]; + result->modulus = bn[1]; + + return p - data; +} + +void rsaencrypt(unsigned char *data, int length, struct RSAKey *key) { + Bignum b1, b2; + int w, i; + unsigned char *p; + + debug(key->exponent); + + memmove(data+key->bytes-length, data, length); + data[0] = 0; + data[1] = 2; + + for (i = 2; i < key->bytes-length-1; i++) { + do { + data[i] = random_byte(); + } while (data[i] == 0); + } + data[key->bytes-length-1] = 0; + + w = (key->bytes+1)/2; + + b1 = newbn(w); + b2 = newbn(w); + + p = data; + for (i=1; i<=w; i++) + b1[i] = 0; + for (i=0; ibytes; i++) { + unsigned char byte = *p++; + if ((key->bytes-i) & 1) + b1[w-i/2] |= byte; + else + b1[w-i/2] |= byte<<8; + } + + debug(b1); + + modpow(b1, key->exponent, key->modulus, b2); + + debug(b2); + + p = data; + for (i=0; ibytes; i++) { + unsigned char b; + if (i & 1) + b = b2[w-i/2] & 0xFF; + else + b = b2[w-i/2] >> 8; + *p++ = b; + } + + freebn(b1); + freebn(b2); +} + +int rsastr_len(struct RSAKey *key) { + Bignum md, ex; + + md = key->modulus; + ex = key->exponent; + return 4 * (ex[0]+md[0]) + 10; +} + +void rsastr_fmt(char *str, struct RSAKey *key) { + Bignum md, ex; + int len = 0, i; + + md = key->modulus; + ex = key->exponent; + + for (i=1; i<=ex[0]; i++) { + sprintf(str+len, "%04x", ex[i]); + len += strlen(str+len); + } + str[len++] = '/'; + for (i=1; i<=md[0]; i++) { + sprintf(str+len, "%04x", md[i]); + len += strlen(str+len); + } + str[len] = '\0'; +} + +#ifdef TESTMODE + +#ifndef NODDY +#define p1 10007 +#define p2 10069 +#define p3 10177 +#else +#define p1 3 +#define p2 7 +#define p3 13 +#endif + +unsigned short P1[2] = { 1, p1 }; +unsigned short P2[2] = { 1, p2 }; +unsigned short P3[2] = { 1, p3 }; +unsigned short bigmod[5] = { 4, 0, 0, 0, 32768U }; +unsigned short mod[5] = { 4, 0, 0, 0, 0 }; +unsigned short a[5] = { 4, 0, 0, 0, 0 }; +unsigned short b[5] = { 4, 0, 0, 0, 0 }; +unsigned short c[5] = { 4, 0, 0, 0, 0 }; +unsigned short One[2] = { 1, 1 }; +unsigned short Two[2] = { 1, 2 }; + +int main(void) { + modmult(P1, P2, bigmod, a); debug(a); + modmult(a, P3, bigmod, mod); debug(mod); + + sub(P1, One, a); debug(a); + sub(P2, One, b); debug(b); + modmult(a, b, bigmod, c); debug(c); + sub(P3, One, a); debug(a); + modmult(a, c, bigmod, b); debug(b); + + modpow(Two, b, mod, a); debug(a); + + return 0; +} + +#endif diff --git a/sshsha.c b/sshsha.c new file mode 100644 index 00000000..a63bf8da --- /dev/null +++ b/sshsha.c @@ -0,0 +1,141 @@ +/* + * The following code was taken directly from drivers/char/random.c + * in the Linux kernel. + */ + +#include "ssh.h" + +/* + * SHA transform algorithm, taken from code written by Peter Gutman, + * and apparently in the public domain. + */ + +/* The SHA f()-functions. */ + +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHA Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) + +#define expand(W,i) ( W[ i & 15 ] = \ + ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + + +void SHATransform(word32 *digest, word32 *data) +{ + word32 A, B, C, D, E; /* Local vars */ + word32 eData[ 16 ]; /* Expanded data */ + + /* Set up first buffer and local data buffer */ + A = digest[ 0 ]; + B = digest[ 1 ]; + C = digest[ 2 ]; + D = digest[ 3 ]; + E = digest[ 4 ]; + memcpy( eData, data, 16*sizeof(word32)); + + /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */ + subRound( A, B, C, D, E, f1, K1, eData[ 0 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 1 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 2 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 3 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 4 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 5 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 6 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 7 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 8 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 9 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 10 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 11 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 12 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 13 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 14 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 15 ] ); + subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) ); + + subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) ); + + subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) ); + + subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) ); + + /* Build message digest */ + digest[ 0 ] += A; + digest[ 1 ] += B; + digest[ 2 ] += C; + digest[ 3 ] += D; + digest[ 4 ] += E; +} diff --git a/telnet.c b/telnet.c new file mode 100644 index 00000000..14881f96 --- /dev/null +++ b/telnet.c @@ -0,0 +1,720 @@ +#include +#include +#include +#include + +#include "putty.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +static SOCKET s = INVALID_SOCKET; + +#define IAC 255 /* interpret as command: */ +#define DONT 254 /* you are not to use option */ +#define DO 253 /* please, you use option */ +#define WONT 252 /* I won't use option */ +#define WILL 251 /* I will use option */ +#define SB 250 /* interpret as subnegotiation */ +#define SE 240 /* end sub negotiation */ + +#define GA 249 /* you may reverse the line */ +#define EL 248 /* erase the current line */ +#define EC 247 /* erase the current character */ +#define AYT 246 /* are you there */ +#define AO 245 /* abort output--but let prog finish */ +#define IP 244 /* interrupt process--permanently */ +#define BREAK 243 /* break */ +#define DM 242 /* data mark--for connect. cleaning */ +#define NOP 241 /* nop */ +#define EOR 239 /* end of record (transparent mode) */ +#define ABORT 238 /* Abort process */ +#define SUSP 237 /* Suspend process */ +#define xEOF 236 /* End of file: EOF is already used... */ + +#define TELOPT_BINARY 0 /* 8-bit data path */ +#define TELOPT_ECHO 1 /* echo */ +#define TELOPT_RCP 2 /* prepare to reconnect */ +#define TELOPT_SGA 3 /* suppress go ahead */ +#define TELOPT_NAMS 4 /* approximate message size */ +#define TELOPT_STATUS 5 /* give status */ +#define TELOPT_TM 6 /* timing mark */ +#define TELOPT_RCTE 7 /* remote controlled transmission and echo */ +#define TELOPT_NAOL 8 /* negotiate about output line width */ +#define TELOPT_NAOP 9 /* negotiate about output page size */ +#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */ +#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */ +#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */ +#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */ +#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */ +#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */ +#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */ +#define TELOPT_XASCII 17 /* extended ascic character set */ +#define TELOPT_LOGOUT 18 /* force logout */ +#define TELOPT_BM 19 /* byte macro */ +#define TELOPT_DET 20 /* data entry terminal */ +#define TELOPT_SUPDUP 21 /* supdup protocol */ +#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */ +#define TELOPT_SNDLOC 23 /* send location */ +#define TELOPT_TTYPE 24 /* terminal type */ +#define TELOPT_EOR 25 /* end or record */ +#define TELOPT_TUID 26 /* TACACS user identification */ +#define TELOPT_OUTMRK 27 /* output marking */ +#define TELOPT_TTYLOC 28 /* terminal location number */ +#define TELOPT_3270REGIME 29 /* 3270 regime */ +#define TELOPT_X3PAD 30 /* X.3 PAD */ +#define TELOPT_NAWS 31 /* window size */ +#define TELOPT_TSPEED 32 /* terminal speed */ +#define TELOPT_LFLOW 33 /* remote flow control */ +#define TELOPT_LINEMODE 34 /* Linemode option */ +#define TELOPT_XDISPLOC 35 /* X Display Location */ +#define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */ +#define TELOPT_AUTHENTICATION 37/* Authenticate */ +#define TELOPT_ENCRYPT 38 /* Encryption option */ +#define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */ +#define TELOPT_EXOPL 255 /* extended-options-list */ + +#define TELQUAL_IS 0 /* option is... */ +#define TELQUAL_SEND 1 /* send option */ +#define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */ +#define BSD_VAR 1 +#define BSD_VALUE 0 +#define RFC_VAR 0 +#define RFC_VALUE 1 + +#define CR 13 +#define LF 10 +#define NUL 0 + +#define iswritable(x) ( (x) != IAC && (x) != CR ) + +static char *telopt(int opt) { +#define i(x) if (opt == TELOPT_ ## x) return #x; + i(BINARY); i(ECHO); i(RCP); i(SGA); i(NAMS); i(STATUS); i(TM); i(RCTE); + i(NAOL); i(NAOP); i(NAOCRD); i(NAOHTS); i(NAOHTD); i(NAOFFD); i(NAOVTS); + i(NAOVTD); i(NAOLFD); i(XASCII); i(LOGOUT); i(BM); i(DET); i(SUPDUP); + i(SUPDUPOUTPUT); i(SNDLOC); i(TTYPE); i(EOR); i(TUID); i(OUTMRK); + i(TTYLOC); i(X3PAD); i(NAWS); i(TSPEED); i(LFLOW); i(LINEMODE); + i(XDISPLOC); i(OLD_ENVIRON); i(AUTHENTICATION); i(ENCRYPT); + i(NEW_ENVIRON); i(EXOPL); +#undef i + return ""; +} + +static void telnet_size(void); + +struct Opt { + int send; /* what we initially send */ + int nsend; /* -ve send if requested to stop it */ + int ack, nak; /* +ve and -ve acknowledgements */ + int option; /* the option code */ + enum { + REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE + } state; +}; + +static struct Opt o_naws = {WILL, WONT, DO, DONT, TELOPT_NAWS, REQUESTED}; +static struct Opt o_tspeed = {WILL, WONT, DO, DONT, TELOPT_TSPEED, REQUESTED}; +static struct Opt o_ttype = {WILL, WONT, DO, DONT, TELOPT_TTYPE, REQUESTED}; +static struct Opt o_oenv = {WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON, + INACTIVE}; +static struct Opt o_nenv = {WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON, + REQUESTED}; +static struct Opt o_echo = {DO, DONT, WILL, WONT, TELOPT_ECHO, REQUESTED}; +static struct Opt o_we_sga = {WILL, WONT, DO, DONT, TELOPT_SGA, REQUESTED}; +static struct Opt o_they_sga = {DO, DONT, WILL, WONT, TELOPT_SGA, REQUESTED}; + +static struct Opt *opts[] = { + &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo, + &o_we_sga, &o_they_sga, NULL +}; + +#if 0 +static int in_synch; +#endif + +static int sb_opt, sb_len; +static char *sb_buf = NULL; +static int sb_size = 0; +#define SB_DELTA 1024 + +static void try_write (void) { + while (outbuf_head != outbuf_reap) { + int end = (outbuf_reap < outbuf_head ? outbuf_head : OUTBUF_SIZE); + int len = end - outbuf_reap; + int ret; + + ret = send (s, outbuf+outbuf_reap, len, 0); + if (ret > 0) + outbuf_reap = (outbuf_reap + ret) & OUTBUF_MASK; + if (ret < len) + return; + } +} + +static void s_write (void *buf, int len) { + unsigned char *p = buf; + while (len--) { + int new_head = (outbuf_head + 1) & OUTBUF_MASK; + if (new_head != outbuf_reap) { + outbuf[outbuf_head] = *p++; + outbuf_head = new_head; + } + } + try_write(); +} + +static void c_write (char *buf, int len) { + while (len--) { + int new_head = (inbuf_head + 1) & INBUF_MASK; + int c = (unsigned char) *buf; + if (new_head != inbuf_reap) { + inbuf[inbuf_head] = *buf++; + inbuf_head = new_head; + } + } +} + +static void log_option (char *sender, int cmd, int option) { + char buf[50]; + sprintf(buf, "%s:\t%s %s", sender, + (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" : + cmd == DO ? "DO" : cmd == DONT ? "DONT" : ""), + telopt(option)); + lognegot(buf); +} + +static void send_opt (int cmd, int option) { + unsigned char b[3]; + + b[0] = IAC; b[1] = cmd; b[2] = option; + s_write (b, 3); + log_option("client", cmd, option); +} + +static void deactivate_option (struct Opt *o) { + if (o->state == REQUESTED || o->state == ACTIVE) + send_opt (o->nsend, o->option); + o->state = REALLY_INACTIVE; +} + +static void activate_option (struct Opt *o) { + if (o->send == WILL && o->option == TELOPT_NAWS) + telnet_size(); + if (o->send == WILL && + (o->option == TELOPT_NEW_ENVIRON || + o->option == TELOPT_OLD_ENVIRON)) { + /* + * We may only have one kind of ENVIRON going at a time. + * This is a hack, but who cares. + */ + deactivate_option (o->option==TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv); + } +} + +static void refused_option (struct Opt *o) { + if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON && + o_oenv.state == INACTIVE) { + send_opt (WILL, TELOPT_OLD_ENVIRON); + o_oenv.state = REQUESTED; + } +} + +static void proc_rec_opt (int cmd, int option) { + struct Opt **o; + + log_option ("server", cmd, option); + for (o = opts; *o; o++) { + if ((*o)->option == option && (*o)->ack == cmd) { + switch ((*o)->state) { + case REQUESTED: + (*o)->state = ACTIVE; + activate_option (*o); + break; + case ACTIVE: + break; + case INACTIVE: + (*o)->state = ACTIVE; + send_opt ((*o)->send, option); + activate_option (*o); + break; + case REALLY_INACTIVE: + send_opt ((*o)->nsend, option); + break; + } + return; + } else if ((*o)->option == option && (*o)->nak == cmd) { + switch ((*o)->state) { + case REQUESTED: + (*o)->state = INACTIVE; + refused_option (*o); + break; + case ACTIVE: + (*o)->state = INACTIVE; + send_opt ((*o)->nsend, option); + break; + case INACTIVE: + case REALLY_INACTIVE: + break; + } + return; + } + } + /* + * If we reach here, the option was one we weren't prepared to + * cope with. So send a negative ack. + */ + send_opt ((cmd == WILL ? DONT : WONT), option); +} + +static void process_subneg (void) { + unsigned char b[2048], *p, *q; + int var, value, n; + char *e; + + switch (sb_opt) { + case TELOPT_TSPEED: + if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) { + char logbuf[sizeof(cfg.termspeed)+80]; + b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED; + b[3] = TELQUAL_IS; + strcpy(b+4, cfg.termspeed); + n = 4 + strlen(cfg.termspeed); + b[n] = IAC; b[n+1] = SE; + s_write (b, n+2); + lognegot("server:\tSB TSPEED SEND"); + sprintf(logbuf, "client:\tSB TSPEED IS %s", cfg.termspeed); + lognegot (logbuf); + } else + lognegot ("server:\tSB TSPEED "); + break; + case TELOPT_TTYPE: + if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) { + char logbuf[sizeof(cfg.termtype)+80]; + b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE; + b[3] = TELQUAL_IS; + for (n = 0; cfg.termtype[n]; n++) + b[n+4] = (cfg.termtype[n] >= 'a' && cfg.termtype[n] <= 'z' ? + cfg.termtype[n] + 'A'-'a' : cfg.termtype[n]); + b[n+4] = IAC; b[n+5] = SE; + s_write (b, n+6); + b[n+4] = 0; + lognegot("server:\tSB TTYPE SEND"); + sprintf(logbuf, "client:\tSB TTYPE IS %s", b+4); + lognegot(logbuf); + } else + lognegot("server:\tSB TTYPE \r\n"); + break; + case TELOPT_OLD_ENVIRON: + case TELOPT_NEW_ENVIRON: + p = sb_buf; + q = p + sb_len; + if (p < q && *p == TELQUAL_SEND) { + char logbuf[50]; + p++; + sprintf (logbuf, "server:\tSB %s SEND", telopt(sb_opt)); + lognegot (logbuf); + if (sb_opt == TELOPT_OLD_ENVIRON) { + if (cfg.rfc_environ) { + value = RFC_VALUE; + var = RFC_VAR; + } else { + value = BSD_VALUE; + var = BSD_VAR; + } + /* + * Try to guess the sense of VAR and VALUE. + */ + while (p < q) { + if (*p == RFC_VAR) { + value = RFC_VALUE; + var = RFC_VAR; + } else if (*p == BSD_VAR) { + value = BSD_VALUE; + var = BSD_VAR; + } + p++; + } + } else { + /* + * With NEW_ENVIRON, the sense of VAR and VALUE + * isn't in doubt. + */ + value = RFC_VALUE; + var = RFC_VAR; + } + b[0] = IAC; b[1] = SB; b[2] = sb_opt; + b[3] = TELQUAL_IS; + n = 4; + e = cfg.environ; + while (*e) { + b[n++] = var; + while (*e && *e != '\t') b[n++] = *e++; + if (*e == '\t') e++; + b[n++] = value; + while (*e) b[n++] = *e++; + e++; + } + if (*cfg.username) { + b[n++] = var; b[n++] = 'U'; b[n++] = 'S'; + b[n++] = 'E'; b[n++] = 'R'; b[n++] = value; + e = cfg.username; + while (*e) b[n++] = *e++; + } + b[n++] = IAC; b[n++] = SE; + s_write (b, n); + sprintf(logbuf, "client:\tSB %s IS %s", telopt(sb_opt), + n==6 ? "" : ""); + lognegot (logbuf); + } + break; + } +} + +static enum { + TOPLEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT, + SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR +} telnet_state = TOPLEVEL; + +static void do_telnet_read (char *buf, int len) { + unsigned char b[10]; + + while (len--) { + int c = (unsigned char) *buf++; + + switch (telnet_state) { + case TOPLEVEL: + case SEENCR: + if (c == NUL && telnet_state == SEENCR) + telnet_state = TOPLEVEL; + else if (c == IAC) + telnet_state = SEENIAC; + else { + b[0] = c; +#if 0 + if (!in_synch) +#endif + c_write (b, 1); + if (c == CR) + telnet_state = SEENCR; + else + telnet_state = TOPLEVEL; + } + break; + case SEENIAC: + if (c == DO) telnet_state = SEENDO; + else if (c == DONT) telnet_state = SEENDONT; + else if (c == WILL) telnet_state = SEENWILL; + else if (c == WONT) telnet_state = SEENWONT; + else if (c == SB) telnet_state = SEENSB; + else telnet_state = TOPLEVEL;/* ignore _everything_ else! */ + break; + case SEENWILL: + proc_rec_opt (WILL, c); + telnet_state = TOPLEVEL; + break; + case SEENWONT: + proc_rec_opt (WONT, c); + telnet_state = TOPLEVEL; + break; + case SEENDO: + proc_rec_opt (DO, c); + telnet_state = TOPLEVEL; + break; + case SEENDONT: + proc_rec_opt (DONT, c); + telnet_state = TOPLEVEL; + break; + case SEENSB: + sb_opt = c; + sb_len = 0; + telnet_state = SUBNEGOT; + break; + case SUBNEGOT: + if (c == IAC) + telnet_state = SUBNEG_IAC; + else { + subneg_addchar: + if (sb_len >= sb_size) { + char *newbuf; + sb_size += SB_DELTA; + newbuf = (sb_buf ? + realloc(sb_buf, sb_size) : + malloc(sb_size)); + if (newbuf) + sb_buf = newbuf; + else + sb_size -= SB_DELTA; + } + if (sb_len < sb_size) + sb_buf[sb_len++] = c; + telnet_state = SUBNEGOT;/* in case we came here by goto */ + } + break; + case SUBNEG_IAC: + if (c != SE) + goto subneg_addchar; /* yes, it's a hack, I know, but... */ + else { + process_subneg(); + telnet_state = TOPLEVEL; + } + break; + } + } +} + +/* + * Called to set up the Telnet connection. Will arrange for + * WM_NETEVENT messages to be passed to the specified window, whose + * window procedure should then call telnet_msg(). + * + * Returns an error message, or NULL on success. + * + * Also places the canonical host name into `realhost'. + */ +static char *telnet_init (HWND hwnd, char *host, int port, char **realhost) { + SOCKADDR_IN addr; + struct hostent *h; + unsigned long a; + + /* + * Try to find host. + */ + if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) { + if ( (h = gethostbyname(host)) == NULL) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAHOST_NOT_FOUND: case WSANO_DATA: + return "Host does not exist"; + case WSATRY_AGAIN: return "Host not found"; + default: return "gethostbyname: unknown error"; + } + memcpy (&a, h->h_addr, sizeof(a)); + *realhost = h->h_name; + } else + *realhost = host; + a = ntohl(a); + + if (port < 0) + port = 23; /* default telnet port */ + + /* + * Open socket. + */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAEAFNOSUPPORT: return "TCP/IP support not present"; + default: return "socket(): unknown error"; + } + +#if 0 + { + BOOL b = TRUE; + setsockopt (s, SOL_SOCKET, SO_OOBINLINE, (void *)&b, sizeof(b)); + } +#endif + + /* + * Bind to local address. + */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + default: return "bind(): unknown error"; + } + + /* + * Connect to remote address. + */ + addr.sin_addr.s_addr = htonl(a); + addr.sin_port = htons((short)port); + if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAEHOSTUNREACH: return "No route to host"; + default: return "connect(): unknown error"; + } + + if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | + FD_WRITE | FD_OOB | FD_CLOSE) == SOCKET_ERROR) + switch (WSAGetLastError()) { + case WSAENETDOWN: return "Network is down"; + default: return "WSAAsyncSelect(): unknown error"; + } + + /* + * Initialise option states. + */ + { + struct Opt **o; + + for (o = opts; *o; o++) + if ((*o)->state == REQUESTED) + send_opt ((*o)->send, (*o)->option); + } + +#if 0 + /* + * Set up SYNCH state. + */ + in_synch = FALSE; +#endif + + return NULL; +} + +/* + * Process a WM_NETEVENT message. Will return 0 if the connection + * has closed, or <0 for a socket error. + */ +static int telnet_msg (WPARAM wParam, LPARAM lParam) { + int ret; + char buf[256]; + + if (s == INVALID_SOCKET) /* how the hell did we get here?! */ + return -5000; + + if (WSAGETSELECTERROR(lParam) != 0) + return -WSAGETSELECTERROR(lParam); + + switch (WSAGETSELECTEVENT(lParam)) { + case FD_READ: + ret = recv(s, buf, sizeof(buf), 0); + if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK) + return 1; + if (ret < 0) /* any _other_ error */ + return -10000-WSAGetLastError(); + if (ret == 0) { + s = INVALID_SOCKET; + return 0; /* can't happen, in theory */ + } +#if 0 + if (in_synch) { + BOOL i; + if (ioctlsocket (s, SIOCATMARK, &i) < 0) { + return -20000-WSAGetLastError(); + } + if (i) + in_synch = FALSE; + } +#endif + do_telnet_read (buf, ret); + return 1; + case FD_OOB: + do { + ret = recv(s, buf, sizeof(buf), 0); + } while (ret > 0); + telnet_state = TOPLEVEL; + do { + ret = recv(s, buf, 1, MSG_OOB); + if (ret > 0) + do_telnet_read (buf, ret); + } while (ret > 0); + if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) + return -30000-WSAGetLastError(); + return 1; + case FD_WRITE: + if (outbuf_head != outbuf_reap) + try_write(); + return 1; + case FD_CLOSE: + s = INVALID_SOCKET; + return 0; + } + return 1; /* shouldn't happen, but WTF */ +} + +/* + * Called to send data down the Telnet connection. + */ +static void telnet_send (char *buf, int len) { + char *p; + static unsigned char iac[2] = { IAC, IAC }; + static unsigned char cr[2] = { CR, NUL }; + + if (s == INVALID_SOCKET) + return; + + p = buf; + while (p < buf+len) { + char *q = p; + + while (iswritable((unsigned char)*p) && p < buf+len) p++; + s_write (q, p-q); + + while (p < buf+len && !iswritable((unsigned char)*p)) { + s_write ((unsigned char)*p == IAC ? iac : cr, 2); + p++; + } + } +} + +/* + * Called to set the size of the window from Telnet's POV. + */ +static void telnet_size(void) { + unsigned char b[16]; + char logbuf[50]; + + if (s == INVALID_SOCKET || o_naws.state != ACTIVE) + return; + b[0] = IAC; b[1] = SB; b[2] = TELOPT_NAWS; + b[3] = cols >> 8; b[4] = cols & 0xFF; + b[5] = rows >> 8; b[6] = rows & 0xFF; + b[7] = IAC; b[8] = SE; + s_write (b, 9); + sprintf(logbuf, "client:\tSB NAWS %d,%d", + ((unsigned char)b[3] << 8) + (unsigned char)b[4], + ((unsigned char)b[5] << 8) + (unsigned char)b[6]); + lognegot (logbuf); +} + +/* + * Send Telnet special codes. + */ +static void telnet_special (Telnet_Special code) { + unsigned char b[2]; + + if (s == INVALID_SOCKET) + return; + + b[0] = IAC; + switch (code) { + case TS_AYT: b[1] = AYT; s_write (b, 2); break; + case TS_BRK: b[1] = BREAK; s_write (b, 2); break; + case TS_EC: b[1] = EC; s_write (b, 2); break; + case TS_EL: b[1] = EL; s_write (b, 2); break; + case TS_GA: b[1] = GA; s_write (b, 2); break; + case TS_NOP: b[1] = NOP; s_write (b, 2); break; + case TS_ABORT: b[1] = ABORT; s_write (b, 2); break; + case TS_AO: b[1] = AO; s_write (b, 2); break; + case TS_IP: b[1] = IP; s_write (b, 2); break; + case TS_SUSP: b[1] = SUSP; s_write (b, 2); break; + case TS_EOR: b[1] = EOR; s_write (b, 2); break; + case TS_EOF: b[1] = xEOF; s_write (b, 2); break; + case TS_SYNCH: + outbuf_head = outbuf_reap = 0; + b[0] = DM; + send (s, b, 1, MSG_OOB); + break; + } +} + +Backend telnet_backend = { + telnet_init, + telnet_msg, + telnet_send, + telnet_size, + telnet_special +}; diff --git a/terminal.c b/terminal.c new file mode 100644 index 00000000..1e286cea --- /dev/null +++ b/terminal.c @@ -0,0 +1,1447 @@ +#include + +#include +#include + +#include "putty.h" + +static unsigned long *text; /* buffer of text on terminal screen */ +static unsigned long *scrtop; /* top of working screen */ +static unsigned long *disptop; /* top of displayed screen */ +static unsigned long *sbtop; /* top of scrollback */ +static unsigned long *cpos; /* cursor position (convenience) */ +static unsigned long *disptext; /* buffer of text on real screen */ +static unsigned long *wanttext; /* buffer of text we want on screen */ +static unsigned long *alttext; /* buffer of text on alt. screen */ + +static unsigned char *selspace; /* buffer for building selections in */ + +#define TSIZE (sizeof(*text)) +#define fix_cpos do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0) + +static unsigned long curr_attr, save_attr; + +static int curs_x, curs_y; /* cursor */ +static int save_x, save_y; /* saved cursor position */ +static int marg_t, marg_b; /* scroll margins */ +static int dec_om; /* DEC origin mode flag */ +static int wrap, wrapnext; /* wrap flags */ +static int insert; /* insert-mode flag */ +static int cset; /* 0 or 1: which char set */ +static int save_cset, save_csattr; /* saved with cursor position */ +static int rvideo; /* global reverse video flag */ + +static unsigned long cset_attr[2]; + +/* + * Saved settings on the alternate screen. + */ +static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset; +static int alt_t, alt_b; +static int alt_which; + +#define ARGS_MAX 32 /* max # of esc sequence arguments */ +#define ARG_DEFAULT -1 /* if an arg isn't specified */ +#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) ) +static int esc_args[ARGS_MAX]; +static int esc_nargs; +static int esc_query; + +#define OSC_STR_MAX 2048 +static int osc_strlen; +static char osc_string[OSC_STR_MAX+1]; +static int osc_w; + +static unsigned char *tabs; + +#define MAXNL 5 +static int nl_count; + +static int scroll_heuristic; + +static enum { + TOPLEVEL, IGNORE_NEXT, + SEEN_ESC, SEEN_CSI, SET_GL, SET_GR, + SEEN_OSC, SEEN_OSC_P, SEEN_OSC_W, OSC_STRING, OSC_MAYBE_ST, + SEEN_ESCHASH +} termstate; + +static enum { + NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED +} selstate; +static enum { + SM_CHAR, SM_WORD, SM_LINE +} selmode; +static unsigned long *selstart, *selend, *selanchor; + +static short wordness[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 01 */ + 0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2, 2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1, /* 23 */ + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2, /* 45 */ + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1, /* 67 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 89 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* AB */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* CD */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* EF */ +}; + +static unsigned char sel_nl[] = SEL_NL; + +/* + * Internal prototypes. + */ +static void do_paint (Context, int); +static void erase_lots (int, int, int); +static void swap_screen (int); +static void update_sbar (void); +static void deselect (void); + +/* + * Set up power-on settings for the terminal. + */ +static void power_on(void) { + curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0; + alt_t = marg_t = 0; + if (rows != -1) + alt_b = marg_b = rows - 1; + else + alt_b = marg_b = 0; + if (cols != -1) { + int i; + for (i = 0; i < cols; i++) + tabs[i] = (i % 8 == 0 ? TRUE : FALSE); + } + alt_om = dec_om = cfg.dec_om; + alt_wnext = wrapnext = alt_ins = insert = FALSE; + alt_wrap = wrap = cfg.wrap_mode; + alt_cset = cset = 0; + cset_attr[0] = cset_attr[1] = ATTR_ASCII; + rvideo = 0; + save_attr = curr_attr = ATTR_DEFAULT; + app_cursor_keys = cfg.app_cursor; + app_keypad_keys = cfg.app_keypad; + alt_which = 0; + { + int i; + for (i = 0; i < 256; i++) + wordness[i] = cfg.wordness[i]; + } + if (text) { + swap_screen (1); + erase_lots (FALSE, TRUE, TRUE); + swap_screen (0); + erase_lots (FALSE, TRUE, TRUE); + } +} + +/* + * Force a screen update. + */ +void term_update(void) { + Context ctx; + ctx = get_ctx(); + if (ctx) { + do_paint (ctx, TRUE); + free_ctx (ctx); + nl_count = 0; + scroll_heuristic = 0; + } +} + +/* + * Same as power_on(), but an external function. + */ +void term_pwron(void) { + power_on(); + fix_cpos; + disptop = scrtop; + deselect(); + term_update(); +} + +/* + * Clear the scrollback. + */ +void term_clrsb(void) { + disptop = sbtop = scrtop; + update_sbar(); +} + +/* + * Initialise the terminal. + */ +void term_init(void) { + text = sbtop = scrtop = disptop = cpos = NULL; + disptext = wanttext = NULL; + tabs = NULL; + selspace = NULL; + deselect(); + rows = cols = -1; + nl_count = 0; + power_on(); +} + +/* + * Set up the terminal for a given size. + */ +void term_size(int newrows, int newcols, int newsavelines) { + unsigned long *newtext, *newdisp, *newwant, *newalt; + int i, j, crows, ccols; + + if (newrows == rows && newcols == cols && newsavelines == savelines) + return; /* nothing to do */ + + alt_t = marg_t = 0; + alt_b = marg_b = newrows - 1; + + newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE); + disptop = newtext + newsavelines*(newcols+1); + for (i=0; i<(newrows+newsavelines)*(newcols+1); i++) + newtext[i] = ERASE_CHAR; + if (rows != -1) { + crows = rows + (scrtop - sbtop) / (cols+1); + if (crows > newrows+newsavelines) + crows = newrows+newsavelines; + ccols = (cols < newcols ? cols : newcols); + for (i=0; i disptop) + sbtop = disptop; + } else + sbtop = disptop; + scrtop = disptop; + sfree (text); + text = newtext; + + newdisp = smalloc (newrows*(newcols+1)*TSIZE); + for (i=0; i 0 ? cols : 0); i < newcols; i++) + tabs[i] = (i % 8 == 0 ? TRUE : FALSE); + } + + if (rows > 0) + curs_y += newrows - rows; + if (curs_y < 0) + curs_y = 0; + if (curs_y >= newrows) + curs_y = newrows-1; + if (curs_x >= newcols) + curs_x = newcols-1; + alt_x = alt_y = 0; + wrapnext = alt_wnext = FALSE; + + rows = newrows; + cols = newcols; + savelines = newsavelines; + fix_cpos; + + deselect(); + update_sbar(); + term_update(); +} + +/* + * Swap screens. + */ +static void swap_screen (int which) { + int t; + unsigned long tt; + + if (which == alt_which) + return; + + alt_which = which; + + for (t=0; t 0 && topline == 0 && botline == (rows-1) && sb) { + /* + * Since we're going to scroll the whole screen upwards, + * let's also affect the scrollback buffer. + */ + sbtop -= lines * (cols+1); + if (sbtop < text) + sbtop = text; + scroll_size += scroll_top - sbtop; + scroll_top = sbtop; + update_sbar(); + } + + if (scroll_size < 0) { + size += scroll_size; + scroll_size = 0; + } + + if (lines > 0) { + if (scroll_size) + memmove (scroll_top, scroll_top + size, scroll_size*TSIZE); + for (i = 0; i < size; i++) + scroll_top[i+scroll_size] = ERASE_CHAR; + if (selstart > scroll_top && + selstart < scroll_top + size + scroll_size) { + selstart -= size; + if (selstart < scroll_top) + selstart = scroll_top; + } + if (selend > scroll_top && + selend < scroll_top + size + scroll_size) { + selend -= size; + if (selend < scroll_top) + selend = scroll_top; + } + } else { + if (scroll_size) + memmove (scroll_top + size, scroll_top, scroll_size*TSIZE); + for (i = 0; i < size; i++) + scroll_top[i] = ERASE_CHAR; + if (selstart > scroll_top && + selstart < scroll_top + size + scroll_size) { + selstart += size; + if (selstart > scroll_top + size + scroll_size) + selstart = scroll_top + size + scroll_size; + } + if (selend > scroll_top && + selend < scroll_top + size + scroll_size) { + selend += size; + if (selend > scroll_top + size + scroll_size) + selend = scroll_top + size + scroll_size; + } + } + + scroll_heuristic += lines; +} + +/* + * Move the cursor to a given position, clipping at boundaries. We + * may or may not want to clip at the scroll margin: marg_clip is 0 + * not to, 1 to disallow _passing_ the margins, and 2 to disallow + * even _being_ outside the margins. + */ +static void move (int x, int y, int marg_clip) { + if (x < 0) + x = 0; + if (x >= cols) + x = cols-1; + if (marg_clip) { + if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t) + y = marg_t; + if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b) + y = marg_b; + } + if (y < 0) + y = 0; + if (y >= rows) + y = rows-1; + curs_x = x; + curs_y = y; + fix_cpos; + wrapnext = FALSE; +} + +/* + * Save or restore the cursor and SGR mode. + */ +static void save_cursor(int save) { + if (save) { + save_x = curs_x; + save_y = curs_y; + save_attr = curr_attr; + save_cset = cset; + save_csattr = cset_attr[cset]; + } else { + curs_x = save_x; + curs_y = save_y; + curr_attr = save_attr; + cset = save_cset; + cset_attr[cset] = save_csattr; + fix_cpos; + } +} + +/* + * Erase a large portion of the screen: the whole screen, or the + * whole line, or parts thereof. + */ +static void erase_lots (int line_only, int from_begin, int to_end) { + unsigned long *startpos, *endpos; + + if (line_only) { + startpos = cpos - curs_x; + endpos = startpos + cols+1; + } else { + startpos = scrtop; + endpos = startpos + rows * (cols+1); + } + if (!from_begin) + startpos = cpos; + if (!to_end) + endpos = cpos; + check_selection (startpos, endpos); + while (startpos < endpos) + *startpos++ = ERASE_CHAR; +} + +/* + * Insert or delete characters within the current line. n is +ve if + * insertion is desired, and -ve for deletion. + */ +static void insch (int n) { + int dir = (n < 0 ? -1 : +1); + int m; + + n = (n < 0 ? -n : n); + if (n > cols - curs_x) + n = cols - curs_x; + m = cols - curs_x - n; + check_selection (cpos, cpos+n); + if (dir < 0) { + memmove (cpos, cpos+n, m*TSIZE); + while (n--) + cpos[m++] = ERASE_CHAR; + } else { + memmove (cpos+n, cpos, m*TSIZE); + while (n--) + cpos[n] = ERASE_CHAR; + } +} + +/* + * Toggle terminal mode `mode' to state `state'. (`query' indicates + * whether the mode is a DEC private one or a normal one.) + */ +static void toggle_mode (int mode, int query, int state) { + if (query) switch (mode) { + case 1: /* application cursor keys */ + app_cursor_keys = state; + break; + case 3: /* 80/132 columns */ + deselect(); + request_resize (state ? 132 : 80, rows); + break; + case 5: /* reverse video */ + rvideo = state; + disptop = scrtop; + break; + case 6: /* DEC origin mode */ + dec_om = state; + break; + case 7: /* auto wrap */ + wrap = state; + break; + case 47: /* alternate screen */ + deselect(); + swap_screen (state); + disptop = scrtop; + break; + } else switch (mode) { + case 4: /* set insert mode */ + insert = state; + break; + } +} + +/* + * Process an OSC sequence: set window title or icon name. + */ +static void do_osc(void) { + if (osc_w) { + while (osc_strlen--) + wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0]; + } else { + osc_string[osc_strlen] = '\0'; + switch (esc_args[0]) { + case 0: + case 1: + set_icon (osc_string); + if (esc_args[0] == 1) + break; + /* fall through: parameter 0 means set both */ + case 2: + case 21: + set_title (osc_string); + break; + } + } +} + +/* + * Remove everything currently in `inbuf' and stick it up on the + * in-memory display. There's a big state machine in here to + * process escape sequences... + */ +void term_out(void) { + int c; + int must_update = FALSE; + + while ( (c = inbuf_getc()) != -1) { +#ifdef LOG + { + static FILE *fp = NULL; + if (!fp) fp = fopen("putty.log", "wb"); + if (fp) fputc (c, fp); + } +#endif + switch (termstate) { + case TOPLEVEL: + do_toplevel: + switch (c) { + case '\005': /* terminal type query */ + back->send ("\033[?1;2c", 7); + break; + case '\007': + beep(); + disptop = scrtop; + must_update = TRUE; + break; + case '\b': + if (curs_x == 0 && curs_y > 0) + curs_x = cols-1, curs_y--; + else if (wrapnext) + wrapnext = FALSE; + else + curs_x--; + fix_cpos; + disptop = scrtop; + must_update = TRUE; + break; + case '\016': + cset = 1; + break; + case '\017': + cset = 0; + break; + case '\033': + termstate = SEEN_ESC; + break; + case 0233: + termstate = SEEN_CSI; + esc_nargs = 1; + esc_args[0] = ARG_DEFAULT; + esc_query = FALSE; + break; + case 0235: + termstate = SEEN_OSC; + esc_args[0] = 0; + break; + case '\r': + curs_x = 0; + wrapnext = FALSE; + fix_cpos; + disptop = scrtop; + must_update = TRUE; + break; + case '\013': + case '\014': + case '\n': + if (curs_y == marg_b) + scroll (marg_t, marg_b, 1, TRUE); + else if (curs_y < rows-1) + curs_y++; + fix_cpos; + wrapnext = FALSE; + disptop = scrtop; + nl_count++; + break; + case '\t': + do { + curs_x++; + } while (curs_x < cols-1 && !tabs[curs_x]); + if (curs_x >= cols) + curs_x = cols-1; + { + unsigned long *old_cpos = cpos; + fix_cpos; + check_selection (old_cpos, cpos); + } + disptop = scrtop; + must_update = TRUE; + break; + default: + if (c >= ' ' && c != 0234) { + if (wrapnext) { + cpos[1] = ATTR_WRAPPED; + if (curs_y == marg_b) + scroll (marg_t, marg_b, 1, TRUE); + else if (curs_y < rows-1) + curs_y++; + curs_x = 0; + fix_cpos; + wrapnext = FALSE; + nl_count++; + } + if (insert) + insch (1); + check_selection (cpos, cpos+1); + *cpos++ = c | curr_attr | + (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII); + curs_x++; + if (curs_x == cols) { + cpos--; + curs_x--; + wrapnext = wrap; + } + disptop = scrtop; + } + } + break; + case IGNORE_NEXT: + termstate = TOPLEVEL; + break; + case OSC_MAYBE_ST: + /* + * This state is virtually identical to SEEN_ESC, with the + * exception that we have an OSC sequence in the pipeline, + * and _if_ we see a backslash, we process it. + */ + if (c == '\\') { + do_osc(); + termstate = TOPLEVEL; + break; + } + /* else fall through */ + case SEEN_ESC: + termstate = TOPLEVEL; + switch (c) { + case '\005': case '\007': case '\b': case '\016': case '\017': + case '\033': case 0233: case 0234: case 0235: case '\r': + case '\013': case '\014': case '\n': case '\t': + termstate = TOPLEVEL; + goto do_toplevel; /* hack... */ + case ' ': /* some weird sequence? */ + termstate = IGNORE_NEXT; + break; + case '[': /* enter CSI mode */ + termstate = SEEN_CSI; + esc_nargs = 1; + esc_args[0] = ARG_DEFAULT; + esc_query = FALSE; + break; + case ']': /* xterm escape sequences */ + termstate = SEEN_OSC; + esc_args[0] = 0; + break; + case '(': /* should set GL */ + termstate = SET_GL; + break; + case ')': /* should set GR */ + termstate = SET_GR; + break; + case '7': /* save cursor */ + save_cursor (TRUE); + break; + case '8': /* restore cursor */ + save_cursor (FALSE); + disptop = scrtop; + must_update = TRUE; + break; + case '=': + app_keypad_keys = TRUE; + break; + case '>': + app_keypad_keys = FALSE; + break; + case 'D': /* exactly equivalent to LF */ + if (curs_y == marg_b) + scroll (marg_t, marg_b, 1, TRUE); + else if (curs_y < rows-1) + curs_y++; + fix_cpos; + wrapnext = FALSE; + disptop = scrtop; + nl_count++; + break; + case 'E': /* exactly equivalent to CR-LF */ + curs_x = 0; + wrapnext = FALSE; + if (curs_y == marg_b) + scroll (marg_t, marg_b, 1, TRUE); + else if (curs_y < rows-1) + curs_y++; + fix_cpos; + wrapnext = FALSE; + nl_count++; + disptop = scrtop; + break; + case 'M': /* reverse index - backwards LF */ + if (curs_y == marg_t) + scroll (marg_t, marg_b, -1, TRUE); + else if (curs_y > 0) + curs_y--; + fix_cpos; + wrapnext = FALSE; + disptop = scrtop; + must_update = TRUE; + break; + case 'Z': /* terminal type query */ + back->send ("\033[?6c", 5); + break; + case 'c': /* restore power-on settings */ + power_on(); + fix_cpos; + disptop = scrtop; + must_update = TRUE; + break; + case '#': /* ESC # 8 fills screen with Es :-) */ + termstate = SEEN_ESCHASH; + break; + case 'H': /* set a tab */ + tabs[curs_x] = TRUE; + break; + } + break; + case SEEN_CSI: + termstate = TOPLEVEL; /* default */ + switch (c) { + case '\005': case '\007': case '\b': case '\016': case '\017': + case '\033': case 0233: case 0234: case 0235: case '\r': + case '\013': case '\014': case '\n': case '\t': + termstate = TOPLEVEL; + goto do_toplevel; /* hack... */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (esc_nargs <= ARGS_MAX) { + if (esc_args[esc_nargs-1] == ARG_DEFAULT) + esc_args[esc_nargs-1] = 0; + esc_args[esc_nargs-1] = + 10 * esc_args[esc_nargs-1] + c - '0'; + } + termstate = SEEN_CSI; + break; + case ';': + if (++esc_nargs <= ARGS_MAX) + esc_args[esc_nargs-1] = ARG_DEFAULT; + termstate = SEEN_CSI; + break; + case '?': + esc_query = TRUE; + termstate = SEEN_CSI; + break; + case 'A': /* move up N lines */ + move (curs_x, curs_y - def(esc_args[0], 1), 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'B': case 'e': /* move down N lines */ + move (curs_x, curs_y + def(esc_args[0], 1), 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'C': case 'a': /* move right N cols */ + move (curs_x + def(esc_args[0], 1), curs_y, 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'D': /* move left N cols */ + move (curs_x - def(esc_args[0], 1), curs_y, 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'E': /* move down N lines and CR */ + move (0, curs_y + def(esc_args[0], 1), 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'F': /* move up N lines and CR */ + move (0, curs_y - def(esc_args[0], 1), 1); + disptop = scrtop; + must_update = TRUE; + break; + case 'G': case '`': /* set horizontal posn */ + move (def(esc_args[0], 1) - 1, curs_y, 0); + disptop = scrtop; + must_update = TRUE; + break; + case 'd': /* set vertical posn */ + move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1, + (dec_om ? 2 : 0)); + disptop = scrtop; + must_update = TRUE; + break; + case 'H': case 'f': /* set horz and vert posns at once */ + if (esc_nargs < 2) + esc_args[1] = ARG_DEFAULT; + move (def(esc_args[1], 1) - 1, + (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1, + (dec_om ? 2 : 0)); + disptop = scrtop; + must_update = TRUE; + break; + case 'J': /* erase screen or parts of it */ + { + unsigned int i = def(esc_args[0], 0) + 1; + if (i > 3) + i = 0; + erase_lots(FALSE, !!(i & 2), !!(i & 1)); + } + disptop = scrtop; + must_update = TRUE; + break; + case 'K': /* erase line or parts of it */ + { + unsigned int i = def(esc_args[0], 0) + 1; + if (i > 3) + i = 0; + erase_lots(TRUE, !!(i & 2), !!(i & 1)); + } + disptop = scrtop; + must_update = TRUE; + break; + case 'L': /* insert lines */ + if (curs_y <= marg_b) + scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE); + disptop = scrtop; + must_update = TRUE; + break; + case 'M': /* delete lines */ + if (curs_y <= marg_b) + scroll (curs_y, marg_b, def(esc_args[0], 1), FALSE); + disptop = scrtop; + must_update = TRUE; + break; + case '@': /* insert chars */ + insch (def(esc_args[0], 1)); + disptop = scrtop; + must_update = TRUE; + break; + case 'P': /* delete chars */ + insch (-def(esc_args[0], 1)); + disptop = scrtop; + must_update = TRUE; + break; + case 'c': /* terminal type query */ + back->send ("\033[?6c", 5); + break; + case 'n': /* cursor position query */ + if (esc_args[0] == 6) { + char buf[32]; + sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1); + back->send (buf, strlen(buf)); + } + break; + case 'h': /* toggle a mode to high */ + toggle_mode (esc_args[0], esc_query, TRUE); + break; + case 'l': /* toggle a mode to low */ + toggle_mode (esc_args[0], esc_query, FALSE); + break; + case 'g': /* clear tabs */ + if (esc_nargs == 1) { + if (esc_args[0] == 0) { + tabs[curs_x] = FALSE; + } else if (esc_args[0] == 3) { + int i; + for (i = 0; i < cols; i++) + tabs[i] = FALSE; + } + } + break; + case 'r': /* set scroll margins */ + if (esc_nargs <= 2) { + int top, bot; + top = def(esc_args[0], 1) - 1; + if (top < 0) + top = 0; + bot = (esc_nargs == 1 ? rows : + def(esc_args[1], rows)) - 1; + if (bot >= rows) + bot = rows-1; + if (top <= bot) { + marg_t = top; + marg_b = bot; + curs_x = 0; + /* + * I used to think the cursor should be + * placed at the top of the newly marginned + * area. Apparently not: VMS TPU falls over + * if so. + */ + curs_y = 0; + fix_cpos; + disptop = scrtop; + must_update = TRUE; + } + } + break; + case 'm': /* set graphics rendition */ + { + int i; + for (i=0; i cols - curs_x) + n = cols - curs_x; + check_selection (cpos, cpos+n); + while (n--) + *p++ = ERASE_CHAR; + disptop = scrtop; + must_update = TRUE; + } + break; + case 'x': /* report terminal characteristics */ + { + char buf[32]; + int i = def(esc_args[0], 0); + if (i == 0 || i == 1) { + strcpy (buf, "\033[2;1;1;112;112;1;0x"); + buf[2] += i; + back->send (buf, 20); + } + } + break; + } + break; + case SET_GL: + case SET_GR: + switch (c) { + case 'A': + cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR; + break; + case '0': + cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW; + break; + default: /* specifically, 'B' */ + cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII; + break; + } + termstate = TOPLEVEL; + break; + case SEEN_OSC: + osc_w = FALSE; + switch (c) { + case '\005': case '\007': case '\b': case '\016': case '\017': + case '\033': case 0233: case 0234: case 0235: case '\r': + case '\013': case '\014': case '\n': case '\t': + termstate = TOPLEVEL; + goto do_toplevel; /* hack... */ + case 'P': /* Linux palette sequence */ + termstate = SEEN_OSC_P; + osc_strlen = 0; + break; + case 'R': /* Linux palette reset */ + palette_reset(); + term_invalidate(); + termstate = TOPLEVEL; + break; + case 'W': /* word-set */ + termstate = SEEN_OSC_W; + osc_w = TRUE; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + esc_args[0] = 10 * esc_args[0] + c - '0'; + break; + case 'L': + /* + * Grotty hack to support xterm and DECterm title + * sequences concurrently. + */ + if (esc_args[0] == 2) { + esc_args[0] = 1; + break; + } + /* else fall through */ + default: + termstate = OSC_STRING; + osc_strlen = 0; + } + break; + case OSC_STRING: + if (c == 0234 || c == '\007') { + /* + * These characters terminate the string; ST and BEL + * terminate the sequence and trigger instant + * processing of it, whereas ESC goes back to SEEN_ESC + * mode unless it is followed by \, in which case it is + * synonymous with ST in the first place. + */ + do_osc(); + termstate = TOPLEVEL; + } else if (c == '\033') + termstate = OSC_MAYBE_ST; + else if (osc_strlen < OSC_STR_MAX) + osc_string[osc_strlen++] = c; + break; + case SEEN_OSC_P: + { + int max = (osc_strlen == 0 ? 21 : 16); + int val; + if (c >= '0' && c <= '9') + val = c - '0'; + else if (c >= 'A' && c <= 'A'+max-10) + val = c - 'A' + 10; + else if (c >= 'a' && c <= 'a'+max-10) + val = c - 'a' + 10; + else + termstate = TOPLEVEL; + osc_string[osc_strlen++] = val; + if (osc_strlen >= 7) { + palette_set (osc_string[0], + osc_string[1] * 16 + osc_string[2], + osc_string[3] * 16 + osc_string[4], + osc_string[5] * 16 + osc_string[6]); + term_invalidate(); + termstate = TOPLEVEL; + } + } + break; + case SEEN_OSC_W: + switch (c) { + case '\005': case '\007': case '\b': case '\016': case '\017': + case '\033': case 0233: case 0234: case 0235: case '\r': + case '\013': case '\014': case '\n': case '\t': + termstate = TOPLEVEL; + goto do_toplevel; /* hack... */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + esc_args[0] = 10 * esc_args[0] + c - '0'; + break; + default: + termstate = OSC_STRING; + osc_strlen = 0; + } + break; + case SEEN_ESCHASH: + if (c == '8') { + unsigned long *p = scrtop; + int n = rows * (cols+1); + while (n--) + *p++ = ATTR_DEFAULT | 'E'; + disptop = scrtop; + must_update = TRUE; + check_selection (scrtop, scrtop + rows * (cols+1)); + } + termstate = TOPLEVEL; + break; + } + check_selection (cpos, cpos+1); + } + + if (must_update || nl_count > MAXNL) + term_update(); +} + +/* + * Compare two lines to determine whether they are sufficiently + * alike to scroll-optimise one to the other. Return the degree of + * similarity. + */ +static int linecmp (unsigned long *a, unsigned long *b) { + int i, n; + + for (i=n=0; i < cols; i++) + n += (*a++ == *b++); + return n; +} + +/* + * Given a context, update the window. Out of paranoia, we don't + * allow WM_PAINT responses to do scrolling optimisations. + */ +static void do_paint (Context ctx, int may_optimise){ + int i, j, start, our_curs_y; + unsigned long attr, rv, cursor; + char ch[1024]; + + cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS); + rv = (rvideo ? ATTR_REVERSE : 0); + our_curs_y = curs_y + (scrtop - disptop) / (cols+1); + + for (i=0; i 0 ? sbtop : disptop) + n; + if (disptop < sbtop) + disptop = sbtop; + if (disptop > scrtop) + disptop = scrtop; + update_sbar(); + term_update(); +} + +/* + * Spread the selection outwards according to the selection mode. + */ +static unsigned long *sel_spread_half (unsigned long *p, int dir) { + unsigned long *linestart, *lineend; + int x; + short wvalue; + + x = (p - text) % (cols+1); + linestart = p - x; + lineend = linestart + cols; + + switch (selmode) { + case SM_CHAR: + /* + * In this mode, every character is a separate unit, except + * for runs of spaces at the end of a non-wrapping line. + */ + if (!(linestart[cols] & ATTR_WRAPPED)) { + unsigned long *q = lineend; + while (q > linestart && (q[-1] & CHAR_MASK) == 0x20) + q--; + if (q == lineend) + q--; + if (p >= q) + p = (dir == -1 ? q : lineend - 1); + } + break; + case SM_WORD: + /* + * In this mode, the units are maximal runs of characters + * whose `wordness' has the same value. + */ + wvalue = wordness[*p & CHAR_MASK]; + if (dir == +1) { + while (p < lineend && wordness[p[1] & CHAR_MASK] == wvalue) + p++; + } else { + while (p > linestart && wordness[p[-1] & CHAR_MASK] == wvalue) + p--; + } + break; + case SM_LINE: + /* + * In this mode, every line is a unit. + */ + p = (dir == -1 ? linestart : lineend - 1); + break; + } + return p; +} + +static void sel_spread (void) { + selstart = sel_spread_half (selstart, -1); + selend = sel_spread_half (selend - 1, +1) + 1; +} + +void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) { + unsigned long *selpoint = disptop + y * (cols+1) + x; + + if (b == MB_SELECT && a == MA_CLICK) { + deselect(); + selstate = ABOUT_TO; + selanchor = selpoint; + selmode = SM_CHAR; + } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) { + deselect(); + selmode = (a == MA_2CLK ? SM_WORD : SM_LINE); + selstate = DRAGGING; + selstart = selanchor = selpoint; + selend = selstart + 1; + sel_spread(); + } else if ((b == MB_SELECT && a == MA_DRAG) || + (b == MB_EXTEND && a != MA_RELEASE)) { + if (selstate == ABOUT_TO && selanchor == selpoint) + return; + if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) { + if (selpoint-selstart < (selend-selstart)/2) + selanchor = selend - 1; + else + selanchor = selstart; + selstate = DRAGGING; + } + if (selstate != ABOUT_TO && selstate != DRAGGING) + selanchor = selpoint; + selstate = DRAGGING; + if (selpoint < selanchor) { + selstart = selpoint; + selend = selanchor + 1; + } else { + selstart = selanchor; + selend = selpoint + 1; + } + sel_spread(); + } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) { + if (selstate == DRAGGING) { + /* + * We've completed a selection. We now transfer the + * data to the clipboard. + */ + unsigned char *p = selspace; + unsigned long *q = selstart; + + while (q < selend) { + int nl = FALSE; + unsigned long *lineend = q - (q-text) % (cols+1) + cols; + unsigned long *nlpos = lineend; + + if (!(*nlpos & ATTR_WRAPPED)) { + while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q) + nlpos--; + if (nlpos < selend) + nl = TRUE; + } + while (q < nlpos && q < selend) + *p++ = (unsigned char) (*q++ & CHAR_MASK); + if (nl) { + int i; + for (i=0; isend (q, p-q); + if (p <= data+len-sizeof(sel_nl) && + !memcmp(p, sel_nl, sizeof(sel_nl))) { + back->send ("\r", 1); + p += sizeof(sel_nl); + } + q = p; + } + } + get_clip(NULL, NULL); + } + + term_update(); +} + +static void deselect (void) { + selstate = NO_SELECTION; + selstart = selend = scrtop; +} + +void term_deselect (void) { + deselect(); + term_update(); +} diff --git a/win_res.h b/win_res.h new file mode 100644 index 00000000..56c7a0c7 --- /dev/null +++ b/win_res.h @@ -0,0 +1,116 @@ +#ifndef PUTTY_WIN_RES_H +#define PUTTY_WIN_RES_H + +#define IDI_MAINICON 200 + +#define IDD_MAINBOX 102 +#define IDD_PANEL0 103 +#define IDD_PANEL1 104 +#define IDD_PANEL2 105 +#define IDD_PANEL3 106 +#define IDD_PANEL35 107 +#define IDD_PANEL4 108 +#define IDD_PANEL5 109 +#define IDD_LOGBOX 110 +#define IDD_ABOUTBOX 111 +#define IDD_RECONF 112 +#define IDD_LICENCEBOX 113 + +#define IDN_LIST 1001 + +#define IDA_ICON 1001 +#define IDA_TEXT 1002 +#define IDA_LICENCE 1003 + +#define IDC_TAB 1001 +#define IDC_ABOUT 1002 + +#define IDC0_HOSTSTATIC 1001 +#define IDC0_HOST 1002 +#define IDC0_PORTSTATIC 1003 +#define IDC0_PORT 1004 +#define IDC0_PROTSTATIC 1005 +#define IDC0_PROTTELNET 1006 +#define IDC0_PROTSSH 1007 +#define IDC0_SESSSTATIC 1008 +#define IDC0_SESSEDIT 1009 +#define IDC0_SESSLIST 1010 +#define IDC0_SESSLOAD 1011 +#define IDC0_SESSSAVE 1012 +#define IDC0_SESSDEL 1013 +#define IDC0_CLOSEEXIT 1014 + +#define IDC1_DELSTATIC 1001 +#define IDC1_DEL008 1002 +#define IDC1_DEL127 1003 +#define IDC1_HOMESTATIC 1004 +#define IDC1_HOMETILDE 1005 +#define IDC1_HOMERXVT 1006 +#define IDC1_FUNCSTATIC 1007 +#define IDC1_FUNCTILDE 1008 +#define IDC1_FUNCLINUX 1009 +#define IDC1_KPSTATIC 1010 +#define IDC1_KPNORMAL 1011 +#define IDC1_KPAPPLIC 1012 +#define IDC1_CURSTATIC 1013 +#define IDC1_CURNORMAL 1014 +#define IDC1_CURAPPLIC 1015 + +#define IDC2_WRAPMODE 1001 +#define IDC2_DECOM 1002 +#define IDC2_WINNAME 1003 +#define IDC2_DIMSTATIC 1004 +#define IDC2_ROWSSTATIC 1005 +#define IDC2_ROWSEDIT 1006 +#define IDC2_COLSSTATIC 1007 +#define IDC2_COLSEDIT 1008 +#define IDC2_SAVESTATIC 1009 +#define IDC2_SAVEEDIT 1010 +#define IDC2_FONTSTATIC 1011 +#define IDC2_CHOOSEFONT 1012 +#define IDC2_VTSTATIC 1013 +#define IDC2_VTXWINDOWS 1014 +#define IDC2_VTOEMANSI 1015 +#define IDC2_VTOEMONLY 1016 +#define IDC2_VTPOORMAN 1017 + +#define IDC3_TTSTATIC 1001 +#define IDC3_TTEDIT 1002 +#define IDC3_TSSTATIC 1003 +#define IDC3_TSEDIT 1004 +#define IDC3_LOGSTATIC 1005 +#define IDC3_LOGEDIT 1006 +#define IDC3_ENVSTATIC 1007 +#define IDC3_VARSTATIC 1008 +#define IDC3_VAREDIT 1009 +#define IDC3_VALSTATIC 1010 +#define IDC3_VALEDIT 1011 +#define IDC3_ENVLIST 1012 +#define IDC3_ENVADD 1013 +#define IDC3_ENVREMOVE 1014 +#define IDC3_EMSTATIC 1015 +#define IDC3_EMBSD 1016 +#define IDC3_EMRFC 1017 + +#define IDC4_MBSTATIC 1001 +#define IDC4_MBWINDOWS 1002 +#define IDC4_MBXTERM 1003 +#define IDC4_CCSTATIC 1004 +#define IDC4_CCLIST 1005 +#define IDC4_CCSET 1006 +#define IDC4_CCSTATIC2 1007 +#define IDC4_CCEDIT 1008 + +#define IDC5_BOLDCOLOUR 1001 +#define IDC5_PALETTE 1002 +#define IDC5_STATIC 1003 +#define IDC5_LIST 1004 +#define IDC5_RSTATIC 1005 +#define IDC5_GSTATIC 1006 +#define IDC5_BSTATIC 1007 +#define IDC5_RVALUE 1008 +#define IDC5_GVALUE 1009 +#define IDC5_BVALUE 1010 +#define IDC5_CHANGE 1011 + +#endif diff --git a/win_res.rc b/win_res.rc new file mode 100644 index 00000000..de4216a4 --- /dev/null +++ b/win_res.rc @@ -0,0 +1,235 @@ +#include + +#define TCS_MULTILINE 0x0200 + +#include "win_res.h" + +IDI_MAINICON ICON "putty.ico" + +IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 136, 58 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About PuTTY" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Close", IDOK, 82, 40, 48, 14 + PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 40, 70, 14 + ICON IDI_MAINICON, IDA_ICON, 10, 10, 0, 0 + LTEXT "PuTTY Beta 0.43\n\251 1997-8 Simon Tatham\nAll rights reserved.", + IDA_TEXT, 40, 6, 96, 24 +END + +IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 180, 216 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Configuration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Open", IDOK, 86, 199, 44, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 133, 199, 44, 14 + PUSHBUTTON "&About", IDC_ABOUT, 3, 199, 44, 14, NOT WS_TABSTOP + CONTROL "", IDC_TAB, L"SysTabControl32", TCS_MULTILINE | WS_TABSTOP, + 3, 3, 174, 193 +END + +IDD_RECONF DIALOG DISCARDABLE 0, 0, 180, 216 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Reconfiguration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Apply", IDOK, 86, 199, 44, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 133, 199, 44, 14 + CONTROL "", IDC_TAB, L"SysTabControl32", TCS_MULTILINE | WS_TABSTOP, + 3, 3, 174, 193 +END + +IDD_PANEL0 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Host Name", IDC0_HOSTSTATIC, 3, 3, 122, 8 + EDITTEXT IDC0_HOST, 3, 11, 122, 12, ES_AUTOHSCROLL + LTEXT "Port", IDC0_PORTSTATIC, 131, 3, 34, 8 + EDITTEXT IDC0_PORT, 131, 11, 34, 12 +#ifdef FWHACK + RTEXT "Protocol:", IDC0_PROTSTATIC, 3, 29, 77, 8 + AUTORADIOBUTTON "Telnet", IDC0_PROTTELNET, 86, 29, 33, 10, WS_GROUP + AUTORADIOBUTTON "SSH/hack", IDC0_PROTSSH, 122, 29, 43, 10 +#else + RTEXT "Protocol:", IDC0_PROTSTATIC, 3, 29, 87, 8 + AUTORADIOBUTTON "Telnet", IDC0_PROTTELNET, 96, 29, 33, 10, WS_GROUP + AUTORADIOBUTTON "SSH", IDC0_PROTSSH, 132, 29, 33, 10 +#endif + LTEXT "Stored Sessions", IDC0_SESSSTATIC, 3, 40, 122, 8 + EDITTEXT IDC0_SESSEDIT, 3, 48, 122, 12, ES_AUTOHSCROLL + LISTBOX IDC0_SESSLIST, 3, 63, 122, 81, LBS_HASSTRINGS | WS_VSCROLL + PUSHBUTTON "Load", IDC0_SESSLOAD, 131, 63, 34, 14 + PUSHBUTTON "Save", IDC0_SESSSAVE, 131, 80, 34, 14 + PUSHBUTTON "Delete", IDC0_SESSDEL, 131, 97, 34, 14 + AUTOCHECKBOX "Close Window on Exit", IDC0_CLOSEEXIT, 3, 147, 162, 10 +END + +IDD_PANEL1 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Action of Backspace:", IDC1_DELSTATIC, 3, 3, 162, 8 + AUTORADIOBUTTON "Control-H (ASCII 8)", IDC1_DEL008, 3, 12, 162, 10, WS_GROUP + AUTORADIOBUTTON "Control-? (ASCII 127)", IDC1_DEL127, 3, 22, 162, 10 + LTEXT "Action of Home and End:", IDC1_HOMESTATIC, 3, 35, 162, 8 + AUTORADIOBUTTON "Standard (ESC [ 1 ~ and ESC [ 4 ~)", + IDC1_HOMETILDE, 3, 44, 162, 10, WS_GROUP + AUTORADIOBUTTON "rxvt (ESC [ H and ESC O w)", + IDC1_HOMERXVT, 3, 54, 162, 10 + LTEXT "Action of F1 through F5:", IDC1_FUNCSTATIC, 3, 67, 162, 8 + AUTORADIOBUTTON "Standard (ESC [ 11 ~ through ESC [ 15 ~)", + IDC1_FUNCTILDE, 3, 76, 162, 10, WS_GROUP + AUTORADIOBUTTON "Linux (ESC [ [ A through ESC [ [ E)", + IDC1_FUNCLINUX, 3, 86, 162, 10 + LTEXT "Initial state of numeric keypad:", IDC1_KPSTATIC, 3, 99, 162, 8 + AUTORADIOBUTTON "Normal (depends on NumLock)", + IDC1_KPNORMAL, 3, 108, 162, 10, WS_GROUP + AUTORADIOBUTTON "Application (ESC O P etc)", + IDC1_KPAPPLIC, 3, 118, 162, 10 + LTEXT "Initial state of cursor keys:", IDC1_CURSTATIC, 3, 131, 162, 8 + AUTORADIOBUTTON "Normal (ESC [ A through ESC [ D)", + IDC1_CURNORMAL, 3, 140, 162, 10, WS_GROUP + AUTORADIOBUTTON "Application (ESC O A through ESC O D)", + IDC1_CURAPPLIC, 3, 150, 162, 10 +END + +IDD_PANEL2 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + AUTOCHECKBOX "Auto wrap mode initially on", IDC2_WRAPMODE, 3, 3, 162, 10 + AUTOCHECKBOX "DEC Origin Mode initially on", IDC2_DECOM, 3, 13, 162, 10 + AUTOCHECKBOX "Avoid ever using icon title", IDC2_WINNAME, 3, 23, 162, 10 + LTEXT "Terminal screen dimensions:", IDC2_DIMSTATIC, 3, 33, 162, 8 + RTEXT "Rows", IDC2_ROWSSTATIC, 20, 44, 90, 8 + EDITTEXT IDC2_ROWSEDIT, 118, 42, 30, 12 + RTEXT "Columns", IDC2_COLSSTATIC, 20, 59, 90, 8 + EDITTEXT IDC2_COLSEDIT, 118, 57, 30, 12 + RTEXT "Saved lines of scrollback", IDC2_SAVESTATIC, 20, 74, 90, 8 + EDITTEXT IDC2_SAVEEDIT, 118, 72, 30, 12 + LTEXT "Font:", IDC2_FONTSTATIC, 3, 93, 99, 8 + PUSHBUTTON "Change...", IDC2_CHOOSEFONT, 105, 90, 60, 14 + LTEXT "Handling of VT100 line drawing characters:",IDC2_VTSTATIC, 3, 111, 162, 8 + AUTORADIOBUTTON "Font has XWindows encoding", + IDC2_VTXWINDOWS, 3, 120, 162, 10, WS_GROUP + AUTORADIOBUTTON "Use font in both ANSI and OEM modes", + IDC2_VTOEMANSI, 3, 130, 162, 10 + AUTORADIOBUTTON "Use font in OEM mode only", + IDC2_VTOEMONLY, 3, 140, 162, 10 + AUTORADIOBUTTON "Poor man's line drawing (""+"", ""-"" and ""|"")", + IDC2_VTPOORMAN, 3, 150, 162, 10 +END + +IDD_PANEL3 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Terminal-type string", IDC3_TTSTATIC, 3, 5, 90, 8 + EDITTEXT IDC3_TTEDIT, 96, 3, 69, 12, ES_AUTOHSCROLL + LTEXT "Terminal-speed string", IDC3_TSSTATIC, 3, 20, 90, 8 + EDITTEXT IDC3_TSEDIT, 96, 18, 69, 12, ES_AUTOHSCROLL + LTEXT "Auto-login username", IDC3_LOGSTATIC, 3, 35, 90, 8 + EDITTEXT IDC3_LOGEDIT, 96, 33, 69, 12, ES_AUTOHSCROLL + LTEXT "Environment variables:", IDC3_ENVSTATIC, 3, 53, 162, 8 + LTEXT "Variable", IDC3_VARSTATIC, 3, 70, 29, 8 + EDITTEXT IDC3_VAREDIT, 35, 68, 35, 12, ES_AUTOHSCROLL + LTEXT "Value", IDC3_VALSTATIC, 76, 70, 19, 8 + EDITTEXT IDC3_VALEDIT, 98, 68, 67, 12, ES_AUTOHSCROLL + LISTBOX IDC3_ENVLIST, 3, 85, 122, 55, + LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL + PUSHBUTTON "Add", IDC3_ENVADD, 131, 85, 34, 14 + PUSHBUTTON "Remove", IDC3_ENVREMOVE, 131, 102, 34, 14 + LTEXT "ENVIRON interpretation:", IDC3_EMSTATIC, 3, 147, 90, 8 + AUTORADIOBUTTON "BSD", IDC3_EMBSD, 96, 147, 33, 10, WS_GROUP + AUTORADIOBUTTON "RFC", IDC3_EMRFC, 132, 147, 33, 10 +END + +IDD_PANEL35 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Terminal-type string", IDC3_TTSTATIC, 3, 5, 90, 8 + EDITTEXT IDC3_TTEDIT, 96, 3, 69, 12, ES_AUTOHSCROLL + LTEXT "Auto-login username", IDC3_LOGSTATIC, 3, 35, 90, 8 + EDITTEXT IDC3_LOGEDIT, 96, 33, 69, 12, ES_AUTOHSCROLL +END + +IDD_PANEL4 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Action of mouse buttons:", IDC4_MBSTATIC, 3, 3, 162, 8 + AUTORADIOBUTTON "Windows (Right pastes, Middle extends)", + IDC4_MBWINDOWS, 3, 12, 162, 10, WS_GROUP + AUTORADIOBUTTON "xterm (Right extends, Middle pastes)", + IDC4_MBXTERM, 3, 22, 162, 10 + LTEXT "Character classes:", IDC4_CCSTATIC, 3, 35, 162, 8 + LISTBOX IDC4_CCLIST, 3, 45, 162, 96, + LBS_HASSTRINGS | WS_VSCROLL | LBS_USETABSTOPS | LBS_MULTIPLESEL + PUSHBUTTON "Set", IDC4_CCSET, 33, 145, 34, 14 + LTEXT "to class", IDC4_CCSTATIC2, 73, 148, 26, 8 + EDITTEXT IDC4_CCEDIT, 105, 146, 36, 12 +END + +IDD_PANEL5 DIALOG DISCARDABLE 6, 30, 168, 163 +STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + AUTOCHECKBOX "Bolded text is a different colour", IDC5_BOLDCOLOUR,3, 10, 162, 10 + AUTOCHECKBOX "Attempt to use logical palettes", IDC5_PALETTE, 3, 25, 162, 10 + LTEXT "Colours:", IDC5_STATIC, 3, 40, 162, 8 + LISTBOX IDC5_LIST, 3, 50, 100, 110, LBS_HASSTRINGS | WS_VSCROLL + LTEXT "Red:", IDC5_RSTATIC, 109, 50, 27, 8 + RTEXT "", IDC5_RVALUE, 138, 50, 27, 8 + LTEXT "Green:", IDC5_GSTATIC, 109, 58, 27, 8 + RTEXT "", IDC5_GVALUE, 138, 58, 27, 8 + LTEXT "Blue:", IDC5_BSTATIC, 109, 66, 27, 8 + RTEXT "", IDC5_BVALUE, 138, 66, 27, 8 + PUSHBUTTON "Change...", IDC5_CHANGE, 109, 140, 56, 14 +END + +IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 160, 119 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Negotiation Log" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Close", IDOK, 58, 102, 44, 14 + LISTBOX IDN_LIST, 3, 3, 154, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL +END + +IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 231 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Licence" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK", IDOK, 98, 211, 44, 14 + + LTEXT "Copyright \251 1998 Simon Tatham", 1000, 10, 10, 206, 8 + LTEXT "Portions copyright Gary S. Brown and Eric Young", 1100, 10, 18, 206, 8 + + LTEXT "Permission is hereby granted, free of charge, to any person", 1002, 10, 34, 206, 8 + LTEXT "obtaining a copy of this software and associated documentation", 1003, 10, 42, 206, 8 + LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1004, 10, 50, 206, 8 + LTEXT "including without limitation the rights to use, copy, modify, merge,", 1005, 10, 58, 206, 8 + LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1006, 10, 66, 206, 8 + LTEXT "and to permit persons to whom the Software is furnished to do so,", 1007, 10, 74, 206, 8 + LTEXT "subject to the following conditions:", 1008, 10, 82, 206, 8 + + LTEXT "The above copyright notice and this permission notice shall be", 1010, 10, 98, 206, 8 + LTEXT "included in all copies or substantial portions of the Software.", 1011, 10, 106, 206, 8 + + LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1013, 10, 122, 206, 8 + LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1014, 10, 130, 206, 8 + LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1015, 10, 138, 206, 8 + LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1016, 10, 146, 206, 8 + LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", 1017, 10, 154, 206, 8 + LTEXT "SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR", 1018, 10, 162, 206, 8 + LTEXT "OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1019, 10, 170, 206, 8 + LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1020, 10, 178, 206, 8 + LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1021, 10, 186, 206, 8 + LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1022, 10, 194, 206, 8 + +END diff --git a/windlg.c b/windlg.c new file mode 100644 index 00000000..ecd3e5f2 --- /dev/null +++ b/windlg.c @@ -0,0 +1,1336 @@ +#include +#include +#include +#include +#include +#include + +#include "putty.h" +#include "ssh.h" +#include "win_res.h" + +#define NPANELS 7 +#define MAIN_NPANELS 7 +#define RECONF_NPANELS 4 + +static const char *const puttystr = PUTTY_REG_POS "\\Sessions"; + +static void get_sesslist(int allocate); + +static char **negots = NULL; +static int nnegots = 0, negsize = 0; +static HWND logbox = NULL, abtbox = NULL; + +static char hex[16] = "0123456789ABCDEF"; + +static void mungestr(char *in, char *out) { + int candot = 0; + + while (*in) { + if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || + *in == '%' || *in < ' ' || *in > '~' || (*in == '.' && !candot)) { + *out++ = '%'; + *out++ = hex[((unsigned char)*in) >> 4]; + *out++ = hex[((unsigned char)*in) & 15]; + } else + *out++ = *in; + in++; + candot = 1; + } + *out = '\0'; + return; +} + +static void unmungestr(char *in, char *out) { + int candot = 0; + + while (*in) { + if (*in == '%' && in[1] && in[2]) { + int i, j; + + i = in[1] - '0'; i -= (i > 9 ? 7 : 0); + j = in[2] - '0'; j -= (j > 9 ? 7 : 0); + + *out++ = (i<<4) + j; + in += 3; + } else + *out++ = *in++; + } + *out = '\0'; + return; +} + +static void wpps(HKEY key, LPCTSTR name, LPCTSTR value) { + RegSetValueEx(key, name, 0, REG_SZ, value, 1+strlen(value)); +} + +static void wppi(HKEY key, LPCTSTR name, int value) { + RegSetValueEx(key, name, 0, REG_DWORD, + (CONST BYTE *)&value, sizeof(value)); +} + +static void gpps(HKEY key, LPCTSTR name, LPCTSTR def, + LPTSTR val, int len) { + DWORD type, size; + size = len; + + if (key == NULL || + RegQueryValueEx(key, name, 0, &type, val, &size) != ERROR_SUCCESS || + type != REG_SZ) { + strncpy(val, def, len); + val[len-1] = '\0'; + } +} + +static void gppi(HKEY key, LPCTSTR name, int def, int *i) { + DWORD type, val, size; + size = sizeof(val); + + if (key == NULL || + RegQueryValueEx(key, name, 0, &type, + (BYTE *)&val, &size) != ERROR_SUCCESS || + size != sizeof(val) || type != REG_DWORD) + *i = def; + else + *i = val; +} + +typedef struct { + void *posn; + void *temp; + char dataspace[2048]; +} DTemplate; + +static HINSTANCE hinst; + +static char **sessions; +static int nsessions; + +static void save_settings (char *section, int do_host) { + int i; + HKEY subkey1, sesskey; + char *p; + + p = malloc(3*strlen(section)+1); + mungestr(section, p); + + if (RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1)!=ERROR_SUCCESS || + RegCreateKey(subkey1, p, &sesskey) != ERROR_SUCCESS) { + free(p); + sesskey = NULL; + } + + free(p); + RegCloseKey(subkey1); + + wppi (sesskey, "Present", 1); + if (do_host) { + wpps (sesskey, "HostName", cfg.host); + wppi (sesskey, "PortNumber", cfg.port); + wpps (sesskey, "Protocol", + cfg.protocol == PROT_SSH ? "ssh" : "telnet"); + } + wppi (sesskey, "CloseOnExit", !!cfg.close_on_exit); + wpps (sesskey, "TerminalType", cfg.termtype); + wpps (sesskey, "TerminalSpeed", cfg.termspeed); + { + char buf[2*sizeof(cfg.environ)], *p, *q; + p = buf; + q = cfg.environ; + while (*q) { + while (*q) { + int c = *q++; + if (c == '=' || c == ',' || c == '\\') + *p++ = '\\'; + if (c == '\t') + c = '='; + *p++ = c; + } + *p++ = ','; + q++; + } + *p = '\0'; + wpps (sesskey, "Environment", buf); + } + wpps (sesskey, "UserName", cfg.username); + wppi (sesskey, "RFCEnviron", cfg.rfc_environ); + wppi (sesskey, "BackspaceIsDelete", cfg.bksp_is_delete); + wppi (sesskey, "RXVTHomeEnd", cfg.rxvt_homeend); + wppi (sesskey, "LinuxFunctionKeys", cfg.linux_funkeys); + wppi (sesskey, "ApplicationCursorKeys", cfg.app_cursor); + wppi (sesskey, "ApplicationKeypad", cfg.app_keypad); + wppi (sesskey, "ScrollbackLines", cfg.savelines); + wppi (sesskey, "DECOriginMode", cfg.dec_om); + wppi (sesskey, "AutoWrapMode", cfg.wrap_mode); + wppi (sesskey, "WinNameAlways", cfg.win_name_always); + wppi (sesskey, "TermWidth", cfg.width); + wppi (sesskey, "TermHeight", cfg.height); + wpps (sesskey, "Font", cfg.font); + wppi (sesskey, "FontIsBold", cfg.fontisbold); + wppi (sesskey, "FontHeight", cfg.fontheight); + wppi (sesskey, "FontVTMode", cfg.vtmode); + wppi (sesskey, "TryPalette", cfg.try_palette); + wppi (sesskey, "BoldAsColour", cfg.bold_colour); + for (i=0; i<22; i++) { + char buf[20], buf2[30]; + sprintf(buf, "Colour%d", i); + sprintf(buf2, "%d,%d,%d", cfg.colours[i][0], + cfg.colours[i][1], cfg.colours[i][2]); + wpps (sesskey, buf, buf2); + } + wppi (sesskey, "MouseIsXterm", cfg.mouse_is_xterm); + for (i=0; i<256; i+=32) { + char buf[20], buf2[256]; + int j; + sprintf(buf, "Wordness%d", i); + *buf2 = '\0'; + for (j=i; j 0) { + if (!*p) + goto disaster; + while (*p) p++; + p++; + i--; + } + q = p; + if (!*p) + goto disaster; + while (*p) p++; + p++; + while (*p) { + while (*p) + *q++ = *p++; + *q++ = *p++; + } + *q = '\0'; + disaster:; + } + break; + } + break; + } + return GeneralPanelProc (hwnd, msg, wParam, lParam); +} + +static int CALLBACK SshProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: + SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype); + SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC3_TTEDIT: + if (HIWORD(wParam) == EN_CHANGE) + GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype, + sizeof(cfg.termtype)-1); + break; + case IDC3_LOGEDIT: + if (HIWORD(wParam) == EN_CHANGE) + GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username, + sizeof(cfg.username)-1); + break; + } + break; + } + return GeneralPanelProc (hwnd, msg, wParam, lParam); +} + +static int CALLBACK SelectionProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + int i; + + switch (msg) { + case WM_INITDIALOG: + CheckRadioButton (hwnd, IDC4_MBWINDOWS, IDC4_MBXTERM, + cfg.mouse_is_xterm ? IDC4_MBXTERM : IDC4_MBWINDOWS); + { + static int tabs[4] = {25, 61, 96, 128}; + SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_SETTABSTOPS, 4, + (LPARAM) tabs); + } + for (i=0; i<256; i++) { + char str[100]; + sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, + (i>=0x21 && i != 0x7F) ? i : ' ', + cfg.wordness[i]); + SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_ADDSTRING, 0, + (LPARAM) str); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC4_MBWINDOWS: + case IDC4_MBXTERM: + cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC4_MBXTERM); + break; + case IDC4_CCSET: + { + BOOL ok; + int i; + int n = GetDlgItemInt (hwnd, IDC4_CCEDIT, &ok, FALSE); + + if (!ok) + MessageBeep (0); + else { + for (i=0; i<256; i++) + if (SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_GETSEL, + i, 0)) { + char str[100]; + cfg.wordness[i] = n; + SendDlgItemMessage (hwnd, IDC4_CCLIST, + LB_DELETESTRING, i, 0); + sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, + (i>=0x21 && i != 0x7F) ? i : ' ', + cfg.wordness[i]); + SendDlgItemMessage (hwnd, IDC4_CCLIST, + LB_INSERTSTRING, i, + (LPARAM)str); + } + } + } + break; + } + break; + } + return GeneralPanelProc (hwnd, msg, wParam, lParam); +} + +static int CALLBACK ColourProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + static const char *const colours[] = { + "Default Foreground", "Default Bold Foreground", + "Default Background", "Default Bold Background", + "Cursor Text", "Cursor Colour", + "ANSI Black", "ANSI Black Bold", + "ANSI Red", "ANSI Red Bold", + "ANSI Green", "ANSI Green Bold", + "ANSI Yellow", "ANSI Yellow Bold", + "ANSI Blue", "ANSI Blue Bold", + "ANSI Magenta", "ANSI Magenta Bold", + "ANSI Cyan", "ANSI Cyan Bold", + "ANSI White", "ANSI White Bold" + }; + static const int permanent[] = { + TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, + TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, + TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE + }; + switch (msg) { + case WM_INITDIALOG: + CheckDlgButton (hwnd, IDC5_BOLDCOLOUR, cfg.bold_colour); + CheckDlgButton (hwnd, IDC5_PALETTE, cfg.try_palette); + { + int i; + for (i=0; i<22; i++) + if (cfg.bold_colour || permanent[i]) + SendDlgItemMessage (hwnd, IDC5_LIST, LB_ADDSTRING, 0, + (LPARAM) colours[i]); + } + SendDlgItemMessage (hwnd, IDC5_LIST, LB_SETCURSEL, 0, 0); + SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[0][0], FALSE); + SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[0][1], FALSE); + SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[0][2], FALSE); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC5_BOLDCOLOUR: + if (HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) { + int n, i; + cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC5_BOLDCOLOUR); + n = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCOUNT, 0, 0); + if (cfg.bold_colour && n!=22) { + for (i=0; i<22; i++) + if (!permanent[i]) + SendDlgItemMessage (hwnd, IDC5_LIST, + LB_INSERTSTRING, i, + (LPARAM) colours[i]); + } else if (!cfg.bold_colour && n!=12) { + for (i=22; i-- ;) + if (!permanent[i]) + SendDlgItemMessage (hwnd, IDC5_LIST, + LB_DELETESTRING, i, 0); + } + } + break; + case IDC5_PALETTE: + if (HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) + cfg.try_palette = IsDlgButtonChecked (hwnd, IDC5_PALETTE); + break; + case IDC5_LIST: + if (HIWORD(wParam) == LBN_DBLCLK || + HIWORD(wParam) == LBN_SELCHANGE) { + int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL, + 0, 0); + if (!cfg.bold_colour) + i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2); + SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], FALSE); + SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], FALSE); + SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], FALSE); + } + break; + case IDC5_CHANGE: + if (HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) { + static CHOOSECOLOR cc; + static DWORD custom[16] = {0}; /* zero initialisers */ + int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL, + 0, 0); + if (!cfg.bold_colour) + i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2); + cc.lStructSize = sizeof(cc); + cc.hwndOwner = hwnd; + cc.hInstance = hinst; + cc.lpCustColors = custom; + cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1], + cfg.colours[i][2]); + cc.Flags = CC_FULLOPEN | CC_RGBINIT; + if (ChooseColor(&cc)) { + cfg.colours[i][0] = + (unsigned char) (cc.rgbResult & 0xFF); + cfg.colours[i][1] = + (unsigned char) (cc.rgbResult >> 8) & 0xFF; + cfg.colours[i][2] = + (unsigned char) (cc.rgbResult >> 16) & 0xFF; + SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], + FALSE); + SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], + FALSE); + SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], + FALSE); + } + } + break; + } + break; + } + return GeneralPanelProc (hwnd, msg, wParam, lParam); +} + +static DTemplate negot, main, reconf, panels[NPANELS]; +static DLGPROC panelproc[NPANELS] = { + ConnectionProc, KeyboardProc, TerminalProc, + TelnetProc, SshProc, SelectionProc, ColourProc +}; +static char *panelids[NPANELS] = { + MAKEINTRESOURCE(IDD_PANEL0), + MAKEINTRESOURCE(IDD_PANEL1), + MAKEINTRESOURCE(IDD_PANEL2), + MAKEINTRESOURCE(IDD_PANEL3), + MAKEINTRESOURCE(IDD_PANEL35), + MAKEINTRESOURCE(IDD_PANEL4), + MAKEINTRESOURCE(IDD_PANEL5) +}; +static char *names[NPANELS] = { + "Connection", "Keyboard", "Terminal", "Telnet", "SSH", "Selection", "Colours" +}; + +static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6 }; +static int reconfp[RECONF_NPANELS] = { 1, 2, 5, 6 }; + +static int GenericMainDlgProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam, + int npanels, int *panelnums, HWND *page) { + HWND hw; + + switch (msg) { + case WM_INITDIALOG: + { /* centre the window */ + RECT rs, rd; + + hw = GetDesktopWindow(); + if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd)) + MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2, + (rs.bottom + rs.top + rd.top - rd.bottom)/2, + rd.right-rd.left, rd.bottom-rd.top, TRUE); + } + *page = NULL; + { /* initialise the tab control */ + TC_ITEMHEADER tab; + int i; + + hw = GetDlgItem (hwnd, IDC_TAB); + for (i=0; icode == TCN_SELCHANGE) { + int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); + if (*page) + DestroyWindow (*page); +/* *page = CreateDialogIndirect (hinst, panels[panelnums[i]].temp, + hwnd, panelproc[panelnums[i]]);*/ + *page = CreateDialog (hinst, panelids[panelnums[i]], + hwnd, panelproc[panelnums[i]]); + SetWindowLong (*page, GWL_EXSTYLE, + GetWindowLong (*page, GWL_EXSTYLE) | + WS_EX_CONTROLPARENT); + SetFocus (((LPNMHDR)lParam)->hwndFrom); /* ensure focus stays */ + return 0; + } + break; +/* case WM_CTLCOLORDLG: */ +/* return (int) GetStockObject (LTGRAY_BRUSH); */ + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (*cfg.host) + EndDialog (hwnd, 1); + else + MessageBeep (0); + return 0; + case IDCANCEL: + EndDialog (hwnd, 0); + return 0; + } + return 0; + case WM_CLOSE: + EndDialog (hwnd, 0); + return 0; + } + return 0; +} + +static int CALLBACK MainDlgProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { +#if 0 + HWND hw; + int i; +#endif + static HWND page = NULL; + + if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) { +#if 0 + /* + * If the Connection panel is active and the Session List + * box is selected, we treat a press of Open to have an + * implicit press of Load preceding it. + */ + hw = GetDlgItem (hwnd, IDC_TAB); + i = TabCtrl_GetCurSel(hw); + if (panelproc[mainp[i]] == ConnectionProc && + page && implicit_load_ok) { + SendMessage (page, WM_COMMAND, + MAKELONG(IDC0_SESSLOAD, BN_CLICKED), 0); + } +#endif + } + if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) { + EnableWindow(hwnd, 0); + DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), + GetParent(hwnd), AboutProc); + EnableWindow(hwnd, 1); + } + return GenericMainDlgProc (hwnd, msg, wParam, lParam, + MAIN_NPANELS, mainp, &page); +} + +static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + static HWND page; + return GenericMainDlgProc (hwnd, msg, wParam, lParam, + RECONF_NPANELS, reconfp, &page); +} + +static void get_sesslist(int allocate) { + static char *buffer; + int buflen, bufsize, i, ret; + char otherbuf[2048]; + char *p; + HKEY subkey1; + + if (allocate) { + if (RegCreateKey(HKEY_CURRENT_USER, + puttystr, &subkey1) != ERROR_SUCCESS) + return; + + buflen = bufsize = 0; + buffer = NULL; + i = 0; + do { + ret = RegEnumKey(subkey1, i++, otherbuf, sizeof(otherbuf)); + if (ret == ERROR_SUCCESS) { + bufsize = buflen + 2048; + buffer = srealloc(buffer, bufsize); + unmungestr(otherbuf, buffer+buflen); + buflen += strlen(buffer+buflen)+1; + } + } while (ret == ERROR_SUCCESS); + buffer = srealloc(buffer, buflen+1); + buffer[buflen] = '\0'; + + p = buffer; + nsessions = 1; /* "Default Settings" counts as one */ + while (*p) { + if (strcmp(p, "Default Settings")) + nsessions++; + while (*p) p++; + p++; + } + + sessions = smalloc(nsessions * sizeof(char *)); + sessions[0] = "Default Settings"; + p = buffer; + i = 1; + while (*p) { + if (strcmp(p, "Default Settings")) + sessions[i++] = p; + while (*p) p++; + p++; + } + } else { + sfree (buffer); + sfree (sessions); + } +} + +int do_config (void) { + int ret; + + get_sesslist(TRUE); + ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc); + get_sesslist(FALSE); + + return ret; +} + +int do_reconfig (HWND hwnd) { + Config backup_cfg; + int ret; + + backup_cfg = cfg; /* structure copy */ + ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc); + if (!ret) + cfg = backup_cfg; /* structure copy */ + return ret; +} + +void do_defaults (char *session) { + if (session) + load_settings (session, TRUE); + else + load_settings ("Default Settings", FALSE); +} + +void lognegot (char *string) { + if (nnegots >= negsize) { + negsize += 64; + negots = srealloc (negots, negsize * sizeof(*negots)); + } + negots[nnegots] = smalloc(1+strlen(string)); + strcpy (negots[nnegots], string); + nnegots++; + if (logbox) + SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING, + 0, (LPARAM)string); +} + +void shownegot (HWND hwnd) { + if (!logbox) { + logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX), + hwnd, LogProc); + ShowWindow (logbox, SW_SHOWNORMAL); + } +} + +void showabout (HWND hwnd) { + if (!abtbox) { + abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), + hwnd, AboutProc); + ShowWindow (abtbox, SW_SHOWNORMAL); + } +} + +void verify_ssh_host_key(char *host, struct RSAKey *key) { + char *keystr, *otherstr, *mungedhost; + int len; + HKEY rkey; + + /* + * Format the key into a string. + */ + len = rsastr_len(key); + keystr = malloc(len); + if (!keystr) + fatalbox("Out of memory"); + rsastr_fmt(keystr, key); + + /* + * Now read a saved key in from the registry and see what it + * says. + */ + otherstr = malloc(len); + mungedhost = malloc(3*strlen(host)+1); + if (!otherstr || !mungedhost) + fatalbox("Out of memory"); + + mungestr(host, mungedhost); + + if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", + &rkey) != ERROR_SUCCESS) { + if (MessageBox(NULL, "PuTTY was unable to open the host key cache\n" + "in the registry. There is thus no way to tell\n" + "if the remote host is what you think it is.\n" + "Connect anyway?", "PuTTY Problem", + MB_ICONWARNING | MB_YESNO) == IDNO) + exit(0); + } else { + DWORD readlen = len; + DWORD type; + int ret; + + ret = RegQueryValueEx(rkey, mungedhost, NULL, + &type, otherstr, &readlen); + + if (ret == ERROR_MORE_DATA || + (ret == ERROR_SUCCESS && type == REG_SZ && + strcmp(otherstr, keystr))) { + if (MessageBox(NULL, + "This host's host key is different from the\n" + "one cached in the registry! Someone may be\n" + "impersonating this host for malicious reasons;\n" + "alternatively, the host key may have changed\n" + "due to sloppy system administration.\n" + "Replace key in registry and connect?", + "PuTTY: Security Warning", + MB_ICONWARNING | MB_YESNO) == IDNO) + exit(0); + RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr, + strlen(keystr)+1); + } else if (ret != ERROR_SUCCESS || type != REG_SZ) { + if (MessageBox(NULL, + "This host's host key is not cached in the\n" + "registry. Do you want to add it to the cache\n" + "and carry on connecting?", + "PuTTY: New Host", + MB_ICONWARNING | MB_YESNO) == IDNO) + exit(0); + RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr, + strlen(keystr)+1); + } + + RegCloseKey(rkey); + } +} diff --git a/window.c b/window.c new file mode 100644 index 00000000..151441f8 --- /dev/null +++ b/window.c @@ -0,0 +1,1499 @@ +#include +#include +#include +#include +#include + +#define PUTTY_DO_GLOBALS /* actually _define_ globals */ +#include "putty.h" +#include "win_res.h" + +#define IDM_SHOWLOG 501 +#define IDM_NEWSESS 502 +#define IDM_DUPSESS 503 +#define IDM_RECONF 504 +#define IDM_CLRSB 505 +#define IDM_RESET 506 +#define IDM_TEL_AYT 507 +#define IDM_TEL_BRK 508 +#define IDM_TEL_SYNCH 509 +#define IDM_TEL_EC 510 +#define IDM_TEL_EL 511 +#define IDM_TEL_GA 512 +#define IDM_TEL_NOP 513 +#define IDM_TEL_ABORT 514 +#define IDM_TEL_AO 515 +#define IDM_TEL_IP 516 +#define IDM_TEL_SUSP 517 +#define IDM_TEL_EOR 518 +#define IDM_TEL_EOF 519 +#define IDM_ABOUT 520 + +#define WM_IGNORE_SIZE (WM_USER + 2) +#define WM_IGNORE_CLIP (WM_USER + 3) + +static int WINAPI WndProc (HWND, UINT, WPARAM, LPARAM); +static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output); +static void cfgtopalette(void); +static void init_palette(void); +static void init_fonts(void); + +static int extra_width, extra_height; + +#define FONT_NORMAL 0 +#define FONT_BOLD 1 +#define FONT_UNDERLINE 2 +#define FONT_BOLDUND 3 +#define FONT_OEM 4 +#define FONT_OEMBOLD 5 +#define FONT_OEMBOLDUND 6 +#define FONT_OEMUND 7 +static HFONT fonts[8]; +static enum { + BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT +} bold_mode; +static enum { + UND_LINE, UND_FONT +} und_mode; +static int descent; + +#define NCOLOURS 24 +static COLORREF colours[NCOLOURS]; +static HPALETTE pal; +static LPLOGPALETTE logpal; +static RGBTRIPLE defpal[NCOLOURS]; + +static HWND hwnd; + +static int dbltime, lasttime, lastact; +static Mouse_Button lastbtn; + +static char *window_name, *icon_name; + +int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { + static char appname[] = "PuTTY"; + WORD winsock_ver; + WSADATA wsadata; + WNDCLASS wndclass; + MSG msg; + int guess_width, guess_height; + + winsock_ver = MAKEWORD(1, 1); + if (WSAStartup(winsock_ver, &wsadata)) { + MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error", + MB_OK | MB_ICONEXCLAMATION); + return 1; + } + if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { + MessageBox(NULL, "WinSock version is incompatible with 1.1", + "WinSock Error", MB_OK | MB_ICONEXCLAMATION); + WSACleanup(); + return 1; + } + /* WISHLIST: maybe allow config tweaking even if winsock not present? */ + + InitCommonControls(); + + /* + * Process the command line. + */ + { + char *p; + + do_defaults(NULL); + + p = cmdline; + while (*p && isspace(*p)) p++; + + /* + * An initial @ means to activate a saved session. + */ + if (*p == '@') { + do_defaults (p+1); + if (!*cfg.host && !do_config()) { + WSACleanup(); + return 0; + } + } else if (*p == '&') { + /* + * An initial & means we've been given a command line + * containing the hex value of a HANDLE for a file + * mapping object, which we must then extract as a + * config. + */ + HANDLE filemap; + Config *cp; + if (sscanf(p+1, "%x", &filemap) == 1 && + (cp = MapViewOfFile(filemap, FILE_MAP_READ, + 0, 0, sizeof(Config))) != NULL) { + cfg = *cp; + UnmapViewOfFile(cp); + CloseHandle(filemap); + } else if (!do_config()) { + WSACleanup(); + return 0; + } + } else if (*p) { + char *q = p; + while (*p && !isspace(*p)) p++; + if (*p) + *p++ = '\0'; + strncpy (cfg.host, q, sizeof(cfg.host)-1); + cfg.host[sizeof(cfg.host)-1] = '\0'; + while (*p && isspace(*p)) p++; + if (*p) + cfg.port = atoi(p); + else + cfg.port = -1; + } else { + if (!do_config()) { + WSACleanup(); + return 0; + } + } + } + + back = (cfg.protocol == PROT_SSH ? &ssh_backend : &telnet_backend); + + if (!prev) { + wndclass.style = 0; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = inst; + wndclass.hIcon = LoadIcon (inst, + MAKEINTRESOURCE(IDI_MAINICON)); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = GetStockObject (BLACK_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = appname; + + RegisterClass (&wndclass); + } + + hwnd = NULL; + + savelines = cfg.savelines; + term_init(); + + cfgtopalette(); + + /* + * Guess some defaults for the window size. This all gets + * updated later, so we don't really care too much. However, we + * do want the font width/height guesses to correspond to a + * large font rather than a small one... + */ + + font_width = 10; + font_height = 20; + extra_width = 25; + extra_height = 28; + term_size (cfg.height, cfg.width, cfg.savelines); + guess_width = extra_width + font_width * cols; + guess_height = extra_height + font_height * rows; + { + RECT r; + HWND w = GetDesktopWindow(); + GetWindowRect (w, &r); + if (guess_width > r.right - r.left) + guess_width = r.right - r.left; + if (guess_height > r.bottom - r.top) + guess_height = r.bottom - r.top; + } + + hwnd = CreateWindow (appname, appname, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, + guess_width, guess_height, + NULL, NULL, inst, NULL); + + /* + * Initialise the fonts, simultaneously correcting the guesses + * for font_{width,height}. + */ + bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; + und_mode = UND_FONT; + init_fonts(); + + /* + * Correct the guesses for extra_{width,height}. + */ + { + RECT cr, wr; + GetWindowRect (hwnd, &wr); + GetClientRect (hwnd, &cr); + extra_width = wr.right - wr.left - cr.right + cr.left; + extra_height = wr.bottom - wr.top - cr.bottom + cr.top; + } + + /* + * Resize the window, now we know what size we _really_ want it + * to be. + */ + guess_width = extra_width + font_width * cols; + guess_height = extra_height + font_height * rows; + SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0); + SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height, + SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); + + /* + * Initialise the scroll bar. + */ + { + SCROLLINFO si; + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = rows-1; + si.nPage = rows; + si.nPos = 0; + SetScrollInfo (hwnd, SB_VERT, &si, FALSE); + } + + /* + * Start up the telnet connection. + */ + { + char *error; + char msg[1024]; + char *realhost; + + error = back->init (hwnd, cfg.host, cfg.port, &realhost); + if (error) { + sprintf(msg, "Unable to open connection:\n%s", error); + MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK); + return 0; + } + window_name = icon_name = NULL; + sprintf(msg, "PuTTY: %s", realhost); + set_title (msg); + set_icon (msg); + } + + /* + * Set up the input and output buffers. + */ + inbuf_reap = inbuf_head = 0; + outbuf_reap = outbuf_head = 0; + + /* + * Prepare the mouse handler. + */ + lastact = MA_NOTHING; + lastbtn = MB_NOTHING; + dbltime = GetDoubleClickTime(); + + /* + * Set up the session-control options on the system menu. + */ + { + HMENU m = GetSystemMenu (hwnd, FALSE); + HMENU p; + + AppendMenu (m, MF_SEPARATOR, 0, 0); + if (cfg.protocol == PROT_TELNET) { + p = CreateMenu(); + AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There"); + AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break"); + AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch"); + AppendMenu (p, MF_SEPARATOR, 0, 0); + AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character"); + AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line"); + AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead"); + AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation"); + AppendMenu (p, MF_SEPARATOR, 0, 0); + AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process"); + AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output"); + AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process"); + AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process"); + AppendMenu (p, MF_SEPARATOR, 0, 0); + AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record"); + AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File"); + AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command"); + AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation"); + AppendMenu (m, MF_SEPARATOR, 0, 0); + } + AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New Session"); + AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "Duplicate Session"); + AppendMenu (m, MF_ENABLED, IDM_RECONF, "Change Settings"); + AppendMenu (m, MF_SEPARATOR, 0, 0); + AppendMenu (m, MF_ENABLED, IDM_CLRSB, "Clear Scrollback"); + AppendMenu (m, MF_ENABLED, IDM_RESET, "Reset Terminal"); + AppendMenu (m, MF_SEPARATOR, 0, 0); + AppendMenu (m, MF_ENABLED, IDM_ABOUT, "About PuTTY"); + } + + /* + * Finally show the window! + */ + ShowWindow (hwnd, show); + + /* + * Set the palette up. + */ + pal = NULL; + logpal = NULL; + init_palette(); + + has_focus = (GetForegroundWindow() == hwnd); + UpdateWindow (hwnd); + + while (GetMessage (&msg, NULL, 0, 0)) { + DispatchMessage (&msg); + if (inbuf_reap != inbuf_head) + term_out(); + /* In idle moments, do a full screen update */ + if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + term_update(); + } + + /* + * Clean up. + */ + { + int i; + for (i=0; i<8; i++) + if (fonts[i]) + DeleteObject(fonts[i]); + } + sfree(logpal); + if (pal) + DeleteObject(pal); + WSACleanup(); + + if (cfg.protocol == PROT_SSH) + random_save_seed(); + + return msg.wParam; +} + +/* + * Copy the colour palette from the configuration data into defpal. + * This is non-trivial because the colour indices are different. + */ +static void cfgtopalette(void) { + int i; + static const int ww[] = { + 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, + 0, 1, 2, 3, 4, 4, 5, 5 + }; + + for (i=0; i<24; i++) { + int w = ww[i]; + defpal[i].rgbtRed = cfg.colours[w][0]; + defpal[i].rgbtGreen = cfg.colours[w][1]; + defpal[i].rgbtBlue = cfg.colours[w][2]; + } +} + +/* + * Set up the colour palette. + */ +static void init_palette(void) { + int i; + HDC hdc = GetDC (hwnd); + if (hdc) { + if (cfg.try_palette && + GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { + logpal = smalloc(sizeof(*logpal) + - sizeof(logpal->palPalEntry) + + NCOLOURS * sizeof(PALETTEENTRY)); + logpal->palVersion = 0x300; + logpal->palNumEntries = NCOLOURS; + for (i = 0; i < NCOLOURS; i++) { + logpal->palPalEntry[i].peRed = defpal[i].rgbtRed; + logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen; + logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue; + logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; + } + pal = CreatePalette (logpal); + if (pal) { + SelectPalette (hdc, pal, FALSE); + RealizePalette (hdc); + SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE), + FALSE); + } + } + ReleaseDC (hwnd, hdc); + } + if (pal) + for (i=0; i= font_height) + descent = font_height - 1; + } + widths[i] = tm.tmAveCharWidth; + } + } + + ReleaseDC (hwnd, hdc); + + if (widths[FONT_UNDERLINE] != widths[FONT_NORMAL] || + (bold_mode == BOLD_FONT && + widths[FONT_BOLDUND] != widths[FONT_BOLD])) { + und_mode = UND_LINE; + DeleteObject (fonts[FONT_UNDERLINE]); + if (bold_mode == BOLD_FONT) + DeleteObject (fonts[FONT_BOLDUND]); + } + + if (bold_mode == BOLD_FONT && + widths[FONT_BOLD] != widths[FONT_NORMAL]) { + bold_mode = BOLD_SHADOW; + DeleteObject (fonts[FONT_BOLD]); + if (und_mode == UND_FONT) + DeleteObject (fonts[FONT_BOLDUND]); + } + + if (cfg.vtmode == VT_OEMANSI && widths[FONT_OEM] != widths[FONT_NORMAL]) { + MessageBox(NULL, "The OEM and ANSI versions of this font are\n" + "different sizes. Using OEM-only mode instead", + "Font Size Mismatch", MB_ICONINFORMATION | MB_OK); + cfg.vtmode = VT_OEMONLY; + for (i=0; i<4; i++) + if (fonts[i]) + DeleteObject (fonts[i]); + } +} + +void request_resize (int w, int h) { + int width = extra_width + font_width * w; + int height = extra_height + font_height * h; + + SetWindowPos (hwnd, NULL, 0, 0, width, height, + SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOZORDER); +} + +static void click (Mouse_Button b, int x, int y) { + if (lastbtn == b && GetMessageTime() - lasttime < dbltime) { + lastact = (lastact == MA_CLICK ? MA_2CLK : + lastact == MA_2CLK ? MA_3CLK : + lastact == MA_3CLK ? MA_CLICK : MA_NOTHING); + } else { + lastbtn = b; + lastact = MA_CLICK; + } + if (lastact != MA_NOTHING) + term_mouse (b, lastact, x, y); + lasttime = GetMessageTime(); +} + +static int WINAPI WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) { + HDC hdc; + static int ignore_size = FALSE; + static int ignore_clip = FALSE; + static int just_reconfigged = FALSE; + + switch (message) { + case WM_CREATE: + break; + case WM_DESTROY: + PostQuitMessage (0); + return 0; + case WM_SYSCOMMAND: + switch (wParam) { + case IDM_SHOWLOG: + shownegot(hwnd); + break; + case IDM_NEWSESS: + case IDM_DUPSESS: + { + char b[2048]; + char c[30], *cl; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HANDLE filemap = NULL; + + if (wParam == IDM_DUPSESS) { + /* + * Allocate a file-mapping memory chunk for the + * config structure. + */ + SECURITY_ATTRIBUTES sa; + Config *p; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + filemap = CreateFileMapping((HANDLE)0xFFFFFFFF, + &sa, + PAGE_READWRITE, + 0, + sizeof(Config), + NULL); + if (filemap) { + p = (Config *)MapViewOfFile(filemap, + FILE_MAP_WRITE, + 0, 0, sizeof(Config)); + if (p) { + *p = cfg; /* structure copy */ + UnmapViewOfFile(p); + } + } + sprintf(c, "putty &%08x", filemap); + cl = c; + } else + cl = NULL; + + GetModuleFileName (NULL, b, sizeof(b)-1); + si.cb = sizeof(si); + si.lpReserved = NULL; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.dwFlags = 0; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + CreateProcess (b, cl, NULL, NULL, TRUE, + NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + + if (filemap) + CloseHandle(filemap); + } + break; + case IDM_RECONF: + if (!do_reconfig(hwnd)) + break; + just_reconfigged = TRUE; + { + int i; + for (i=0; i<8; i++) + if (fonts[i]) + DeleteObject(fonts[i]); + } + bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; + und_mode = UND_FONT; + init_fonts(); + sfree(logpal); + if (pal) + DeleteObject(pal); + logpal = NULL; + pal = NULL; + cfgtopalette(); + init_palette(); + term_size(cfg.height, cfg.width, cfg.savelines); + InvalidateRect(hwnd, NULL, TRUE); + SetWindowPos (hwnd, NULL, 0, 0, + extra_width + font_width * cfg.width, + extra_height + font_height * cfg.height, + SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOZORDER); + if (IsIconic(hwnd)) { + SetWindowText (hwnd, + cfg.win_name_always ? window_name : icon_name); + } + break; + case IDM_CLRSB: + term_clrsb(); + break; + case IDM_RESET: + term_pwron(); + break; + case IDM_TEL_AYT: back->special (TS_AYT); break; + case IDM_TEL_BRK: back->special (TS_BRK); break; + case IDM_TEL_SYNCH: back->special (TS_SYNCH); break; + case IDM_TEL_EC: back->special (TS_EC); break; + case IDM_TEL_EL: back->special (TS_EL); break; + case IDM_TEL_GA: back->special (TS_GA); break; + case IDM_TEL_NOP: back->special (TS_NOP); break; + case IDM_TEL_ABORT: back->special (TS_ABORT); break; + case IDM_TEL_AO: back->special (TS_AO); break; + case IDM_TEL_IP: back->special (TS_IP); break; + case IDM_TEL_SUSP: back->special (TS_SUSP); break; + case IDM_TEL_EOR: back->special (TS_EOR); break; + case IDM_TEL_EOF: back->special (TS_EOF); break; + case IDM_ABOUT: + showabout (hwnd); + break; + } + break; + case WM_LBUTTONDOWN: + click (MB_SELECT, LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_LBUTTONUP: + term_mouse (MB_SELECT, MA_RELEASE, LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_MBUTTONDOWN: + click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND, + LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_MBUTTONUP: + term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND, + MA_RELEASE, LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_RBUTTONDOWN: + click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE, + LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_RBUTTONUP: + term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE, + MA_RELEASE, LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + return 0; + case WM_MOUSEMOVE: + /* + * Add the mouse position and message time to the random + * number noise, if we're using ssh. + */ + if (cfg.protocol == PROT_SSH) + noise_ultralight(lParam); + + if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) { + Mouse_Button b; + if (wParam & MK_LBUTTON) + b = MB_SELECT; + else if (wParam & MK_MBUTTON) + b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND; + else + b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE; + term_mouse (b, MA_DRAG, LOWORD(lParam) / font_width, + HIWORD(lParam) / font_height); + } + lastbtn = MB_NOTHING; + lastact = MA_NOTHING; + lasttime = GetMessageTime(); + return 0; + case WM_IGNORE_CLIP: + ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */ + break; + case WM_DESTROYCLIPBOARD: + if (!ignore_clip) + term_deselect(); + ignore_clip = FALSE; + return 0; + case WM_PAINT: + { + PAINTSTRUCT p; + hdc = BeginPaint (hwnd, &p); + if (pal) { + SelectPalette (hdc, pal, TRUE); + RealizePalette (hdc); + } + term_paint (hdc, p.rcPaint.left, p.rcPaint.top, + p.rcPaint.right, p.rcPaint.bottom); + SelectObject (hdc, GetStockObject(SYSTEM_FONT)); + SelectObject (hdc, GetStockObject(WHITE_PEN)); + EndPaint (hwnd, &p); + } + return 0; + case WM_NETEVENT: + { + int i = back->msg (wParam, lParam); + if (i < 0) { + char buf[1024]; + switch (WSABASEERR + (-i) % 10000) { + case WSAECONNRESET: + sprintf(buf, "Connection reset by peer"); + break; + default: + sprintf(buf, "Unexpected network error %d", -i); + break; + } + MessageBox(hwnd, buf, "PuTTY Fatal Error", + MB_ICONERROR | MB_OK); + PostQuitMessage(1); + } else if (i == 0) { + if (cfg.close_on_exit) + PostQuitMessage(0); + else { + MessageBox(hwnd, "Connection closed by remote host", + "PuTTY", MB_OK | MB_ICONINFORMATION); + SetWindowText (hwnd, "PuTTY (inactive)"); + } + } + } + return 0; + case WM_SETFOCUS: + has_focus = TRUE; + term_out(); + term_update(); + break; + case WM_KILLFOCUS: + has_focus = FALSE; + term_out(); + term_update(); + break; + case WM_IGNORE_SIZE: + ignore_size = TRUE; /* don't panic on next WM_SIZE msg */ + break; + case WM_SIZING: + { + int width, height, w, h, ew, eh; + LPRECT r = (LPRECT)lParam; + + width = r->right - r->left - extra_width; + height = r->bottom - r->top - extra_height; + w = (width + font_width/2) / font_width; if (w < 1) w = 1; + h = (height + font_height/2) / font_height; if (h < 1) h = 1; + ew = width - w * font_width; + eh = height - h * font_height; + if (ew != 0) { + if (wParam == WMSZ_LEFT || + wParam == WMSZ_BOTTOMLEFT || + wParam == WMSZ_TOPLEFT) + r->left += ew; + else + r->right -= ew; + } + if (eh != 0) { + if (wParam == WMSZ_TOP || + wParam == WMSZ_TOPRIGHT || + wParam == WMSZ_TOPLEFT) + r->top += eh; + else + r->bottom -= eh; + } + if (ew || eh) + return 1; + else + return 0; + } + break; + case WM_SIZE: + if (wParam == SIZE_MINIMIZED) { + SetWindowText (hwnd, + cfg.win_name_always ? window_name : icon_name); + break; + } + if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) + SetWindowText (hwnd, window_name); + if (!ignore_size) { + int width, height, w, h, ew, eh; + + width = LOWORD(lParam); + height = HIWORD(lParam); + w = width / font_width; if (w < 1) w = 1; + h = height / font_height; if (h < 1) h = 1; +#if 0 /* we have fixed this using WM_SIZING now */ + ew = width - w * font_width; + eh = height - h * font_height; + if (ew != 0 || eh != 0) { + RECT r; + GetWindowRect (hwnd, &r); + SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0); + SetWindowPos (hwnd, NULL, 0, 0, + r.right - r.left - ew, r.bottom - r.top - eh, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); + } +#endif + if (w != cols || h != rows || just_reconfigged) { + term_invalidate(); + term_size (h, w, cfg.savelines); + back->size(); + just_reconfigged = FALSE; + } + } + ignore_size = FALSE; + return 0; + case WM_VSCROLL: + switch (LOWORD(wParam)) { + case SB_BOTTOM: term_scroll(-1, 0); break; + case SB_TOP: term_scroll(+1, 0); break; + case SB_LINEDOWN: term_scroll (0, +1); break; + case SB_LINEUP: term_scroll (0, -1); break; + case SB_PAGEDOWN: term_scroll (0, +rows/2); break; + case SB_PAGEUP: term_scroll (0, -rows/2); break; + case SB_THUMBPOSITION: case SB_THUMBTRACK: + term_scroll (1, HIWORD(wParam)); break; + } + break; + case WM_PALETTECHANGED: + if ((HWND) wParam != hwnd && pal != NULL) { + HDC hdc = get_ctx(); + if (hdc) { + if (RealizePalette (hdc) > 0) + UpdateColors (hdc); + free_ctx (hdc); + } + } + break; + case WM_QUERYNEWPALETTE: + if (pal != NULL) { + HDC hdc = get_ctx(); + if (hdc) { + if (RealizePalette (hdc) > 0) + UpdateColors (hdc); + free_ctx (hdc); + return TRUE; + } + } + return FALSE; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + /* + * Add the scan code and keypress timing to the random + * number noise, if we're using ssh. + */ + if (cfg.protocol == PROT_SSH) + noise_ultralight(lParam); + + /* + * We don't do TranslateMessage since it disassociates the + * resulting CHAR message from the KEYDOWN that sparked it, + * which we occasionally don't want. Instead, we process + * KEYDOWN, and call the Win32 translator functions so that + * we get the translations under _our_ control. + */ + { + unsigned char buf[20]; + int len; + + len = TranslateKey (wParam, lParam, buf); + back->send (buf, len); + } + return 0; + case WM_CHAR: + case WM_SYSCHAR: + /* + * Nevertheless, we are prepared to deal with WM_CHAR + * messages, should they crop up. So if someone wants to + * post the things to us as part of a macro manoeuvre, + * we're ready to cope. + */ + { + char c = wParam; + back->send (&c, 1); + } + return 0; + } + + return DefWindowProc (hwnd, message, wParam, lParam); +} + +/* + * Draw a line of text in the window, at given character + * coordinates, in given attributes. + * + * We are allowed to fiddle with the contents of `text'. + */ +void do_text (Context ctx, int x, int y, char *text, int len, + unsigned long attr) { + COLORREF fg, bg, t; + int nfg, nbg, nfont; + HDC hdc = ctx; + + x *= font_width; + y *= font_height; + + if (attr & ATTR_ACTCURS) { + attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300); + attr ^= ATTR_CUR_XOR; + } + + nfont = 0; + if (cfg.vtmode == VT_OEMONLY) + nfont |= FONT_OEM; + + /* + * Map high-half characters in order to approximate ISO using + * OEM character set. Characters missing are 0xC3 (Atilde) and + * 0xCC (Igrave). + */ + if (nfont & FONT_OEM) { + int i; + for (i=0; i= '\xA0' && text[i] <= '\xFF') { + static const char oemhighhalf[] = + "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */ + "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */ + "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */ + "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */ + "\xB7\xB5\xB6\x41\x8E\x8F\x92\x80" /* C0-C7 */ + "\xD4\x90\xD2\xD3\x49\xD6\xD7\xD8" /* C8-CF */ + "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */ + "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */ + "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */ + "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */ + "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */ + "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */ + ; + text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0]; + } + } + + if (attr & ATTR_GBCHR) { + int i; + /* + * GB mapping: map # to pound, and everything else stays + * normal. + */ + for (i=0; i= '\x60' && text[i] <= '\x7E') + text[i] += '\x01' - '\x60'; + break; + case VT_OEMANSI: + case VT_OEMONLY: + nfont |= FONT_OEM; + for (i=0; i= '\x60' && text[i] <= '\x7E') + text[i] = oemmap[(unsigned char)text[i] - 0x60]; + break; + case VT_POORMAN: + for (i=0; i= '\x60' && text[i] <= '\x7E') + text[i] = poorman[(unsigned char)text[i] - 0x60]; + break; + } + } + + nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); + nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); + if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD)) + nfont |= FONT_BOLD; + if (und_mode == UND_FONT && (attr & ATTR_UNDER)) + nfont |= FONT_UNDERLINE; + if (attr & ATTR_REVERSE) { + t = nfg; nfg = nbg; nbg = t; + } + if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) + nfg++; + fg = colours[nfg]; + bg = colours[nbg]; + SelectObject (hdc, fonts[nfont]); + SetTextColor (hdc, fg); + SetBkColor (hdc, bg); + SetBkMode (hdc, OPAQUE); + TextOut (hdc, x, y, text, len); + if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + SetBkMode (hdc, TRANSPARENT); + TextOut (hdc, x-1, y, text, len); + } + if (und_mode == UND_LINE && (attr & ATTR_UNDER)) { + SelectObject (hdc, CreatePen(PS_SOLID, 0, fg)); + MoveToEx (hdc, x, y+descent, NULL); + LineTo (hdc, x+len*font_width, y+descent); + } + if (attr & ATTR_PASCURS) { + POINT pts[5]; + pts[0].x = pts[1].x = pts[4].x = x; + pts[2].x = pts[3].x = x+font_width-1; + pts[0].y = pts[3].y = pts[4].y = y; + pts[1].y = pts[2].y = y+font_height-1; + SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23])); + Polyline (hdc, pts, 5); + } +} + +/* + * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII + * codes. Returns number of bytes used. + */ +static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { + unsigned char *p = output; + BYTE keystate[256]; + int ret, code; + + /* + * Prepend ESC if ALT was pressed at the time. + */ + if (lParam & 0x20000000) + *p++ = 0x1B; + + /* + * Get hold of the keyboard state, because we'll need it a few + * times shortly. + */ + ret = GetKeyboardState(keystate); + + /* + * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window + * events: we'll deal with those now. + */ + if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) { + SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0); + return 0; + } + if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) { + SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); + return 0; + } + if ((lParam & 0x20000000) && wParam == VK_F4) { + SendMessage (hwnd, WM_DESTROY, 0, 0); + return 0; + } + + /* + * In general, the strategy is to see what the Windows keymap + * translation has to say for itself, and then process function + * keys and suchlike ourselves if that fails. But first we must + * deal with the small number of special cases which the + * Windows keymap translator thinks it can do but gets wrong. + * + * First special case: we might want the Backspace key to send + * 0x7F not 0x08. + */ + if (wParam == VK_BACK) { + *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); + return p - output; + } + + /* + * Control-Space should send ^@ (0x00), not Space. + */ + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) { + *p++ = 0x00; + return p - output; + } + + /* + * If we're in applications keypad mode, we have to process it + * before char-map translation, because it will pre-empt lots + * of stuff, even if NumLock is off. + */ + if (app_keypad_keys) { + if (ret) { + /* + * Hack to ensure NumLock doesn't interfere with + * perception of Shift for Keypad Plus. I don't pretend + * to understand this, but it seems to work as is. + * Leave it alone, or die. + */ + keystate[VK_NUMLOCK] = 0; + SetKeyboardState (keystate); + GetKeyboardState (keystate); + } + switch ( (lParam >> 16) & 0x1FF ) { + case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output; + case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output; + case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output; + case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output; + case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output; + case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output; + case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output; + case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output; + case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output; + case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output; + case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */ + p += sprintf((char *)p, + (ret && (keystate[VK_SHIFT] & 0x80)) ? + "\x1BOm" : "\x1BOl"); + return p - output; + case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output; + case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output; + case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output; + case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output; + case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output; + case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output; + } + } + + /* + * Before doing Windows charmap translation, remove ALT from + * the keymap, since its sole effect should be to prepend ESC, + * which we've already done. Note that removal of ALT has to + * happen _after_ the above call to SetKeyboardState, or dire + * things will befall. + */ + keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0; + + /* + * Attempt the Windows char-map translation. + */ + if (ret) { + WORD chr; + int r = ToAscii (wParam, (lParam >> 16) & 0xFF, + keystate, &chr, 0); + if (r == 1) { + *p++ = chr & 0xFF; + return p - output; + } + } + + /* + * OK, we haven't had a key code from the keymap translation. + * We'll try our various special cases and function keys, and + * then give up. (There's nothing wrong with giving up: + * Scrollock, Pause/Break, and of course the various buckybit + * keys all produce KEYDOWN events that we really _do_ want to + * ignore.) + */ + + /* + * Control-2 should return ^@ (0x00), Control-6 should return + * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since + * the DOS keyboard handling did it, and we have nothing better + * to do with the key combo in question, we'll also map + * Control-Backquote to ^\ (0x1C). + */ + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') { + *p++ = 0x00; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') { + *p++ = 0x1E; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) { + *p++ = 0x1F; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) { + *p++ = 0x1C; + return p - output; + } + + /* + * First, all the keys that do tilde codes. (ESC '[' nn '~', + * for integer decimal nn.) + * + * We also deal with the weird ones here. Linux VCs replace F1 + * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but + * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w + * respectively. + */ + code = 0; + switch (wParam) { + case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break; + case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break; + case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break; + case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break; + case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break; + case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break; + case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break; + case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break; + case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break; + case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break; + case VK_F11: code = 23; break; + case VK_F12: code = 24; break; + case VK_HOME: code = 1; break; + case VK_INSERT: code = 2; break; + case VK_DELETE: code = 3; break; + case VK_END: code = 4; break; + case VK_PRIOR: code = 5; break; + case VK_NEXT: code = 6; break; + } + if (cfg.linux_funkeys && code >= 11 && code <= 15) { + p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11); + return p - output; + } + if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw"); + return p - output; + } + if (code) { + p += sprintf((char *)p, "\x1B[%d~", code); + return p - output; + } + + /* + * Now the remaining keys (arrows and Keypad 5. Keypad 5 for + * some reason seems to send VK_CLEAR to Windows...). + */ + switch (wParam) { + case VK_UP: + p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A"); + return p - output; + case VK_DOWN: + p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B"); + return p - output; + case VK_RIGHT: + p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C"); + return p - output; + case VK_LEFT: + p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D"); + return p - output; + case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output; + } + + return 0; +} + +void set_title (char *title) { + sfree (window_name); + window_name = smalloc(1+strlen(title)); + strcpy (window_name, title); + if (!IsIconic(hwnd)) + SetWindowText (hwnd, title); +} + +void set_icon (char *title) { + sfree (icon_name); + icon_name = smalloc(1+strlen(title)); + strcpy (icon_name, title); + if (IsIconic(hwnd)) + SetWindowText (hwnd, title); +} + +void set_sbar (int total, int start, int page) { + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = total - 1; + si.nPage = page; + si.nPos = start; + SetScrollInfo (hwnd, SB_VERT, &si, TRUE); +} + +Context get_ctx() { + HDC hdc; + if (hwnd) { + hdc = GetDC (hwnd); + if (hdc && pal) + SelectPalette (hdc, pal, FALSE); + return hdc; + } else + return NULL; +} + +void free_ctx (Context ctx) { + SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE); + ReleaseDC (hwnd, ctx); +} + +static void real_palette_set (int n, int r, int g, int b) { + if (pal) { + logpal->palPalEntry[n].peRed = r; + logpal->palPalEntry[n].peGreen = g; + logpal->palPalEntry[n].peBlue = b; + logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE; + colours[n] = PALETTERGB(r, g, b); + SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry); + } else + colours[n] = RGB(r, g, b); +} + +void palette_set (int n, int r, int g, int b) { + static const int first[21] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 20, 22 + }; + real_palette_set (first[n], r, g, b); + if (first[n] >= 18) + real_palette_set (first[n]+1, r, g, b); + if (pal) { + HDC hdc = get_ctx(); + UnrealizeObject (pal); + RealizePalette (hdc); + free_ctx (hdc); + } +} + +void palette_reset (void) { + int i; + + for (i = 0; i < NCOLOURS; i++) { + if (pal) { + logpal->palPalEntry[i].peRed = defpal[i].rgbtRed; + logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen; + logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue; + logpal->palPalEntry[i].peFlags = 0; + colours[i] = PALETTERGB(defpal[i].rgbtRed, + defpal[i].rgbtGreen, + defpal[i].rgbtBlue); + } else + colours[i] = RGB(defpal[i].rgbtRed, + defpal[i].rgbtGreen, + defpal[i].rgbtBlue); + } + + if (pal) { + HDC hdc; + SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry); + hdc = get_ctx(); + RealizePalette (hdc); + free_ctx (hdc); + } +} + +void write_clip (void *data, int len) { + HGLOBAL clipdata; + void *lock; + + clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1); + if (!clipdata) + return; + lock = GlobalLock (clipdata); + if (!lock) + return; + memcpy (lock, data, len); + ((unsigned char *) lock) [len] = 0; + GlobalUnlock (clipdata); + + SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0); + if (OpenClipboard (hwnd)) { + EmptyClipboard(); + SetClipboardData (CF_TEXT, clipdata); + CloseClipboard(); + } else + GlobalFree (clipdata); + SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0); +} + +void get_clip (void **p, int *len) { + static HGLOBAL clipdata = NULL; + + if (!p) { + if (clipdata) + GlobalUnlock (clipdata); + clipdata = NULL; + return; + } else { + if (OpenClipboard (NULL)) { + clipdata = GetClipboardData (CF_TEXT); + CloseClipboard(); + if (clipdata) { + *p = GlobalLock (clipdata); + if (*p) { + *len = strlen(*p); + return; + } + } + } + } + + *p = NULL; + *len = 0; +} + +/* + * Move `lines' lines from position `from' to position `to' in the + * window. + */ +void optimised_move (int to, int from, int lines) { + RECT r; + int min, max, d; + + min = (to < from ? to : from); + max = to + from - min; + d = max - min; + + r.left = 0; r.right = cols * font_width; + r.top = min * font_height; r.bottom = (max+lines) * font_height; + ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r); +} + +/* + * Print a message box and perform a fatal exit. + */ +void fatalbox(char *fmt, ...) { + va_list ap; + char stuff[200]; + + va_start(ap, fmt); + vsprintf(stuff, fmt, ap); + va_end(ap); + MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK); + exit(1); +} + +/* + * Beep. + */ +void beep(void) { + MessageBeep(MB_OK); +} -- 2.11.0