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