1implement Tftpd; 2 3include "sys.m"; 4 sys: Sys; 5 stderr: ref Sys->FD; 6 7include "draw.m"; 8 9include "arg.m"; 10 11include "dial.m"; 12 dial: Dial; 13 14include "ip.m"; 15 ip: IP; 16 IPaddr, Udphdr: import ip; 17 18Tftpd: module 19{ 20 init: fn (nil: ref Draw->Context, argv: list of string); 21}; 22 23dir:= "/services/tftpd"; 24net:= "/net"; 25 26Tftp_READ: con 1; 27Tftp_WRITE: con 2; 28Tftp_DATA: con 3; 29Tftp_ACK: con 4; 30Tftp_ERROR: con 5; 31 32Segsize: con 512; 33 34dbg := 0; 35restricted := 0; 36port := 69; 37 38Udphdrsize: con IP->Udphdrlen; 39 40tftpcon: ref Sys->Connection; 41tftpreq: ref Sys->FD; 42 43dokill(pid: int, scope: string) 44{ 45 fd := sys->open("/prog/" + string pid + "/ctl", sys->OWRITE); 46 if(fd == nil) 47 fd = sys->open("#p/" + string pid + "/ctl", sys->OWRITE); 48 if(fd != nil) 49 sys->fprint(fd, "kill%s", scope); 50} 51 52kill(pid: int) { dokill(pid, ""); } 53killgrp(pid: int) { dokill(pid, "grp"); } 54killme() { kill(sys->pctl(0,nil)); } 55killus() { killgrp(sys->pctl(0,nil)); } 56 57DBG(s: string) 58{ 59 if(dbg) 60 sys->fprint(stderr, "tfptd: %d: %s\n", sys->pctl(0,nil), s); 61} 62 63false, true: con iota; 64 65Timer: adt { 66 KILL: con -1; 67 ALARM: con -2; 68 RETRY: con -3; 69 sig: chan of int; 70 create: fn(): ref Timer; 71 destroy: fn(t: self ref Timer); 72 set: fn(t: self ref Timer, msec, nretry: int); 73 74 ticker: fn(t: self ref Timer); 75 ticking: int; 76 wakeup: int; 77 timeout: int; 78 nretry: int; 79}; 80 81Timer.create(): ref Timer 82{ 83 t := ref Timer; 84 t.wakeup = 0; 85 t.ticking = false; 86 t.sig = chan of int; 87 return t; 88} 89 90Timer.destroy(t: self ref Timer) 91{ 92 DBG("Timer.destroy"); 93 alt { 94 t.sig <-= t.KILL => 95 DBG("sent final msg"); 96 * => 97 DBG("couldn't send final msg"); 98 } 99 DBG("Timer.destroy done"); 100} 101 102Timer.ticker(t: self ref Timer) 103{ 104 DBG("spawn: ticker"); 105 t.ticking = true; 106 while(t.wakeup > sys->millisec()) { 107 DBG("Timer.ticker sleeping for " 108 +string (t.wakeup-sys->millisec())); 109 sys->sleep(t.wakeup-sys->millisec()); 110 } 111 if(t.wakeup) { 112 DBG("Timer.ticker wakeup"); 113 if(t.nretry) { 114 alt { t.sig <-= t.RETRY => ; } 115 t.ticking = false; 116 t.set(t.timeout, t.nretry-1); 117 } else 118 alt { t.sig <-= t.ALARM => ; } 119 } 120 t.ticking = false; 121 DBG("unspawn: ticker"); 122} 123 124Timer.set(t: self ref Timer, msec, nretry: int) 125{ 126 DBG(sys->sprint("Timer.set(%d, %d)", msec, nretry)); 127 if(msec == 0) { 128 t.wakeup = 0; 129 t.timeout = 0; 130 t.nretry = 0; 131 } else { 132 t.wakeup = sys->millisec()+msec; 133 t.timeout = msec; 134 t.nretry = nretry; 135 if(!t.ticking) 136 spawn t.ticker(); 137 } 138} 139 140killer(c: chan of int, pgid: int) 141{ 142 DBG("spawn: killer"); 143 cmd := <- c; 144 DBG(sys->sprint("killer has awakened (flag=%d)", cmd)); 145 if(cmd == Timer.ALARM) { 146 killgrp(pgid); 147 DBG(sys->sprint("group %d has been killed", pgid)); 148 } 149 DBG("unspawn killer"); 150} 151 152init(nil: ref Draw->Context, args: list of string) 153{ 154 sys = load Sys Sys->PATH; 155 sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKNS, nil); 156 stderr = sys->fildes(2); 157 158 dial = load Dial Dial->PATH; 159 if(dial == nil) 160 fatal("can't load Dial"); 161 162 arg := load Arg Arg->PATH; 163 if(arg == nil) 164 fatal("can't load Arg"); 165 166 arg->init(args); 167 arg->setusage("tftpd [-dr] [-p port] [-h homedir] [-x network-dir]"); 168 while((o := arg->opt()) != 0) 169 case o { 170 'd' => dbg++; 171 'h' => dir = arg->earg(); 172 'r' => restricted = 1; 173 'p' => port = int arg->earg(); 174 'x' => net = arg->earg(); 175 * => arg->usage(); 176 } 177 args =arg->argv(); 178 if(args != nil){ 179 net = hd args; 180 args = tl args; 181 } 182 if(args != nil) 183 arg->usage(); 184 arg = nil; 185 186 ip = load IP IP->PATH; 187 if(ip == nil) 188 fatal(sys->sprint("can't load %s: %r", IP->PATH)); 189 ip->init(); 190 191 if(sys->chdir(dir) < 0) 192 fatal("can't chdir to " + dir); 193 194 spawn mainthing(); 195} 196 197mainthing() 198{ 199 DBG("spawn: mainthing"); 200 bigbuf := array[32768] of byte; 201 202 openlisten(); 203 setuser(); 204 for(;;) { 205 dlen := sys->read(tftpreq, bigbuf, len bigbuf); 206 if(dlen < 0) 207 fatal("listen"); 208 if(dlen < Udphdrsize) 209 continue; 210 211 hdr := Udphdr.unpack(bigbuf, Udphdrsize); 212 213 raddr := sys->sprint("%s/udp!%s!%d", net, hdr.raddr.text(), hdr.rport); 214 215 DBG(sys->sprint("raddr=%s", raddr)); 216 cx := dial->dial(raddr, nil); 217 if(cx == nil) 218 fatal("dialing "+raddr); 219 220# showbuf("bigbuf", bigbuf[0:dlen]); 221 222 op := ip->get2(bigbuf, Udphdrsize); 223 mbuf := bigbuf[Udphdrsize+2:dlen]; # get past Udphdr and op 224 dlen -= 14; 225 226 case op { 227 Tftp_READ or Tftp_WRITE => 228 ; 229 Tftp_ERROR => 230 DBG("tftp error"); 231 continue; 232 * => 233 nak(cx.dfd, 4, "Illegal TFTP operation"); 234 continue; 235 } 236 237# showbuf("mbuf", mbuf[0:dlen]); 238 239 i := 0; 240 while(dlen > 0 && mbuf[i] != byte 0) { 241 dlen--; 242 i++; 243 } 244 245 p := i++; 246 dlen--; 247 while(dlen > 0 && mbuf[i] != byte 0) { 248 dlen--; 249 i++; 250 } 251 252 path := string mbuf[0:p]; 253 mode := string mbuf[p+1:i]; 254 DBG(sys->sprint("path = %s, mode = %s", path, mode)); 255 256 if(dlen == 0) { 257 nak(cx.dfd, 0, "bad tftpmode"); 258 continue; 259 } 260 261 if(restricted && dodgy(path)){ 262 nak(cx.dfd, 4, "Permission denied"); 263 continue; 264 } 265 266 if(op == Tftp_READ) 267 spawn sendfile(cx.dfd, path, mode); 268 else 269 spawn recvfile(cx.dfd, path, mode); 270 } 271} 272 273dodgy(path: string): int 274{ 275 n := len path; 276 nd := len dir; 277 if(n == 0 || 278 path[0] == '#' || 279 path[0] == '/' && (n < nd+1 || path[0:nd] != dir || path[nd] != '/')) 280 return 1; 281 (nil, flds) := sys->tokenize(path, "/"); 282 for(; flds != nil; flds = tl flds) 283 if(hd flds == "..") 284 return 1; 285 return 0; 286} 287 288showbuf(msg: string, b: array of byte) 289{ 290 sys->fprint(stderr, "%s: size %d: ", msg, len b); 291 for(i:=0; i<len b; i++) 292 sys->fprint(stderr, "%.2ux ", int b[i]); 293 sys->fprint(stderr, "\n"); 294 for(i=0; i<len b; i++) 295 if(int b[i] >= 32 && int b[i] <= 126) 296 sys->fprint(stderr, " %c", int b[i]); 297 else 298 sys->fprint(stderr, " ."); 299 sys->fprint(stderr, "\n"); 300} 301 302sendblock(sig: chan of int, buf: array of byte, net: ref sys->FD, ksig: chan of int) 303{ 304 DBG("spawn: sendblocks"); 305 nbytes := 0; 306 loop: for(;;) { 307 DBG("sendblock: waiting for cmd"); 308 cmd := <- sig; 309 DBG(sys->sprint("sendblock: cmd=%d", cmd)); 310 case cmd { 311 Timer.KILL => 312 DBG("sendblock: killed"); 313 return; 314 Timer.RETRY => 315 ; 316 Timer.ALARM => 317 DBG("too many retries"); 318 break loop; 319 * => 320 nbytes = cmd; 321 } 322# showbuf("sendblock", buf[0:nbytes]); 323 ret := sys->write(net, buf, 4+nbytes); 324 DBG(sys->sprint("ret=%d", ret)); 325 326 if(ret < 0) { 327 ksig <-= Timer.ALARM; 328 fatal("tftp: network write error"); 329 } 330 if(ret != 4+nbytes) 331 return; 332 } 333 DBG("sendblock: exiting"); 334 alt { ksig <-= Timer.ALARM => ; } 335 DBG("unspawn: sendblocks"); 336} 337 338sendfile(net: ref sys->FD, name: string, mode: string) 339{ 340 341 DBG(sys->sprint("spawn: sendfile: name=%s mode=%s", name, mode)); 342 343 pgrp := sys->pctl(Sys->NEWPGRP, nil); 344 ack := array[1024] of byte; 345 if(name == "") { 346 nak(net, 0, "not in our database"); 347 return; 348 } 349 350 file := sys->open(name, Sys->OREAD); 351 if(file == nil) { 352 DBG(sys->sprint("open failed: %s", name)); 353 errbuf := sys->sprint("%r"); 354 nak(net, 0, errbuf); 355 return; 356 } 357 DBG(sys->sprint("opened %s", name)); 358 359 block := 0; 360 timer := Timer.create(); 361 ksig := chan of int; 362 buf := array[4+Segsize] of byte; 363 364 spawn killer(ksig, pgrp); 365 spawn sendblock(timer.sig, buf, net, ksig); 366 367 mainloop: for(;;) { 368 block++; 369 buf[0:] = array[] of {byte 0, byte Tftp_DATA, 370 byte (block>>8), byte block}; 371 n := sys->read(file, buf[4:], len buf-4); 372 DBG(sys->sprint("n=%d", n)); 373 if(n < 0) { 374 errbuf := sys->sprint("%r"); 375 nak(net, 0, errbuf); 376 break; 377 } 378 DBG(sys->sprint("signalling write of %d to block %d", n, block)); 379 timer.sig <-= n; 380 for(rxl := 0; rxl < 10; rxl++) { 381 382 timer.set(1000, 15); 383 al := sys->read(net, ack, len ack); 384 timer.set(0, 0); 385 if(al < 0) { 386 timer.sig <-= Timer.ALARM; 387 break; 388 } 389 op := (int ack[0]<<8) | int ack[1]; 390 if(op == Tftp_ERROR) 391 break mainloop; 392 ackblock := (int ack[2]<<8) | int ack[3]; 393 DBG(sys->sprint("got ack: block=%d ackblock=%d", 394 block, ackblock)); 395 if(ackblock == block) 396 break; 397 if(ackblock == 16rffff) { 398 block--; 399 break; 400 } 401 } 402 if(n < len buf-4) 403 break; 404 } 405 timer.destroy(); 406 ksig <-= Timer.KILL; 407} 408 409recvfile(fd: ref sys->FD, name: string, mode: string) 410{ 411 DBG(sys->sprint("spawn: recvfile: name=%s mode=%s", name, mode)); 412 413 pgrp := sys->pctl(Sys->NEWPGRP, nil); 414 415 file := sys->create(name, sys->OWRITE, 8r666); 416 if(file == nil) { 417 errbuf := sys->sprint("%r"); 418 nak(fd, 0, errbuf); 419 return; 420 } 421 422 block := 0; 423 ack(fd, block); 424 block++; 425 426 buf := array[8+Segsize] of byte; 427 timer := Timer.create(); 428 spawn killer(timer.sig, pgrp); 429 430 for(;;) { 431 timer.set(15000, 0); 432 DBG(sys->sprint("reading block %d", block)); 433 n := sys->read(fd, buf, len buf); 434 DBG(sys->sprint("read %d bytes", n)); 435 timer.set(0, 0); 436 437 if(n < 0) 438 break; 439 op := int buf[0]<<8 | int buf[1]; 440 if(op == Tftp_ERROR) 441 break; 442 443# showbuf("got", buf[0:n]); 444 n -= 4; 445 inblock := int buf[2]<<8 | int buf[3]; 446# showbuf("hdr", buf[0:4]); 447 if(op == Tftp_DATA) { 448 if(inblock == block) { 449 ret := sys->write(file, buf[4:], n); 450 if(ret < 0) { 451 errbuf := sys->sprint("%r"); 452 nak(fd, 0, errbuf); 453 break; 454 } 455 block++; 456 } 457 if(inblock < block) { 458 ack(fd, inblock); 459 DBG(sys->sprint("ok: inblock=%d block=%d", 460 inblock, block)); 461 } else 462 DBG(sys->sprint("FAIL: inblock=%d block=%d", 463 inblock, block)); 464 ack(fd, 16rffff); 465 if(n < 512) 466 break; 467 } 468 } 469 timer.destroy(); 470} 471 472ack(fd: ref Sys->FD, block: int) 473{ 474 buf := array[] of {byte 0, byte Tftp_ACK, byte (block>>8), byte block}; 475# showbuf("ack", buf); 476 if(sys->write(fd, buf, 4) < 0) 477 fatal("write ack"); 478} 479 480 481nak(fd: ref Sys->FD, code: int, msg: string) 482{ 483sys->print("nak: %s\n", msg); 484 buf := array[128] of {byte 0, byte Tftp_ERROR, byte 0, byte code}; 485 bmsg := array of byte msg; 486 buf[4:] = bmsg; 487 buf[4+len bmsg] = byte 0; 488 if(sys->write(fd, buf, 4+len bmsg+1) < 0) 489 fatal("write nak"); 490} 491 492fatal(msg: string) 493{ 494 sys->fprint(stderr, "tftpd: %s: %r\n", msg); 495 killus(); 496 raise "fail:error"; 497} 498 499openlisten() 500{ 501 name := net+"/udp!*!" + string port; 502 tftpcon = dial->announce(name); 503 if(tftpcon == nil) 504 fatal("can't announce "+name); 505 if(sys->fprint(tftpcon.cfd, "headers") < 0) 506 fatal("can't set header mode"); 507 tftpreq = sys->open(tftpcon.dir+"/data", sys->ORDWR); 508 if(tftpreq == nil) 509 fatal("open udp data"); 510} 511 512setuser() 513{ 514 f := sys->open("/dev/user", sys->OWRITE); 515 if(f != nil) 516 sys->fprint(f, "none"); 517} 518 519