AmendHub

Download

jcs

/

subtext

/

telnet.c

 

(View History)

jcs   telnet: Static init nit Latest amendment: 595 on 2024-02-19

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