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