source: trunk/minix/commands/telnet/ttn.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 12.3 KB
Line 
1/*
2ttn.c
3*/
4
5#ifndef _POSIX_SOURCE
6#define _POSIX_SOURCE 1
7#endif
8
9#include <sys/types.h>
10#include <sys/ioctl.h>
11#include <assert.h>
12#include <errno.h>
13#include <fcntl.h>
14#include <termios.h>
15#include <signal.h>
16#include <stdarg.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <net/hton.h>
22#include <net/netlib.h>
23#include <net/gen/in.h>
24#include <net/gen/inet.h>
25#include <net/gen/netdb.h>
26#include <net/gen/tcp.h>
27#include <net/gen/tcp_io.h>
28#include "ttn.h"
29
30#if __STDC__
31#define PROTOTYPE(func,args) func args
32#else
33#define PROTOTYPE(func,args) func()
34#endif
35
36static int do_read(int fd, char *buf, unsigned len);
37static void screen(void);
38static void keyboard(void);
39static void send_brk(void);
40static int process_opt (char *bp, int count);
41static void do_option (int optsrt);
42static void dont_option (int optsrt);
43static void will_option (int optsrt);
44static void wont_option (int optsrt);
45static int writeall (int fd, char *buffer, int buf_size);
46static int sb_termtype (char *sb, int count);
47static void fatal(char *fmt, ...);
48static void usage(void);
49
50#if DEBUG
51#define where() (fprintf(stderr, "%s %d:", __FILE__, __LINE__))
52#endif
53
54static char *prog_name;
55static tcp_fd;
56static char *term_env;
57static int esc_char= '~';
58static enum { LS_NORM, LS_BOL, LS_ESC } line_state= LS_BOL;
59
60int main(int argc, char *argv[])
61{
62 struct hostent *hostent;
63 struct servent *servent;
64 ipaddr_t host;
65 tcpport_t port;
66 int pid, ppid;
67 nwio_tcpconf_t tcpconf;
68 int c, r;
69 nwio_tcpcl_t tcpconnopt;
70 struct termios termios;
71 char *tcp_device, *remote_name, *port_name;
72 char *e_arg;
73
74 (prog_name=strrchr(argv[0],'/')) ? prog_name++ : (prog_name=argv[0]);
75
76 e_arg= NULL;
77 while (c= getopt(argc, argv, "?e:"), c != -1)
78 {
79 switch(c)
80 {
81 case '?': usage();
82 case 'e': e_arg= optarg; break;
83 default:
84 fatal("Optind failed: '%c'", c);
85 }
86 }
87
88 if (optind >= argc)
89 usage();
90 remote_name= argv[optind++];
91 if (optind < argc)
92 port_name= argv[optind++];
93 else
94 port_name= NULL;
95 if (optind != argc)
96 usage();
97
98 if (e_arg)
99 {
100 switch(strlen(e_arg))
101 {
102 case 0: esc_char= -1; break;
103 case 1: esc_char= e_arg[0]; break;
104 default: fatal("Invalid escape character '%s'", e_arg);
105 }
106 }
107
108 hostent= gethostbyname(remote_name);
109 if (!hostent)
110 fatal("Unknown host %s", remote_name);
111 host= *(ipaddr_t *)(hostent->h_addr);
112
113 if (!port_name)
114 port= htons(TCPPORT_TELNET);
115 else
116 {
117 servent= getservbyname (port_name, "tcp");
118 if (!servent)
119 {
120 port= htons(strtol(port_name, (char **)0, 0));
121 if (!port)
122 fatal("Unknown port %s", port_name);
123 }
124 else
125 port= (tcpport_t)(servent->s_port);
126 }
127
128 fprintf(stderr, "Connecting to %s:%u...\n",
129 inet_ntoa(host), ntohs(port));
130
131 tcp_device= getenv("TCP_DEVICE");
132 if (tcp_device == NULL)
133 tcp_device= TCP_DEVICE;
134 tcp_fd= open (tcp_device, O_RDWR);
135 if (tcp_fd == -1)
136 fatal("Unable to open %s: %s", tcp_device, strerror(errno));
137
138 tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP;
139 tcpconf.nwtc_remaddr= host;
140 tcpconf.nwtc_remport= port;
141
142 r= ioctl (tcp_fd, NWIOSTCPCONF, &tcpconf);
143 if (r == -1)
144 fatal("NWIOSTCPCONF failed: %s", strerror(errno));
145
146 tcpconnopt.nwtcl_flags= 0;
147 do
148 {
149 r= ioctl (tcp_fd, NWIOTCPCONN, &tcpconnopt);
150 if (r == -1 && errno == EAGAIN)
151 {
152 fprintf(stderr, "%s: Got EAGAIN, sleeping(1s)\n",
153 prog_name);
154 sleep(1);
155 }
156 } while (r == -1 && errno == EAGAIN);
157 if (r == -1)
158 fatal("Unable to connect: %s", strerror(errno));
159 printf("Connected\n");
160 ppid= getpid();
161 pid= fork();
162 switch(pid)
163 {
164 case 0:
165 keyboard();
166#if DEBUG
167fprintf(stderr, "killing %d with %d\r\n", ppid, SIGKILL);
168#endif
169 kill(ppid, SIGKILL);
170 break;
171 case -1:
172 fprintf(stderr, "%s: fork failed: %s\r\n", argv[0],
173 strerror(errno));
174 exit(1);
175 break;
176 default:
177 tcgetattr(0, &termios);
178 screen();
179#if DEBUG
180fprintf(stderr, "killing %d with %d\r\n", pid, SIGKILL);
181#endif
182 kill(pid, SIGKILL);
183 tcsetattr(0, TCSANOW, &termios);
184 break;
185 }
186 exit(0);
187}
188
189static int do_read(fd, buf, len)
190int fd;
191char *buf;
192unsigned len;
193{
194 nwio_tcpopt_t tcpopt;
195 int count;
196
197 for (;;)
198 {
199 count= read (fd, buf, len);
200 if (count <0)
201 {
202 if (errno == EURG || errno == ENOURG)
203 {
204 /* Toggle urgent mode. */
205 tcpopt.nwto_flags= errno == EURG ?
206 NWTO_RCV_URG : NWTO_RCV_NOTURG;
207 if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) == -1)
208 {
209 return -1;
210 }
211 continue;
212 }
213 return -1;
214 }
215 return count;
216 }
217}
218
219static void screen()
220{
221 char buffer[1024], *bp, *iacptr;
222 int count, optsize;
223
224 for (;;)
225 {
226 count= do_read (tcp_fd, buffer, sizeof(buffer));
227#if DEBUG && 0
228 { where(); fprintf(stderr, "read %d bytes\r\n", count); }
229#endif
230 if (count <0)
231 {
232 perror ("read");
233 return;
234 }
235 if (!count)
236 return;
237 bp= buffer;
238 do
239 {
240 iacptr= memchr (bp, IAC, count);
241 if (!iacptr)
242 {
243 write(1, bp, count);
244 count= 0;
245 }
246 if (iacptr && iacptr>bp)
247 {
248#if DEBUG
249 { where(); fprintf(stderr, "iacptr-bp= %d\r\n", iacptr-bp); }
250#endif
251 write(1, bp, iacptr-bp);
252 count -= (iacptr-bp);
253 bp= iacptr;
254 continue;
255 }
256 if (iacptr)
257 {
258assert (iacptr == bp);
259 optsize= process_opt(bp, count);
260#if DEBUG && 0
261 { where(); fprintf(stderr, "process_opt(...)= %d\r\n", optsize); }
262#endif
263 if (optsize<0)
264 return;
265assert (optsize);
266 bp += optsize;
267 count -= optsize;
268 }
269 } while (count);
270 }
271}
272
273static void keyboard()
274{
275 char c, buffer[1024];
276 int count;
277
278 for (;;)
279 {
280 count= read (0, buffer, 1 /* sizeof(buffer) */);
281 if (count == -1)
282 fatal("Read: %s\r\n", strerror(errno));
283 if (!count)
284 return;
285
286 if (line_state != LS_NORM)
287 {
288 c= buffer[0];
289 if (line_state == LS_BOL)
290 {
291 if (c == esc_char)
292 {
293 line_state= LS_ESC;
294 continue;
295 }
296 line_state= LS_NORM;
297 }
298 else if (line_state == LS_ESC)
299 {
300 line_state= LS_NORM;
301 if (c == '.')
302 return;
303 if (c == '#')
304 {
305 send_brk();
306 continue;
307 }
308
309 /* Not a valid command or a repeat of the
310 * escape char
311 */
312 if (c != esc_char)
313 {
314 c= esc_char;
315 write(tcp_fd, &c, 1);
316 }
317 }
318 }
319 if (buffer[0] == '\n')
320 write(tcp_fd, "\r", 1);
321 count= write(tcp_fd, buffer, count);
322 if (buffer[0] == '\r')
323 {
324 line_state= LS_BOL;
325 write(tcp_fd, "\0", 1);
326 }
327 if (count<0)
328 {
329 perror("write");
330 fprintf(stderr, "errno= %d\r\n", errno);
331 return;
332 }
333 if (!count)
334 return;
335 }
336}
337
338static void send_brk(void)
339{
340 int r;
341 unsigned char buffer[2];
342
343 buffer[0]= IAC;
344 buffer[1]= IAC_BRK;
345
346 r= writeall(tcp_fd, (char *)buffer, 2);
347 if (r == -1)
348 fatal("Error writing to TCP connection: %s", strerror(errno));
349}
350
351#define next_char(var) \
352 if (offset<count) { (var) = bp[offset++]; } \
353 else if (do_read(tcp_fd, (char *)&(var), 1) <= 0) \
354 { perror ("read"); return -1; }
355
356static int process_opt (char *bp, int count)
357{
358 unsigned char iac, command, optsrt, sb_command;
359 int offset, result; ;
360#if DEBUG && 0
361 { where(); fprintf(stderr, "process_opt(bp= 0x%x, count= %d)\r\n",
362 bp, count); }
363#endif
364
365 offset= 0;
366assert (count);
367 next_char(iac);
368assert (iac == IAC);
369 next_char(command);
370 switch(command)
371 {
372 case IAC_NOP:
373 break;
374 case IAC_DataMark:
375 /* Ought to flush input queue or something. */
376 break;
377 case IAC_BRK:
378fprintf(stderr, "got a BRK\r\n");
379 break;
380 case IAC_IP:
381fprintf(stderr, "got a IP\r\n");
382 break;
383 case IAC_AO:
384fprintf(stderr, "got a AO\r\n");
385 break;
386 case IAC_AYT:
387fprintf(stderr, "got a AYT\r\n");
388 break;
389 case IAC_EC:
390fprintf(stderr, "got a EC\r\n");
391 break;
392 case IAC_EL:
393fprintf(stderr, "got a EL\r\n");
394 break;
395 case IAC_GA:
396fprintf(stderr, "got a GA\r\n");
397 break;
398 case IAC_SB:
399 next_char(sb_command);
400 switch (sb_command)
401 {
402 case OPT_TERMTYPE:
403#if DEBUG && 0
404fprintf(stderr, "got SB TERMINAL-TYPE\r\n");
405#endif
406 result= sb_termtype(bp+offset, count-offset);
407 if (result<0)
408 return result;
409 else
410 return result+offset;
411 default:
412fprintf(stderr, "got an unknown SB (skiping)\r\n");
413 for (;;)
414 {
415 next_char(iac);
416 if (iac != IAC)
417 continue;
418 next_char(optsrt);
419 if (optsrt == IAC)
420 continue;
421if (optsrt != IAC_SE)
422 fprintf(stderr, "got IAC %d\r\n", optsrt);
423 break;
424 }
425 }
426 break;
427 case IAC_WILL:
428 next_char(optsrt);
429 will_option(optsrt);
430 break;
431 case IAC_WONT:
432 next_char(optsrt);
433 wont_option(optsrt);
434 break;
435 case IAC_DO:
436 next_char(optsrt);
437 do_option(optsrt);
438 break;
439 case IAC_DONT:
440 next_char(optsrt);
441 dont_option(optsrt);
442 break;
443 case IAC:
444fprintf(stderr, "got a IAC\r\n");
445 break;
446 default:
447fprintf(stderr, "got unknown command (%d)\r\n", command);
448 }
449 return offset;
450}
451
452static void do_option (int optsrt)
453{
454 unsigned char reply[3];
455 int result;
456
457 switch (optsrt)
458 {
459 case OPT_TERMTYPE:
460 if (WILL_terminal_type)
461 return;
462 if (!WILL_terminal_type_allowed)
463 {
464 reply[0]= IAC;
465 reply[1]= IAC_WONT;
466 reply[2]= optsrt;
467 }
468 else
469 {
470 WILL_terminal_type= TRUE;
471 term_env= getenv("TERM");
472 if (!term_env)
473 term_env= "unknown";
474 reply[0]= IAC;
475 reply[1]= IAC_WILL;
476 reply[2]= optsrt;
477 }
478 break;
479 default:
480#if DEBUG
481 fprintf(stderr, "got a DO (%d)\r\n", optsrt);
482 fprintf(stderr, "WONT (%d)\r\n", optsrt);
483#endif
484 reply[0]= IAC;
485 reply[1]= IAC_WONT;
486 reply[2]= optsrt;
487 break;
488 }
489 result= writeall(tcp_fd, (char *)reply, 3);
490 if (result<0)
491 perror("write");
492}
493
494static void will_option (int optsrt)
495{
496 unsigned char reply[3];
497 int result;
498
499 switch (optsrt)
500 {
501 case OPT_ECHO:
502 if (DO_echo)
503 break;
504 if (!DO_echo_allowed)
505 {
506 reply[0]= IAC;
507 reply[1]= IAC_DONT;
508 reply[2]= optsrt;
509 }
510 else
511 {
512 struct termios termios;
513
514 tcgetattr(0, &termios);
515 termios.c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF);
516 termios.c_oflag &= ~(OPOST);
517 termios.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
518 tcsetattr(0, TCSANOW, &termios);
519
520 DO_echo= TRUE;
521 reply[0]= IAC;
522 reply[1]= IAC_DO;
523 reply[2]= optsrt;
524 }
525 result= writeall(tcp_fd, (char *)reply, 3);
526 if (result<0)
527 perror("write");
528 break;
529 case OPT_SUPP_GA:
530 if (DO_suppress_go_ahead)
531 break;
532 if (!DO_suppress_go_ahead_allowed)
533 {
534 reply[0]= IAC;
535 reply[1]= IAC_DONT;
536 reply[2]= optsrt;
537 }
538 else
539 {
540 DO_suppress_go_ahead= TRUE;
541 reply[0]= IAC;
542 reply[1]= IAC_DO;
543 reply[2]= optsrt;
544 }
545 result= writeall(tcp_fd, (char *)reply, 3);
546 if (result<0)
547 perror("write");
548 break;
549 default:
550#if DEBUG
551 fprintf(stderr, "got a WILL (%d)\r\n", optsrt);
552 fprintf(stderr, "DONT (%d)\r\n", optsrt);
553#endif
554 reply[0]= IAC;
555 reply[1]= IAC_DONT;
556 reply[2]= optsrt;
557 result= writeall(tcp_fd, (char *)reply, 3);
558 if (result<0)
559 perror("write");
560 break;
561 }
562}
563
564static int writeall (fd, buffer, buf_size)
565int fd;
566char *buffer;
567int buf_size;
568{
569 int result;
570
571 while (buf_size)
572 {
573 result= write (fd, buffer, buf_size);
574 if (result <= 0)
575 return -1;
576assert (result <= buf_size);
577 buffer += result;
578 buf_size -= result;
579 }
580 return 0;
581}
582
583static void dont_option (int optsrt)
584{
585 switch (optsrt)
586 {
587 default:
588#if DEBUG
589 fprintf(stderr, "got a DONT (%d)\r\n", optsrt);
590#endif
591 break;
592 }
593}
594
595static void wont_option (int optsrt)
596{
597 switch (optsrt)
598 {
599 default:
600#if DEBUG
601 fprintf(stderr, "got a WONT (%d)\r\n", optsrt);
602#endif
603 break;
604 }
605}
606
607static int sb_termtype (char *bp, int count)
608{
609 unsigned char command, iac, optsrt;
610 unsigned char buffer[4];
611 int offset, result;
612
613 offset= 0;
614 next_char(command);
615 if (command == TERMTYPE_SEND)
616 {
617 buffer[0]= IAC;
618 buffer[1]= IAC_SB;
619 buffer[2]= OPT_TERMTYPE;
620 buffer[3]= TERMTYPE_IS;
621 result= writeall(tcp_fd, (char *)buffer,4);
622 if (result<0)
623 return result;
624 count= strlen(term_env);
625 if (!count)
626 {
627 term_env= "unknown";
628 count= strlen(term_env);
629 }
630 result= writeall(tcp_fd, term_env, count);
631 if (result<0)
632 return result;
633 buffer[0]= IAC;
634 buffer[1]= IAC_SE;
635 result= writeall(tcp_fd, (char *)buffer,2);
636 if (result<0)
637 return result;
638
639 }
640 else
641 {
642#if DEBUG
643 where();
644#endif
645 fprintf(stderr, "got an unknown command (skipping)\r\n");
646 }
647 for (;;)
648 {
649 next_char(iac);
650 if (iac != IAC)
651 continue;
652 next_char(optsrt);
653 if (optsrt == IAC)
654 continue;
655 if (optsrt != IAC_SE)
656 {
657#if DEBUG
658 where();
659#endif
660 fprintf(stderr, "got IAC %d\r\n", optsrt);
661 }
662 break;
663 }
664 return offset;
665}
666
667static void fatal(char *fmt, ...)
668{
669 va_list ap;
670
671 va_start(ap, fmt);
672 fprintf(stderr, "%s: ", prog_name);
673 vfprintf(stderr, fmt, ap);
674 fprintf(stderr, "\n");
675 va_end(ap);
676
677 exit(1);
678}
679
680static void usage(void)
681{
682 fprintf(stderr, "Usage: %s [-e esc-char] host [port]\r\n",
683 prog_name);
684 exit(1);
685}
686
687/*
688 * $PchId: ttn.c,v 1.5 2002/05/07 12:06:41 philip Exp $
689 */
Note: See TracBrowser for help on using the repository browser.