ssh.c 17 KB

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