1 #include <u.h> 2 #include <libc.h> 3 4 #include "rustream.h" 5 #include "ruttyio.h" 6 #include "rusignal.h" 7 #include "rufilio.h" 8 9 int debug; /* true if debugging */ 10 int ctl = -1; /* control fd (for break's) */ 11 int raw; /* true if raw is on */ 12 int consctl = -1; /* control fd for cons */ 13 int ttypid; /* pid's if the 2 processes (used to kill them) */ 14 int msgfd = -1; /* mesgld file descriptor (for signals to be written to) */ 15 int outfd = 1; /* local output file descriptor */ 16 int cooked; /* non-zero forces cooked mode */ 17 int returns; /* non-zero forces carriage returns not to be filtered out */ 18 int crtonl; /* non-zero forces carriage returns to be converted to nls coming from net */ 19 int strip; /* strip off parity bits */ 20 char firsterr[2*ERRMAX]; 21 char transerr[2*ERRMAX]; 22 int limited; 23 char *remuser; /* for BSD rlogin authentication */ 24 int verbose; 25 int baud; 26 int notkbd; 27 int nltocr; /* translate kbd nl to cr and vice versa */ 28 29 typedef struct Msg Msg; 30 #define MAXMSG (2*8192) 31 32 int dodial(char*, char*, char*); 33 void fromkbd(int); 34 void fromnet(int); 35 long iread(int, void*, int); 36 long iwrite(int, void*, int); 37 int menu(int); 38 void msgfromkbd(int); 39 void msgfromnet(int); 40 int msgwrite(int, void*, int); 41 void notifyf(void*, char*); 42 void pass(int, int, int); 43 void rawoff(void); 44 void rawon(void); 45 int readupto(int, char*, int); 46 int sendctl(int, int); 47 int sendctl1(int, int, int); 48 void stdcon(int); 49 char* system(int, char*); 50 void dosystem(int, char*); 51 int wasintr(void); 52 void punt(char*); 53 char* syserr(void); 54 void seterr(char*); 55 56 /* protocols */ 57 void device(char*, char*); 58 void rlogin(char*, char*); 59 void simple(char*, char*); 60 61 void 62 usage(void) 63 { 64 punt("usage: con [-CdnrRsTv] [-b baud] [-l [user]] [-c cmd] net!host[!service]"); 65 } 66 67 void 68 main(int argc, char *argv[]) 69 { 70 char *dest; 71 char *cmd = 0; 72 73 returns = 1; 74 ARGBEGIN{ 75 case 'b': 76 baud = atoi(EARGF(usage())); 77 break; 78 case 'd': 79 debug = 1; 80 break; 81 case 'l': 82 limited = 1; 83 if(argv[1] != nil && argv[1][0] != '-') 84 remuser = ARGF(); 85 break; 86 case 'n': 87 notkbd = 1; 88 break; 89 case 'r': 90 returns = 0; 91 break; 92 case 'R': 93 nltocr = 1; 94 break; 95 case 'T': 96 crtonl = 1; 97 break; 98 case 'C': 99 cooked = 1; 100 break; 101 case 'c': 102 cmd = ARGF(); 103 break; 104 case 'v': 105 verbose = 1; 106 break; 107 case 's': 108 strip = 1; 109 break; 110 default: 111 usage(); 112 }ARGEND 113 114 if(argc != 1){ 115 if(remuser == 0) 116 usage(); 117 dest = remuser; 118 remuser = 0; 119 } else 120 dest = argv[0]; 121 if(*dest == '/' && strchr(dest, '!') == 0) 122 device(dest, cmd); 123 else if(limited){ 124 simple(dest, cmd); /* doesn't return if dialout succeeds */ 125 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 126 } else { 127 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 128 simple(dest, cmd); /* doesn't return if dialout succeeds */ 129 } 130 punt(firsterr); 131 } 132 133 /* 134 * just dial and use as a byte stream with remote echo 135 */ 136 void 137 simple(char *dest, char *cmd) 138 { 139 int net; 140 141 net = dodial(dest, 0, 0); 142 if(net < 0) 143 return; 144 145 if(cmd) 146 dosystem(net, cmd); 147 148 if(!cooked) 149 rawon(); 150 stdcon(net); 151 exits(0); 152 } 153 154 /* 155 * dial, do UCB authentication, use as a byte stream with local echo 156 * 157 * return if dial failed 158 */ 159 void 160 rlogin(char *dest, char *cmd) 161 { 162 int net; 163 char buf[128]; 164 char *p; 165 char *localuser; 166 167 /* only useful on TCP */ 168 if(strchr(dest, '!') 169 && (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0)) 170 return; 171 172 net = dodial(dest, "tcp", "login"); 173 if(net < 0) 174 return; 175 176 /* 177 * do UCB rlogin authentication 178 */ 179 localuser = getuser(); 180 if(remuser == 0){ 181 if(limited) 182 remuser = ":"; 183 else 184 remuser = localuser; 185 } 186 p = getenv("TERM"); 187 if(p == 0) 188 p = "p9"; 189 if(write(net, "", 1)<0 190 || write(net, localuser, strlen(localuser)+1)<0 191 || write(net, remuser, strlen(remuser)+1)<0 192 || write(net, p, strlen(p)+1)<0){ 193 close(net); 194 punt("BSD authentication failed"); 195 } 196 if(read(net, buf, 1) != 1) 197 punt("BSD authentication failed1"); 198 if(buf[0] != 0){ 199 fprint(2, "con: remote error: "); 200 while(read(net, buf, 1) == 1){ 201 write(2, buf, 1); 202 if(buf[0] == '\n') 203 break; 204 } 205 exits("read"); 206 } 207 208 if(cmd) 209 dosystem(net, cmd); 210 211 if(!cooked) 212 rawon(); 213 nltocr = 1; 214 stdcon(net); 215 exits(0); 216 } 217 218 /* 219 * just open a device and use it as a connection 220 */ 221 void 222 device(char *dest, char *cmd) 223 { 224 int net; 225 char cname[128]; 226 227 net = open(dest, ORDWR); 228 if(net < 0) { 229 fprint(2, "con: cannot open %s: %r\n", dest); 230 exits("open"); 231 } 232 snprint(cname, sizeof cname, "%sctl", dest); 233 ctl = open(cname, ORDWR); 234 if (baud > 0) { 235 if(ctl >= 0){ 236 /* set speed and use fifos if available */ 237 fprint(ctl, "b%d i1", baud); 238 } 239 else 240 fprint(2, "con: cannot open %s: %r\n", cname); 241 } 242 243 if(cmd) 244 dosystem(net, cmd); 245 246 if(!cooked) 247 rawon(); 248 stdcon(net); 249 exits(0); 250 } 251 252 /* 253 * ignore interrupts 254 */ 255 void 256 notifyf(void *a, char *msg) 257 { 258 USED(a); 259 260 if(strstr(msg, "yankee")) 261 noted(NDFLT); 262 if(strstr(msg, "closed pipe") 263 || strcmp(msg, "interrupt") == 0 264 || strcmp(msg, "hangup") == 0) 265 noted(NCONT); 266 noted(NDFLT); 267 } 268 269 /* 270 * turn keyboard raw mode on 271 */ 272 void 273 rawon(void) 274 { 275 if(debug) 276 fprint(2, "rawon\n"); 277 if(raw) 278 return; 279 if(consctl < 0) 280 consctl = open("/dev/consctl", OWRITE); 281 if(consctl < 0){ 282 // fprint(2, "can't open consctl\n"); 283 return; 284 } 285 write(consctl, "rawon", 5); 286 raw = 1; 287 } 288 289 /* 290 * turn keyboard raw mode off 291 */ 292 void 293 rawoff(void) 294 { 295 if(debug) 296 fprint(2, "rawoff\n"); 297 if(raw == 0) 298 return; 299 if(consctl < 0) 300 consctl = open("/dev/consctl", OWRITE); 301 if(consctl < 0){ 302 // fprint(2, "can't open consctl\n"); 303 return; 304 } 305 write(consctl, "rawoff", 6); 306 raw = 0; 307 } 308 309 /* 310 * control menu 311 */ 312 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n" 313 314 int 315 menu(int net) 316 { 317 char buf[MAXMSG]; 318 long n; 319 int done; 320 int wasraw = raw; 321 322 if(wasraw) 323 rawoff(); 324 325 fprint(2, ">>> "); 326 for(done = 0; !done; ){ 327 n = read(0, buf, sizeof(buf)-1); 328 if(n <= 0) 329 return -1; 330 buf[n] = 0; 331 switch(buf[0]){ 332 case '!': 333 print(buf); 334 system(net, buf+1); 335 print("!\n"); 336 done = 1; 337 break; 338 case '.': 339 done = 1; 340 break; 341 case 'q': 342 return -1; 343 case 'i': 344 buf[0] = 0x1c; 345 if(msgfd <= 0) 346 write(net, buf, 1); 347 else 348 sendctl1(msgfd, M_SIGNAL, SIGQUIT); 349 done = 1; 350 break; 351 case 'b': 352 if(msgfd >= 0) 353 sendctl(msgfd, M_BREAK); 354 else if(ctl >= 0) 355 write(ctl, "k", 1); 356 done = 1; 357 break; 358 case 'r': 359 returns = 1-returns; 360 done = 1; 361 break; 362 default: 363 fprint(2, STDHELP); 364 break; 365 } 366 if(!done) 367 fprint(2, ">>> "); 368 } 369 370 if(wasraw) 371 rawon(); 372 else 373 rawoff(); 374 return 0; 375 } 376 377 /* 378 * the real work. two processes pass bytes back and forth between the 379 * terminal and the network. 380 */ 381 void 382 stdcon(int net) 383 { 384 int netpid; 385 386 ttypid = getpid(); 387 switch(netpid = rfork(RFMEM|RFPROC)){ 388 case -1: 389 perror("con"); 390 exits("fork"); 391 case 0: 392 notify(notifyf); 393 fromnet(net); 394 postnote(PNPROC, ttypid, "die yankee dog"); 395 exits(0); 396 default: 397 notify(notifyf); 398 fromkbd(net); 399 if(notkbd) 400 for(;;)sleep(0); 401 postnote(PNPROC, netpid, "die yankee dog"); 402 exits(0); 403 } 404 } 405 406 /* 407 * Read the keyboard and write it to the network. '^\' gets us into 408 * the menu. 409 */ 410 void 411 fromkbd(int net) 412 { 413 long n; 414 char buf[MAXMSG]; 415 char *p, *ep; 416 int eofs; 417 418 eofs = 0; 419 for(;;){ 420 n = read(0, buf, sizeof(buf)); 421 if(n < 0){ 422 if(wasintr()){ 423 if(!raw){ 424 buf[0] = 0x7f; 425 n = 1; 426 } else 427 continue; 428 } else 429 return; 430 } 431 if(n == 0){ 432 if(++eofs > 32) 433 return; 434 } else 435 eofs = 0; 436 if(n && memchr(buf, 0x1c, n)){ 437 if(menu(net) < 0) 438 return; 439 }else{ 440 if(!raw && n==0){ 441 buf[0] = 0x4; 442 n = 1; 443 } 444 if(nltocr){ 445 ep = buf+n; 446 for(p = buf; p < ep; p++) 447 switch(*p){ 448 case '\r': 449 *p = '\n'; 450 break; 451 case '\n': 452 *p = '\r'; 453 break; 454 } 455 } 456 if(iwrite(net, buf, n) != n) 457 return; 458 } 459 } 460 } 461 462 /* 463 * Read from the network and write to the screen. 464 * Filter out spurious carriage returns. 465 */ 466 void 467 fromnet(int net) 468 { 469 long n; 470 char buf[MAXMSG]; 471 char *cp, *ep; 472 473 for(;;){ 474 n = iread(net, buf, sizeof(buf)); 475 if(n < 0) 476 return; 477 if(n == 0) 478 continue; 479 480 if (strip) 481 for (cp=buf; cp<buf+n; cp++) 482 *cp &= 0177; 483 484 if(crtonl) { 485 /* convert cr's to nl's */ 486 for (cp = buf; cp < buf + n; cp++) 487 if (*cp == '\r') 488 *cp = '\n'; 489 } 490 else if(!returns){ 491 /* convert cr's to null's */ 492 cp = buf; 493 ep = buf + n; 494 while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ 495 memmove(cp, cp+1, ep-cp-1); 496 ep--; 497 n--; 498 } 499 } 500 501 if(n > 0 && iwrite(outfd, buf, n) != n){ 502 if(outfd == 1) 503 return; 504 outfd = 1; 505 if(iwrite(1, buf, n) != n) 506 return; 507 } 508 } 509 } 510 511 /* 512 * dial and return a data connection 513 */ 514 int 515 dodial(char *dest, char *net, char *service) 516 { 517 char name[128]; 518 char devdir[128]; 519 int data; 520 521 devdir[0] = 0; 522 strcpy(name, netmkaddr(dest, net, service)); 523 data = dial(name, 0, devdir, &ctl); 524 if(data < 0){ 525 seterr(name); 526 return -1; 527 } 528 fprint(2, "connected to %s on %s\n", name, devdir); 529 return data; 530 } 531 532 void 533 dosystem(int fd, char *cmd) 534 { 535 char *p; 536 537 p = system(fd, cmd); 538 if(p){ 539 print("con: %s terminated with %s\n", cmd, p); 540 exits(p); 541 } 542 } 543 544 /* 545 * run a command with the network connection as standard IO 546 */ 547 char * 548 system(int fd, char *cmd) 549 { 550 int pid; 551 int p; 552 static Waitmsg msg; 553 int pfd[2]; 554 int n; 555 char buf[4096]; 556 557 if(pipe(pfd) < 0){ 558 perror("pipe"); 559 return "pipe failed"; 560 } 561 outfd = pfd[1]; 562 563 close(consctl); 564 consctl = -1; 565 switch(pid = fork()){ 566 case -1: 567 perror("con"); 568 return "fork failed"; 569 case 0: 570 close(pfd[1]); 571 dup(pfd[0], 0); 572 dup(fd, 1); 573 close(ctl); 574 close(fd); 575 close(pfd[0]); 576 if(*cmd) 577 execl("/bin/rc", "rc", "-c", cmd, nil); 578 else 579 execl("/bin/rc", "rc", nil); 580 perror("con"); 581 exits("exec"); 582 break; 583 default: 584 close(pfd[0]); 585 while((n = read(pfd[1], buf, sizeof(buf))) > 0){ 586 if(msgfd >= 0){ 587 if(msgwrite(fd, buf, n) != n) 588 break; 589 } else { 590 if(write(fd, buf, n) != n) 591 break; 592 } 593 } 594 p = waitpid(); 595 outfd = 1; 596 close(pfd[1]); 597 if(p < 0 || p != pid) 598 return "lost child"; 599 break; 600 } 601 return msg.msg; 602 } 603 604 int 605 wasintr(void) 606 { 607 return strcmp(syserr(), "interrupted") == 0; 608 } 609 610 void 611 punt(char *msg) 612 { 613 if(*msg == 0) 614 msg = transerr; 615 fprint(2, "con: %s\n", msg); 616 exits(msg); 617 } 618 619 char* 620 syserr(void) 621 { 622 static char err[ERRMAX]; 623 errstr(err, sizeof err); 624 return err; 625 } 626 627 void 628 seterr(char *addr) 629 { 630 char *se = syserr(); 631 632 if(verbose) 633 fprint(2, "'%s' calling %s\n", se, addr); 634 if(firsterr[0] && (strstr(se, "translate") || 635 strstr(se, "file does not exist") || 636 strstr(se, "unknown address") || 637 strstr(se, "directory entry not found"))) 638 return; 639 strcpy(firsterr, se); 640 } 641 642 643 long 644 iread(int f, void *a, int n) 645 { 646 long m; 647 648 for(;;){ 649 m = read(f, a, n); 650 if(m >= 0 || !wasintr()) 651 break; 652 } 653 return m; 654 } 655 656 long 657 iwrite(int f, void *a, int n) 658 { 659 long m; 660 661 m = write(f, a, n); 662 if(m < 0 && wasintr()) 663 return n; 664 return m; 665 } 666 667 /* 668 * The rest is to support the V10 mesgld protocol. 669 */ 670 671 /* 672 * network orderings 673 */ 674 #define get2byte(p) ((p)[0] + ((p)[1]<<8)) 675 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24)) 676 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8) 677 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24) 678 679 /* 680 * tty parameters 681 */ 682 int sgflags = ECHO; 683 684 /* 685 * a mesgld message 686 */ 687 struct Msg { 688 struct mesg h; 689 char b[MAXMSG]; 690 }; 691 692 693 /* 694 * send an empty mesgld message 695 */ 696 int 697 sendctl(int net, int type) 698 { 699 Msg m; 700 701 m.h.type = type; 702 m.h.magic = MSGMAGIC; 703 put2byte(m.h.size, 0); 704 if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg)) 705 return -1; 706 return 0; 707 } 708 709 /* 710 * send a one byte mesgld message 711 */ 712 int 713 sendctl1(int net, int type, int parm) 714 { 715 Msg m; 716 717 m.h.type = type; 718 m.h.magic = MSGMAGIC; 719 m.b[0] = parm; 720 put2byte(m.h.size, 1); 721 if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1) 722 return -1; 723 return 0; 724 } 725 726 /* 727 * read n bytes. return -1 if it fails, 0 otherwise. 728 */ 729 int 730 readupto(int from, char *a, int len) 731 { 732 int n; 733 734 while(len > 0){ 735 n = iread(from, a, len); 736 if(n < 0) 737 return -1; 738 a += n; 739 len -= n; 740 } 741 return 0; 742 } 743 744 /* 745 * Decode a mesgld message from the network 746 */ 747 void 748 msgfromnet(int net) 749 { 750 ulong com; 751 struct stioctl *io; 752 struct sgttyb *sg; 753 struct ttydevb *td; 754 struct tchars *tc; 755 int len; 756 Msg m; 757 758 for(;;){ 759 /* get a complete mesgld message */ 760 if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0) 761 break; 762 if(m.h.magic != MSGMAGIC){ 763 fprint(2, "con: bad message magic 0x%ux\n", m.h.magic); 764 break; 765 } 766 len = get2byte(m.h.size); 767 if(len > sizeof(m.b)){ 768 len = sizeof(m.b); 769 fprint(2, "con: mesgld message too long\n"); 770 } 771 if(len && readupto(net, m.b, len) < 0) 772 break; 773 774 /* decode */ 775 switch(m.h.type){ 776 case M_HANGUP: 777 if(debug) 778 fprint(2, "M_HANGUP\n"); 779 return; 780 case M_DATA: 781 if(debug) 782 fprint(2, "M_DATA %d bytes\n", len); 783 if(iwrite(outfd, m.b, len) != len){ 784 if(outfd == 1) 785 return; 786 outfd = 1; 787 if(iwrite(outfd, m.b, len) != len) 788 return; 789 } 790 continue; 791 case M_IOCTL: 792 break; 793 default: 794 /* ignore */ 795 if(debug) 796 fprint(2, "con: unknown message\n"); 797 continue; 798 } 799 800 /* 801 * answer an ioctl 802 */ 803 io = (struct stioctl *)m.b; 804 com = get4byte(io->com); 805 if(debug) 806 fprint(2, "M_IOCTL %lud\n", com); 807 switch(com){ 808 case FIOLOOKLD: 809 put4byte(io->data, tty_ld); 810 len = 0; 811 break; 812 case TIOCGETP: 813 sg = (struct sgttyb *)io->data; 814 sg->sg_ispeed = sg->sg_ospeed = B9600; 815 sg->sg_erase = 0010; /* back space */ 816 sg->sg_kill = 0025; /* CNTL U */ 817 put2byte(sg->sg_flags, sgflags); 818 len = sizeof(struct sgttyb); 819 break; 820 case TIOCSETN: 821 case TIOCSETP: 822 sg = (struct sgttyb *)io->data; 823 sgflags = get2byte(sg->sg_flags); 824 if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO)) 825 rawon(); 826 else 827 rawoff(); 828 len = 0; 829 break; 830 case TIOCGETC: 831 tc = (struct tchars *)io->data; 832 tc->t_intrc = 0177; 833 tc->t_quitc = 0034; 834 tc->t_startc = 0; 835 tc->t_stopc = 0; 836 tc->t_eofc = 0004; 837 tc->t_brkc = 0; 838 len = sizeof(struct tchars); 839 break; 840 case TIOCSETC: 841 len = 0; 842 break; 843 case TIOCGDEV: 844 td = (struct ttydevb *)io->data; 845 td->ispeed = td->ospeed = B9600; 846 put2byte(td->flags, 0); 847 len = sizeof(struct ttydevb); 848 break; 849 case TIOCSDEV: 850 len = 0; 851 break; 852 default: 853 /* 854 * unimplemented 855 */ 856 m.b[len] = 0; 857 if(sendctl(net, M_IOCNAK) < 0) 858 return; 859 continue; 860 } 861 862 /* 863 * acknowledge 864 */ 865 m.h.type = M_IOCACK; 866 m.h.magic = MSGMAGIC; 867 len += 4; 868 put2byte(m.h.size, len); 869 len += sizeof(struct mesg); 870 if(iwrite(net, &m, len) != len) 871 return; 872 } 873 } 874 875 /* 876 * Read the keyboard, convert to mesgld messages, and write it to the network. 877 * '^\' gets us into the menu. 878 */ 879 void 880 msgfromkbd(int net) 881 { 882 long n; 883 char buf[MAXMSG]; 884 885 for(;;){ 886 n = iread(0, buf, sizeof(buf)); 887 if(n < 0) 888 return; 889 if(n && memchr(buf, 0034, n)){ 890 if(menu(net) < 0) 891 return; 892 } else { 893 if(msgwrite(net, buf, n) != n) 894 return; 895 } 896 } 897 } 898 899 int 900 msgwrite(int fd, void *buf, int len) 901 { 902 Msg m; 903 int n; 904 905 n = len; 906 memmove(m.b, buf, n); 907 put2byte(m.h.size, n); 908 m.h.magic = MSGMAGIC; 909 m.h.type = M_DATA; 910 n += sizeof(struct mesg); 911 if(iwrite(fd, &m, n) != n) 912 return -1; 913 914 put2byte(m.h.size, 0); 915 m.h.magic = MSGMAGIC; 916 m.h.type = M_DELIM; 917 n = sizeof(struct mesg); 918 if(iwrite(fd, &m, n) != n) 919 return -1; 920 921 return len; 922 } 923 924