1implement Telnet; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "dial.m"; 9 dial: Dial; 10 Connection: import dial; 11 12Telnet: module 13{ 14 init: fn(ctxt: ref Draw->Context, args: list of string); 15}; 16 17Debug: con 0; 18 19Inbuf: adt { 20 fd: ref Sys->FD; 21 out: ref Outbuf; 22 buf: array of byte; 23 ptr: int; 24 nbyte: int; 25}; 26 27Outbuf: adt { 28 buf: array of byte; 29 ptr: int; 30}; 31 32BS: con 8; # ^h backspace character 33BSW: con 23; # ^w bacspace word 34BSL: con 21; # ^u backspace line 35EOT: con 4; # ^d end of file 36ESC: con 27; # hold mode 37 38net: ref Connection; 39stdin, stdout, stderr: ref Sys->FD; 40 41# control characters 42Se: con 240; # end subnegotiation 43NOP: con 241; 44Mark: con 242; # data mark 45Break: con 243; 46Interrupt: con 244; 47Abort: con 245; # TENEX ^O 48AreYouThere: con 246; 49Erasechar: con 247; # erase last character 50Eraseline: con 248; # erase line 51GoAhead: con 249; # half duplex clear to send 52Sb: con 250; # start subnegotiation 53Will: con 251; 54Wont: con 252; 55Do: con 253; 56Dont: con 254; 57Iac: con 255; 58 59# options 60Binary, Echo, SGA, Stat, Timing, 61Det, Term, EOR, Uid, Outmark, 62Ttyloc, M3270, Padx3, Window, Speed, 63Flow, Line, Xloc, Extend: con iota; 64 65Opt: adt 66{ 67 name: string; 68 code: int; 69 noway: int; 70 remote: int; # remote value 71 local: int; # local value 72}; 73 74opt := array[] of 75{ 76 Binary => Opt("binary", 0, 0, 0, 0), 77 Echo => Opt("echo", 1, 0, 0, 0), 78 SGA => Opt("suppress go ahead", 3, 0, 0, 0), 79 Stat => Opt("status", 5, 1, 0, 0), 80 Timing => Opt("timing", 6, 1, 0, 0), 81 Det=> Opt("det", 20, 1, 0, 0), 82 Term => Opt("terminal", 24, 0, 0, 0), 83 EOR => Opt("end of record", 25, 1, 0, 0), 84 Uid => Opt("uid", 26, 1, 0, 0), 85 Outmark => Opt("outmark", 27, 1, 0, 0), 86 Ttyloc => Opt("ttyloc", 28, 1, 0, 0), 87 M3270 => Opt("3270 mode", 29, 1, 0, 0), 88 Padx3 => Opt("pad x.3", 30, 1, 0, 0), 89 Window => Opt("window size", 31, 1, 0, 0), 90 Speed => Opt("speed", 32, 1, 0, 0), 91 Flow => Opt("flow control", 33, 1, 0, 0), 92 Line => Opt("line mode", 34, 1, 0, 0), 93 Xloc => Opt("X display loc", 35, 1, 0, 0), 94 Extend => Opt("Extended", 255, 1, 0, 0), 95}; 96 97usage() 98{ 99 sys->fprint(stderr, "usage: telnet host [port]\n"); 100 raise "fail:usage"; 101} 102 103init(nil: ref Draw->Context, argv: list of string) 104{ 105 sys = load Sys Sys->PATH; 106 stderr = sys->fildes(2); 107 stdout = sys->fildes(1); 108 stdin = sys->fildes(0); 109 dial = load Dial Dial->PATH; 110 111 if (len argv < 2) 112 usage(); 113 argv = tl argv; 114 host := hd argv; 115 argv = tl argv; 116 port := "23"; 117 if(argv != nil) 118 port = hd argv; 119 connect(host, port); 120} 121 122ccfd: ref Sys->FD; 123connect(addr: string, port: string) 124{ 125 dest := dial->netmkaddr(addr, "tcp", port); 126 net = dial->dial(dest, nil); 127 if(net == nil) { 128 sys->fprint(stderr, "telnet: can't dial %s: %r\n", dest); 129 raise "fail:dial"; 130 } 131 sys->fprint(stderr, "telnet: connected to %s\n", addr); 132 133 raw(1); 134 pidch := chan of int; 135 finished := chan of int; 136 spawn fromnet(pidch, finished); 137 spawn fromuser(pidch, finished); 138 pids := array[2] of {* => <-pidch}; 139 kill(pids[<-finished == pids[0]]); 140 raw(0); 141} 142 143 144fromuser(pidch, finished: chan of int) 145{ 146 pidch <-= sys->pctl(0, nil); 147 b := array[1024] of byte; 148 while((n := sys->read(stdin, b, len b)) > 0) { 149 if (opt[Echo].remote == 0) 150 sys->write(stdout, b, n); 151 sys->write(net.dfd, b, n); 152 } 153 sys->fprint(stderr, "telnet: error reading stdin: %r\n"); 154 finished <-= sys->pctl(0, nil); 155} 156 157getc(b: ref Inbuf): int 158{ 159 if(b.nbyte == 0) { 160 if(b.out != nil) 161 flushout(b.out); 162 b.nbyte = sys->read(b.fd, b.buf, len b.buf); 163 if(b.nbyte <= 0) 164 return -1; 165 b.ptr = 0; 166 } 167 b.nbyte--; 168 return int b.buf[b.ptr++]; 169} 170 171putc(b: ref Outbuf, c: int) 172{ 173 b.buf[b.ptr++] = byte c; 174 if(b.ptr == len b.buf) 175 flushout(b); 176} 177 178flushout(b: ref Outbuf) 179{ 180 sys->write(stdout, b.buf, b.ptr); 181 b.ptr = 0; 182} 183 184BUFSIZE: con 2048; 185fromnet(pidch, finished: chan of int) 186{ 187 pidch <-= sys->pctl(0, nil); 188 conout := ref Outbuf(array[BUFSIZE] of byte, 0); 189 netinp := ref Inbuf(net.dfd, conout, array[BUFSIZE] of byte, 0, 0); 190 191loop: for(;;) { 192 c := getc(netinp); 193 case c { 194 -1 => 195 break loop; 196 Iac => 197 c = getc(netinp); 198 if(c != Iac) { 199 flushout(conout); 200 if(control(netinp, c) < 0) 201 break loop; 202 } else 203 putc(conout, c); 204 * => 205 putc(conout, c); 206 } 207 } 208 sys->fprint(stderr, "telnet: remote host closed connection\n"); 209 finished <-= sys->pctl(0, nil); 210} 211 212control(bp: ref Inbuf, c: int): int 213{ 214 r := 0; 215 case c { 216 AreYouThere => 217 sys->fprint(net.dfd, "Inferno telnet\r\n"); 218 Sb => 219 r = sub(bp); 220 Will => 221 r = will(bp); 222 Wont => 223 r = wont(bp); 224 Do => 225 r = doit(bp); 226 Dont => 227 r = dont(bp); 228 Se => 229 sys->fprint(stderr, "telnet: SE without an SB\n"); 230 -1 => 231 r = -1; 232 } 233 234 return r; 235} 236 237sub(bp: ref Inbuf): int 238{ 239 subneg: string; 240 i := 0; 241 for(;;){ 242 c := getc(bp); 243 if(c == Iac) { 244 c = getc(bp); 245 if(c == Se) 246 break; 247 subneg[i++] = Iac; 248 } 249 if(c < 0) 250 return -1; 251 subneg[i++] = c; 252 } 253 if(i == 0) 254 return 0; 255 256 if (Debug) 257 sys->fprint(stderr, "telnet: sub(%s, %d, n = %d)\n", optname(subneg[0]), subneg[1], i); 258 259 for(i = 0; i < len opt; i++) 260 if(opt[i].code == subneg[0]) 261 break; 262 263 if(i >= len opt) 264 return 0; 265 266 case i { 267 Term => 268 sbsend(opt[Term].code, array of byte "network"); 269 } 270 271 return 0; 272} 273 274sbsend(code: int, data: array of byte): int 275{ 276 buf := array[4+len data+2] of byte; 277 o := 4+len data; 278 279 buf[0] = byte Iac; 280 buf[1] = byte Sb; 281 buf[2] = byte code; 282 buf[3] = byte 0; 283 buf[4:] = data; 284 buf[o] = byte Iac; 285 o++; 286 buf[o] = byte Se; 287 288 return sys->write(net.dfd, buf, len buf); 289} 290 291will(bp: ref Inbuf): int 292{ 293 c := getc(bp); 294 if(c < 0) 295 return -1; 296 297 if (Debug) 298 sys->fprint(stderr, "telnet: will(%s)\n", optname(c)); 299 300 for(i := 0; i < len opt; i++) 301 if(opt[i].code == c) 302 break; 303 304 if(i >= len opt) { 305 send3(bp, Iac, Dont, c); 306 return 0; 307 } 308 309 rv := 0; 310 if(opt[i].noway) 311 send3(bp, Iac, Dont, c); 312 else 313 if(opt[i].remote == 0) 314 rv |= send3(bp, Iac, Do, c); 315 316 if(opt[i].remote == 0) 317 rv |= change(bp, i, Will); 318 opt[i].remote = 1; 319 return rv; 320} 321 322wont(bp: ref Inbuf): int 323{ 324 c := getc(bp); 325 if(c < 0) 326 return -1; 327 328 if (Debug) 329 sys->fprint(stderr, "telnet: wont(%s)\n", optname(c)); 330 331 for(i := 0; i < len opt; i++) 332 if(opt[i].code == c) 333 break; 334 335 if(i >= len opt) 336 return 0; 337 338 rv := 0; 339 if(opt[i].remote) { 340 rv |= change(bp, i, Wont); 341 rv |= send3(bp, Iac, Dont, c); 342 } 343 opt[i].remote = 0; 344 return rv; 345} 346 347doit(bp: ref Inbuf): int 348{ 349 c := getc(bp); 350 if(c < 0) 351 return -1; 352 353 if (Debug) 354 sys->fprint(stderr, "telnet: do(%s)\n", optname(c)); 355 356 for(i := 0; i < len opt; i++) 357 if(opt[i].code == c) 358 break; 359 360 if(i >= len opt || opt[i].noway) { 361 send3(bp, Iac, Wont, c); 362 return 0; 363 } 364 rv := 0; 365 if(opt[i].local == 0) { 366 rv |= change(bp, i, Do); 367 rv |= send3(bp, Iac, Will, c); 368 } 369 opt[i].local = 1; 370 return rv; 371} 372 373dont(bp: ref Inbuf): int 374{ 375 c := getc(bp); 376 if(c < 0) 377 return -1; 378 379 if (Debug) 380 sys->fprint(stderr, "telnet: dont(%s)\n", optname(c)); 381 382 for(i := 0; i < len opt; i++) 383 if(opt[i].code == c) 384 break; 385 386 if(i >= len opt || opt[i].noway) 387 return 0; 388 389 rv := 0; 390 if(opt[i].local){ 391 opt[i].local = 0; 392 rv |= change(bp, i, Dont); 393 rv |= send3(bp, Iac, Wont, c); 394 } 395 opt[i].local = 0; 396 return rv; 397} 398 399change(bp: ref Inbuf, o: int, what: int): int 400{ 401 if(bp != nil) 402 {} 403 if(o != 0) 404 {} 405 if(what != 0) 406 {} 407 return 0; 408} 409 410send3(bp: ref Inbuf, c0: int, c1: int, c2: int): int 411{ 412 if (Debug) 413 sys->fprint(stderr, "telnet: reply(%s(%s))\n", negname(c1), optname(c2)); 414 415 buf := array[3] of byte; 416 417 buf[0] = byte c0; 418 buf[1] = byte c1; 419 buf[2] = byte c2; 420 421 if (sys->write(bp.fd, buf, 3) != 3) 422 return -1; 423 return 0; 424} 425 426kill(pid: int): int 427{ 428 fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); 429 if (fd == nil) 430 return -1; 431 if (sys->write(fd, array of byte "kill", 4) != 4) 432 return -1; 433 return 0; 434} 435 436negname(c: int): string 437{ 438 t := "Unknown"; 439 case c { 440 Will => t = "will"; 441 Wont => t = "wont"; 442 Do => t = "do"; 443 Dont => t = "dont"; 444 } 445 return t; 446} 447 448optname(c: int): string 449{ 450 for (i := 0; i < len opt; i++) 451 if (opt[i].code == c) 452 return opt[i].name; 453 return "unknown"; 454} 455 456raw(on: int) 457{ 458 if(ccfd == nil) { 459 ccfd = sys->open("/dev/consctl", Sys->OWRITE); 460 if(ccfd == nil) { 461 sys->fprint(stderr, "telnet: cannot open /dev/consctl: %r\n"); 462 return; 463 } 464 } 465 if(on) 466 sys->fprint(ccfd, "rawon"); 467 else 468 sys->fprint(ccfd, "rawoff"); 469} 470