opendir change: refinement
[minix.git] / commands / telnet / ttn.c
blobcabfb83e32a62e25b3460f8661fb9b753117d7c9
1 /*
2 ttn.c
3 */
5 #ifndef _POSIX_SOURCE
6 #define _POSIX_SOURCE 1
7 #endif
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"
30 #if __STDC__
31 #define PROTOTYPE(func,args) func args
32 #else
33 #define PROTOTYPE(func,args) func()
34 #endif
36 static int do_read(int fd, char *buf, unsigned len);
37 static void screen(void);
38 static void keyboard(void);
39 static void send_brk(void);
40 static int process_opt (char *bp, int count);
41 static void do_option (int optsrt);
42 static void dont_option (int optsrt);
43 static void will_option (int optsrt);
44 static void wont_option (int optsrt);
45 static int writeall (int fd, char *buffer, int buf_size);
46 static int sb_termtype (char *sb, int count);
47 static void fatal(char *fmt, ...);
48 static void usage(void);
50 #if DEBUG
51 #define where() (fprintf(stderr, "%s %d:", __FILE__, __LINE__))
52 #endif
54 static char *prog_name;
55 static int tcp_fd;
56 static char *term_env;
57 static int esc_char= '~';
58 static enum { LS_NORM, LS_BOL, LS_ESC } line_state= LS_BOL;
60 int main(int argc, char *argv[])
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;
74 (prog_name=strrchr(argv[0],'/')) ? prog_name++ : (prog_name=argv[0]);
76 e_arg= NULL;
77 while (c= getopt(argc, argv, "?e:"), c != -1)
79 switch(c)
81 case '?': usage();
82 case 'e': e_arg= optarg; break;
83 default:
84 fatal("Optind failed: '%c'", c);
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();
98 if (e_arg)
100 switch(strlen(e_arg))
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);
108 hostent= gethostbyname(remote_name);
109 if (!hostent)
110 fatal("Unknown host %s", remote_name);
111 host= *(ipaddr_t *)(hostent->h_addr);
113 if (!port_name)
114 port= htons(TCPPORT_TELNET);
115 else
117 servent= getservbyname (port_name, "tcp");
118 if (!servent)
120 port= htons(strtol(port_name, (char **)0, 0));
121 if (!port)
122 fatal("Unknown port %s", port_name);
124 else
125 port= (tcpport_t)(servent->s_port);
128 fprintf(stderr, "Connecting to %s:%u...\n",
129 inet_ntoa(host), ntohs(port));
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));
138 tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP;
139 tcpconf.nwtc_remaddr= host;
140 tcpconf.nwtc_remport= port;
142 r= ioctl (tcp_fd, NWIOSTCPCONF, &tcpconf);
143 if (r == -1)
144 fatal("NWIOSTCPCONF failed: %s", strerror(errno));
146 tcpconnopt.nwtcl_flags= 0;
149 r= ioctl (tcp_fd, NWIOTCPCONN, &tcpconnopt);
150 if (r == -1 && errno == EAGAIN)
152 fprintf(stderr, "%s: Got EAGAIN, sleeping(1s)\n",
153 prog_name);
154 sleep(1);
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)
164 case 0:
165 keyboard();
166 #if DEBUG
167 fprintf(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
180 fprintf(stderr, "killing %d with %d\r\n", pid, SIGKILL);
181 #endif
182 kill(pid, SIGKILL);
183 tcsetattr(0, TCSANOW, &termios);
184 break;
186 exit(0);
189 static int do_read(fd, buf, len)
190 int fd;
191 char *buf;
192 unsigned len;
194 nwio_tcpopt_t tcpopt;
195 int count;
197 for (;;)
199 count= read (fd, buf, len);
200 if (count <0)
202 if (errno == EURG || errno == ENOURG)
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)
209 return -1;
211 continue;
213 return -1;
215 return count;
219 static void screen()
221 char buffer[1024], *bp, *iacptr;
222 int count, optsize;
224 for (;;)
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)
232 perror ("read");
233 return;
235 if (!count)
236 return;
237 bp= buffer;
240 iacptr= memchr (bp, IAC, count);
241 if (!iacptr)
243 write(1, bp, count);
244 count= 0;
246 if (iacptr && iacptr>bp)
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;
256 if (iacptr)
258 assert (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;
265 assert (optsize);
266 bp += optsize;
267 count -= optsize;
269 } while (count);
273 static void keyboard()
275 char c, buffer[1024];
276 int count;
278 for (;;)
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;
286 if (line_state != LS_NORM)
288 c= buffer[0];
289 if (line_state == LS_BOL)
291 if (c == esc_char)
293 line_state= LS_ESC;
294 continue;
296 line_state= LS_NORM;
298 else if (line_state == LS_ESC)
300 line_state= LS_NORM;
301 if (c == '.')
302 return;
303 if (c == '#')
305 send_brk();
306 continue;
309 /* Not a valid command or a repeat of the
310 * escape char
312 if (c != esc_char)
314 c= esc_char;
315 write(tcp_fd, &c, 1);
319 if (buffer[0] == '\n')
320 write(tcp_fd, "\r", 1);
321 count= write(tcp_fd, buffer, count);
322 if (buffer[0] == '\r')
324 line_state= LS_BOL;
325 write(tcp_fd, "\0", 1);
327 if (count<0)
329 perror("write");
330 fprintf(stderr, "errno= %d\r\n", errno);
331 return;
333 if (!count)
334 return;
338 static void send_brk(void)
340 int r;
341 unsigned char buffer[2];
343 buffer[0]= IAC;
344 buffer[1]= IAC_BRK;
346 r= writeall(tcp_fd, (char *)buffer, 2);
347 if (r == -1)
348 fatal("Error writing to TCP connection: %s", strerror(errno));
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; }
356 static int process_opt (char *bp, int count)
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
365 offset= 0;
366 assert (count);
367 next_char(iac);
368 assert (iac == IAC);
369 next_char(command);
370 switch(command)
372 case IAC_NOP:
373 break;
374 case IAC_DataMark:
375 /* Ought to flush input queue or something. */
376 break;
377 case IAC_BRK:
378 fprintf(stderr, "got a BRK\r\n");
379 break;
380 case IAC_IP:
381 fprintf(stderr, "got a IP\r\n");
382 break;
383 case IAC_AO:
384 fprintf(stderr, "got a AO\r\n");
385 break;
386 case IAC_AYT:
387 fprintf(stderr, "got a AYT\r\n");
388 break;
389 case IAC_EC:
390 fprintf(stderr, "got a EC\r\n");
391 break;
392 case IAC_EL:
393 fprintf(stderr, "got a EL\r\n");
394 break;
395 case IAC_GA:
396 fprintf(stderr, "got a GA\r\n");
397 break;
398 case IAC_SB:
399 next_char(sb_command);
400 switch (sb_command)
402 case OPT_TERMTYPE:
403 #if DEBUG && 0
404 fprintf(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:
412 fprintf(stderr, "got an unknown SB (skiping)\r\n");
413 for (;;)
415 next_char(iac);
416 if (iac != IAC)
417 continue;
418 next_char(optsrt);
419 if (optsrt == IAC)
420 continue;
421 if (optsrt != IAC_SE)
422 fprintf(stderr, "got IAC %d\r\n", optsrt);
423 break;
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:
444 fprintf(stderr, "got a IAC\r\n");
445 break;
446 default:
447 fprintf(stderr, "got unknown command (%d)\r\n", command);
449 return offset;
452 static void do_option (int optsrt)
454 unsigned char reply[3];
455 int result;
457 switch (optsrt)
459 case OPT_TERMTYPE:
460 if (WILL_terminal_type)
461 return;
462 if (!WILL_terminal_type_allowed)
464 reply[0]= IAC;
465 reply[1]= IAC_WONT;
466 reply[2]= optsrt;
468 else
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;
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;
489 result= writeall(tcp_fd, (char *)reply, 3);
490 if (result<0)
491 perror("write");
494 static void will_option (int optsrt)
496 unsigned char reply[3];
497 int result;
499 switch (optsrt)
501 case OPT_ECHO:
502 if (DO_echo)
503 break;
504 if (!DO_echo_allowed)
506 reply[0]= IAC;
507 reply[1]= IAC_DONT;
508 reply[2]= optsrt;
510 else
512 struct termios termios;
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);
520 DO_echo= TRUE;
521 reply[0]= IAC;
522 reply[1]= IAC_DO;
523 reply[2]= optsrt;
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)
534 reply[0]= IAC;
535 reply[1]= IAC_DONT;
536 reply[2]= optsrt;
538 else
540 DO_suppress_go_ahead= TRUE;
541 reply[0]= IAC;
542 reply[1]= IAC_DO;
543 reply[2]= optsrt;
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;
564 static int writeall (fd, buffer, buf_size)
565 int fd;
566 char *buffer;
567 int buf_size;
569 int result;
571 while (buf_size)
573 result= write (fd, buffer, buf_size);
574 if (result <= 0)
575 return -1;
576 assert (result <= buf_size);
577 buffer += result;
578 buf_size -= result;
580 return 0;
583 static void dont_option (int optsrt)
585 switch (optsrt)
587 default:
588 #if DEBUG
589 fprintf(stderr, "got a DONT (%d)\r\n", optsrt);
590 #endif
591 break;
595 static void wont_option (int optsrt)
597 switch (optsrt)
599 default:
600 #if DEBUG
601 fprintf(stderr, "got a WONT (%d)\r\n", optsrt);
602 #endif
603 break;
607 static int sb_termtype (char *bp, int count)
609 unsigned char command, iac, optsrt;
610 unsigned char buffer[4];
611 int offset, result;
613 offset= 0;
614 next_char(command);
615 if (command == TERMTYPE_SEND)
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)
627 term_env= "unknown";
628 count= strlen(term_env);
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;
640 else
642 #if DEBUG
643 where();
644 #endif
645 fprintf(stderr, "got an unknown command (skipping)\r\n");
647 for (;;)
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)
657 #if DEBUG
658 where();
659 #endif
660 fprintf(stderr, "got IAC %d\r\n", optsrt);
662 break;
664 return offset;
667 static void fatal(char *fmt, ...)
669 va_list ap;
671 va_start(ap, fmt);
672 fprintf(stderr, "%s: ", prog_name);
673 vfprintf(stderr, fmt, ap);
674 fprintf(stderr, "\n");
675 va_end(ap);
677 exit(1);
680 static void usage(void)
682 fprintf(stderr, "Usage: %s [-e esc-char] host [port]\r\n",
683 prog_name);
684 exit(1);
688 * $PchId: ttn.c,v 1.5 2002/05/07 12:06:41 philip Exp $