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