Let's spell '\t' as VK_TAB in that last patch, in fact, just in case
[u/mdw/putty] / ssh.c
CommitLineData
374330e2 1#include <stdio.h>
2#include <stdlib.h>
3#include <winsock.h>
4
5#include "putty.h"
6
7#ifndef FALSE
8#define FALSE 0
9#endif
10#ifndef TRUE
11#define TRUE 1
12#endif
13
14#include "ssh.h"
15
972a41c8 16#define SSH_MSG_DISCONNECT 1
17#define SSH_SMSG_PUBLIC_KEY 2
18#define SSH_CMSG_SESSION_KEY 3
19#define SSH_CMSG_USER 4
20#define SSH_CMSG_AUTH_PASSWORD 9
21#define SSH_CMSG_REQUEST_PTY 10
22#define SSH_CMSG_EXEC_SHELL 12
23#define SSH_CMSG_STDIN_DATA 16
24#define SSH_SMSG_STDOUT_DATA 17
25#define SSH_SMSG_STDERR_DATA 18
26#define SSH_SMSG_SUCCESS 14
27#define SSH_SMSG_FAILURE 15
28#define SSH_SMSG_EXITSTATUS 20
29#define SSH_MSG_IGNORE 32
30#define SSH_CMSG_EXIT_CONFIRMATION 33
31#define SSH_MSG_DEBUG 36
ccbfb941 32#define SSH_CMSG_AUTH_TIS 39
33#define SSH_SMSG_AUTH_TIS_CHALLENGE 40
34#define SSH_CMSG_AUTH_TIS_RESPONSE 41
35
36#define SSH_AUTH_TIS 5
972a41c8 37
374330e2 38/* Coroutine mechanics for the sillier bits of the code */
39#define crBegin1 static int crLine = 0;
40#define crBegin2 switch(crLine) { case 0:;
41#define crBegin crBegin1; crBegin2;
42#define crFinish(z) } crLine = 0; return (z)
43#define crFinishV } crLine = 0; return
44#define crReturn(z) \
45 do {\
46 crLine=__LINE__; return (z); case __LINE__:;\
47 } while (0)
48#define crReturnV \
49 do {\
50 crLine=__LINE__; return; case __LINE__:;\
51 } while (0)
52#define crStop(z) do{ crLine = 0; return (z); }while(0)
53#define crStopV do{ crLine = 0; return; }while(0)
54
55#ifndef FALSE
56#define FALSE 0
57#endif
58#ifndef TRUE
59#define TRUE 1
60#endif
61
62static SOCKET s = INVALID_SOCKET;
63
64static unsigned char session_key[32];
65static struct ssh_cipher *cipher = NULL;
66
67static char *savedhost;
68
69static enum {
70 SSH_STATE_BEFORE_SIZE,
71 SSH_STATE_INTERMED,
21248260 72 SSH_STATE_SESSION,
73 SSH_STATE_CLOSED
374330e2 74} ssh_state = SSH_STATE_BEFORE_SIZE;
75
76static int size_needed = FALSE;
77
78static void s_write (char *buf, int len) {
79 while (len > 0) {
80 int i = send (s, buf, len, 0);
81 if (i > 0)
82 len -= i, buf += i;
83 }
84}
85
86static int s_read (char *buf, int len) {
87 int ret = 0;
88 while (len > 0) {
89 int i = recv (s, buf, len, 0);
90 if (i > 0)
91 len -= i, buf += i, ret += i;
92 else
93 return i;
94 }
95 return ret;
96}
97
98static void c_write (char *buf, int len) {
99 while (len--) {
100 int new_head = (inbuf_head + 1) & INBUF_MASK;
374330e2 101 if (new_head != inbuf_reap) {
102 inbuf[inbuf_head] = *buf++;
103 inbuf_head = new_head;
cabfd08c 104 } else {
105 term_out();
106 if( inbuf_head == inbuf_reap ) len++; else break;
374330e2 107 }
108 }
109}
110
111struct Packet {
112 long length;
113 int type;
114 unsigned long crc;
115 unsigned char *data;
116 unsigned char *body;
117 long maxlen;
118};
119
120static struct Packet pktin = { 0, 0, 0, NULL, 0 };
121static struct Packet pktout = { 0, 0, 0, NULL, 0 };
122
123static void ssh_protocol(unsigned char *in, int inlen, int ispkt);
124static void ssh_size(void);
125
126static void ssh_gotdata(unsigned char *data, int datalen) {
127 static long len, biglen, to_read;
f67b4e85 128 static unsigned char *p;
374330e2 129 static int i, pad;
374330e2 130
131 crBegin;
132 while (1) {
133 for (i = len = 0; i < 4; i++) {
134 while (datalen == 0)
135 crReturnV;
136 len = (len << 8) + *data;
137 data++, datalen--;
138 }
139
37508af4 140#ifdef FWHACK
141 if (len == 0x52656d6f) { /* "Remo"te server has closed ... */
142 len = 0x300; /* big enough to carry to end */
143 }
144#endif
145
374330e2 146 pad = 8 - (len%8);
147
148 biglen = len + pad;
149
150 len -= 5; /* type and CRC */
151
152 pktin.length = len;
153 if (pktin.maxlen < biglen) {
154 pktin.maxlen = biglen;
155 pktin.data = (pktin.data == NULL ? malloc(biglen) :
156 realloc(pktin.data, biglen));
157 if (!pktin.data)
158 fatalbox("Out of memory");
159 }
160
161 p = pktin.data, to_read = biglen;
162 while (to_read > 0) {
163 static int chunk;
164 chunk = to_read;
165 while (datalen == 0)
166 crReturnV;
167 if (chunk > datalen)
168 chunk = datalen;
169 memcpy(p, data, chunk);
170 data += chunk;
171 datalen -= chunk;
172 p += chunk;
173 to_read -= chunk;
174 }
175
176 if (cipher)
177 cipher->decrypt(pktin.data, biglen);
178
179 pktin.type = pktin.data[pad];
180 pktin.body = pktin.data+pad+1;
181
972a41c8 182 if (pktin.type == SSH_MSG_DEBUG) {
374330e2 183 /* FIXME: log it */
972a41c8 184 } else if (pktin.type == SSH_MSG_IGNORE) {
96b9dc0a 185 /* do nothing */;
374330e2 186 } else
187 ssh_protocol(NULL, 0, 1);
188 }
189 crFinishV;
190}
191
192static void s_wrpkt_start(int type, int len) {
193 int pad, biglen;
194
195 len += 5; /* type and CRC */
196 pad = 8 - (len%8);
197 biglen = len + pad;
198
199 pktout.length = len-5;
200 if (pktout.maxlen < biglen) {
201 pktout.maxlen = biglen;
c1f5f956 202 pktout.data = (pktout.data == NULL ? malloc(biglen+4) :
203 realloc(pktout.data, biglen+4));
374330e2 204 if (!pktout.data)
205 fatalbox("Out of memory");
206 }
207
208 pktout.type = type;
209 pktout.body = pktout.data+4+pad+1;
210}
211
212static void s_wrpkt(void) {
213 int pad, len, biglen, i;
214 unsigned long crc;
215
216 len = pktout.length + 5; /* type and CRC */
217 pad = 8 - (len%8);
218 biglen = len + pad;
219
220 pktout.body[-1] = pktout.type;
221 for (i=0; i<pad; i++)
222 pktout.data[i+4] = random_byte();
223 crc = crc32(pktout.data+4, biglen-4);
224
225 pktout.data[biglen+0] = (unsigned char) ((crc >> 24) & 0xFF);
226 pktout.data[biglen+1] = (unsigned char) ((crc >> 16) & 0xFF);
227 pktout.data[biglen+2] = (unsigned char) ((crc >> 8) & 0xFF);
228 pktout.data[biglen+3] = (unsigned char) (crc & 0xFF);
229
230 pktout.data[0] = (len >> 24) & 0xFF;
231 pktout.data[1] = (len >> 16) & 0xFF;
232 pktout.data[2] = (len >> 8) & 0xFF;
233 pktout.data[3] = len & 0xFF;
234
235 if (cipher)
236 cipher->encrypt(pktout.data+4, biglen);
237
238 s_write(pktout.data, biglen+4);
239}
240
9697bfd2 241static int ssh_versioncmp(char *a, char *b) {
242 char *ae, *be;
243 unsigned long av, bv;
244
43aa02a7 245 av = strtoul(a, &ae, 10);
246 bv = strtoul(b, &be, 10);
9697bfd2 247 if (av != bv) return (av < bv ? -1 : +1);
248 if (*ae == '.') ae++;
249 if (*be == '.') be++;
43aa02a7 250 av = strtoul(ae, &ae, 10);
251 bv = strtoul(be, &be, 10);
9697bfd2 252 if (av != bv) return (av < bv ? -1 : +1);
253 return 0;
254}
255
374330e2 256static int do_ssh_init(void) {
c5e9c988 257 char c, *vsp;
374330e2 258 char version[10];
c5e9c988 259 char vstring[80];
260 char vlog[sizeof(vstring)+20];
374330e2 261 int i;
262
263#ifdef FWHACK
264 i = 0;
265 while (s_read(&c, 1) == 1) {
266 if (c == 'S' && i < 2) i++;
267 else if (c == 'S' && i == 2) i = 2;
268 else if (c == 'H' && i == 2) break;
269 else i = 0;
270 }
271#else
272 if (s_read(&c,1) != 1 || c != 'S') return 0;
273 if (s_read(&c,1) != 1 || c != 'S') return 0;
274 if (s_read(&c,1) != 1 || c != 'H') return 0;
275#endif
c5e9c988 276 strcpy(vstring, "SSH-");
277 vsp = vstring+4;
374330e2 278 if (s_read(&c,1) != 1 || c != '-') return 0;
279 i = 0;
280 while (1) {
281 if (s_read(&c,1) != 1)
282 return 0;
c5e9c988 283 if (vsp < vstring+sizeof(vstring)-1)
284 *vsp++ = c;
374330e2 285 if (i >= 0) {
286 if (c == '-') {
287 version[i] = '\0';
288 i = -1;
289 } else if (i < sizeof(version)-1)
290 version[i++] = c;
291 }
292 else if (c == '\n')
293 break;
294 }
295
c5e9c988 296 *vsp = 0;
297 sprintf(vlog, "Server version: %s", vstring);
298 vlog[strcspn(vlog, "\r\n")] = '\0';
299 logevent(vlog);
300
9697bfd2 301 sprintf(vstring, "SSH-%s-PuTTY\n",
302 (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"));
c5e9c988 303 sprintf(vlog, "We claim version: %s", vstring);
304 vlog[strcspn(vlog, "\r\n")] = '\0';
305 logevent(vlog);
374330e2 306 s_write(vstring, strlen(vstring));
fef97f43 307 return 1;
374330e2 308}
309
310static void ssh_protocol(unsigned char *in, int inlen, int ispkt) {
311 int i, j, len;
312 unsigned char session_id[16];
313 unsigned char *rsabuf, *keystr1, *keystr2;
314 unsigned char cookie[8];
315 struct RSAKey servkey, hostkey;
316 struct MD5Context md5c;
ccbfb941 317 static unsigned long supported_ciphers_mask, supported_auths_mask;
bea1ef5f 318 int cipher_type;
374330e2 319
320 extern struct ssh_cipher ssh_3des;
9697bfd2 321 extern struct ssh_cipher ssh_des;
bea1ef5f 322 extern struct ssh_cipher ssh_blowfish;
374330e2 323
324 crBegin;
325
326 random_init();
327
328 while (!ispkt)
329 crReturnV;
330
972a41c8 331 if (pktin.type != SSH_SMSG_PUBLIC_KEY)
374330e2 332 fatalbox("Public key packet not received");
333
c5e9c988 334 logevent("Received public keys");
374330e2 335
c5e9c988 336 memcpy(cookie, pktin.body, 8);
374330e2 337
338 i = makekey(pktin.body+8, &servkey, &keystr1);
339
340 j = makekey(pktin.body+8+i, &hostkey, &keystr2);
341
c5e9c988 342 /*
343 * Hash the host key and print the hash in the log box. Just as
344 * a last resort in case the registry's host key checking is
345 * compromised, we'll allow the user some ability to verify
346 * host keys by eye.
347 */
348 MD5Init(&md5c);
349 MD5Update(&md5c, keystr2, hostkey.bytes);
350 MD5Final(session_id, &md5c);
351 {
352 char logmsg[80];
353 int i;
354 logevent("Host key MD5 is:");
355 strcpy(logmsg, " ");
356 for (i = 0; i < 16; i++)
357 sprintf(logmsg+strlen(logmsg), "%02x", session_id[i]);
358 logevent(logmsg);
359 }
360
ccbfb941 361 supported_ciphers_mask = ((pktin.body[12+i+j] << 24) |
362 (pktin.body[13+i+j] << 16) |
363 (pktin.body[14+i+j] << 8) |
364 (pktin.body[15+i+j]));
365
366 supported_auths_mask = ((pktin.body[16+i+j] << 24) |
367 (pktin.body[17+i+j] << 16) |
368 (pktin.body[18+i+j] << 8) |
369 (pktin.body[19+i+j]));
bea1ef5f 370
c5e9c988 371 MD5Init(&md5c);
372
374330e2 373 MD5Update(&md5c, keystr2, hostkey.bytes);
374 MD5Update(&md5c, keystr1, servkey.bytes);
375 MD5Update(&md5c, pktin.body, 8);
376
377 MD5Final(session_id, &md5c);
378
379 for (i=0; i<32; i++)
380 session_key[i] = random_byte();
381
382 len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
383
384 rsabuf = malloc(len);
385 if (!rsabuf)
386 fatalbox("Out of memory");
387
388 verify_ssh_host_key(savedhost, &hostkey);
389
390 for (i=0; i<32; i++) {
391 rsabuf[i] = session_key[i];
392 if (i < 16)
393 rsabuf[i] ^= session_id[i];
394 }
395
396 if (hostkey.bytes > servkey.bytes) {
397 rsaencrypt(rsabuf, 32, &servkey);
398 rsaencrypt(rsabuf, servkey.bytes, &hostkey);
399 } else {
400 rsaencrypt(rsabuf, 32, &hostkey);
401 rsaencrypt(rsabuf, hostkey.bytes, &servkey);
402 }
403
c5e9c988 404 logevent("Encrypted session key");
405
bea1ef5f 406 cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
9697bfd2 407 cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES :
bea1ef5f 408 SSH_CIPHER_3DES;
409 if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
410 c_write("Selected cipher not supported, falling back to 3DES\r\n", 53);
411 cipher_type = SSH_CIPHER_3DES;
412 }
c5e9c988 413 switch (cipher_type) {
414 case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break;
415 case SSH_CIPHER_DES: logevent("Using single-DES encryption"); break;
416 case SSH_CIPHER_BLOWFISH: logevent("Using Blowfish encryption"); break;
417 }
bea1ef5f 418
972a41c8 419 s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15);
bea1ef5f 420 pktout.body[0] = cipher_type;
374330e2 421 memcpy(pktout.body+1, cookie, 8);
422 pktout.body[9] = (len*8) >> 8;
423 pktout.body[10] = (len*8) & 0xFF;
424 memcpy(pktout.body+11, rsabuf, len);
425 pktout.body[len+11] = pktout.body[len+12] = 0; /* protocol flags */
426 pktout.body[len+13] = pktout.body[len+14] = 0;
427 s_wrpkt();
c5e9c988 428 logevent("Trying to enable encryption...");
374330e2 429
430 free(rsabuf);
431
bea1ef5f 432 cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish :
9697bfd2 433 cipher_type == SSH_CIPHER_DES ? &ssh_des :
bea1ef5f 434 &ssh_3des;
374330e2 435 cipher->sesskey(session_key);
436
437 do { crReturnV; } while (!ispkt);
438
972a41c8 439 if (pktin.type != SSH_SMSG_SUCCESS)
374330e2 440 fatalbox("Encryption not successfully enabled");
441
c5e9c988 442 logevent("Successfully started encryption");
443
374330e2 444 fflush(stdout);
445 {
446 static char username[100];
447 static int pos = 0;
448 static char c;
449 if (!*cfg.username) {
450 c_write("login as: ", 10);
451 while (pos >= 0) {
452 do { crReturnV; } while (ispkt);
453 while (inlen--) switch (c = *in++) {
454 case 10: case 13:
455 username[pos] = 0;
456 pos = -1;
457 break;
458 case 8: case 127:
459 if (pos > 0) {
460 c_write("\b \b", 3);
461 pos--;
462 }
463 break;
464 case 21: case 27:
465 while (pos > 0) {
466 c_write("\b \b", 3);
467 pos--;
468 }
469 break;
470 case 3: case 4:
471 random_save_seed();
472 exit(0);
473 break;
474 default:
475 if (c >= ' ' && c <= '~' && pos < 40) {
476 username[pos++] = c;
477 c_write(&c, 1);
478 }
479 break;
480 }
481 }
482 c_write("\r\n", 2);
483 username[strcspn(username, "\n\r")] = '\0';
484 } else {
485 char stuff[200];
486 strncpy(username, cfg.username, 99);
487 username[99] = '\0';
488 sprintf(stuff, "Sent username \"%s\".\r\n", username);
489 c_write(stuff, strlen(stuff));
490 }
972a41c8 491 s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username));
c5e9c988 492 {
493 char userlog[20+sizeof(username)];
494 sprintf(userlog, "Sent username \"%s\"", username);
495 logevent(userlog);
496 }
374330e2 497 pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
498 pktout.body[3] = strlen(username);
499 memcpy(pktout.body+4, username, strlen(username));
500 s_wrpkt();
501 }
502
503 do { crReturnV; } while (!ispkt);
504
972a41c8 505 while (pktin.type == SSH_SMSG_FAILURE) {
374330e2 506 static char password[100];
507 static int pos;
508 static char c;
ccbfb941 509 static int pwpkt_type;
510
511 /*
512 * Show password prompt, having first obtained it via a TIS
513 * exchange if we're doing TIS authentication.
514 */
515 pwpkt_type = SSH_CMSG_AUTH_PASSWORD;
516 if (pktin.type == SSH_SMSG_FAILURE &&
517 cfg.try_tis_auth &&
518 (supported_auths_mask & (1<<SSH_AUTH_TIS))) {
519 pwpkt_type = SSH_CMSG_AUTH_TIS_RESPONSE;
c5e9c988 520 logevent("Requested TIS authentication");
ccbfb941 521 s_wrpkt_start(SSH_CMSG_AUTH_TIS, 0);
522 s_wrpkt();
523 do { crReturnV; } while (!ispkt);
524 if (pktin.type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
c5e9c988 525 logevent("TIS authentication declined");
ccbfb941 526 c_write("TIS authentication refused.\r\n", 29);
527 } else {
528 int challengelen = ((pktin.body[0] << 24) |
529 (pktin.body[1] << 16) |
530 (pktin.body[2] << 8) |
531 (pktin.body[3]));
c5e9c988 532 logevent("Received TIS challenge");
ccbfb941 533 c_write(pktin.body+4, challengelen);
534 }
535 }
536 if (pwpkt_type == SSH_CMSG_AUTH_PASSWORD)
537 c_write("password: ", 10);
538
374330e2 539 pos = 0;
540 while (pos >= 0) {
541 do { crReturnV; } while (ispkt);
542 while (inlen--) switch (c = *in++) {
543 case 10: case 13:
544 password[pos] = 0;
545 pos = -1;
546 break;
547 case 8: case 127:
548 if (pos > 0)
549 pos--;
550 break;
551 case 21: case 27:
552 pos = 0;
553 break;
554 case 3: case 4:
555 random_save_seed();
556 exit(0);
557 break;
558 default:
559 if (c >= ' ' && c <= '~' && pos < 40)
560 password[pos++] = c;
561 break;
562 }
563 }
564 c_write("\r\n", 2);
ccbfb941 565 s_wrpkt_start(pwpkt_type, 4+strlen(password));
374330e2 566 pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
567 pktout.body[3] = strlen(password);
568 memcpy(pktout.body+4, password, strlen(password));
569 s_wrpkt();
c5e9c988 570 logevent("Sent password");
374330e2 571 memset(password, 0, strlen(password));
572 do { crReturnV; } while (!ispkt);
573 if (pktin.type == 15) {
574 c_write("Access denied\r\n", 15);
c5e9c988 575 logevent("Authentication refused");
374330e2 576 } else if (pktin.type != 14) {
577 fatalbox("Strange packet received, type %d", pktin.type);
578 }
579 }
580
c5e9c988 581 logevent("Authentication successful");
582
fef97f43 583 if (!cfg.nopty) {
584 i = strlen(cfg.termtype);
972a41c8 585 s_wrpkt_start(SSH_CMSG_REQUEST_PTY, i+5*4+1);
fef97f43 586 pktout.body[0] = (i >> 24) & 0xFF;
587 pktout.body[1] = (i >> 16) & 0xFF;
588 pktout.body[2] = (i >> 8) & 0xFF;
589 pktout.body[3] = i & 0xFF;
590 memcpy(pktout.body+4, cfg.termtype, i);
591 i += 4;
592 pktout.body[i++] = (rows >> 24) & 0xFF;
593 pktout.body[i++] = (rows >> 16) & 0xFF;
594 pktout.body[i++] = (rows >> 8) & 0xFF;
595 pktout.body[i++] = rows & 0xFF;
596 pktout.body[i++] = (cols >> 24) & 0xFF;
597 pktout.body[i++] = (cols >> 16) & 0xFF;
598 pktout.body[i++] = (cols >> 8) & 0xFF;
599 pktout.body[i++] = cols & 0xFF;
600 memset(pktout.body+i, 0, 9); /* 0 pixwidth, 0 pixheight, 0.b endofopt */
601 s_wrpkt();
602 ssh_state = SSH_STATE_INTERMED;
603 do { crReturnV; } while (!ispkt);
ccbfb941 604 if (pktin.type != SSH_SMSG_SUCCESS && pktin.type != SSH_SMSG_FAILURE) {
fef97f43 605 fatalbox("Protocol confusion");
ccbfb941 606 } else if (pktin.type == SSH_SMSG_FAILURE) {
fef97f43 607 c_write("Server refused to allocate pty\r\n", 32);
608 }
c5e9c988 609 logevent("Allocated pty");
374330e2 610 }
611
972a41c8 612 s_wrpkt_start(SSH_CMSG_EXEC_SHELL, 0);
374330e2 613 s_wrpkt();
c5e9c988 614 logevent("Started session");
374330e2 615
616 ssh_state = SSH_STATE_SESSION;
617 if (size_needed)
618 ssh_size();
619
620 while (1) {
621 crReturnV;
622 if (ispkt) {
972a41c8 623 if (pktin.type == SSH_SMSG_STDOUT_DATA ||
624 pktin.type == SSH_SMSG_STDERR_DATA) {
374330e2 625 long len = 0;
626 for (i = 0; i < 4; i++)
627 len = (len << 8) + pktin.body[i];
628 c_write(pktin.body+4, len);
972a41c8 629 } else if (pktin.type == SSH_MSG_DISCONNECT) {
21248260 630 ssh_state = SSH_STATE_CLOSED;
c5e9c988 631 logevent("Received disconnect request");
ccbfb941 632 } else if (pktin.type == SSH_SMSG_SUCCESS) {
972a41c8 633 /* may be from EXEC_SHELL on some servers */
ccbfb941 634 } else if (pktin.type == SSH_SMSG_FAILURE) {
972a41c8 635 /* may be from EXEC_SHELL on some servers
374330e2 636 * if no pty is available or in other odd cases. Ignore */
972a41c8 637 } else if (pktin.type == SSH_SMSG_EXITSTATUS) {
638 s_wrpkt_start(SSH_CMSG_EXIT_CONFIRMATION, 0);
374330e2 639 s_wrpkt();
640 } else {
641 fatalbox("Strange packet received: type %d", pktin.type);
642 }
643 } else {
972a41c8 644 s_wrpkt_start(SSH_CMSG_STDIN_DATA, 4+inlen);
374330e2 645 pktout.body[0] = (inlen >> 24) & 0xFF;
646 pktout.body[1] = (inlen >> 16) & 0xFF;
647 pktout.body[2] = (inlen >> 8) & 0xFF;
648 pktout.body[3] = inlen & 0xFF;
649 memcpy(pktout.body+4, in, inlen);
650 s_wrpkt();
651 }
652 }
653
654 crFinishV;
655}
656
657/*
658 * Called to set up the connection. Will arrange for WM_NETEVENT
659 * messages to be passed to the specified window, whose window
660 * procedure should then call telnet_msg().
661 *
662 * Returns an error message, or NULL on success.
663 *
664 * Also places the canonical host name into `realhost'.
665 */
666static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) {
667 SOCKADDR_IN addr;
668 struct hostent *h;
669 unsigned long a;
670#ifdef FWHACK
671 char *FWhost;
672 int FWport;
673#endif
674
675 savedhost = malloc(1+strlen(host));
676 if (!savedhost)
677 fatalbox("Out of memory");
678 strcpy(savedhost, host);
679
680#ifdef FWHACK
681 FWhost = host;
682 FWport = port;
683 host = FWSTR;
684 port = 23;
685#endif
686
687 /*
688 * Try to find host.
689 */
690 if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
691 if ( (h = gethostbyname(host)) == NULL)
692 switch (WSAGetLastError()) {
693 case WSAENETDOWN: return "Network is down";
694 case WSAHOST_NOT_FOUND: case WSANO_DATA:
695 return "Host does not exist";
696 case WSATRY_AGAIN: return "Host not found";
697 default: return "gethostbyname: unknown error";
698 }
699 memcpy (&a, h->h_addr, sizeof(a));
700 *realhost = h->h_name;
701 } else
702 *realhost = host;
703#ifdef FWHACK
704 *realhost = FWhost;
705#endif
706 a = ntohl(a);
707
708 if (port < 0)
709 port = 22; /* default ssh port */
710
711 /*
712 * Open socket.
713 */
714 s = socket(AF_INET, SOCK_STREAM, 0);
715 if (s == INVALID_SOCKET)
716 switch (WSAGetLastError()) {
717 case WSAENETDOWN: return "Network is down";
718 case WSAEAFNOSUPPORT: return "TCP/IP support not present";
719 default: return "socket(): unknown error";
720 }
721
722 /*
723 * Bind to local address.
724 */
725 addr.sin_family = AF_INET;
726 addr.sin_addr.s_addr = htonl(INADDR_ANY);
727 addr.sin_port = htons(0);
728 if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
729 switch (WSAGetLastError()) {
730 case WSAENETDOWN: return "Network is down";
731 default: return "bind(): unknown error";
732 }
733
734 /*
735 * Connect to remote address.
736 */
737 addr.sin_addr.s_addr = htonl(a);
738 addr.sin_port = htons((short)port);
739 if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
740 switch (WSAGetLastError()) {
741 case WSAENETDOWN: return "Network is down";
742 case WSAECONNREFUSED: return "Connection refused";
743 case WSAENETUNREACH: return "Network is unreachable";
744 case WSAEHOSTUNREACH: return "No route to host";
745 default: return "connect(): unknown error";
746 }
747
748#ifdef FWHACK
749 send(s, "connect ", 8, 0);
750 send(s, FWhost, strlen(FWhost), 0);
751 {
752 char buf[20];
753 sprintf(buf, " %d\n", FWport);
754 send (s, buf, strlen(buf), 0);
755 }
756#endif
757
758 if (!do_ssh_init())
759 return "Protocol initialisation error";
760
761 if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | FD_CLOSE) == SOCKET_ERROR)
762 switch (WSAGetLastError()) {
763 case WSAENETDOWN: return "Network is down";
764 default: return "WSAAsyncSelect(): unknown error";
765 }
766
767 return NULL;
768}
769
770/*
771 * Process a WM_NETEVENT message. Will return 0 if the connection
772 * has closed, or <0 for a socket error.
773 */
774static int ssh_msg (WPARAM wParam, LPARAM lParam) {
775 int ret;
776 char buf[256];
777
8ce72d2c 778 /*
779 * Because reading less than the whole of the available pending
780 * data can generate an FD_READ event, we need to allow for the
781 * possibility that FD_READ may arrive with FD_CLOSE already in
782 * the queue; so it's possible that we can get here even with s
783 * invalid. If so, we return 1 and don't worry about it.
784 */
785 if (s == INVALID_SOCKET)
786 return 1;
374330e2 787
788 if (WSAGETSELECTERROR(lParam) != 0)
789 return -WSAGETSELECTERROR(lParam);
790
791 switch (WSAGETSELECTEVENT(lParam)) {
792 case FD_READ:
8ce72d2c 793 case FD_CLOSE:
374330e2 794 ret = recv(s, buf, sizeof(buf), 0);
795 if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
796 return 1;
797 if (ret < 0) /* any _other_ error */
798 return -10000-WSAGetLastError();
799 if (ret == 0) {
800 s = INVALID_SOCKET;
8ce72d2c 801 return 0;
374330e2 802 }
803 ssh_gotdata (buf, ret);
804 return 1;
374330e2 805 }
806 return 1; /* shouldn't happen, but WTF */
807}
808
809/*
810 * Called to send data down the Telnet connection.
811 */
812static void ssh_send (char *buf, int len) {
813 if (s == INVALID_SOCKET)
814 return;
815
816 ssh_protocol(buf, len, 0);
817}
818
819/*
820 * Called to set the size of the window from Telnet's POV.
821 */
822static void ssh_size(void) {
823 switch (ssh_state) {
824 case SSH_STATE_BEFORE_SIZE:
21248260 825 case SSH_STATE_CLOSED:
374330e2 826 break; /* do nothing */
827 case SSH_STATE_INTERMED:
828 size_needed = TRUE; /* buffer for later */
829 break;
830 case SSH_STATE_SESSION:
fef97f43 831 if (!cfg.nopty) {
832 s_wrpkt_start(11, 16);
833 pktout.body[0] = (rows >> 24) & 0xFF;
834 pktout.body[1] = (rows >> 16) & 0xFF;
835 pktout.body[2] = (rows >> 8) & 0xFF;
836 pktout.body[3] = rows & 0xFF;
837 pktout.body[4] = (cols >> 24) & 0xFF;
838 pktout.body[5] = (cols >> 16) & 0xFF;
839 pktout.body[6] = (cols >> 8) & 0xFF;
840 pktout.body[7] = cols & 0xFF;
841 memset(pktout.body+8, 0, 8);
842 s_wrpkt();
843 }
374330e2 844 }
845}
846
847/*
848 * (Send Telnet special codes)
849 */
850static void ssh_special (Telnet_Special code) {
851 /* do nothing */
852}
853
854Backend ssh_backend = {
855 ssh_init,
856 ssh_msg,
857 ssh_send,
858 ssh_size,
859 ssh_special
860};