AmendHub

Download

jcs

/

subtext

/

telnet.c

 

(View History)

jcs   telnet: Add failsafe to avoid sending more than sizeof(obuf) Latest amendment: 267 on 2022-10-26

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "logger.h"
21 #include "session.h"
22 #include "subtext.h"
23 #include "telnet.h"
24 #include "tcp.h"
25 #include "util.h"
26
27 #define SE 240 /* end of sub-negotiation options */
28 #define SB 250 /* start of sub-negotiation options */
29 #define WILL 251 /* confirm willingness to negotiate */
30 #define WONT 252 /* confirm unwillingness to negotiate */
31 #define DO 253 /* indicate willingness to negotiate */
32 #define DONT 254 /* indicate unwillingness to negotiate */
33 #define IAC 255 /* start of a negotiation sequence */
34
35 #define IS 0 /* sub-negotiation */
36 #define SEND 1 /* sub-negotiation */
37
38 #define IAC_BINARY 0 /* Transmit Binary */
39 #define IAC_ECHO 1 /* Echo Option */
40 #define IAC_SGA 3 /* Suppress Go Ahead Option */
41 #define IAC_STATUS 5 /* Status Option */
42 #define IAC_TM 6 /* Timing Mark Option */
43 #define IAC_NAOCRD 10 /* Output Carriage-Return Disposition Option */
44 #define IAC_NAOHTS 11 /* Output Horizontal Tabstops Option */
45 #define IAC_NAOHTD 12 /* Output Horizontal Tab Disposition Option */
46 #define IAC_NAOFFD 13 /* Output Formfeed Disposition Option */
47 #define IAC_NAOVTS 14 /* Output Vertical Tabstops Option */
48 #define IAC_NAOVTD 15 /* Output Vertical Tab Disposition Option */
49 #define IAC_NAOLFD 16 /* Output Linefeed Disposition */
50 #define IAC_XASCII 17 /* Extended Ascii Option */
51 #define IAC_LOGOUT 18 /* Logout Option */
52 #define IAC_BM 19 /* Byte Macro Option */
53 #define IAC_SUPDUP 22 /* SUPDUP-OUTPUT Option */
54 #define IAC_SENDLOC 23 /* SEND-LOCATION Option */
55 #define IAC_TTYPE 24 /* Terminal Type Option */
56 #define IAC_EOR 25 /* End of Record Option */
57 #define IAC_OUTMRK 27 /* Marking Telnet Option */
58 #define IAC_TTYLOC 28 /* Terminal Location Number Option */
59 #define IAC_DET 20 /* Data Entry Terminal Option DODIIS */
60 #define IAC_X3PAD 30 /* X.3 PAD Option */
61 #define IAC_NAWS 31 /* Window Size Option */
62 #define IAC_TSPEED 32 /* Terminal Speed Option */
63 #define IAC_FLOWCTRL 33 /* Remote Flow Control Option */
64 #define IAC_LINEMODE 34 /* Linemode Option */
65 #define IAC_XDISPLOC 35 /* X Display Location Option */
66 #define IAC_ENVIRON 36 /* Environment Option */
67 #define IAC_AUTH 37 /* Authentication */
68 #define IAC_ENCRYPT 38 /* Encryption Option */
69 #define IAC_NEWENV 39 /* Environment Option */
70 #define IAC_CHARSET 42 /* Charset Option */
71 #define IAC_COMPORT 44 /* Com Port Control Option */
72
73 enum {
74 TELNET_PB_STATE_UNUSED = 0,
75 TELNET_PB_STATE_LISTENING,
76 TELNET_PB_STATE_CONNECTED
77 };
78
79 enum {
80 TELNET_IAC_STATE_IDLE = 0,
81 TELNET_IAC_STATE_IAC,
82 TELNET_IAC_STATE_WILL,
83 TELNET_IAC_STATE_WONT,
84 TELNET_IAC_STATE_DO,
85 TELNET_IAC_STATE_DONT,
86 TELNET_IAC_STATE_SB
87 };
88
89 struct telnet_node {
90 short id;
91 short state;
92 char name[8];
93 ip_addr ip;
94 char ip_s[20];
95 struct session *session;
96 unsigned short obuflen;
97 unsigned char obuf[512];
98 unsigned char ibuf[64];
99 unsigned short ibuflen;
100 unsigned short ibufoff;
101 unsigned short sending_iac;
102 unsigned short escaped_obuflen;
103 short iac_state;
104 unsigned char iac_sb[128];
105 short sb_len;
106 unsigned short naws_count;
107 /* docs say 4*MTU+1024, but MTU will probably be <1500 */
108 unsigned char tcp_buf[(4 * 1500) + 1024];
109 bool from_trusted_proxy;
110 TCPiopb rcv_pb, send_pb, listen_pb;
111 StreamPtr stream;
112 wdsEntry tcp_wds[2];
113 };
114
115 /* add one to be able to print busy messages */
116 #define MAX_TELNET_NODES (MAX_SESSIONS + 1)
117
118 static struct telnet_node *telnet_nodes[MAX_TELNET_NODES] = { NULL };
119 static struct telnet_node *telnet_listener_node = NULL;
120 static TCPiopb telnet_exit_pb;
121 static TCPStatusPB telnet_status_pb;
122 static bool did_initial_log = false;
123 static UDPiopb udp_ban_pb;
124 static StreamPtr udp_ban_stream;
125 static char *udp_ban_rcv_buf;
126 #define UDP_BAN_RCV_BUF_SIZE 2048
127 static wdsEntry udp_ban_wds[2];
128 static char udp_ban_send_buf[16];
129
130 /* terminals that will have vt100 set by default */
131 static const char * vt100_terms[] = {
132 "ansi",
133 "syncterm",
134 "xterm",
135 "tmux",
136 "screen",
137 "vt100",
138 NULL,
139 };
140
141 void telnet_setup(struct session *session);
142 void telnet_listen_on_node(struct telnet_node *node);
143 void telnet_output_iac(struct session *session, const char *iacs,
144 size_t len);
145 void telnet_print_busy(struct telnet_node *node);
146
147 struct node_funcs telnet_node_funcs = {
148 telnet_setup,
149 telnet_input,
150 telnet_output,
151 telnet_close
152 };
153
154 void
155 telnet_init(void)
156 {
157 short n, error;
158
159 if (!db->config.telnet_port)
160 return;
161
162 logger_printf("[telnet] Initializing MacTCP");
163
164 if (_TCPInit() != noErr)
165 panic("Failed initializing MacTCP");
166
167 /* pre-allocate nodes */
168 for (n = 0; n < MAX_TELNET_NODES; n++) {
169 telnet_nodes[n] = xmalloczero(sizeof(struct telnet_node),
170 "telnet node");
171 telnet_nodes[n]->id = n;
172
173 if (n == 0)
174 telnet_listen_on_node(telnet_nodes[n]);
175 }
176
177 if (db->config.trusted_proxy_ip != 0 &&
178 db->config.trusted_proxy_udp_port != 0) {
179 udp_ban_rcv_buf = xmalloc(UDP_BAN_RCV_BUF_SIZE, "udp_ban_rcv_buf");
180 error = _UDPCreate(&udp_ban_pb, &udp_ban_stream,
181 (Ptr)udp_ban_rcv_buf, UDP_BAN_RCV_BUF_SIZE, NULL, NULL,
182 NULL, false);
183 if (error)
184 panic("UDPCreate failed: %d", error);
185 }
186 }
187
188 void
189 telnet_atexit(void)
190 {
191 struct telnet_node *node;
192 short n, error;
193
194 for (n = 0; n < MAX_TELNET_NODES; n++) {
195 node = telnet_nodes[n];
196 if (!node)
197 continue;
198
199 if (node->state > TELNET_PB_STATE_UNUSED)
200 _TCPRelease(&telnet_exit_pb, node->stream, nil, nil, false);
201
202 xfree(&node);
203 telnet_nodes[n] = NULL;
204 }
205
206 telnet_listener_node = NULL;
207
208 if (db->config.trusted_proxy_ip != 0 &&
209 db->config.trusted_proxy_udp_port != 0)
210 _UDPRelease(&udp_ban_pb, udp_ban_stream, NULL, NULL, false);
211 }
212
213 void
214 telnet_listen_on_node(struct telnet_node *node)
215 {
216 char ip_s[20];
217 short error, id;
218 ip_addr ip;
219 tcp_port port;
220 long mask;
221
222 id = node->id;
223 memset(node, 0, sizeof(struct telnet_node));
224 node->id = id;
225
226 snprintf(node->name, sizeof(node->name), "ttyt%d", id);
227
228 error = _TCPCreate(&node->listen_pb, &node->stream,
229 (Ptr)&node->tcp_buf, sizeof(node->tcp_buf), nil, nil, nil, false);
230 if (error)
231 panic("TCPCreate[%d] failed: %d", node->id, error);
232
233 error = _TCPGetOurIP(&ip, &mask);
234 if (error)
235 panic("TCPGetOurIP failed: %d", error);
236
237 port = db->config.telnet_port;
238
239 if (!did_initial_log) {
240 long2ip(ip, ip_s);
241 logger_printf("[telnet] Listening on %s:%d", ip_s, port);
242 did_initial_log = true;
243 }
244
245 error = _TCPPassiveOpen(&node->listen_pb, node->stream, nil, nil, &ip,
246 &port, nil, nil, true);
247 if (error)
248 panic("TCPPassiveOpen[%d] on port %d failed: %d",
249 node->id, db->config.telnet_port, error);
250
251 node->state = TELNET_PB_STATE_LISTENING;
252 telnet_listener_node = node;
253 }
254
255 void
256 telnet_idle(void)
257 {
258 struct telnet_node *node;
259 short n, j, error;
260
261 if (!db->config.telnet_port)
262 return;
263
264 for (n = 0; n < MAX_TELNET_NODES; n++) {
265 node = telnet_nodes[n];
266
267 switch (node->state) {
268 case TELNET_PB_STATE_UNUSED:
269 if (telnet_listener_node == NULL)
270 telnet_listen_on_node(node);
271 break;
272 case TELNET_PB_STATE_LISTENING:
273 error = _TCPStatus(&node->send_pb, node->stream,
274 &telnet_status_pb, nil, nil, false);
275 if (error == connectionDoesntExist) {
276 /*
277 * The connection closed before it could be handled, but
278 * telnet_status_pb.connectionState will still be
279 * listening, so force it into a bogus state to recycle it.
280 */
281 telnet_status_pb.connectionState = -1;
282 }
283
284 switch (telnet_status_pb.connectionState) {
285 case ConnectionStateListening:
286 goto next_node;
287 case ConnectionStateSYNReceived:
288 case ConnectionStateSYNSent:
289 /* TODO: enforce our own timeout on these? */
290 break;
291 case ConnectionStateEstablished:
292 /* start up a new socket for listening */
293 telnet_listener_node = NULL;
294
295 node->ip = telnet_status_pb.remoteHost;
296 long2ip(telnet_status_pb.remoteHost, node->ip_s);
297
298 if (db->config.trusted_proxy_ip != 0 &&
299 db->config.trusted_proxy_ip == node->ip) {
300 node->from_trusted_proxy = true;
301 snprintf(node->name, sizeof(node->name), "ttyw%d",
302 node->id);
303 }
304
305 logger_printf("[%s] New telnet connection from %s%s",
306 node->name, node->ip_s,
307 node->from_trusted_proxy ? " (via trusted proxy)" : "");
308
309 node->state = TELNET_PB_STATE_CONNECTED;
310 node->session = session_create(node->name,
311 node->from_trusted_proxy ? "web" : "telnet",
312 &telnet_node_funcs);
313 if (node->session == NULL) {
314 logger_printf("[%s] No free nodes, disconnecting",
315 node->name);
316 telnet_print_busy(node);
317 _TCPRelease(&node->listen_pb, node->stream, nil,
318 nil, false);
319 node->state = TELNET_PB_STATE_UNUSED;
320 goto next_node;
321 }
322 node->session->cookie = (void *)node;
323 node->session->tspeed = 19200;
324 node->session->is_telnet = true;
325 node->session->log.ip_address = telnet_status_pb.remoteHost;
326 break;
327 default:
328 if (telnet_status_pb.remoteHost == 0)
329 long2ip(node->listen_pb.csParam.open.remoteHost,
330 node->ip_s);
331 else
332 long2ip(telnet_status_pb.remoteHost, node->ip_s);
333
334 logger_printf("[%s] Telnet connection from "
335 "%s in state %d, aborting", node->name, node->ip_s,
336 telnet_status_pb.connectionState);
337
338 if (node->session)
339 node->session->ending = 1;
340 else {
341 _TCPRelease(&node->listen_pb, node->stream, nil, nil,
342 false);
343 node->state = TELNET_PB_STATE_UNUSED;
344 telnet_listener_node = NULL;
345 }
346 goto next_node;
347 }
348 break;
349 case TELNET_PB_STATE_CONNECTED:
350 if (!node->session) {
351 node->state = TELNET_PB_STATE_UNUSED;
352 break;
353 }
354
355 /*
356 * Send buffered data any time we can, the user might not be
357 * in an explicit session_flush() like during chat
358 */
359 telnet_output(node->session);
360 break;
361 }
362
363 next_node:
364 continue;
365 }
366 }
367
368 void
369 telnet_setup(struct session *session)
370 {
371 const char iacs[] = {
372 IAC, WILL, IAC_SGA, /* we will supress go-ahead */
373 IAC, WILL, IAC_ECHO, /* and we will echo for you */
374 IAC, DO, IAC_TSPEED, /* send your TSPEED */
375 IAC, DO, IAC_NAWS, /* send your NAWS */
376 IAC, DO, IAC_TTYPE, /* send your TTYPE */
377 IAC, DO, IAC_NEWENV, /* send your NEWENV vars */
378
379 /* IAC, DO, IAC_LINEMODE, */
380 };
381
382 telnet_output_iac(session, iacs, sizeof(iacs));
383 }
384
385 short
386 telnet_input(struct session *session)
387 {
388 struct telnet_node *node = (struct telnet_node *)session->cookie;
389 unsigned short rlen;
390 short error, n, j, used;
391 unsigned char c;
392 char iac_out[8] = { IAC, 0 };
393
394 error = _TCPStatus(&node->rcv_pb, node->stream, &telnet_status_pb, nil,
395 nil, false);
396 if (error ||
397 telnet_status_pb.connectionState != ConnectionStateEstablished) {
398 session->ending = 1;
399 return 0;
400 }
401
402 if (telnet_status_pb.amtUnreadData == 0)
403 return 0;
404
405 rlen = telnet_status_pb.amtUnreadData;
406
407 if (session->transferring_file) {
408 if (session->ibuflen + session->ibufoff + rlen >
409 sizeof(session->ibuf)) {
410 /* if we're already halfway through the buffer, reset */
411 if (session->ibufoff >= (sizeof(session->ibuf) / 2)) {
412 memmove(session->ibuf, session->ibuf + session->ibufoff,
413 session->ibuflen);
414 session->ibufoff = 0;
415 }
416 rlen = sizeof(session->ibuf) - session->ibuflen -
417 session->ibufoff;
418 if (rlen == 0)
419 return 0;
420 }
421
422 error = _TCPRcv(&node->rcv_pb, node->stream,
423 (Ptr)(session->ibuf + session->ibufoff + session->ibuflen), &rlen,
424 nil, nil, false);
425 if (error) {
426 session_logf(session, "TCP read failed (%d), closing connection",
427 error);
428 session->ending = 1;
429 return 0;
430 }
431 if (rlen == 0)
432 return 0;
433 session->last_input_at = Time;
434 session->ibuflen += rlen;
435 return rlen;
436 }
437
438 if (node->ibuflen + rlen > sizeof(node->ibuf))
439 rlen = sizeof(node->ibuf) - node->ibuflen;
440
441 error = _TCPRcv(&node->rcv_pb, node->stream,
442 (Ptr)(node->ibuf + node->ibuflen), &rlen, nil, nil, false);
443 if (error) {
444 session_logf(session, "TCP read failed (%d), closing connection",
445 error);
446 session->ending = 1;
447 return 0;
448 }
449
450 if (rlen == 0)
451 return 0;
452
453 session->last_input_at = Time;
454 node->ibuflen += rlen;
455
456 used = 0;
457 for (n = 0; n < node->ibuflen; n++) {
458 if (session->ibuflen >= sizeof(session->ibuf))
459 break;
460
461 c = node->ibuf[n];
462 used++;
463
464 switch (node->iac_state) {
465 case TELNET_IAC_STATE_IDLE:
466 if (c == IAC)
467 node->iac_state = TELNET_IAC_STATE_IAC;
468 else
469 session->ibuf[session->ibuflen++] = c;
470 break;
471 case TELNET_IAC_STATE_IAC:
472 switch (c) {
473 case IAC:
474 /* escaped iac */
475 session->ibuf[session->ibuflen++] = c;
476 node->iac_state = TELNET_IAC_STATE_IDLE;
477 break;
478 case WILL:
479 /* client will do something */
480 node->iac_state = TELNET_IAC_STATE_WILL;
481 break;
482 case WONT:
483 /* client will not do something */
484 node->iac_state = TELNET_IAC_STATE_WONT;
485 break;
486 case DO:
487 /* client wants us to do something */
488 node->iac_state = TELNET_IAC_STATE_DO;
489 break;
490 case DONT:
491 /* client wants us not to do something */
492 node->iac_state = TELNET_IAC_STATE_DONT;
493 break;
494 case SB:
495 /* sub-negotiate */
496 node->iac_state = TELNET_IAC_STATE_SB;
497 node->sb_len = 0;
498 break;
499 default:
500 node->iac_state = TELNET_IAC_STATE_IDLE;
501 /* XXX: what are we supposed to do here? */
502 session->ibuf[session->ibuflen++] = IAC;
503 session->ibuf[session->ibuflen++] = c;
504 break;
505 }
506 break;
507 case TELNET_IAC_STATE_SB: {
508 unsigned short sboff;
509
510 /* keep reading until we see [^IAC] IAC SE */
511 if (c == SE && node->sb_len > 0 &&
512 node->iac_sb[node->sb_len - 1] == IAC) {
513 switch (node->iac_sb[0]) {
514 case IAC_NAWS:
515 /* IAC SB NAWS 0 80 0 24 (80x24) */
516 /* IAC SB NAWS 1 255 255 1 123 (256x378) */
517 /* if either value is 255, it will be 255,255 */
518 sboff = 1;
519
520 if (node->iac_sb[sboff] == 255)
521 sboff++;
522 session->terminal_columns = (node->iac_sb[sboff++] << 8);
523 if (node->iac_sb[sboff] == 255)
524 sboff++;
525 session->terminal_columns |= node->iac_sb[sboff++];
526
527 if (node->iac_sb[sboff] == 255)
528 sboff++;
529 session->terminal_lines = (node->iac_sb[sboff++] << 8);
530 if (node->iac_sb[sboff] == 255)
531 sboff++;
532 session->terminal_lines |= node->iac_sb[sboff++];
533 if (node->naws_count++ < 20)
534 session_logf_buffered(session, "IAC NAWS %d, %d",
535 session->terminal_columns,
536 session->terminal_lines);
537 break;
538 case IAC_TTYPE:
539 /* IAC SB TTYPE IS XTERM IAC SE */
540 node->iac_sb[node->sb_len - 1] = '\0';
541 strlcpy(session->terminal_type,
542 (char *)&node->iac_sb + 2,
543 sizeof(session->terminal_type));
544 session_logf_buffered(session, "IAC TTYPE IS %s",
545 session->terminal_type);
546
547 for (j = 0; vt100_terms[j] != NULL; j++) {
548 if (strncasecmp(session->terminal_type,
549 vt100_terms[j], strlen(vt100_terms[j])) == 0) {
550 session_logf_buffered(session,
551 "Activating vt100 ANSI support for "
552 "matching %s", vt100_terms[j]);
553 session->vt100 = true;
554 break;
555 }
556 }
557
558 break;
559 case IAC_NEWENV: {
560 char k[sizeof(node->iac_sb)], v[sizeof(node->iac_sb)];
561 char l = 0;
562 bool inv = false;
563
564 k[0] = v[0] = '\0';
565
566 /* \40\0\0USER\1blah\0DISPLAY\1... */
567 for (j = 3; j < node->sb_len; j++) {
568 if (j == node->sb_len - 1 ||
569 node->iac_sb[j] == 0 || node->iac_sb[j] == 3) {
570 v[l] = '\0';
571 if (l) {
572 session_logf_buffered(session,
573 "NEWENV \"%s\" = \"%s\"", k, v);
574
575 if (node->from_trusted_proxy &&
576 strcmp(k, "REMOTE_ADDR") == 0) {
577 node->ip = ip2long(v);
578 strlcpy(node->ip_s, v,
579 sizeof(node->ip_s));
580 node->session->log.ip_address =
581 node->ip;
582 }
583 }
584 l = 0;
585 inv = false;
586 } else if (node->iac_sb[j] == 1) {
587 k[l] = '\0';
588 l = 0;
589 inv = true;
590 } else {
591 if (inv)
592 v[l++] = node->iac_sb[j];
593 else
594 k[l++] = node->iac_sb[j];
595 }
596 }
597 break;
598 }
599 case IAC_TSPEED: {
600 unsigned long tspeed;
601
602 /* IAC SB TSPEED IS 38400,19200 SE */
603 node->iac_sb[node->sb_len - 1] = '\0';
604
605 session_logf_buffered(session, "IAC TSPEED IS %s",
606 node->iac_sb + 2);
607
608 for (j = 2; j < node->sb_len; j++) {
609 if (node->iac_sb[j] == ',') {
610 node->iac_sb[j] = '\0';
611 break;
612 }
613 }
614
615 tspeed = atol((const char *)(node->iac_sb + 2));
616 if (tspeed > 0) {
617 if (tspeed > 57600)
618 tspeed = 57600;
619 node->session->tspeed =
620 node->session->log.tspeed =
621 (unsigned short)tspeed;
622 }
623 break;
624 }
625 }
626 node->iac_state = TELNET_IAC_STATE_IDLE;
627 } else {
628 /* accumulate bytes into iac_sb buffer */
629 if (node->sb_len < sizeof(node->iac_sb))
630 node->iac_sb[node->sb_len++] = c;
631 else {
632 /* IAC overflow */
633 node->iac_state = TELNET_IAC_STATE_IDLE;
634 node->sb_len = 0;
635 session_logf(session, "IAC SB overflow");
636 }
637 }
638 break;
639 }
640 case TELNET_IAC_STATE_WILL:
641 switch (c) {
642 case IAC_ECHO:
643 case IAC_SGA:
644 iac_out[1] = DO;
645 iac_out[2] = IAC_ECHO;
646 telnet_output_iac(session, iac_out, 3);
647 break;
648 case IAC_NEWENV:
649 case IAC_TTYPE:
650 case IAC_TSPEED:
651 iac_out[1] = SB;
652 iac_out[2] = c;
653 iac_out[3] = SEND;
654 iac_out[4] = IAC;
655 iac_out[5] = SE;
656 telnet_output_iac(session, iac_out, 6);
657 break;
658 case IAC_LINEMODE:
659 #if 0
660 iac_out[1] = SB;
661 iac_out[2] = IAC_LINEMODE;
662 iac_out[3] = 1;
663 iac_out[4] = 0;
664 iac_out[5] = IAC;
665 iac_out[6] = SE;
666 telnet_output_iac(session, iac_out, 7);
667 #endif
668 break;
669 case IAC_ENCRYPT:
670 iac_out[1] = DONT;
671 iac_out[2] = IAC_ENCRYPT;
672 telnet_output_iac(session, iac_out, 3);
673 break;
674 }
675 node->iac_state = TELNET_IAC_STATE_IDLE;
676 break;
677 case TELNET_IAC_STATE_WONT:
678 /* we don't care about any of these yet */
679 node->iac_state = TELNET_IAC_STATE_IDLE;
680 break;
681 case TELNET_IAC_STATE_DO:
682 switch (c) {
683 case IAC_BINARY:
684 iac_out[1] = WILL;
685 iac_out[2] = IAC_BINARY;
686 telnet_output_iac(session, iac_out, 3);
687 break;
688 case IAC_ECHO:
689 case IAC_NAWS:
690 case IAC_TSPEED:
691 case IAC_FLOWCTRL:
692 case IAC_SGA:
693 case IAC_LINEMODE:
694 /* we already sent WILLs for these at the beginning */
695 break;
696 default:
697 iac_out[1] = WONT;
698 iac_out[2] = c;
699 telnet_output_iac(session, iac_out, 3);
700 break;
701 }
702 node->iac_state = TELNET_IAC_STATE_IDLE;
703 break;
704 case TELNET_IAC_STATE_DONT:
705 node->iac_state = TELNET_IAC_STATE_IDLE;
706 break;
707 default:
708 warn("telnet_input in bogus IAC state %d", node->iac_state);
709 break;
710 }
711 }
712
713 if (used == node->ibuflen)
714 node->ibuflen = 0;
715 else {
716 memmove(node->ibuf, node->ibuf + used, node->ibuflen - used);
717 node->ibuflen -= used;
718 }
719
720 return rlen;
721 }
722
723 short
724 telnet_output(struct session *session)
725 {
726 struct telnet_node *node = (struct telnet_node *)session->cookie;
727 short n, error;
728 unsigned char c;
729 unsigned long now;
730
731 if (session->obuflen == 0 || session->ending)
732 return 0;
733
734 if (node->send_pb.ioResult > 0)
735 /* previous _TCPSend has not completed yet */
736 return 0;
737
738 process_result:
739 if (node->tcp_wds[0].length) {
740 /* previous _TCPSend completed, shift out those bytes */
741 if (session->obuflen < node->obuflen) {
742 warn("bogus obuflen %d", session->obuflen);
743 session->obuflen = 0;
744 } else
745 session->obuflen -= node->obuflen;
746 if (session->obuflen > 0)
747 memmove(session->obuf, session->obuf + node->obuflen,
748 session->obuflen);
749
750 node->tcp_wds[0].length = 0;
751
752 if (session->obuflen == 0)
753 return node->obuflen;
754 }
755
756 if (session->obuflen == 1 && session->obuf[0] != IAC) {
757 node->obuf[0] = session->obuf[0];
758 node->escaped_obuflen = node->obuflen = 1;
759 } else if (session->transferring_file) {
760 memcpy(node->obuf, session->obuf, session->obuflen);
761 node->escaped_obuflen = node->obuflen = session->obuflen;
762 } else {
763 /* copy obuf to node buffer, escaping IACs */
764 for (node->escaped_obuflen = 0, node->obuflen = 0;
765 node->obuflen < session->obuflen &&
766 node->escaped_obuflen < sizeof(node->obuf);
767 node->obuflen++) {
768 c = session->obuf[node->obuflen];
769
770 if (!node->sending_iac && c == IAC) {
771 node->obuf[node->escaped_obuflen++] = IAC;
772 node->obuf[node->escaped_obuflen++] = IAC;
773 } else
774 node->obuf[node->escaped_obuflen++] = c;
775 }
776 }
777
778 if (node->escaped_obuflen > sizeof(node->obuf)) {
779 warn("bogus obuflen %d > %ld", node->escaped_obuflen,
780 sizeof(node->obuf));
781 session->ending = true;
782 session_close(session);
783 return 0;
784 }
785
786 /*
787 * _TCPSend only knows how many wds pointers were passed in when it
788 * reads the next one and its pointer is zero (or size is zero?)
789 */
790 node->tcp_wds[0].ptr = (Ptr)&node->obuf;
791 node->tcp_wds[0].length = node->escaped_obuflen;
792 node->tcp_wds[1].ptr = 0;
793 node->tcp_wds[1].length = 0;
794
795 now = Ticks;
796 error = _TCPSend(&node->send_pb, node->stream, node->tcp_wds, nil, nil,
797 true);
798 if (error) {
799 warn("TCPSend[%d] failed: %d", node->id, error);
800 session->ending = true;
801 }
802
803 /* if we can send in less than 500ms, avoid a uthread switch */
804 while (Ticks - now <= 30) {
805 if (node->send_pb.ioResult <= 0)
806 goto process_result;
807 }
808
809 return 0;
810 }
811
812 void
813 telnet_output_iac(struct session *session, const char *iacs, size_t len)
814 {
815 struct telnet_node *node = (struct telnet_node *)session->cookie;
816
817 session_flush(session);
818 if (session->ending)
819 return;
820
821 node->sending_iac = 1;
822 session_output(session, iacs, len);
823 session_flush(session);
824 node->sending_iac = 0;
825 }
826
827 void
828 telnet_close(struct session *session)
829 {
830 struct telnet_node *node = (struct telnet_node *)session->cookie;
831 size_t len;
832 short error, n;
833 unsigned char *tmp;
834
835 if (node->from_trusted_proxy)
836 session->ban_node_source = false;
837
838 if (session->ban_node_source && !node->from_trusted_proxy &&
839 db->config.trusted_proxy_ip != 0 &&
840 db->config.trusted_proxy_udp_port != 0) {
841 session_logf(session, "Closing telnet connection from %s and "
842 "banning IP", node->ip_s);
843
844 tmp = (unsigned char *)&node->ip;
845 len = snprintf(udp_ban_send_buf, sizeof(udp_ban_send_buf),
846 "%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]);
847 udp_ban_wds[0].ptr = (Ptr)&udp_ban_send_buf;
848 udp_ban_wds[0].length = len;
849 udp_ban_wds[1].ptr = 0;
850 udp_ban_wds[1].length = 0;
851
852 error = _UDPSend(&udp_ban_pb, udp_ban_stream, udp_ban_wds,
853 db->config.trusted_proxy_ip,
854 db->config.trusted_proxy_udp_port, NULL, NULL, false);
855 if (error)
856 session_logf(session, "Failed sending IP ban UDP packet: %d",
857 error);
858 } else {
859 session_logf(session, "Closing telnet connection from %s",
860 node->ip_s);
861
862 error = _TCPClose(&telnet_exit_pb, node->stream, nil, nil, false);
863 /* TODO: allow some time to fully close? */
864 }
865
866 error = _TCPRelease(&telnet_exit_pb, node->stream, nil, nil, false);
867 if (error) {
868 warn("error shutting down telnet session: %d", error);
869 return;
870 }
871
872 session->cookie = NULL;
873 node->state = TELNET_PB_STATE_UNUSED;
874 }
875
876 void
877 telnet_print_busy(struct telnet_node *node)
878 {
879 size_t len, n, olen;
880 unsigned long now;
881 short error;
882 char *data = NULL;
883
884 if ((len = bile_read_alloc(db->bile, DB_TEXT_TYPE,
885 DB_TEXT_NO_FREE_NODES_ID, &data)) == 0) {
886 len = snprintf((char *)&node->obuf, sizeof(node->obuf),
887 "No free nodes, please call back later.\r\n");
888 } else {
889 for (n = 0, olen = 0; n < len && olen < sizeof(node->obuf) - 1;
890 n++) {
891 node->obuf[olen++] = data[n];
892 if (data[n] == '\r' && data[n + 1] != '\n')
893 node->obuf[olen++] = '\n';
894 }
895 xfree(&data);
896 }
897
898 node->tcp_wds[0].ptr = (Ptr)&node->obuf;
899 node->tcp_wds[0].length = len;
900 node->tcp_wds[1].ptr = 0;
901 node->tcp_wds[1].length = 0;
902
903 now = Ticks;
904 error = _TCPSend(&node->send_pb, node->stream, node->tcp_wds, nil, nil,
905 true);
906 if (error)
907 return;
908
909 for (;;) {
910 /* spin for 500ms to let the send finish */
911 if (Ticks - now > 30 || node->send_pb.ioResult <= 0)
912 return;
913 }
914 }