AmendHub

Download

jcs

/

subtext

/

telnet.c

 

(View History)

jcs   telnet: Speed up initial IAC negotiation to avoid delay after connect Latest amendment: 573 on 2023-12-02

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