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; 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 [-drCvsn] [-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(ARGF()); 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 fprint(ctl, "b%d", baud); 237 else 238 fprint(2, "con: cannot open %s: %r\n", cname); 239 } 240 241 if(cmd) 242 dosystem(net, cmd); 243 244 if(!cooked) 245 rawon(); 246 stdcon(net); 247 exits(0); 248 } 249 250 /* 251 * ignore interrupts 252 */ 253 void 254 notifyf(void *a, char *msg) 255 { 256 USED(a); 257 258 if(strstr(msg, "yankee")) 259 noted(NDFLT); 260 if(strstr(msg, "closed pipe") 261 || strcmp(msg, "interrupt") == 0 262 || strcmp(msg, "hangup") == 0) 263 noted(NCONT); 264 noted(NDFLT); 265 } 266 267 /* 268 * turn keyboard raw mode on 269 */ 270 void 271 rawon(void) 272 { 273 if(debug) 274 fprint(2, "rawon\n"); 275 if(raw) 276 return; 277 if(consctl < 0) 278 consctl = open("/dev/consctl", OWRITE); 279 if(consctl < 0){ 280 // fprint(2, "can't open consctl\n"); 281 return; 282 } 283 write(consctl, "rawon", 5); 284 raw = 1; 285 } 286 287 /* 288 * turn keyboard raw mode off 289 */ 290 void 291 rawoff(void) 292 { 293 if(debug) 294 fprint(2, "rawoff\n"); 295 if(raw == 0) 296 return; 297 if(consctl < 0) 298 consctl = open("/dev/consctl", OWRITE); 299 if(consctl < 0){ 300 // fprint(2, "can't open consctl\n"); 301 return; 302 } 303 write(consctl, "rawoff", 6); 304 raw = 0; 305 } 306 307 /* 308 * control menu 309 */ 310 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n" 311 312 int 313 menu(int net) 314 { 315 char buf[MAXMSG]; 316 long n; 317 int done; 318 int wasraw = raw; 319 320 if(wasraw) 321 rawoff(); 322 323 fprint(2, ">>> "); 324 for(done = 0; !done; ){ 325 n = read(0, buf, sizeof(buf)-1); 326 if(n <= 0) 327 return -1; 328 buf[n] = 0; 329 switch(buf[0]){ 330 case '!': 331 print(buf); 332 system(net, buf+1); 333 print("!\n"); 334 done = 1; 335 break; 336 case '.': 337 done = 1; 338 break; 339 case 'q': 340 return -1; 341 break; 342 case 'i': 343 buf[0] = 0x1c; 344 if(msgfd <= 0) 345 write(net, buf, 1); 346 else 347 sendctl1(msgfd, M_SIGNAL, SIGQUIT); 348 done = 1; 349 break; 350 case 'b': 351 if(msgfd >= 0) 352 sendctl(msgfd, M_BREAK); 353 else if(ctl >= 0) 354 write(ctl, "k", 1); 355 done = 1; 356 break; 357 case 'r': 358 returns = 1-returns; 359 done = 1; 360 break; 361 default: 362 fprint(2, STDHELP); 363 break; 364 } 365 if(!done) 366 fprint(2, ">>> "); 367 } 368 369 if(wasraw) 370 rawon(); 371 else 372 rawoff(); 373 return 0; 374 } 375 376 /* 377 * the real work. two processes pass bytes back and forth between the 378 * terminal and the network. 379 */ 380 void 381 stdcon(int net) 382 { 383 int netpid; 384 385 ttypid = getpid(); 386 switch(netpid = rfork(RFMEM|RFPROC)){ 387 case -1: 388 perror("con"); 389 exits("fork"); 390 case 0: 391 notify(notifyf); 392 fromnet(net); 393 postnote(PNPROC, ttypid, "die yankee dog"); 394 exits(0); 395 default: 396 notify(notifyf); 397 fromkbd(net); 398 if(notkbd) 399 for(;;)sleep(0); 400 postnote(PNPROC, netpid, "die yankee dog"); 401 exits(0); 402 } 403 } 404 405 /* 406 * Read the keyboard and write it to the network. '^\' gets us into 407 * the menu. 408 */ 409 void 410 fromkbd(int net) 411 { 412 long n; 413 char buf[MAXMSG]; 414 char *p, *ep; 415 int eofs; 416 417 eofs = 0; 418 for(;;){ 419 n = read(0, buf, sizeof(buf)); 420 if(n < 0){ 421 if(wasintr()){ 422 if(!raw){ 423 buf[0] = 0x7f; 424 n = 1; 425 } else 426 continue; 427 } else 428 return; 429 } 430 if(n == 0){ 431 if(++eofs > 32) 432 return; 433 } else 434 eofs = 0; 435 if(n && memchr(buf, 0x1c, n)){ 436 if(menu(net) < 0) 437 return; 438 }else{ 439 if(!raw && n==0){ 440 buf[0] = 0x4; 441 n = 1; 442 } 443 if(nltocr){ 444 ep = buf+n; 445 for(p = buf; p < ep; p++) 446 switch(*p){ 447 case '\r': 448 *p = '\n'; 449 break; 450 case '\n': 451 *p = '\r'; 452 break; 453 } 454 } 455 if(iwrite(net, buf, n) != n) 456 return; 457 } 458 } 459 } 460 461 /* 462 * Read from the network and write to the screen. 463 * Filter out spurious carriage returns. 464 */ 465 void 466 fromnet(int net) 467 { 468 long n; 469 char buf[MAXMSG]; 470 char *cp, *ep; 471 472 for(;;){ 473 n = iread(net, buf, sizeof(buf)); 474 if(n < 0) 475 return; 476 if(n == 0) 477 continue; 478 479 if (strip) 480 for (cp=buf; cp<buf+n; cp++) 481 *cp &= 0177; 482 483 if(crtonl) { 484 /* convert cr's to nl's */ 485 for (cp = buf; cp < buf + n; cp++) 486 if (*cp == '\r') 487 *cp = '\n'; 488 } 489 else if(!returns){ 490 /* convert cr's to null's */ 491 cp = buf; 492 ep = buf + n; 493 while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ 494 memmove(cp, cp+1, ep-cp-1); 495 ep--; 496 n--; 497 } 498 } 499 500 if(n > 0 && iwrite(outfd, buf, n) != n){ 501 if(outfd == 1) 502 return; 503 outfd = 1; 504 if(iwrite(1, buf, n) != n) 505 return; 506 } 507 } 508 } 509 510 /* 511 * dial and return a data connection 512 */ 513 int 514 dodial(char *dest, char *net, char *service) 515 { 516 char name[128]; 517 char devdir[128]; 518 int data; 519 520 devdir[0] = 0; 521 strcpy(name, netmkaddr(dest, net, service)); 522 data = dial(name, 0, devdir, &ctl); 523 if(data < 0){ 524 seterr(name); 525 return -1; 526 } 527 fprint(2, "connected to %s on %s\n", name, devdir); 528 return data; 529 } 530 531 void 532 dosystem(int fd, char *cmd) 533 { 534 char *p; 535 536 p = system(fd, cmd); 537 if(*p){ 538 print("con: %s terminated with %s\n", cmd, p); 539 exits(p); 540 } 541 } 542 543 /* 544 * run a command with the network connection as standard IO 545 */ 546 char * 547 system(int fd, char *cmd) 548 { 549 int pid; 550 int p; 551 static Waitmsg msg; 552 int pfd[2]; 553 int n; 554 char buf[4096]; 555 556 if(pipe(pfd) < 0){ 557 perror("pipe"); 558 return "pipe failed"; 559 } 560 outfd = pfd[1]; 561 562 close(consctl); 563 consctl = -1; 564 switch(pid = fork()){ 565 case -1: 566 perror("con"); 567 return "fork failed"; 568 case 0: 569 close(pfd[1]); 570 dup(pfd[0], 0); 571 dup(fd, 1); 572 close(ctl); 573 close(fd); 574 close(pfd[0]); 575 if(*cmd) 576 execl("/bin/rc", "rc", "-c", cmd, 0); 577 else 578 execl("/bin/rc", "rc", 0); 579 perror("con"); 580 exits("exec"); 581 break; 582 default: 583 close(pfd[0]); 584 while((n = read(pfd[1], buf, sizeof(buf))) > 0){ 585 if(msgfd >= 0){ 586 if(msgwrite(fd, buf, n) != n) 587 break; 588 } else { 589 if(write(fd, buf, n) != n) 590 break; 591 } 592 } 593 p = waitpid(); 594 outfd = 1; 595 close(pfd[1]); 596 if(p < 0 || p != pid) 597 return "lost child"; 598 break; 599 } 600 return msg.msg; 601 } 602 603 int 604 wasintr(void) 605 { 606 return strcmp(syserr(), "interrupted") == 0; 607 } 608 609 void 610 punt(char *msg) 611 { 612 if(*msg == 0) 613 msg = transerr; 614 fprint(2, "con: %s\n", msg); 615 exits(msg); 616 } 617 618 char* 619 syserr(void) 620 { 621 static char err[ERRMAX]; 622 errstr(err, sizeof err); 623 return err; 624 } 625 626 void 627 seterr(char *addr) 628 { 629 char *se = syserr(); 630 631 if(verbose) 632 fprint(2, "'%s' calling %s\n", se, addr); 633 if(firsterr[0] && (strstr(se, "translate") || 634 strstr(se, "file does not exist") || 635 strstr(se, "unknown address") || 636 strstr(se, "directory entry not found"))) 637 return; 638 strcpy(firsterr, se); 639 } 640 641 642 long 643 iread(int f, void *a, int n) 644 { 645 long m; 646 647 for(;;){ 648 m = read(f, a, n); 649 if(m >= 0 || !wasintr()) 650 break; 651 } 652 return m; 653 } 654 655 long 656 iwrite(int f, void *a, int n) 657 { 658 long m; 659 660 m = write(f, a, n); 661 if(m < 0 && wasintr()) 662 return n; 663 return m; 664 } 665 666 /* 667 * The rest is to support the V10 mesgld protocol. 668 */ 669 670 /* 671 * network orderings 672 */ 673 #define get2byte(p) ((p)[0] + ((p)[1]<<8)) 674 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24)) 675 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8) 676 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24) 677 678 /* 679 * tty parameters 680 */ 681 int sgflags = ECHO; 682 683 /* 684 * a mesgld message 685 */ 686 struct Msg { 687 struct mesg h; 688 char b[MAXMSG]; 689 }; 690 691 692 /* 693 * send an empty mesgld message 694 */ 695 int 696 sendctl(int net, int type) 697 { 698 Msg m; 699 700 m.h.type = type; 701 m.h.magic = MSGMAGIC; 702 put2byte(m.h.size, 0); 703 if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg)) 704 return -1; 705 return 0; 706 } 707 708 /* 709 * send a one byte mesgld message 710 */ 711 int 712 sendctl1(int net, int type, int parm) 713 { 714 Msg m; 715 716 m.h.type = type; 717 m.h.magic = MSGMAGIC; 718 m.b[0] = parm; 719 put2byte(m.h.size, 1); 720 if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1) 721 return -1; 722 return 0; 723 } 724 725 /* 726 * read n bytes. return -1 if it fails, 0 otherwise. 727 */ 728 int 729 readupto(int from, char *a, int len) 730 { 731 int n; 732 733 while(len > 0){ 734 n = iread(from, a, len); 735 if(n < 0) 736 return -1; 737 a += n; 738 len -= n; 739 } 740 return 0; 741 } 742 743 /* 744 * Decode a mesgld message from the network 745 */ 746 void 747 msgfromnet(int net) 748 { 749 ulong com; 750 struct stioctl *io; 751 struct sgttyb *sg; 752 struct ttydevb *td; 753 struct tchars *tc; 754 int len; 755 Msg m; 756 757 for(;;){ 758 /* get a complete mesgld message */ 759 if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0) 760 break; 761 if(m.h.magic != MSGMAGIC){ 762 fprint(2, "con: bad message magic 0x%ux\n", m.h.magic); 763 break; 764 } 765 len = get2byte(m.h.size); 766 if(len > sizeof(m.b)){ 767 len = sizeof(m.b); 768 fprint(2, "con: mesgld message too long\n"); 769 } 770 if(len && readupto(net, m.b, len) < 0) 771 break; 772 773 /* decode */ 774 switch(m.h.type){ 775 case M_HANGUP: 776 if(debug) 777 fprint(2, "M_HANGUP\n"); 778 return; 779 case M_DATA: 780 if(debug) 781 fprint(2, "M_DATA %d bytes\n", len); 782 if(iwrite(outfd, m.b, len) != len){ 783 if(outfd == 1) 784 return; 785 outfd = 1; 786 if(iwrite(outfd, m.b, len) != len) 787 return; 788 } 789 continue; 790 case M_IOCTL: 791 break; 792 default: 793 /* ignore */ 794 if(debug) 795 fprint(2, "con: unknown message\n"); 796 continue; 797 } 798 799 /* 800 * answer an ioctl 801 */ 802 io = (struct stioctl *)m.b; 803 com = get4byte(io->com); 804 if(debug) 805 fprint(2, "M_IOCTL %lud\n", com); 806 switch(com){ 807 case FIOLOOKLD: 808 put4byte(io->data, tty_ld); 809 len = 0; 810 break; 811 case TIOCGETP: 812 sg = (struct sgttyb *)io->data; 813 sg->sg_ispeed = sg->sg_ospeed = B9600; 814 sg->sg_erase = 0010; /* back space */ 815 sg->sg_kill = 0025; /* CNTL U */ 816 put2byte(sg->sg_flags, sgflags); 817 len = sizeof(struct sgttyb); 818 break; 819 case TIOCSETN: 820 case TIOCSETP: 821 sg = (struct sgttyb *)io->data; 822 sgflags = get2byte(sg->sg_flags); 823 if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO)) 824 rawon(); 825 else 826 rawoff(); 827 len = 0; 828 break; 829 case TIOCGETC: 830 tc = (struct tchars *)io->data; 831 tc->t_intrc = 0177; 832 tc->t_quitc = 0034; 833 tc->t_startc = 0; 834 tc->t_stopc = 0; 835 tc->t_eofc = 0004; 836 tc->t_brkc = 0; 837 len = sizeof(struct tchars); 838 break; 839 case TIOCSETC: 840 len = 0; 841 break; 842 case TIOCGDEV: 843 td = (struct ttydevb *)io->data; 844 td->ispeed = td->ospeed = B9600; 845 put2byte(td->flags, 0); 846 len = sizeof(struct ttydevb); 847 break; 848 case TIOCSDEV: 849 len = 0; 850 break; 851 default: 852 /* 853 * unimplemented 854 */ 855 m.b[len] = 0; 856 if(sendctl(net, M_IOCNAK) < 0) 857 return; 858 continue; 859 } 860 861 /* 862 * acknowledge 863 */ 864 m.h.type = M_IOCACK; 865 m.h.magic = MSGMAGIC; 866 len += 4; 867 put2byte(m.h.size, len); 868 len += sizeof(struct mesg); 869 if(iwrite(net, &m, len) != len) 870 return; 871 } 872 } 873 874 /* 875 * Read the keyboard, convert to mesgld messages, and write it to the network. 876 * '^\' gets us into the menu. 877 */ 878 void 879 msgfromkbd(int net) 880 { 881 long n; 882 char buf[MAXMSG]; 883 884 for(;;){ 885 n = iread(0, buf, sizeof(buf)); 886 if(n < 0) 887 return; 888 if(n && memchr(buf, 0034, n)){ 889 if(menu(net) < 0) 890 return; 891 } else { 892 if(msgwrite(net, buf, n) != n) 893 return; 894 } 895 } 896 } 897 898 int 899 msgwrite(int fd, void *buf, int len) 900 { 901 Msg m; 902 int n; 903 904 n = len; 905 memmove(m.b, buf, n); 906 put2byte(m.h.size, n); 907 m.h.magic = MSGMAGIC; 908 m.h.type = M_DATA; 909 n += sizeof(struct mesg); 910 if(iwrite(fd, &m, n) != n) 911 return -1; 912 913 put2byte(m.h.size, 0); 914 m.h.magic = MSGMAGIC; 915 m.h.type = M_DELIM; 916 n = sizeof(struct mesg); 917 if(iwrite(fd, &m, n) != n) 918 return -1; 919 920 return len; 921 } 922 923