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