From: simon Date: Fri, 8 Jan 1999 13:02:13 +0000 (+0000) Subject: Initial checkin: beta 0.43 X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/commitdiff_plain/374330e25a6b51c40436fa869a381dd510790f6e Initial checkin: beta 0.43 git-svn-id: svn://svn.tartarus.org/sgt/putty@11 cda61777-01e9-0310-a592-d414129be87e --- 374330e25a6b51c40436fa869a381dd510790f6e 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 00000000..c0c7cfc6 Binary files /dev/null and b/putty.ico differ 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); +}