1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include "telnet.h" 5 6 int ctl = -1; /* control fd (for break's) */ 7 int consctl = -1; /* consctl fd */ 8 9 int ttypid; /* pid's if the 2 processes (used to kill them) */ 10 int netpid; 11 int interrupted; 12 int localecho; 13 int notkbd; 14 15 static char *srv; 16 17 typedef struct Comm Comm; 18 struct Comm { 19 int returns; 20 int stopped; 21 }; 22 Comm *comm; 23 24 int dodial(char*); 25 void fromkbd(int); 26 void fromnet(int); 27 int menu(Biobuf*, int); 28 void notifyf(void*, char*); 29 void rawoff(void); 30 void rawon(void); 31 void telnet(int); 32 char* system(int, char*); 33 int echochange(Biobuf*, int); 34 int termsub(Biobuf*, uchar*, int); 35 int xlocsub(Biobuf*, uchar*, int); 36 void* share(ulong); 37 38 static int islikeatty(int); 39 40 void 41 usage(void) 42 { 43 fatal("usage: telnet [-Cdnr] [-s srv] net!host[!service]", 0, 0); 44 } 45 46 void 47 main(int argc, char *argv[]) 48 { 49 int returns; 50 51 returns = 1; 52 ARGBEGIN{ 53 case 'C': 54 opt[Echo].noway = 1; 55 break; 56 case 'd': 57 debug = 1; 58 break; 59 case 'n': 60 notkbd = 1; 61 break; 62 case 'r': 63 returns = 0; 64 break; 65 case 's': 66 srv = EARGF(usage()); 67 break; 68 default: 69 usage(); 70 }ARGEND 71 72 if(argc != 1) 73 usage(); 74 75 /* options we need routines for */ 76 opt[Echo].change = echochange; 77 opt[Term].sub = termsub; 78 opt[Xloc].sub = xlocsub; 79 80 comm = share(sizeof(comm)); 81 comm->returns = returns; 82 83 telnet(dodial(argv[0])); 84 } 85 86 /* 87 * dial and return a data connection 88 */ 89 int 90 dodial(char *dest) 91 { 92 char *name; 93 int data; 94 char devdir[NETPATHLEN]; 95 96 name = netmkaddr(dest, "tcp", "telnet"); 97 data = dial(name, 0, devdir, 0); 98 if(data < 0) 99 fatal("%s: %r", name, 0); 100 fprint(2, "connected to %s on %s\n", name, devdir); 101 return data; 102 } 103 104 void 105 post(char *srv, int fd) 106 { 107 int f; 108 char buf[32]; 109 110 f = create(srv, OWRITE, 0666); 111 if(f < 0) 112 sysfatal("create %s: %r", srv); 113 snprint(buf, sizeof buf, "%d", fd); 114 if(write(f, buf, strlen(buf)) != strlen(buf)) 115 sysfatal("write %s: %r", srv); 116 close(f); 117 } 118 119 /* 120 * two processes pass bytes back and forth between the 121 * terminal and the network. 122 */ 123 void 124 telnet(int net) 125 { 126 int pid; 127 int p[2]; 128 char *svc; 129 130 rawoff(); 131 svc = nil; 132 if (srv) { 133 if(pipe(p) < 0) 134 sysfatal("pipe: %r"); 135 if (srv[0] != '/') 136 svc = smprint("/srv/%s", srv); 137 else 138 svc = srv; 139 post(svc, p[0]); 140 close(p[0]); 141 dup(p[1], 0); 142 dup(p[1], 1); 143 /* pipe is now std in & out */ 144 } 145 ttypid = getpid(); 146 switch(pid = rfork(RFPROC|RFFDG|RFMEM)){ 147 case -1: 148 perror("con"); 149 exits("fork"); 150 case 0: 151 rawoff(); 152 notify(notifyf); 153 fromnet(net); 154 if (svc) 155 remove(svc); 156 sendnote(ttypid, "die"); 157 exits(0); 158 default: 159 netpid = pid; 160 notify(notifyf); 161 fromkbd(net); 162 if(notkbd) 163 for(;;) 164 sleep(0); 165 if (svc) 166 remove(svc); 167 sendnote(netpid, "die"); 168 exits(0); 169 } 170 } 171 172 /* 173 * Read the keyboard and write it to the network. '^\' gets us into 174 * the menu. 175 */ 176 void 177 fromkbd(int net) 178 { 179 Biobuf ib, ob; 180 int c, likeatty; 181 int eofs; 182 183 Binit(&ib, 0, OREAD); 184 Binit(&ob, net, OWRITE); 185 186 likeatty = islikeatty(0); 187 eofs = 0; 188 for(;;){ 189 c = Bgetc(&ib); 190 191 /* 192 * with raw off, all ^D's get turned into Eof's. 193 * change them back. 194 * 10 in a row implies that the terminal is really gone so 195 * just hang up. 196 */ 197 if(c < 0){ 198 if(notkbd) 199 return; 200 if(eofs++ > 10) 201 return; 202 c = 004; 203 } else 204 eofs = 0; 205 206 /* 207 * if not in binary mode, look for the ^\ escape to menu. 208 * also turn \n into \r\n 209 */ 210 if(likeatty || !opt[Binary].local){ 211 if(c == 0034){ /* CTRL \ */ 212 if(Bflush(&ob) < 0) 213 return; 214 if(menu(&ib, net) < 0) 215 return; 216 continue; 217 } 218 } 219 if(!opt[Binary].local){ 220 if(c == '\n'){ 221 /* 222 * This is a very strange use of the SGA option. 223 * I did this because some systems that don't 224 * announce a willingness to supress-go-ahead 225 * need the \r\n sequence to recognize input. 226 * If someone can explain this to me, please 227 * send me mail. - presotto 228 */ 229 if(opt[SGA].remote){ 230 c = '\r'; 231 } else { 232 if(Bputc(&ob, '\r') < 0) 233 return; 234 } 235 } 236 } 237 if(Bputc(&ob, c) < 0) 238 return; 239 if(Bbuffered(&ib) == 0) 240 if(Bflush(&ob) < 0) 241 return; 242 } 243 } 244 245 /* 246 * Read from the network and write to the screen. If 'stopped' is set 247 * spin and don't read. Filter out spurious carriage returns. 248 */ 249 void 250 fromnet(int net) 251 { 252 int c; 253 int crnls = 0, freenl = 0, eofs; 254 Biobuf ib, ob; 255 256 Binit(&ib, net, OREAD); 257 Binit(&ob, 1, OWRITE); 258 eofs = 0; 259 for(;;){ 260 if(Bbuffered(&ib) == 0) 261 Bflush(&ob); 262 if(interrupted){ 263 interrupted = 0; 264 send2(net, Iac, Interrupt); 265 } 266 c = Bgetc(&ib); 267 if(c < 0){ 268 if(eofs++ >= 2) 269 return; 270 continue; 271 } 272 eofs = 0; 273 switch(c){ 274 case '\n': /* skip nl after string of cr's */ 275 if(!opt[Binary].local && !comm->returns){ 276 ++crnls; 277 if(freenl == 0) 278 break; 279 freenl = 0; 280 continue; 281 } 282 break; 283 case '\r': /* first cr becomes nl, remainder dropped */ 284 if(!opt[Binary].local && !comm->returns){ 285 if(crnls++ == 0){ 286 freenl = 1; 287 c = '\n'; 288 break; 289 } 290 continue; 291 } 292 break; 293 case 0: /* remove nulls from crnl string */ 294 if(crnls) 295 continue; 296 break; 297 298 case Iac: 299 crnls = 0; 300 freenl = 0; 301 c = Bgetc(&ib); 302 if(c == Iac) 303 break; 304 if(Bflush(&ob) < 0) 305 return; 306 if(control(&ib, c) < 0) 307 return; 308 continue; 309 310 default: 311 crnls = 0; 312 freenl = 0; 313 break; 314 } 315 if(Bputc(&ob, c) < 0) 316 return; 317 } 318 } 319 320 /* 321 * turn keyboard raw mode on 322 */ 323 void 324 rawon(void) 325 { 326 if(debug) 327 fprint(2, "rawon\n"); 328 if(consctl < 0) 329 consctl = open("/dev/consctl", OWRITE); 330 if(consctl < 0){ 331 fprint(2, "can't open consctl: %r\n"); 332 return; 333 } 334 write(consctl, "rawon", 5); 335 } 336 337 /* 338 * turn keyboard raw mode off 339 */ 340 void 341 rawoff(void) 342 { 343 if(debug) 344 fprint(2, "rawoff\n"); 345 if(consctl < 0) 346 consctl = open("/dev/consctl", OWRITE); 347 if(consctl < 0){ 348 fprint(2, "can't open consctl: %r\n"); 349 return; 350 } 351 write(consctl, "rawoff", 6); 352 } 353 354 /* 355 * control menu 356 */ 357 #define STDHELP "\t(b)reak, (i)nterrupt, (q)uit, (r)eturns, (!cmd), (.)continue\n" 358 359 int 360 menu(Biobuf *bp, int net) 361 { 362 char *cp; 363 int done; 364 365 comm->stopped = 1; 366 367 rawoff(); 368 fprint(2, ">>> "); 369 for(done = 0; !done; ){ 370 cp = Brdline(bp, '\n'); 371 if(cp == 0){ 372 comm->stopped = 0; 373 return -1; 374 } 375 cp[Blinelen(bp)-1] = 0; 376 switch(*cp){ 377 case '!': 378 system(Bfildes(bp), cp+1); 379 done = 1; 380 break; 381 case '.': 382 done = 1; 383 break; 384 case 'q': 385 comm->stopped = 0; 386 return -1; 387 case 'o': 388 switch(*(cp+1)){ 389 case 'd': 390 send3(net, Iac, Do, atoi(cp+2)); 391 break; 392 case 'w': 393 send3(net, Iac, Will, atoi(cp+2)); 394 break; 395 } 396 break; 397 case 'r': 398 comm->returns = !comm->returns; 399 done = 1; 400 break; 401 case 'i': 402 send2(net, Iac, Interrupt); 403 break; 404 case 'b': 405 send2(net, Iac, Break); 406 break; 407 default: 408 fprint(2, STDHELP); 409 break; 410 } 411 if(!done) 412 fprint(2, ">>> "); 413 } 414 415 rawon(); 416 comm->stopped = 0; 417 return 0; 418 } 419 420 /* 421 * ignore interrupts 422 */ 423 void 424 notifyf(void *a, char *msg) 425 { 426 USED(a); 427 if(strcmp(msg, "interrupt") == 0){ 428 interrupted = 1; 429 noted(NCONT); 430 } 431 if(strcmp(msg, "hangup") == 0) 432 noted(NCONT); 433 noted(NDFLT); 434 } 435 436 /* 437 * run a command with the network connection as standard IO 438 */ 439 char * 440 system(int fd, char *cmd) 441 { 442 int pid; 443 int p; 444 static Waitmsg msg; 445 446 if((pid = fork()) == -1){ 447 perror("con"); 448 return "fork failed"; 449 } 450 else if(pid == 0){ 451 dup(fd, 0); 452 close(ctl); 453 close(fd); 454 if(*cmd) 455 execl("/bin/rc", "rc", "-c", cmd, nil); 456 else 457 execl("/bin/rc", "rc", nil); 458 perror("con"); 459 exits("exec"); 460 } 461 for(p = waitpid(); p >= 0; p = waitpid()){ 462 if(p == pid) 463 return msg.msg; 464 } 465 return "lost child"; 466 } 467 468 /* 469 * suppress local echo if the remote side is doing it 470 */ 471 int 472 echochange(Biobuf *bp, int cmd) 473 { 474 USED(bp); 475 476 switch(cmd){ 477 case Will: 478 rawon(); 479 break; 480 case Wont: 481 rawoff(); 482 break; 483 } 484 return 0; 485 } 486 487 /* 488 * send terminal type to the other side 489 */ 490 int 491 termsub(Biobuf *bp, uchar *sub, int n) 492 { 493 char buf[64]; 494 char *term; 495 char *p = buf; 496 497 if(n < 1) 498 return 0; 499 if(sub[0] == 1){ 500 *p++ = Iac; 501 *p++ = Sb; 502 *p++ = opt[Term].code; 503 *p++ = 0; 504 term = getenv("TERM"); 505 if(term == 0 || *term == 0) 506 term = "p9win"; 507 strncpy(p, term, sizeof(buf) - (p - buf) - 2); 508 buf[sizeof(buf)-2] = 0; 509 p += strlen(p); 510 *p++ = Iac; 511 *p++ = Se; 512 return iwrite(Bfildes(bp), buf, p-buf); 513 } 514 return 0; 515 } 516 517 /* 518 * send an x display location to the other side 519 */ 520 int 521 xlocsub(Biobuf *bp, uchar *sub, int n) 522 { 523 char buf[64]; 524 char *term; 525 char *p = buf; 526 527 if(n < 1) 528 return 0; 529 if(sub[0] == 1){ 530 *p++ = Iac; 531 *p++ = Sb; 532 *p++ = opt[Xloc].code; 533 *p++ = 0; 534 term = getenv("XDISP"); 535 if(term == 0 || *term == 0) 536 term = "unknown"; 537 strncpy(p, term, p - buf - 2); 538 p += strlen(term); 539 *p++ = Iac; 540 *p++ = Se; 541 return iwrite(Bfildes(bp), buf, p-buf); 542 } 543 return 0; 544 } 545 546 static int 547 islikeatty(int fd) 548 { 549 char buf[64]; 550 551 if(fd2path(fd, buf, sizeof buf) != 0) 552 return 0; 553 554 /* might be /mnt/term/dev/cons */ 555 return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; 556 } 557 558 /* 559 * create a shared segment. Make is start 2 meg higher than the current 560 * end of process memory. 561 */ 562 void* 563 share(ulong len) 564 { 565 uchar *vastart; 566 567 vastart = sbrk(0); 568 if(vastart == (void*)-1) 569 return 0; 570 vastart += 2*1024*1024; 571 572 if(segattach(0, "shared", vastart, len) == (void*)-1) 573 return 0; 574 575 return vastart; 576 } 577