1 /*
2 * tcp.c -- telnet protocol communication module for powwow
3 *
4 * Copyright (C) 1998,2002 by Massimiliano Ghilardi
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <signal.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <netdb.h>
24 #include <netinet/in.h>
25 #include <netinet/tcp.h>
26 #include <arpa/telnet.h>
27 #ifndef TELOPT_NAWS
28 # define TELOPT_NAWS 31
29 #endif
30 #include <arpa/inet.h>
31 #ifndef NEXT
32 # include <unistd.h>
33 #endif
34
35 #ifdef TERM
36 # include "client.h"
37 #endif
38
39 #include "defines.h"
40 #include "main.h"
41 #include "utils.h"
42 #include "tcp.h"
43 #include "tty.h"
44 #include "edit.h"
45 #include "beam.h"
46 #include "log.h"
47
48 #ifdef TELOPTS
49 # define TELOPTSTR(n) ((n) > NTELOPTS ? "unknown" : telopts[n])
50 #endif
51
52 int tcp_fd = -1; /* current socket file descriptor
53 * -1 means no socket */
54 int tcp_main_fd = -1; /* socket file descriptor of main connect.
55 * -1 means no socket */
56 int tcp_max_fd = 0; /* highest used fd */
57
58 int tcp_count = 0; /* number of open connections */
59 int tcp_attachcount = 0; /* number of spawned commands */
60
61 int conn_max_index; /* 1 + highest used conn_list[] index */
62
63 connsess conn_list[MAX_CONNECTS]; /* connection list */
64
65 byte conn_table[MAX_FDSCAN]; /* fd -> index translation table */
66
67 fd_set fdset; /* set of descriptors to select() on */
68
69 /*
70 * process suboptions.
71 * so far, only terminal type is processed but future extensions are
72 * window size, X display location, etc.
73 */
__P1(byte *,str)74 static void dosubopt __P1 (byte *,str)
75 {
76 char buf[256], *term;
77 int len, err;
78
79 if (str[0] == TELOPT_TTYPE) {
80 if (str[1] == 1) {
81 /* 1 == SEND */
82 #ifdef TELOPTS
83 tty_printf("[got SB TERMINAL TYPE SEND]\n");
84 #endif
85 if (!(term = getenv("TERM"))) term = "unknown";
86 sprintf(buf, "%c%c%c%c%.*s%c%c", IAC, SB, TELOPT_TTYPE, 0,
87 256-7, term, IAC, SE); /* 0 == IS */
88
89 len = strlen(term) + 6;
90 while ((err = write(tcp_fd, buf, len)) < 0 && errno == EINTR)
91 ;
92 if (err != len) {
93 errmsg("write subopt to socket");
94 return;
95 }
96 #ifdef TELOPTS
97 tty_printf("[sent SB TERMINAL TYPE IS %s]\n", term);
98 #endif
99 }
100 }
101 }
102
103 /*
104 * send an option negotiation
105 * 'what' is one of WILL, WONT, DO, DONT
106 */
__P2(byte,what,byte,opt)107 static void sendopt __P2 (byte,what, byte,opt)
108 {
109 static byte buf[3] = { IAC, 0, 0 };
110 int i;
111 buf[1] = what; buf[2] = opt;
112
113 while ((i = write(tcp_fd, buf, 3)) < 0 && errno == EINTR)
114 ;
115 if (i != 3) {
116 errmsg("write option to socket");
117 return;
118 }
119
120 #ifdef TELOPTS
121 tty_printf("[sent %s %s]\n", (what == WILL) ? "WILL" :
122 (what == WONT) ? "WONT" :
123 (what == DO) ? "DO" : (what == DONT) ? "DONT" : "error",
124 TELOPTSTR(opt));
125 #endif
126 }
127
128 /*
129 * connect to remote host
130 * Warning: some voodoo code here
131 */
__P2(char *,addr,int,port)132 int tcp_connect __P2 (char *,addr, int,port)
133 {
134 struct sockaddr_in address;
135 struct hostent *host_info;
136 int err, newtcp_fd;
137
138 status(1);
139
140 memzero((char *)&address, sizeof(address));
141 /*
142 * inet_addr has a strange design: It is documented to return -1 for
143 * malformed requests, but it is declared to return unsigned long!
144 * Anyway, this works.
145 */
146
147 #ifndef TERM
148 address.sin_addr.s_addr = inet_addr(addr);
149 if (address.sin_addr.s_addr != (unsigned int)-1)
150 address.sin_family = AF_INET;
151 else
152 {
153 if (opt_info)
154 tty_printf("#looking up %s... ", addr);
155 tty_flush();
156 host_info = gethostbyname(addr);
157 if (host_info == 0) {
158 if (!opt_info) {
159 tty_printf("#looking up %s... ", addr);
160 }
161 tty_printf("unknown host!\n");
162 return -1;
163 }
164 memmove((char *)&address.sin_addr, host_info->h_addr,
165 host_info->h_length);
166 address.sin_family = host_info->h_addrtype;
167 if (opt_info)
168 tty_puts("found.\n");
169 }
170 address.sin_port = htons(port);
171
172 newtcp_fd = socket(address.sin_family, SOCK_STREAM, 0);
173 if (newtcp_fd == -1) {
174 errmsg("create socket");
175 return -1;
176 } else if (newtcp_fd >= MAX_FDSCAN) {
177 tty_printf("#connect: #error: too many open connections\n");
178 close(newtcp_fd);
179 return -1;
180 }
181
182 tty_printf("#trying %s... ", addr);
183 tty_flush();
184
185 err = connect(newtcp_fd, (struct sockaddr *)&address, sizeof(address));
186
187 if (err == -1) { /* CTRL-C pressed, or other errors */
188 errmsg("connect to host");
189 close(newtcp_fd);
190 return -1;
191 }
192
193 #else /* term */
194
195 if ((newtcp_fd = connect_server(0)) < 0) {
196 tty_puts("\n#powwow: unable to connect to term server\n");
197 return -1;
198 } else {
199 if (newtcp_fd >= MAX_FDSCAN) {
200 tty_printf("#connect: #error: too many open connections\n");
201 close(newtcp_fd);
202 return -1;
203 }
204 send_command(newtcp_fd, C_PORT, 0, "%s:%d", addr, port);
205 tty_puts("Connected to term server...\n");
206 #ifdef TERM_COMPRESS
207 send_command(newtcp_fd, C_COMPRESS, 1, "y");
208 #endif
209 send_command(newtcp_fd, C_DUMB, 1, 0);
210 }
211
212 #endif /* term */
213
214 tty_puts("connected!\n");
215
216
217 {
218 /*
219 * Now set some options on newtcp_fd :
220 * First, no-nagle
221 */
222 int opt = 1;
223 # ifndef SOL_TCP
224 # define SOL_TCP IPPROTO_TCP
225 # endif
226 if (setsockopt(newtcp_fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)))
227 errmsg("setsockopt(TCP_NODELAY) failed");
228
229 /* TCP keep-alive */
230 if (setsockopt(newtcp_fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)))
231 errmsg("setsockopt(SO_KEEPALIVE) failed");
232
233 /*
234 * Then, close-on-exec:
235 * we don't want children to inherit the socket!
236 */
237
238 fcntl(newtcp_fd, F_SETFD, FD_CLOEXEC);
239 }
240
241 return newtcp_fd;
242 }
243
244 /*
245 * we don't expect IAC commands here, except IAC IAC (a protected ASCII 255)
246 * which we replace with a single IAC (a plain ASCII 255)
247 */
__P2(char *,buffer,int,len)248 int tcp_unIAC __P2 (char *,buffer, int,len)
249 {
250 char *s, *start, warnIACs = 1;
251 if (!memchr(buffer, IAC, len))
252 return len;
253
254 for (s = start = buffer; len > 0; buffer++, len--) {
255 if (buffer[0] == (char)(byte)IAC) {
256 if (len > 1 && buffer[1] == (char)(byte)IAC)
257 buffer++, len--;
258 else if (warnIACs) {
259 PRINTF("#warning: received IAC inside MPI message, treating as IAC IAC.\n");
260 warnIACs = 0;
261 }
262 }
263 *s++ = *buffer;
264 }
265 return s - start;
266 }
267
268 /*
269 * the reverse step: protect ASCII 255 as IAC IAC
270 * the dest buffer is assumed to be big enough to hold the whole data
271 */
__P3(char *,dest,char *,txt,int,len)272 static int tcp_addIAC __P3 (char *,dest, char *,txt, int,len)
273 {
274 char *s = dest;
275 while (len-- > 0) {
276 if ((*s++ = *txt++) == (char)(byte)IAC)
277 *s++ = (char)(byte)IAC;
278 }
279 return s - dest;
280 }
281
282 /*
283 * read from an fd, protecting ASCII 255 as IAC IAC while we read.
284 * the buffer is assumed to be big enough to hold the whole file
285 */
__P3(int,fd,char *,data,int,len)286 int tcp_read_addIAC __P3 (int,fd, char *,data, int,len)
287 {
288 char *s = data;
289 char buf[BUFSIZE];
290 int i;
291
292 while (len > 0) {
293 while ((i = read(fd, buf, MIN2(len, BUFSIZE))) < 0 && errno == EINTR)
294 ;
295 if (i < 0) {
296 errmsg("read from file");
297 return -1;
298 } else if (i == 0)
299 break;
300 s += tcp_addIAC(s, buf, i);
301 len -= i;
302 }
303 return s - data;
304 }
305
306 /*
307 * read a maximum of size chars from remote host
308 * using the telnet protocol. return chars read.
309 */
__P3(int,fd,char *,buffer,int,maxsize)310 int tcp_read __P3 (int,fd, char *,buffer, int,maxsize)
311 {
312 char state = CONN_LIST(fd).state;
313 int i;
314 static byte subopt[MAX_SUBOPT];
315 static int subchars;
316 byte *p, *s, *linestart;
317
318 while ((i = read(fd, buffer, maxsize)) < 0 && errno == EINTR)
319 ;
320
321 if (i == 0) {
322 CONN_LIST(fd).state = NORMAL;
323 tcp_close(NULL);
324 return 0;
325 }
326 if (i < 0) {
327 errmsg("read from socket");
328 return 0;
329 }
330
331 /*
332 * scan through the buffer,
333 * interpret telnet protocol escapes and MUME MPI messages
334 */
335 for (s = p = linestart = (byte *)buffer; i; s++, i--) {
336 switch (state) {
337 case NORMAL:
338 case ALTNORMAL:
339 case GOT_R:
340 case GOT_N:
341 /*
342 * Some servers like to send NULs and other funny chars.
343 * Clean up as much as possible.
344 */
345 switch (*s) {
346 case IAC:
347 state = GOTIAC;
348 break;
349 case '\r':
350 /* start counting \r, unless just got \n */
351 if (state == NORMAL || state == ALTNORMAL) {
352 /*
353 * ALTNORMAL is safe here: \r cannot be in MPI header,
354 * and any previous MPI header has already been rejected
355 */
356 state = GOT_R;
357 } else if (state == GOT_N)
358 /* after \n\r, we forbid MPI messages */
359 state = ALTNORMAL;
360 break;
361 case '\n':
362 state = GOT_N;
363 *p++ = *s;
364 linestart = p;
365 break;
366 case '\0':
367 /* skip NULs */
368 break;
369 default:
370 /* first, flush any missing \r */
371 if (state == GOT_R)
372 *p++ = '\r';
373
374 *p++ = *s;
375
376 /* check for MUME MPI messages: */
377 if (p - linestart == MPILEN && !memcmp(linestart, MPI, MPILEN)) {
378 if (!(CONN_LIST(fd).flags & IDEDITOR)) {
379 PRINTF("#warning: MPI message received without #request editor!\n");
380 } else if (state == ALTNORMAL) {
381 /* no MPI messages after \n\r */
382 PRINTF("#warning: MPI attack?\n");
383 } else {
384 subchars = process_message((char*)s+1, i-1);
385 /* no +MPILEN here, as it was already processed. */
386 s += subchars; i-= subchars;
387 p = linestart;
388 }
389 }
390
391 if (state != ALTNORMAL)
392 state = NORMAL;
393 break;
394 }
395 break;
396
397 case GOTIAC:
398 switch (*s) {
399 case WILL:
400 state = GOTWILL; break;
401 case WONT:
402 state = GOTWONT; break;
403 case DO:
404 state = GOTDO; break;
405 case DONT:
406 state = GOTDONT; break;
407 case SB: /* BUG (multiple connections): */
408 state = GOTSB; /* there is only one subopt buffer */
409 subchars = 0;
410 break;
411 case IAC:
412 *p++ = IAC;
413 state = NORMAL;
414 break;
415 case GA:
416 /* I should handle GA as end-of-prompt marker one day */
417 /* one day has come ;) - Max */
418 prompt_set_iac((char*)p);
419 state = NORMAL;
420 break;
421 default:
422 /* ignore the rest of the telnet commands */
423 #ifdef TELOPTS
424 tty_printf("[skipped IAC <%d>]\n", *s);
425 #endif
426 state = NORMAL;
427 break;
428 }
429 break;
430
431 case GOTWILL:
432 #ifdef TELOPTS
433 tty_printf("[got WILL %s]\n", TELOPTSTR(*s));
434 #endif
435 switch(*s) {
436 case TELOPT_ECHO:
437 /* host echoes, turn off echo here
438 * but only for main connection, since we do not want
439 * subsidiary connection password entries to block anything
440 * in the main connection
441 */
442 if (fd == tcp_main_fd)
443 linemode |= LM_NOECHO;
444 sendopt(DO, *s);
445 break;
446 case TELOPT_SGA:
447 /* this can't hurt */
448 linemode |= LM_CHAR;
449 tty_special_keys();
450 sendopt(DO, *s);
451 break;
452 default:
453 /* don't accept other options */
454 sendopt(DONT, *s);
455 break;
456 }
457 state = NORMAL;
458 break;
459
460 case GOTWONT:
461 #ifdef TELOPTS
462 tty_printf("[got WONT %s]\n", TELOPTSTR(*s));
463 #endif
464 if (*s == TELOPT_ECHO) {
465 /* host no longer echoes, we do it instead */
466 linemode &= ~LM_NOECHO;
467 }
468 /* accept any WONT */
469 sendopt(DONT, *s);
470 state = NORMAL;
471 break;
472
473 case GOTDO:
474 #ifdef TELOPTS
475 tty_printf("[got DO %s]\n", TELOPTSTR(*s));
476 #endif
477 switch(*s) {
478 case TELOPT_SGA:
479 linemode |= LM_CHAR;
480 tty_special_keys();
481 /* FALLTHROUGH */
482 case TELOPT_TTYPE:
483 sendopt(WILL, *s);
484 break;
485 case TELOPT_NAWS:
486 sendopt(WILL, *s);
487 tcp_write_tty_size();
488 break;
489 default:
490 /* accept nothing else */
491 sendopt(WONT, *s);
492 break;
493 }
494 state = NORMAL;
495 break;
496
497 case GOTDONT:
498 #ifdef TELOPTS
499 tty_printf("[got DONT %s]\n", TELOPTSTR(*s));
500 #endif
501 if (*s == TELOPT_SGA) {
502 linemode &= ~LM_CHAR;
503 tty_special_keys();
504 }
505 sendopt(WONT, *s);
506 state = NORMAL;
507 break;
508
509 case GOTSB:
510 if (*s == IAC) {
511 state = GOTSBIAC;
512 } else {
513 if (subchars < MAX_SUBOPT)
514 subopt[subchars++] = *s;
515 }
516 break;
517
518 case GOTSBIAC:
519 if (*s == IAC) {
520 if (subchars < MAX_SUBOPT)
521 subopt[subchars++] = IAC;
522 state = GOTSB;
523 } else if (*s == SE) {
524 subopt[subchars] = '\0';
525 dosubopt(subopt);
526 state = NORMAL;
527 } else {
528 /* problem! I haven't the foggiest idea of what to do here.
529 * I'll just ignore it and hope it goes away. */
530 PRINTF("#telnet: got IAC <%d> instead of IAC SE!\n", (int)*s);
531 state = NORMAL;
532 }
533 break;
534 }
535 }
536 CONN_LIST(fd).state = state;
537
538 if (!(CONN_LIST(tcp_fd).flags & SPAWN)) {
539 log_write(buffer, (char *)p - buffer, 0);
540 }
541
542 return (char *)p - buffer;
543 }
544
__P3(int,fd,const char *,data,int,len)545 static void internal_tcp_raw_write __P3 (int,fd, const char *,data, int,len)
546 {
547 while (len > 0) {
548 int i;
549 while ((i = write(fd, data, len)) < 0 && errno == EINTR)
550 ;
551 if (i < 0) {
552 errmsg("write to socket");
553 break;
554 }
555 data += i;
556 len -= i;
557 }
558 }
559
__P3(int,fd,const char *,data,int,len)560 void tcp_raw_write __P3 (int,fd, const char *,data, int,len)
561 {
562 tcp_flush();
563 internal_tcp_raw_write(fd, data, len);
564 }
565
566 /* write data, escape any IACs */
__P3(int,fd,const char *,data,int,len)567 void tcp_write_escape_iac __P3 (int,fd, const char *,data, int,len)
568 {
569 tcp_flush();
570
571 for (;;) {
572 const char *iac = memchr(data, IAC, len);
573 size_t l = iac ? (iac - data) + 1 : len;
574 internal_tcp_raw_write(fd, data, l);
575 if (iac == NULL)
576 return;
577 internal_tcp_raw_write(fd, iac, 1);
578 len -= l;
579 data = iac + 1;
580 }
581 }
582
583 /*
584 * Send current terminal size (RFC 1073)
585 */
__P0(void)586 void tcp_write_tty_size __P0 (void)
587 {
588 static byte buf[] = { IAC, SB, TELOPT_NAWS, 0, 0, 0, 0, IAC, SE };
589
590 buf[3] = cols >> 8;
591 buf[4] = cols & 0xff;
592 buf[5] = lines >> 8;
593 buf[6] = lines & 0xff;
594
595 tcp_raw_write(tcp_main_fd, (char *)buf, 9);
596 #ifdef TELOPTS
597 tty_printf("[sent term size %d %d]\n", cols, lines);
598 #endif
599 }
600
601 /*
602 * send a string to the main connection on the remote host
603 */
__P1(char *,data)604 void tcp_main_write __P1 (char *,data)
605 {
606 tcp_write(tcp_main_fd, data);
607 }
608
609
610 static char output_buffer[BUFSIZE];
611 static int output_len = 0; /* number of characters in output_buffer */
612 static int output_socket = -1; /* to which socket buffer should be sent*/
613
614 /*
615 * put data in the output buffer for transmission to the remote host
616 */
__P2(int,fd,char *,data)617 void tcp_write __P2 (int,fd, char *,data)
618 {
619 char *iacs, *out;
620 int len, space, iacp;
621 len = strlen(data);
622
623 if (tcp_main_fd != -1 && tcp_main_fd == fd) {
624 if (linemode & LM_NOECHO)
625 log_write("", 0, 1); /* log a newline only */
626 else
627 log_write(data, len, 1);
628 reprint_writeline(data);
629 }
630
631 /* must be AFTER reprint_writeline() */
632 if (CONN_LIST(tcp_fd).flags & SPAWN)
633 status(1);
634 else
635 status(-1);
636
637 if (fd != output_socket) { /* is there data to another socket? */
638 tcp_flush(); /* then flush it */
639 output_socket = fd;
640 }
641
642 out = output_buffer + output_len;
643 space = BUFSIZE - output_len;
644
645 while (len) {
646 iacs = memchr(data, IAC, len);
647 iacp = iacs ? iacs - data : len;
648
649 if (iacp == 0) {
650 /* we're at the IAC, send it */
651 if (space < 2) {
652 tcp_flush();
653 out = output_buffer;
654 space = BUFSIZE;
655 }
656 *out++ = (char)IAC; *out++ = (char)IAC; output_len += 2; space -= 2;
657 data++; len--;
658 continue;
659 }
660
661 while (space < iacp) {
662 memcpy(out, data, space);
663 data += space; len -= space;
664 iacp -= space;
665 output_len = BUFSIZE;
666
667 tcp_flush();
668 out = output_buffer;
669 space = BUFSIZE;
670 }
671
672 if (iacp /* && space >= iacp */ ) {
673 memcpy(out, data, iacp);
674 out += iacp; output_len += iacp; space -= iacp;
675 data += iacp; len -= iacp;
676 }
677 }
678 if (!space) {
679 tcp_flush();
680 out = output_buffer;
681 }
682 *out++ = '\n';
683 output_len++;
684 }
685
686 /*
687 * send all buffered data to the remote host
688 */
__P0(void)689 void tcp_flush __P0 (void)
690 {
691 int n;
692 char *p = output_buffer;
693
694 if (output_len && output_socket == -1) {
695 clear_input_line(1);
696 PRINTF("#no open connections. Use '#connect main <address> <port>' to open a connection.\n");
697 output_len = 0;
698 return;
699 }
700
701 if (!output_len)
702 return;
703
704 while (output_len) {
705 while ((n = write(output_socket, p, output_len)) < 0 && errno == EINTR)
706 ;
707 if (n < 0) {
708 output_len = 0;
709 errmsg("write to socket");
710 return;
711 }
712 sent += n;
713 p += n;
714 output_len -= n;
715 }
716
717 if (CONN_LIST(output_socket).flags & SPAWN)
718 status(1);
719 else
720 /* sent stuff, so we expect a prompt */
721 status(-1);
722 }
723
724 /*
725 * Below are multiple-connection support functions:
726 */
727
728 /*
729 * return connection's fd given id,
730 * or -1 if null or invalid id is given
731 */
__P1(char *,id)732 int tcp_find __P1 (char *,id)
733 {
734 int i;
735
736 for (i=0; i<conn_max_index; i++) {
737 if (CONN_INDEX(i).id && !strcmp(CONN_INDEX(i).id, id))
738 return CONN_INDEX(i).fd;
739 }
740 return -1;
741 }
742
743 /*
744 * show list of open connections
745 */
__P0(void)746 void tcp_show __P0 (void)
747 {
748 int i = tcp_count+tcp_attachcount;
749
750 PRINTF("#%s connection%s opened%c\n", i ? "The following" : "No",
751 i==1 ? " is" : "s are", i ? ':' : '.');
752
753
754 for (i=0; i<conn_max_index; i++)
755 if (CONN_INDEX(i).id && !(CONN_INDEX(i).flags & SPAWN)) {
756 tty_printf("MUD %sactive %s ##%s\t (%s %d)\n",
757 CONN_INDEX(i).flags & ACTIVE ? " " : "non",
758 i == tcp_main_fd ? "(default)" : " ",
759 CONN_INDEX(i).id,
760 CONN_INDEX(i).host, CONN_INDEX(i).port);
761 }
762 for (i=0; i<conn_max_index; i++)
763 if (CONN_INDEX(i).id && (CONN_INDEX(i).flags & SPAWN)) {
764 tty_printf("CMD %sactive %s ##%s\t (%s)\n",
765 CONN_INDEX(i).flags & ACTIVE ? " " : "non",
766 i == tcp_main_fd ? "(default)" : " ",
767 CONN_INDEX(i).id, CONN_INDEX(i).host);
768 }
769 }
770
771 /*
772 * permanently change main connection
773 */
__P1(int,fd)774 void tcp_set_main __P1 (int,fd)
775 {
776 /* GH: reset linemode and prompt */
777 tcp_main_fd = fd;
778 if (linemode & LM_CHAR)
779 linemode = 0, tty_special_keys();
780 else
781 linemode = 0;
782 status(-1);
783 reprint_clear();
784 }
785
786 /*
787 * open another connection
788 */
__P4(char *,id,char *,initstring,char *,host,int,port)789 void tcp_open __P4 (char *,id, char *,initstring, char *,host, int,port)
790 {
791 int newtcp_fd, i;
792
793 if (tcp_count+tcp_attachcount >= MAX_CONNECTS) {
794 PRINTF("#too many open connections.\n");
795 return;
796 }
797 if (tcp_find(id)>=0) {
798 PRINTF("#connection \"%s\" already open.\n", id);
799 return;
800 }
801
802 /* find a free slot */
803 for (i=0; i<MAX_CONNECTS; i++) {
804 if (!CONN_INDEX(i).id)
805 break;
806 }
807 if (i == MAX_CONNECTS) {
808 PRINTF("#internal error, connection table full :(\n");
809 return;
810 }
811
812 if (!(CONN_INDEX(i).host = my_strdup(host))) {
813 errmsg("malloc");
814 return;
815 }
816 if (!(CONN_INDEX(i).id = my_strdup(id))) {
817 errmsg("malloc");
818 free(CONN_INDEX(i).host);
819 return;
820 }
821
822 /* dial the number by moving the right index in small circles */
823 if ((newtcp_fd = tcp_connect(host, port)) < 0) {
824 free(CONN_INDEX(i).host);
825 free(CONN_INDEX(i).id);
826 CONN_INDEX(i).id = 0;
827 return;
828 }
829
830 conn_table[newtcp_fd] = i;
831 CONN_INDEX(i).flags = ACTIVE;
832 CONN_INDEX(i).state = NORMAL;
833 CONN_INDEX(i).port = port;
834 CONN_INDEX(i).fd = newtcp_fd;
835
836 if (tcp_max_fd < newtcp_fd)
837 tcp_max_fd = newtcp_fd;
838 if (conn_max_index <= i)
839 conn_max_index = i+1;
840
841 FD_SET(newtcp_fd, &fdset); /* add socket to select() set */
842 tcp_count++;
843
844 if (opt_info && tcp_count) {
845 PRINTF("#default connection is now \"%s\"\n", id);
846 }
847 tcp_set_main(tcp_fd = newtcp_fd);
848 if (opt_sendsize)
849 tcp_write_tty_size();
850
851 if (initstring) {
852 parse_instruction(initstring, 0, 0, 1);
853 history_done = 0;
854 }
855 }
856
857 /*
858 * close a connection
859 */
__P1(char *,id)860 void tcp_close __P1 (char *,id)
861 {
862 int i, sfd;
863
864 status(1);
865 tty_puts(edattrend);
866 /*
867 * because we may be called from get_remote_input()
868 * if tcp_read gets an EOF, before edattrend is
869 * printed by get_remote_input() itself.
870 */
871
872 if (id) { /* #zap cmd */
873 if ((sfd = tcp_find(id)) < 0) {
874 tty_printf("#no such connection: \"%s\"\n", id);
875 return;
876 }
877 } else
878 sfd = tcp_fd; /* connection closed by remote host */
879
880 shutdown(sfd, 2);
881 close(sfd);
882
883 abort_edit_fd(sfd);
884
885 tty_printf("#connection on \"%s\" closed.\n", CONN_LIST(sfd).id);
886
887 if (sfd == tcp_main_fd) { /* main connection closed */
888 if (tcp_count == 1) { /* was last connection */
889 if (opt_exit)
890 exit_powwow();
891 tty_puts("#no connections left. Type #quit to quit.\n");
892 tcp_fd = tcp_main_fd = -1;
893 } else {
894 /* must find another connection and promote it to main */
895 for (i=0; i<conn_max_index; i++) {
896 if (!CONN_INDEX(i).id || CONN_INDEX(i).fd == sfd
897 || (CONN_INDEX(i).flags & SPAWN))
898 continue;
899 tty_printf("#default connection is now \"%s\"\n", CONN_INDEX(i).id);
900 tcp_main_fd = CONN_INDEX(i).fd;
901 break;
902 }
903 if (sfd == tcp_main_fd) {
904 tty_printf("#PANIC! internal error in tcp_close()\nQuitting.\n");
905 syserr(NULL);
906 }
907 }
908 tcp_set_main(tcp_main_fd);
909 }
910
911 if (tcp_fd == sfd)
912 tcp_fd = -1; /* no further I/O allowed on sfd, as we just closed it */
913
914 FD_CLR(sfd, &fdset);
915 if (CONN_LIST(sfd).flags & SPAWN)
916 tcp_attachcount--;
917 else
918 tcp_count--;
919 CONN_LIST(sfd).flags = 0;
920 CONN_LIST(sfd).state = NORMAL;
921 CONN_LIST(sfd).port = 0;
922 free(CONN_LIST(sfd).host); CONN_LIST(sfd).host = 0;
923 free(CONN_LIST(sfd).id); CONN_LIST(sfd).id = 0;
924 if (CONN_LIST(sfd).fragment) {
925 free(CONN_LIST(sfd).fragment);
926 CONN_LIST(sfd).fragment = 0;
927 }
928
929 /* recalculate conn_max_index */
930 i = conn_table[sfd];
931 if (i+1 == conn_max_index) {
932 do {
933 i--;
934 } while (i>=0 && !CONN_INDEX(i).id);
935 conn_max_index = i+1;
936 }
937
938 /* recalculate tcp_max_fd */
939 for (i = tcp_max_fd = 0; i<conn_max_index; i++) {
940 if (CONN_INDEX(i).id && tcp_max_fd < CONN_INDEX(i).fd)
941 tcp_max_fd = CONN_INDEX(i).fd;
942 }
943 }
944
945 /*
946 * toggle output display from another connection
947 */
__P1(char *,id)948 void tcp_togglesnoop __P1 (char *,id)
949 {
950 int sfd;
951
952 sfd = tcp_find(id);
953 if (sfd>=0) {
954 CONN_LIST(sfd).flags ^= ACTIVE;
955 if (opt_info) {
956 PRINTF("#connection %s is now %sactive.\n",
957 CONN_LIST(sfd).id, CONN_LIST(sfd).flags & ACTIVE ? "" : "non");
958 }
959 } else {
960 PRINTF("#no such connection: %s\n", id);
961 }
962 }
963
__P2(char *,id,char *,cmd)964 void tcp_spawn __P2 (char *,id, char *,cmd)
965 {
966 int i, childpid, sockets[2];
967
968 if (tcp_find(id)>=0) {
969 PRINTF("#connection \"%s\" already open.\n", id);
970 return;
971 }
972 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
973 errmsg("create socketpair");
974 return;
975 }
976 unescape(cmd);
977
978 switch (childpid = fork()) {
979 case 0:
980 /* child */
981 close(0); close(1); close(2);
982 setsid();
983 dup2(sockets[1], 0);
984 dup2(sockets[1], 1);
985 dup2(sockets[1], 2);
986 close(sockets[0]);
987 close(sockets[1]);
988 execl("/bin/sh", "sh", "-c", cmd, NULL);
989 syserr("execl");
990 break;
991 case -1:
992 close(sockets[0]);
993 close(sockets[1]);
994 errmsg("fork");
995 return;
996 }
997 close(sockets[1]);
998
999 /* Again, we don't want children to inherit sockets */
1000 fcntl(sockets[0], F_SETFD, FD_CLOEXEC);
1001
1002 /* now find a free slot */
1003 for (i=0; i<MAX_CONNECTS; i++) {
1004 if (!CONN_INDEX(i).id) {
1005 conn_table[sockets[0]] = i;
1006 break;
1007 }
1008 }
1009 if (i == MAX_CONNECTS) {
1010 PRINTF("#internal error, connection table full :(\n");
1011 close(sockets[0]);
1012 return;
1013 }
1014
1015 if (!(CONN_INDEX(i).host = my_strdup(cmd))) {
1016 errmsg("malloc");
1017 close(sockets[0]);
1018 return;
1019 }
1020 if (!(CONN_INDEX(i).id = my_strdup(id))) {
1021 errmsg("malloc");
1022 free(CONN_INDEX(i).host);
1023 close(sockets[0]);
1024 return;
1025 }
1026 CONN_INDEX(i).flags = ACTIVE | SPAWN;
1027 CONN_INDEX(i).state = NORMAL;
1028 CONN_INDEX(i).port = 0;
1029 CONN_INDEX(i).fd = sockets[0];
1030
1031 FD_SET(sockets[0], &fdset); /* add socket to select() set */
1032 tcp_attachcount++;
1033
1034 if (tcp_max_fd < sockets[0])
1035 tcp_max_fd = sockets[0];
1036 if (conn_max_index <= i)
1037 conn_max_index = i+1;
1038
1039 if (opt_info) {
1040 PRINTF("#successfully spawned \"%s\" with pid %d\n", id, childpid);
1041 }
1042
1043 /*
1044 * when the child exits we also get an EOF on the socket,
1045 * so no special care is needed by the SIGCHLD handler.
1046 */
1047 }
1048
1049