1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <bio.h> 5 #include <ip.h> 6 #include <ndb.h> 7 8 enum 9 { 10 Maxpath= 128, 11 Maxerr= 256, 12 }; 13 14 int dbg; 15 int restricted; 16 void sendfile(int, char*, char*); 17 void recvfile(int, char*, char*); 18 void nak(int, int, char*); 19 void ack(int, ushort); 20 void clrcon(void); 21 void setuser(void); 22 char* sunkernel(char*); 23 void remoteaddr(char*, char*, int); 24 void doserve(int); 25 26 char bigbuf[32768]; 27 char raddr[64]; 28 29 char *dir = "/lib/tftpd"; 30 char *dirsl; 31 int dirsllen; 32 char flog[] = "ipboot"; 33 char net[Maxpath]; 34 35 enum 36 { 37 Tftp_READ = 1, 38 Tftp_WRITE = 2, 39 Tftp_DATA = 3, 40 Tftp_ACK = 4, 41 Tftp_ERROR = 5, 42 Segsize = 512, 43 }; 44 45 void 46 usage(void) 47 { 48 fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n", 49 argv0); 50 exits("usage"); 51 } 52 53 void 54 main(int argc, char **argv) 55 { 56 char buf[64]; 57 char adir[64], ldir[64]; 58 int cfd, lcfd, dfd; 59 char *p, *svc = "69"; 60 61 setnetmtpt(net, sizeof(net), nil); 62 ARGBEGIN{ 63 case 'd': 64 dbg++; 65 break; 66 case 'h': 67 dir = ARGF(); 68 break; 69 case 'r': 70 restricted = 1; 71 break; 72 case 's': 73 svc = EARGF(usage()); 74 break; 75 case 'x': 76 p = ARGF(); 77 if(p == nil) 78 usage(); 79 setnetmtpt(net, sizeof(net), p); 80 break; 81 default: 82 usage(); 83 }ARGEND 84 85 snprint(buf, sizeof buf, "%s/", dir); 86 dirsl = strdup(buf); 87 dirsllen = strlen(dirsl); 88 89 fmtinstall('E', eipfmt); 90 fmtinstall('I', eipfmt); 91 92 if(chdir(dir) < 0) 93 sysfatal("can't get to directory %s: %r", dir); 94 95 if(!dbg) 96 switch(rfork(RFNOTEG|RFPROC|RFFDG)) { 97 case -1: 98 sysfatal("fork: %r"); 99 case 0: 100 break; 101 default: 102 exits(0); 103 } 104 105 snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc); 106 cfd = announce(buf, adir); 107 if (cfd < 0) 108 sysfatal("announcing on %s: %r", buf); 109 syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir); 110 setuser(); 111 for(;;) { 112 lcfd = listen(adir, ldir); 113 if(lcfd < 0) 114 sysfatal("listening on %s: %r", adir); 115 116 switch(fork()) { 117 case -1: 118 sysfatal("fork: %r"); 119 case 0: 120 dfd = accept(lcfd, ldir); 121 if(dfd < 0) 122 exits(0); 123 remoteaddr(ldir, raddr, sizeof(raddr)); 124 syslog(0, flog, "tftp connection from %s dir %s", 125 raddr, ldir); 126 doserve(dfd); 127 exits("done"); 128 break; 129 default: 130 close(lcfd); 131 continue; 132 } 133 } 134 } 135 136 void 137 doserve(int fd) 138 { 139 int dlen; 140 char *mode, *p; 141 short op; 142 143 dlen = read(fd, bigbuf, sizeof(bigbuf)); 144 if(dlen < 0) 145 sysfatal("listen read: %r"); 146 147 op = (bigbuf[0]<<8) | bigbuf[1]; 148 dlen -= 2; 149 mode = bigbuf+2; 150 while(*mode != '\0' && dlen--) 151 mode++; 152 mode++; 153 p = mode; 154 while(*p && dlen--) 155 p++; 156 if(dlen == 0) { 157 nak(fd, 0, "bad tftpmode"); 158 close(fd); 159 syslog(dbg, flog, "bad mode from %s", raddr); 160 return; 161 } 162 163 if(op != Tftp_READ && op != Tftp_WRITE) { 164 nak(fd, 4, "Illegal TFTP operation"); 165 close(fd); 166 syslog(dbg, flog, "bad request %d %s", op, raddr); 167 return; 168 } 169 170 if(restricted){ 171 if(bigbuf[2] == '#' || 172 strncmp(bigbuf+2, "../", 3)==0 || strstr(bigbuf+2, "/../") || 173 (bigbuf[2] == '/' && strncmp(bigbuf+2, dirsl, dirsllen)!=0)){ 174 nak(fd, 4, "Permission denied"); 175 close(fd); 176 syslog(dbg, flog, "bad request %d from %s file %s", op, raddr, bigbuf+2); 177 return; 178 } 179 } 180 181 if(op == Tftp_READ) 182 sendfile(fd, bigbuf+2, mode); 183 else 184 recvfile(fd, bigbuf+2, mode); 185 } 186 187 void 188 catcher(void *junk, char *msg) 189 { 190 USED(junk); 191 192 if(strncmp(msg, "exit", 4) == 0) 193 noted(NDFLT); 194 noted(NCONT); 195 } 196 197 void 198 sendfile(int fd, char *name, char *mode) 199 { 200 int file; 201 uchar buf[Segsize+4]; 202 uchar ack[1024]; 203 char errbuf[Maxerr]; 204 int ackblock, block, ret; 205 int rexmit, n, al, txtry, rxl; 206 short op; 207 208 syslog(dbg, flog, "send file '%s' %s to %s", name, mode, raddr); 209 name = sunkernel(name); 210 if(name == 0){ 211 nak(fd, 0, "not in our database"); 212 return; 213 } 214 215 notify(catcher); 216 217 file = open(name, OREAD); 218 if(file < 0) { 219 errstr(errbuf, sizeof errbuf); 220 nak(fd, 0, errbuf); 221 return; 222 } 223 block = 0; 224 rexmit = 0; 225 n = 0; 226 for(txtry = 0; txtry < 5;) { 227 if(rexmit == 0) { 228 block++; 229 buf[0] = 0; 230 buf[1] = Tftp_DATA; 231 buf[2] = block>>8; 232 buf[3] = block; 233 n = read(file, buf+4, Segsize); 234 if(n < 0) { 235 errstr(errbuf, sizeof errbuf); 236 nak(fd, 0, errbuf); 237 return; 238 } 239 txtry = 0; 240 } 241 else { 242 syslog(dbg, flog, "rexmit %d %s:%d to %s", 4+n, name, block, raddr); 243 txtry++; 244 } 245 246 ret = write(fd, buf, 4+n); 247 if(ret < 0) 248 sysfatal("tftpd: network write error: %r"); 249 250 for(rxl = 0; rxl < 10; rxl++) { 251 rexmit = 0; 252 alarm(500); 253 al = read(fd, ack, sizeof(ack)); 254 alarm(0); 255 if(al < 0) { 256 rexmit = 1; 257 break; 258 } 259 op = ack[0]<<8|ack[1]; 260 if(op == Tftp_ERROR) 261 goto error; 262 ackblock = ack[2]<<8|ack[3]; 263 if(ackblock == block) 264 break; 265 if(ackblock == 0xffff) { 266 rexmit = 1; 267 break; 268 } 269 } 270 if(ret != Segsize+4 && rexmit == 0) 271 break; 272 } 273 error: 274 close(fd); 275 close(file); 276 } 277 278 void 279 recvfile(int fd, char *name, char *mode) 280 { 281 ushort op, block, inblock; 282 uchar buf[Segsize+8]; 283 char errbuf[Maxerr]; 284 int n, ret, file; 285 286 syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr); 287 288 file = create(name, OWRITE, 0666); 289 if(file < 0) { 290 errstr(errbuf, sizeof errbuf); 291 nak(fd, 0, errbuf); 292 return; 293 } 294 295 block = 0; 296 ack(fd, block); 297 block++; 298 299 for(;;) { 300 alarm(15000); 301 n = read(fd, buf, sizeof(buf)); 302 alarm(0); 303 if(n < 0) 304 goto error; 305 op = buf[0]<<8|buf[1]; 306 if(op == Tftp_ERROR) 307 goto error; 308 309 n -= 4; 310 inblock = buf[2]<<8|buf[3]; 311 if(op == Tftp_DATA) { 312 if(inblock == block) { 313 ret = write(file, buf, n); 314 if(ret < 0) { 315 errstr(errbuf, sizeof errbuf); 316 nak(fd, 0, errbuf); 317 goto error; 318 } 319 ack(fd, block); 320 block++; 321 } 322 ack(fd, 0xffff); 323 } 324 } 325 error: 326 close(file); 327 } 328 329 void 330 ack(int fd, ushort block) 331 { 332 uchar ack[4]; 333 int n; 334 335 ack[0] = 0; 336 ack[1] = Tftp_ACK; 337 ack[2] = block>>8; 338 ack[3] = block; 339 340 n = write(fd, ack, 4); 341 if(n < 0) 342 sysfatal("network write: %r"); 343 } 344 345 void 346 nak(int fd, int code, char *msg) 347 { 348 char buf[128]; 349 int n; 350 351 buf[0] = 0; 352 buf[1] = Tftp_ERROR; 353 buf[2] = 0; 354 buf[3] = code; 355 strcpy(buf+4, msg); 356 n = strlen(msg) + 4 + 1; 357 n = write(fd, buf, n); 358 if(n < 0) 359 sysfatal("write nak: %r"); 360 } 361 362 void 363 setuser(void) 364 { 365 int fd; 366 367 fd = open("#c/user", OWRITE); 368 if(fd < 0 || write(fd, "none", strlen("none")) < 0) 369 sysfatal("can't become none: %r"); 370 close(fd); 371 if(newns("none", nil) < 0) 372 sysfatal("can't build namespace: %r"); 373 } 374 375 char* 376 lookup(char *sattr, char *sval, char *tattr, char *tval, int len) 377 { 378 static Ndb *db; 379 char *attrs[1]; 380 Ndbtuple *t; 381 382 if(db == nil) 383 db = ndbopen(0); 384 if(db == nil) 385 return nil; 386 387 if(sattr == nil) 388 sattr = ipattr(sval); 389 390 attrs[0] = tattr; 391 t = ndbipinfo(db, sattr, sval, attrs, 1); 392 if(t == nil) 393 return nil; 394 strncpy(tval, t->val, len); 395 tval[len-1] = 0; 396 ndbfree(t); 397 return tval; 398 } 399 400 /* 401 * for sun kernel boots, replace the requested file name with 402 * a one from our database. If the database doesn't specify a file, 403 * don't answer. 404 */ 405 char* 406 sunkernel(char *name) 407 { 408 ulong addr; 409 uchar v4[IPv4addrlen]; 410 uchar v6[IPaddrlen]; 411 char buf[256]; 412 char ipbuf[128]; 413 char *suffix; 414 415 addr = strtoul(name, &suffix, 16); 416 if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0)) 417 return name; 418 419 v4[0] = addr>>24; 420 v4[1] = addr>>16; 421 v4[2] = addr>>8; 422 v4[3] = addr; 423 v4tov6(v6, v4); 424 sprint(ipbuf, "%I", v6); 425 return lookup("ip", ipbuf, "bootf", buf, sizeof buf); 426 } 427 428 void 429 remoteaddr(char *dir, char *raddr, int len) 430 { 431 char buf[64]; 432 int fd, n; 433 434 snprint(buf, sizeof(buf), "%s/remote", dir); 435 fd = open(buf, OREAD); 436 if(fd < 0){ 437 snprint(raddr, sizeof(raddr), "unknown"); 438 return; 439 } 440 n = read(fd, raddr, len-1); 441 close(fd); 442 if(n <= 0){ 443 snprint(raddr, sizeof(raddr), "unknown"); 444 return; 445 } 446 if(n > 0) 447 n--; 448 raddr[n] = 0; 449 } 450