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