1162f803dSDavid du Colombier /* 23e5d0078SDavid du Colombier * tftpd - tftp service, see /lib/rfc/rfc783 (now rfc1350 + 234[789]) 3162f803dSDavid du Colombier */ 43e12c5d1SDavid du Colombier #include <u.h> 53e12c5d1SDavid du Colombier #include <libc.h> 6c0eadb1cSDavid du Colombier #include <auth.h> 73e12c5d1SDavid du Colombier #include <bio.h> 83e12c5d1SDavid du Colombier #include <ip.h> 93e12c5d1SDavid du Colombier #include <ndb.h> 103e12c5d1SDavid du Colombier 119a747e4fSDavid du Colombier enum 129a747e4fSDavid du Colombier { 139a747e4fSDavid du Colombier Maxpath= 128, 149a747e4fSDavid du Colombier Maxerr= 256, 153e5d0078SDavid du Colombier 163e5d0078SDavid du Colombier Debug= 0, 173e5d0078SDavid du Colombier 183e5d0078SDavid du Colombier Opsize= sizeof(short), 193e5d0078SDavid du Colombier Blksize= sizeof(short), 203e5d0078SDavid du Colombier Hdrsize= Opsize + Blksize, 213e5d0078SDavid du Colombier 223e5d0078SDavid du Colombier Ackerr= -1, 233e5d0078SDavid du Colombier Ackok= 0, 243e5d0078SDavid du Colombier Ackrexmit= 1, 253e5d0078SDavid du Colombier 263e5d0078SDavid du Colombier /* op codes */ 273e5d0078SDavid du Colombier Tftp_READ = 1, 283e5d0078SDavid du Colombier Tftp_WRITE = 2, 293e5d0078SDavid du Colombier Tftp_DATA = 3, 303e5d0078SDavid du Colombier Tftp_ACK = 4, 313e5d0078SDavid du Colombier Tftp_ERROR = 5, 323e5d0078SDavid du Colombier Tftp_OACK = 6, /* option acknowledge */ 333e5d0078SDavid du Colombier 343e5d0078SDavid du Colombier Errnotdef = 0, /* see textual error instead */ 353e5d0078SDavid du Colombier Errnotfound = 1, 363e5d0078SDavid du Colombier Errnoaccess = 2, 373e5d0078SDavid du Colombier Errdiskfull = 3, 383e5d0078SDavid du Colombier Errbadop = 4, 393e5d0078SDavid du Colombier Errbadtid = 5, 403e5d0078SDavid du Colombier Errexists = 6, 413e5d0078SDavid du Colombier Errnouser = 7, 423e5d0078SDavid du Colombier Errbadopt = 8, /* really bad option value */ 433e5d0078SDavid du Colombier 443e5d0078SDavid du Colombier Defsegsize = 512, 453e5d0078SDavid du Colombier Maxsegsize = 65464, /* from rfc2348 */ 46*a4796b5cSDavid du Colombier 47*a4796b5cSDavid du Colombier /* 48*a4796b5cSDavid du Colombier * bandt (viaduct) tunnels use smaller mtu than ether's 49*a4796b5cSDavid du Colombier * (1400 bytes for tcp mss of 1300 bytes). 50*a4796b5cSDavid du Colombier */ 51*a4796b5cSDavid du Colombier Bandtmtu = 1400, 52*a4796b5cSDavid du Colombier /* 53*a4796b5cSDavid du Colombier * maximum size of block's data content, excludes hdrs, 54*a4796b5cSDavid du Colombier * notably IP/UDP and TFTP, using worst-case (IPv6) sizes. 55*a4796b5cSDavid du Colombier */ 56*a4796b5cSDavid du Colombier Bandtblksz = Bandtmtu - 40 - 8, 573e5d0078SDavid du Colombier }; 583e5d0078SDavid du Colombier 593e5d0078SDavid du Colombier typedef struct Opt Opt; 603e5d0078SDavid du Colombier struct Opt { 613e5d0078SDavid du Colombier char *name; 623e5d0078SDavid du Colombier int *valp; /* set to client's value if within bounds */ 633e5d0078SDavid du Colombier int min; 643e5d0078SDavid du Colombier int max; 659a747e4fSDavid du Colombier }; 669a747e4fSDavid du Colombier 673e12c5d1SDavid du Colombier int dbg; 68219b2ee8SDavid du Colombier int restricted; 693e5d0078SDavid du Colombier int pid; 703e5d0078SDavid du Colombier 713e5d0078SDavid du Colombier /* options */ 72*a4796b5cSDavid du Colombier int blksize = Defsegsize; /* excluding 4-byte header */ 733e5d0078SDavid du Colombier int timeout = 5; /* seconds */ 743e5d0078SDavid du Colombier int tsize; 753e5d0078SDavid du Colombier static Opt option[] = { 763e5d0078SDavid du Colombier "timeout", &timeout, 1, 255, 77*a4796b5cSDavid du Colombier /* see "hack" below */ 783e5d0078SDavid du Colombier "blksize", &blksize, 8, Maxsegsize, 793e5d0078SDavid du Colombier "tsize", &tsize, 0, ~0UL >> 1, 803e5d0078SDavid du Colombier }; 813e5d0078SDavid du Colombier 823e5d0078SDavid du Colombier void sendfile(int, char*, char*, int); 833e12c5d1SDavid du Colombier void recvfile(int, char*, char*); 843e12c5d1SDavid du Colombier void nak(int, int, char*); 853e12c5d1SDavid du Colombier void ack(int, ushort); 863e12c5d1SDavid du Colombier void clrcon(void); 873e12c5d1SDavid du Colombier void setuser(void); 883e12c5d1SDavid du Colombier char* sunkernel(char*); 897dd7cddfSDavid du Colombier void remoteaddr(char*, char*, int); 907dd7cddfSDavid du Colombier void doserve(int); 913e12c5d1SDavid du Colombier 927dd7cddfSDavid du Colombier char bigbuf[32768]; 937dd7cddfSDavid du Colombier char raddr[64]; 943e12c5d1SDavid du Colombier 953e12c5d1SDavid du Colombier char *dir = "/lib/tftpd"; 96219b2ee8SDavid du Colombier char *dirsl; 97219b2ee8SDavid du Colombier int dirsllen; 983e12c5d1SDavid du Colombier char flog[] = "ipboot"; 999a747e4fSDavid du Colombier char net[Maxpath]; 1003e12c5d1SDavid du Colombier 1013e5d0078SDavid du Colombier static char *opnames[] = { 1023e5d0078SDavid du Colombier [Tftp_READ] "read", 1033e5d0078SDavid du Colombier [Tftp_WRITE] "write", 1043e5d0078SDavid du Colombier [Tftp_DATA] "data", 1053e5d0078SDavid du Colombier [Tftp_ACK] "ack", 1063e5d0078SDavid du Colombier [Tftp_ERROR] "error", 1073e5d0078SDavid du Colombier [Tftp_OACK] "oack", 1083e12c5d1SDavid du Colombier }; 1093e12c5d1SDavid du Colombier 1103e12c5d1SDavid du Colombier void 1117dd7cddfSDavid du Colombier usage(void) 1127dd7cddfSDavid du Colombier { 113c0eadb1cSDavid du Colombier fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n", 114c0eadb1cSDavid du Colombier argv0); 1157dd7cddfSDavid du Colombier exits("usage"); 1167dd7cddfSDavid du Colombier } 1177dd7cddfSDavid du Colombier 1187dd7cddfSDavid du Colombier void 1193e12c5d1SDavid du Colombier main(int argc, char **argv) 1203e12c5d1SDavid du Colombier { 1217dd7cddfSDavid du Colombier char buf[64]; 1227dd7cddfSDavid du Colombier char adir[64], ldir[64]; 1237dd7cddfSDavid du Colombier int cfd, lcfd, dfd; 124162f803dSDavid du Colombier char *svc = "69"; 1253e12c5d1SDavid du Colombier 126162f803dSDavid du Colombier setnetmtpt(net, sizeof net, nil); 1273e12c5d1SDavid du Colombier ARGBEGIN{ 1283e12c5d1SDavid du Colombier case 'd': 1293e12c5d1SDavid du Colombier dbg++; 1303e12c5d1SDavid du Colombier break; 1313e12c5d1SDavid du Colombier case 'h': 132162f803dSDavid du Colombier dir = EARGF(usage()); 1333e12c5d1SDavid du Colombier break; 134219b2ee8SDavid du Colombier case 'r': 135219b2ee8SDavid du Colombier restricted = 1; 136219b2ee8SDavid du Colombier break; 137c0eadb1cSDavid du Colombier case 's': 138c0eadb1cSDavid du Colombier svc = EARGF(usage()); 139c0eadb1cSDavid du Colombier break; 1407dd7cddfSDavid du Colombier case 'x': 141162f803dSDavid du Colombier setnetmtpt(net, sizeof net, EARGF(usage())); 1427dd7cddfSDavid du Colombier break; 1433e12c5d1SDavid du Colombier default: 1447dd7cddfSDavid du Colombier usage(); 1453e12c5d1SDavid du Colombier }ARGEND 1463e12c5d1SDavid du Colombier 147219b2ee8SDavid du Colombier snprint(buf, sizeof buf, "%s/", dir); 148219b2ee8SDavid du Colombier dirsl = strdup(buf); 149219b2ee8SDavid du Colombier dirsllen = strlen(dirsl); 150219b2ee8SDavid du Colombier 1519a747e4fSDavid du Colombier fmtinstall('E', eipfmt); 1529a747e4fSDavid du Colombier fmtinstall('I', eipfmt); 1533e12c5d1SDavid du Colombier 15464eb6bc1SDavid du Colombier /* 15564eb6bc1SDavid du Colombier * setuser calls newns, and typical /lib/namespace files contain 15664eb6bc1SDavid du Colombier * "cd /usr/$user", so call setuser before chdir. 15764eb6bc1SDavid du Colombier */ 15864eb6bc1SDavid du Colombier setuser(); 1593e12c5d1SDavid du Colombier if(chdir(dir) < 0) 1607dd7cddfSDavid du Colombier sysfatal("can't get to directory %s: %r", dir); 1613e12c5d1SDavid du Colombier 1627dd7cddfSDavid du Colombier if(!dbg) 1633e12c5d1SDavid du Colombier switch(rfork(RFNOTEG|RFPROC|RFFDG)) { 1643e12c5d1SDavid du Colombier case -1: 1657dd7cddfSDavid du Colombier sysfatal("fork: %r"); 1663e12c5d1SDavid du Colombier case 0: 1673e12c5d1SDavid du Colombier break; 1683e12c5d1SDavid du Colombier default: 1693e12c5d1SDavid du Colombier exits(0); 1703e12c5d1SDavid du Colombier } 1713e12c5d1SDavid du Colombier 172c0eadb1cSDavid du Colombier snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc); 1737dd7cddfSDavid du Colombier cfd = announce(buf, adir); 174c0eadb1cSDavid du Colombier if (cfd < 0) 175c0eadb1cSDavid du Colombier sysfatal("announcing on %s: %r", buf); 176c0eadb1cSDavid du Colombier syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir); 17764eb6bc1SDavid du Colombier // setuser(); 1783e12c5d1SDavid du Colombier for(;;) { 1797dd7cddfSDavid du Colombier lcfd = listen(adir, ldir); 1807dd7cddfSDavid du Colombier if(lcfd < 0) 181c0eadb1cSDavid du Colombier sysfatal("listening on %s: %r", adir); 1827dd7cddfSDavid du Colombier 1837dd7cddfSDavid du Colombier switch(fork()) { 1847dd7cddfSDavid du Colombier case -1: 1857dd7cddfSDavid du Colombier sysfatal("fork: %r"); 1867dd7cddfSDavid du Colombier case 0: 187c0eadb1cSDavid du Colombier dfd = accept(lcfd, ldir); 1887dd7cddfSDavid du Colombier if(dfd < 0) 1897dd7cddfSDavid du Colombier exits(0); 1907dd7cddfSDavid du Colombier remoteaddr(ldir, raddr, sizeof(raddr)); 1913e5d0078SDavid du Colombier pid = getpid(); 1923e5d0078SDavid du Colombier syslog(0, flog, "tftp %d connection from %s dir %s", 1933e5d0078SDavid du Colombier pid, raddr, ldir); 1947dd7cddfSDavid du Colombier doserve(dfd); 1957dd7cddfSDavid du Colombier exits("done"); 1967dd7cddfSDavid du Colombier break; 1977dd7cddfSDavid du Colombier default: 1987dd7cddfSDavid du Colombier close(lcfd); 1997dd7cddfSDavid du Colombier continue; 2007dd7cddfSDavid du Colombier } 2017dd7cddfSDavid du Colombier } 2027dd7cddfSDavid du Colombier } 2037dd7cddfSDavid du Colombier 2043e5d0078SDavid du Colombier static Opt * 2053e5d0078SDavid du Colombier handleopt(int fd, char *name, char *val) 2063e5d0078SDavid du Colombier { 2073e5d0078SDavid du Colombier int n; 2083e5d0078SDavid du Colombier Opt *op; 2093e5d0078SDavid du Colombier 2103e5d0078SDavid du Colombier for (op = option; op < option + nelem(option); op++) 2113e5d0078SDavid du Colombier if(cistrcmp(name, op->name) == 0) { 2123e5d0078SDavid du Colombier n = strtol(val, nil, 10); 2133e5d0078SDavid du Colombier if (n < op->min || n > op->max) { 2143e5d0078SDavid du Colombier nak(fd, Errbadopt, "option value out of range"); 2153e5d0078SDavid du Colombier syslog(dbg, flog, "tftp bad option value from " 2163e5d0078SDavid du Colombier "client: %s %s", name, val); 2173e5d0078SDavid du Colombier sysfatal("bad option value from client: %s %s", 2183e5d0078SDavid du Colombier name, val); 2193e5d0078SDavid du Colombier } 2203e5d0078SDavid du Colombier *op->valp = n; 2213e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d setting %s to %d", 2223e5d0078SDavid du Colombier pid, name, n); 2233e5d0078SDavid du Colombier return op; 2243e5d0078SDavid du Colombier } 2253e5d0078SDavid du Colombier return nil; 2263e5d0078SDavid du Colombier } 2273e5d0078SDavid du Colombier 2283e5d0078SDavid du Colombier static vlong 2293e5d0078SDavid du Colombier filesize(char *file) 2303e5d0078SDavid du Colombier { 2313e5d0078SDavid du Colombier vlong size; 2323e5d0078SDavid du Colombier Dir *dp; 2333e5d0078SDavid du Colombier 2343e5d0078SDavid du Colombier dp = dirstat(file); 2353e5d0078SDavid du Colombier if (dp == nil) 2363e5d0078SDavid du Colombier return 0; 2373e5d0078SDavid du Colombier size = dp->length; 2383e5d0078SDavid du Colombier free(dp); 2393e5d0078SDavid du Colombier return size; 2403e5d0078SDavid du Colombier } 2413e5d0078SDavid du Colombier 2423e5d0078SDavid du Colombier static int 2433e5d0078SDavid du Colombier options(int fd, char *buf, char *file, ushort oper, char *p, int dlen) 2443e5d0078SDavid du Colombier { 2453e5d0078SDavid du Colombier int nmlen, vallen, nopts; 2463e5d0078SDavid du Colombier vlong size; 2473e5d0078SDavid du Colombier char *val, *bp; 2483e5d0078SDavid du Colombier Opt *op; 2493e5d0078SDavid du Colombier 2503e5d0078SDavid du Colombier buf[0] = 0; 2513e5d0078SDavid du Colombier buf[1] = Tftp_OACK; 2523e5d0078SDavid du Colombier bp = buf + Opsize; 2533e5d0078SDavid du Colombier nopts = 0; 2543e5d0078SDavid du Colombier while (dlen > 0 && *p != '\0') { 2553e5d0078SDavid du Colombier nmlen = strlen(p) + 1; /* include NUL */ 2563e5d0078SDavid du Colombier if (nmlen > dlen) 2573e5d0078SDavid du Colombier break; 2583e5d0078SDavid du Colombier dlen -= nmlen; 2593e5d0078SDavid du Colombier val = p + nmlen; 2603e5d0078SDavid du Colombier if (dlen <= 0 || *val == '\0') 2613e5d0078SDavid du Colombier break; 2623e5d0078SDavid du Colombier 2633e5d0078SDavid du Colombier vallen = strlen(val) + 1; 2643e5d0078SDavid du Colombier if (vallen > dlen) 2653e5d0078SDavid du Colombier break; 2663e5d0078SDavid du Colombier dlen -= vallen; 2673e5d0078SDavid du Colombier 2683e5d0078SDavid du Colombier nopts++; 2693e5d0078SDavid du Colombier op = handleopt(fd, p, val); 2703e5d0078SDavid du Colombier if (op) { 2713e5d0078SDavid du Colombier /* append OACK response to buf */ 2723e5d0078SDavid du Colombier sprint(bp, "%s", p); 2733e5d0078SDavid du Colombier bp += nmlen; 2743e5d0078SDavid du Colombier if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) { 2753e5d0078SDavid du Colombier size = filesize(file); 2763e5d0078SDavid du Colombier sprint(bp, "%lld", size); 2773e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d %s tsize is %,lld", 2783e5d0078SDavid du Colombier pid, file, size); 279*a4796b5cSDavid du Colombier } 280*a4796b5cSDavid du Colombier /* 281*a4796b5cSDavid du Colombier * hack: bandt (viaducts) uses smaller mtu than ether's 282*a4796b5cSDavid du Colombier * (1400 bytes for tcp mss of 1300 bytes), 283*a4796b5cSDavid du Colombier * so offer at most bandt's mtu minus headers, 284*a4796b5cSDavid du Colombier * to avoid failure of pxe booting via viaduct. 285*a4796b5cSDavid du Colombier */ 286*a4796b5cSDavid du Colombier else if (oper == Tftp_READ && 287*a4796b5cSDavid du Colombier cistrcmp(p, "blksize") == 0 && 288*a4796b5cSDavid du Colombier blksize > Bandtblksz) { 289*a4796b5cSDavid du Colombier blksize = Bandtblksz; 290*a4796b5cSDavid du Colombier sprint(bp, "%d", blksize); 291*a4796b5cSDavid du Colombier syslog(dbg, flog, 292*a4796b5cSDavid du Colombier "tftpd %d overriding blksize to %d", 293*a4796b5cSDavid du Colombier pid, blksize); 2943e5d0078SDavid du Colombier } else 295*a4796b5cSDavid du Colombier strcpy(bp, val); /* use requested value */ 2963e5d0078SDavid du Colombier bp += strlen(bp) + 1; 2973e5d0078SDavid du Colombier } 2983e5d0078SDavid du Colombier p = val + vallen; 2993e5d0078SDavid du Colombier } 3003e5d0078SDavid du Colombier if (nopts == 0) 3013e5d0078SDavid du Colombier return 0; /* no options actually seen */ 3023e5d0078SDavid du Colombier *bp++ = '\0'; 3033e5d0078SDavid du Colombier *bp++ = '\0'; /* overkill */ 3043e5d0078SDavid du Colombier *bp++ = '\0'; 3053e5d0078SDavid du Colombier if (write(fd, buf, bp - buf) < bp - buf) { 3063e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd network write error on oack to %s: %r", 3073e5d0078SDavid du Colombier raddr); 3083e5d0078SDavid du Colombier sysfatal("tftpd: network write error: %r"); 3093e5d0078SDavid du Colombier } 3103e5d0078SDavid du Colombier if(Debug) 3113e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd oack: options to %s", raddr); 3123e5d0078SDavid du Colombier return nopts; 3133e5d0078SDavid du Colombier } 3143e5d0078SDavid du Colombier 3153e5d0078SDavid du Colombier /* this doesn't stop the cavium from barging ahead */ 3163e5d0078SDavid du Colombier //static void 3173e5d0078SDavid du Colombier //sendnoopts(int fd, char *name) 3183e5d0078SDavid du Colombier //{ 3193e5d0078SDavid du Colombier // char buf[64]; 3203e5d0078SDavid du Colombier // 3213e5d0078SDavid du Colombier // memset(buf, 0, sizeof buf); 3223e5d0078SDavid du Colombier // buf[0] = 0; 3233e5d0078SDavid du Colombier // buf[1] = Tftp_OACK; 3243e5d0078SDavid du Colombier // 3253e5d0078SDavid du Colombier // if(write(fd, buf, sizeof buf) < sizeof buf) { 3263e5d0078SDavid du Colombier // syslog(dbg, flog, "tftpd network write error on %s oack to %s: %r", 3273e5d0078SDavid du Colombier // name, raddr); 3283e5d0078SDavid du Colombier // sysfatal("tftpd: network write error: %r"); 3293e5d0078SDavid du Colombier // } 3303e5d0078SDavid du Colombier // if(Debug) 3313e5d0078SDavid du Colombier // syslog(dbg, flog, "tftpd oack: no options to %s", raddr); 3323e5d0078SDavid du Colombier //} 3333e5d0078SDavid du Colombier 3343e5d0078SDavid du Colombier static void 3353e5d0078SDavid du Colombier optlog(char *bytes, char *p, int dlen) 3363e5d0078SDavid du Colombier { 3373e5d0078SDavid du Colombier char *bp; 3383e5d0078SDavid du Colombier 3393e5d0078SDavid du Colombier bp = bytes; 3403e5d0078SDavid du Colombier sprint(bp, "tftpd %d option bytes: ", dlen); 3413e5d0078SDavid du Colombier bp += strlen(bp); 3423e5d0078SDavid du Colombier for (; dlen > 0; dlen--, p++) 3433e5d0078SDavid du Colombier *bp++ = *p? *p: ' '; 3443e5d0078SDavid du Colombier *bp = '\0'; 3453e5d0078SDavid du Colombier syslog(dbg, flog, "%s", bytes); 3463e5d0078SDavid du Colombier } 3473e5d0078SDavid du Colombier 3487dd7cddfSDavid du Colombier void 3497dd7cddfSDavid du Colombier doserve(int fd) 3507dd7cddfSDavid du Colombier { 3513e5d0078SDavid du Colombier int dlen, opts; 3523e5d0078SDavid du Colombier char *mode, *p, *file; 3537dd7cddfSDavid du Colombier short op; 3547dd7cddfSDavid du Colombier 3553e5d0078SDavid du Colombier dlen = read(fd, bigbuf, sizeof(bigbuf)-1); 3563e12c5d1SDavid du Colombier if(dlen < 0) 3577dd7cddfSDavid du Colombier sysfatal("listen read: %r"); 3583e12c5d1SDavid du Colombier 3593e5d0078SDavid du Colombier bigbuf[dlen] = '\0'; 3607dd7cddfSDavid du Colombier op = (bigbuf[0]<<8) | bigbuf[1]; 3613e5d0078SDavid du Colombier dlen -= Opsize; 3623e5d0078SDavid du Colombier mode = file = bigbuf + Opsize; 3633e12c5d1SDavid du Colombier while(*mode != '\0' && dlen--) 3643e12c5d1SDavid du Colombier mode++; 3653e12c5d1SDavid du Colombier mode++; 3663e12c5d1SDavid du Colombier p = mode; 3673e12c5d1SDavid du Colombier while(*p && dlen--) 3683e12c5d1SDavid du Colombier p++; 3693e12c5d1SDavid du Colombier if(dlen == 0) { 3707dd7cddfSDavid du Colombier nak(fd, 0, "bad tftpmode"); 3717dd7cddfSDavid du Colombier close(fd); 3723e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s", 3733e5d0078SDavid du Colombier pid, mode, file, raddr); 3747dd7cddfSDavid du Colombier return; 3753e12c5d1SDavid du Colombier } 3763e12c5d1SDavid du Colombier 3773e12c5d1SDavid du Colombier if(op != Tftp_READ && op != Tftp_WRITE) { 3783e5d0078SDavid du Colombier nak(fd, Errbadop, "Illegal TFTP operation"); 3797dd7cddfSDavid du Colombier close(fd); 3803e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad request %d %s", pid, op, raddr); 3817dd7cddfSDavid du Colombier return; 3823e12c5d1SDavid du Colombier } 3837dd7cddfSDavid du Colombier 384219b2ee8SDavid du Colombier if(restricted){ 3853e5d0078SDavid du Colombier if(file[0] == '#' || strncmp(file, "../", 3) == 0 || 3863e5d0078SDavid du Colombier strstr(file, "/../") != nil || 3873e5d0078SDavid du Colombier (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){ 3883e5d0078SDavid du Colombier nak(fd, Errnoaccess, "Permission denied"); 3897dd7cddfSDavid du Colombier close(fd); 3903e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad request %d from %s file %s", 3913e5d0078SDavid du Colombier pid, op, raddr, file); 3927dd7cddfSDavid du Colombier return; 393219b2ee8SDavid du Colombier } 394219b2ee8SDavid du Colombier } 3957dd7cddfSDavid du Colombier 3963e5d0078SDavid du Colombier /* 3973e5d0078SDavid du Colombier * options are supposed to be negotiated, but the cavium board's 3983e5d0078SDavid du Colombier * u-boot really wants us to use a block size of 1432 bytes and won't 3993e5d0078SDavid du Colombier * take `no' for an answer. 4003e5d0078SDavid du Colombier */ 4013e5d0078SDavid du Colombier p++; /* skip NUL after mode */ 4023e5d0078SDavid du Colombier dlen--; 4033e5d0078SDavid du Colombier opts = 0; 4043e5d0078SDavid du Colombier if(dlen > 0) { /* might have options */ 4053e5d0078SDavid du Colombier char bytes[32*1024]; 4063e5d0078SDavid du Colombier 4073e5d0078SDavid du Colombier if(Debug) 4083e5d0078SDavid du Colombier optlog(bytes, p, dlen); 4093e5d0078SDavid du Colombier opts = options(fd, bytes, file, op, p, dlen); 4103e5d0078SDavid du Colombier } 4113e12c5d1SDavid du Colombier if(op == Tftp_READ) 4123e5d0078SDavid du Colombier sendfile(fd, file, mode, opts); 4133e12c5d1SDavid du Colombier else 4143e5d0078SDavid du Colombier recvfile(fd, file, mode); 4153e12c5d1SDavid du Colombier } 4163e12c5d1SDavid du Colombier 4173e12c5d1SDavid du Colombier void 4183e12c5d1SDavid du Colombier catcher(void *junk, char *msg) 4193e12c5d1SDavid du Colombier { 4203e12c5d1SDavid du Colombier USED(junk); 4213e12c5d1SDavid du Colombier 4223e12c5d1SDavid du Colombier if(strncmp(msg, "exit", 4) == 0) 4233e12c5d1SDavid du Colombier noted(NDFLT); 4243e12c5d1SDavid du Colombier noted(NCONT); 4253e12c5d1SDavid du Colombier } 4263e12c5d1SDavid du Colombier 4273e5d0078SDavid du Colombier static int 4283e5d0078SDavid du Colombier awaitack(int fd, int block) 4293e12c5d1SDavid du Colombier { 4303e5d0078SDavid du Colombier int ackblock, al, rxl; 4313e5d0078SDavid du Colombier ushort op; 4323e12c5d1SDavid du Colombier uchar ack[1024]; 4333e12c5d1SDavid du Colombier 4343e5d0078SDavid du Colombier for(rxl = 0; rxl < 10; rxl++) { 4353e5d0078SDavid du Colombier memset(ack, 0, Hdrsize); 4363e5d0078SDavid du Colombier alarm(1000); 4373e5d0078SDavid du Colombier al = read(fd, ack, sizeof(ack)); 4383e5d0078SDavid du Colombier alarm(0); 4393e5d0078SDavid du Colombier if(al < 0) { 4403e5d0078SDavid du Colombier if (Debug) 4413e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d timed out " 4423e5d0078SDavid du Colombier "waiting for ack from %s", pid, raddr); 4433e5d0078SDavid du Colombier return Ackrexmit; 4443e5d0078SDavid du Colombier } 4453e5d0078SDavid du Colombier op = ack[0]<<8|ack[1]; 4463e5d0078SDavid du Colombier if(op == Tftp_ERROR) { 4473e5d0078SDavid du Colombier if (Debug) 4483e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d got error " 4493e5d0078SDavid du Colombier "waiting for ack from %s", pid, raddr); 4503e5d0078SDavid du Colombier return Ackerr; 4513e5d0078SDavid du Colombier } else if(op != Tftp_ACK) { 4523e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid, 4533e5d0078SDavid du Colombier (op < nelem(opnames)? opnames[op]: "gok"), 4543e5d0078SDavid du Colombier raddr); 4553e5d0078SDavid du Colombier return Ackerr; 4563e5d0078SDavid du Colombier } 4573e5d0078SDavid du Colombier ackblock = ack[2]<<8|ack[3]; 4583e5d0078SDavid du Colombier if (Debug) 4593e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d read ack of %d bytes " 4603e5d0078SDavid du Colombier "for block %d", pid, al, ackblock); 4613e5d0078SDavid du Colombier if(ackblock == block) 4623e5d0078SDavid du Colombier return Ackok; /* for block just sent */ 4633e5d0078SDavid du Colombier else if(ackblock == block + 1) /* intel pxe eof bug */ 4643e5d0078SDavid du Colombier return Ackok; 4653e5d0078SDavid du Colombier else if(ackblock == 0xffff) 4663e5d0078SDavid du Colombier return Ackrexmit; 4673e5d0078SDavid du Colombier else 4683e5d0078SDavid du Colombier /* ack is for some other block; ignore it, try again */ 4693e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d expected ack for block %d, " 4703e5d0078SDavid du Colombier "got %d", pid, block, ackblock); 4713e5d0078SDavid du Colombier } 4723e5d0078SDavid du Colombier return Ackrexmit; 4733e5d0078SDavid du Colombier } 4743e5d0078SDavid du Colombier 4753e5d0078SDavid du Colombier void 4763e5d0078SDavid du Colombier sendfile(int fd, char *name, char *mode, int opts) 4773e5d0078SDavid du Colombier { 4783e5d0078SDavid du Colombier int file, block, ret, rexmit, n, txtry; 4793e5d0078SDavid du Colombier uchar buf[Maxsegsize+Hdrsize]; 4803e5d0078SDavid du Colombier char errbuf[Maxerr]; 4813e5d0078SDavid du Colombier 4823e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d send file '%s' %s to %s", 4833e5d0078SDavid du Colombier pid, name, mode, raddr); 4843e12c5d1SDavid du Colombier name = sunkernel(name); 4853e12c5d1SDavid du Colombier if(name == 0){ 4863e12c5d1SDavid du Colombier nak(fd, 0, "not in our database"); 4873e12c5d1SDavid du Colombier return; 4883e12c5d1SDavid du Colombier } 4893e12c5d1SDavid du Colombier 4903e12c5d1SDavid du Colombier notify(catcher); 4913e12c5d1SDavid du Colombier 4923e12c5d1SDavid du Colombier file = open(name, OREAD); 4933e12c5d1SDavid du Colombier if(file < 0) { 4949a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 4953e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 4963e12c5d1SDavid du Colombier return; 4973e12c5d1SDavid du Colombier } 4983e12c5d1SDavid du Colombier block = 0; 4993e5d0078SDavid du Colombier rexmit = Ackok; 5003e12c5d1SDavid du Colombier n = 0; 5013e5d0078SDavid du Colombier /* 5023e5d0078SDavid du Colombier * if we sent an oack previously, wait for the client's ack or error. 5033e5d0078SDavid du Colombier * if we get no ack for our oack, it could be that we returned 5043e5d0078SDavid du Colombier * a tsize that the client can't handle, or it could be intel 5053e5d0078SDavid du Colombier * pxe just read-with-tsize to get size, couldn't be bothered to 5063e5d0078SDavid du Colombier * ack our oack and has just gone ahead and issued another read. 5073e5d0078SDavid du Colombier */ 5083e5d0078SDavid du Colombier if(opts && awaitack(fd, 0) != Ackok) 5093e5d0078SDavid du Colombier goto error; 5103e5d0078SDavid du Colombier 5113e5d0078SDavid du Colombier for(txtry = 0; txtry < timeout;) { 5123e5d0078SDavid du Colombier if(rexmit == Ackok) { 5133e12c5d1SDavid du Colombier block++; 5143e12c5d1SDavid du Colombier buf[0] = 0; 5153e12c5d1SDavid du Colombier buf[1] = Tftp_DATA; 5163e12c5d1SDavid du Colombier buf[2] = block>>8; 5173e12c5d1SDavid du Colombier buf[3] = block; 5183e5d0078SDavid du Colombier n = read(file, buf+Hdrsize, blksize); 5193e12c5d1SDavid du Colombier if(n < 0) { 5209a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 5213e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 5223e12c5d1SDavid du Colombier return; 5233e12c5d1SDavid du Colombier } 524219b2ee8SDavid du Colombier txtry = 0; 5253e12c5d1SDavid du Colombier } 5267dd7cddfSDavid du Colombier else { 5273e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s", 5283e5d0078SDavid du Colombier pid, Hdrsize+n, name, block, raddr); 5293e12c5d1SDavid du Colombier txtry++; 5307dd7cddfSDavid du Colombier } 5317dd7cddfSDavid du Colombier 5323e5d0078SDavid du Colombier ret = write(fd, buf, Hdrsize+n); 5333e5d0078SDavid du Colombier if(ret < Hdrsize+n) { 5343e5d0078SDavid du Colombier syslog(dbg, flog, 5353e5d0078SDavid du Colombier "tftpd network write error on %s to %s: %r", 5363e5d0078SDavid du Colombier name, raddr); 5377dd7cddfSDavid du Colombier sysfatal("tftpd: network write error: %r"); 5383e5d0078SDavid du Colombier } 5393e5d0078SDavid du Colombier if (Debug) 5403e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d sent block %d", pid, block); 5413e12c5d1SDavid du Colombier 5423e5d0078SDavid du Colombier rexmit = awaitack(fd, block); 5433e5d0078SDavid du Colombier if (rexmit == Ackerr) 5443e12c5d1SDavid du Colombier break; 5453e5d0078SDavid du Colombier if(ret != blksize+Hdrsize && rexmit == Ackok) 5463e12c5d1SDavid du Colombier break; 5473e12c5d1SDavid du Colombier } 5483e12c5d1SDavid du Colombier error: 5493e12c5d1SDavid du Colombier close(fd); 5503e12c5d1SDavid du Colombier close(file); 5513e12c5d1SDavid du Colombier } 5523e12c5d1SDavid du Colombier 5533e12c5d1SDavid du Colombier void 5543e12c5d1SDavid du Colombier recvfile(int fd, char *name, char *mode) 5553e12c5d1SDavid du Colombier { 5563e12c5d1SDavid du Colombier ushort op, block, inblock; 5573e5d0078SDavid du Colombier uchar buf[Maxsegsize+8]; 5589a747e4fSDavid du Colombier char errbuf[Maxerr]; 5593e12c5d1SDavid du Colombier int n, ret, file; 5603e12c5d1SDavid du Colombier 5613e12c5d1SDavid du Colombier syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr); 5623e12c5d1SDavid du Colombier 5633e12c5d1SDavid du Colombier file = create(name, OWRITE, 0666); 5643e12c5d1SDavid du Colombier if(file < 0) { 5659a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 5663e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 567162f803dSDavid du Colombier syslog(dbg, flog, "can't create %s: %r", name); 5683e12c5d1SDavid du Colombier return; 5693e12c5d1SDavid du Colombier } 5703e12c5d1SDavid du Colombier 5713e12c5d1SDavid du Colombier block = 0; 5723e12c5d1SDavid du Colombier ack(fd, block); 5733e12c5d1SDavid du Colombier block++; 5743e12c5d1SDavid du Colombier 5753e12c5d1SDavid du Colombier for (;;) { 5763e12c5d1SDavid du Colombier alarm(15000); 5773e5d0078SDavid du Colombier n = read(fd, buf, blksize+8); 5783e12c5d1SDavid du Colombier alarm(0); 579162f803dSDavid du Colombier if(n < 0) { 580162f803dSDavid du Colombier syslog(dbg, flog, "tftpd: network error reading %s: %r", 581162f803dSDavid du Colombier name); 5823e12c5d1SDavid du Colombier goto error; 583162f803dSDavid du Colombier } 584162f803dSDavid du Colombier if(n <= Hdrsize) { 585162f803dSDavid du Colombier syslog(dbg, flog, 586162f803dSDavid du Colombier "tftpd: short read from network, reading %s", 587162f803dSDavid du Colombier name); 588162f803dSDavid du Colombier goto error; 589162f803dSDavid du Colombier } 5903e12c5d1SDavid du Colombier op = buf[0]<<8|buf[1]; 591162f803dSDavid du Colombier if(op == Tftp_ERROR) { 592162f803dSDavid du Colombier syslog(dbg, flog, "tftpd: tftp error reading %s", name); 5933e12c5d1SDavid du Colombier goto error; 594162f803dSDavid du Colombier } 5953e12c5d1SDavid du Colombier 596162f803dSDavid du Colombier n -= Hdrsize; 5973e12c5d1SDavid du Colombier inblock = buf[2]<<8|buf[3]; 5983e12c5d1SDavid du Colombier if(op == Tftp_DATA) { 5993e12c5d1SDavid du Colombier if(inblock == block) { 600162f803dSDavid du Colombier ret = write(file, buf+Hdrsize, n); 601162f803dSDavid du Colombier if(ret != n) { 6029a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 6033e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 604162f803dSDavid du Colombier syslog(dbg, flog, 605162f803dSDavid du Colombier "tftpd: error writing %s: %s", 606162f803dSDavid du Colombier name, errbuf); 6073e12c5d1SDavid du Colombier goto error; 6083e12c5d1SDavid du Colombier } 6093e12c5d1SDavid du Colombier ack(fd, block); 6103e12c5d1SDavid du Colombier block++; 61178307a5fSDavid du Colombier } else 61278307a5fSDavid du Colombier ack(fd, 0xffff); /* tell him to resend */ 6133e12c5d1SDavid du Colombier } 6143e12c5d1SDavid du Colombier } 6153e12c5d1SDavid du Colombier error: 6163e12c5d1SDavid du Colombier close(file); 6173e12c5d1SDavid du Colombier } 6183e12c5d1SDavid du Colombier 6193e12c5d1SDavid du Colombier void 6203e12c5d1SDavid du Colombier ack(int fd, ushort block) 6213e12c5d1SDavid du Colombier { 6223e12c5d1SDavid du Colombier uchar ack[4]; 6233e12c5d1SDavid du Colombier int n; 6243e12c5d1SDavid du Colombier 6253e12c5d1SDavid du Colombier ack[0] = 0; 6263e12c5d1SDavid du Colombier ack[1] = Tftp_ACK; 6273e12c5d1SDavid du Colombier ack[2] = block>>8; 6283e12c5d1SDavid du Colombier ack[3] = block; 6293e12c5d1SDavid du Colombier 6303e12c5d1SDavid du Colombier n = write(fd, ack, 4); 631162f803dSDavid du Colombier if(n < 4) 6327dd7cddfSDavid du Colombier sysfatal("network write: %r"); 6333e12c5d1SDavid du Colombier } 6343e12c5d1SDavid du Colombier 6353e12c5d1SDavid du Colombier void 6363e12c5d1SDavid du Colombier nak(int fd, int code, char *msg) 6373e12c5d1SDavid du Colombier { 6383e12c5d1SDavid du Colombier char buf[128]; 6393e12c5d1SDavid du Colombier int n; 6403e12c5d1SDavid du Colombier 6413e12c5d1SDavid du Colombier buf[0] = 0; 6423e12c5d1SDavid du Colombier buf[1] = Tftp_ERROR; 6433e12c5d1SDavid du Colombier buf[2] = 0; 6443e12c5d1SDavid du Colombier buf[3] = code; 6453e12c5d1SDavid du Colombier strcpy(buf+4, msg); 6463e12c5d1SDavid du Colombier n = strlen(msg) + 4 + 1; 6473e5d0078SDavid du Colombier if(write(fd, buf, n) < n) 6487dd7cddfSDavid du Colombier sysfatal("write nak: %r"); 6493e12c5d1SDavid du Colombier } 6503e12c5d1SDavid du Colombier 6513e12c5d1SDavid du Colombier void 6523e12c5d1SDavid du Colombier setuser(void) 6533e12c5d1SDavid du Colombier { 654c0eadb1cSDavid du Colombier int fd; 6553e12c5d1SDavid du Colombier 656c0eadb1cSDavid du Colombier fd = open("#c/user", OWRITE); 657c0eadb1cSDavid du Colombier if(fd < 0 || write(fd, "none", strlen("none")) < 0) 658c0eadb1cSDavid du Colombier sysfatal("can't become none: %r"); 659c0eadb1cSDavid du Colombier close(fd); 660c0eadb1cSDavid du Colombier if(newns("none", nil) < 0) 661c0eadb1cSDavid du Colombier sysfatal("can't build namespace: %r"); 6623e12c5d1SDavid du Colombier } 6633e12c5d1SDavid du Colombier 6647dd7cddfSDavid du Colombier char* 665da51d93aSDavid du Colombier lookup(char *sattr, char *sval, char *tattr, char *tval, int len) 6667dd7cddfSDavid du Colombier { 6677dd7cddfSDavid du Colombier static Ndb *db; 6687dd7cddfSDavid du Colombier char *attrs[1]; 6697dd7cddfSDavid du Colombier Ndbtuple *t; 6707dd7cddfSDavid du Colombier 6717dd7cddfSDavid du Colombier if(db == nil) 6727dd7cddfSDavid du Colombier db = ndbopen(0); 6737dd7cddfSDavid du Colombier if(db == nil) 6747dd7cddfSDavid du Colombier return nil; 6757dd7cddfSDavid du Colombier 6767dd7cddfSDavid du Colombier if(sattr == nil) 6777dd7cddfSDavid du Colombier sattr = ipattr(sval); 6787dd7cddfSDavid du Colombier 6797dd7cddfSDavid du Colombier attrs[0] = tattr; 6807dd7cddfSDavid du Colombier t = ndbipinfo(db, sattr, sval, attrs, 1); 6817dd7cddfSDavid du Colombier if(t == nil) 6827dd7cddfSDavid du Colombier return nil; 683da51d93aSDavid du Colombier strncpy(tval, t->val, len); 684da51d93aSDavid du Colombier tval[len-1] = 0; 6857dd7cddfSDavid du Colombier ndbfree(t); 6867dd7cddfSDavid du Colombier return tval; 6877dd7cddfSDavid du Colombier } 6887dd7cddfSDavid du Colombier 6893e12c5d1SDavid du Colombier /* 6903e12c5d1SDavid du Colombier * for sun kernel boots, replace the requested file name with 6913e12c5d1SDavid du Colombier * a one from our database. If the database doesn't specify a file, 6923e12c5d1SDavid du Colombier * don't answer. 6933e12c5d1SDavid du Colombier */ 6943e12c5d1SDavid du Colombier char* 6953e12c5d1SDavid du Colombier sunkernel(char *name) 6963e12c5d1SDavid du Colombier { 6973e12c5d1SDavid du Colombier ulong addr; 6987dd7cddfSDavid du Colombier uchar v4[IPv4addrlen]; 6997dd7cddfSDavid du Colombier uchar v6[IPaddrlen]; 700da51d93aSDavid du Colombier char buf[256]; 701da51d93aSDavid du Colombier char ipbuf[128]; 70227e10919SDavid du Colombier char *suffix; 7033e12c5d1SDavid du Colombier 70427e10919SDavid du Colombier addr = strtoul(name, &suffix, 16); 70527e10919SDavid du Colombier if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0)) 7063e12c5d1SDavid du Colombier return name; 7073e12c5d1SDavid du Colombier 7087dd7cddfSDavid du Colombier v4[0] = addr>>24; 7097dd7cddfSDavid du Colombier v4[1] = addr>>16; 7107dd7cddfSDavid du Colombier v4[2] = addr>>8; 7117dd7cddfSDavid du Colombier v4[3] = addr; 7127dd7cddfSDavid du Colombier v4tov6(v6, v4); 7137dd7cddfSDavid du Colombier sprint(ipbuf, "%I", v6); 714da51d93aSDavid du Colombier return lookup("ip", ipbuf, "bootf", buf, sizeof buf); 7157dd7cddfSDavid du Colombier } 7167dd7cddfSDavid du Colombier 7177dd7cddfSDavid du Colombier void 7187dd7cddfSDavid du Colombier remoteaddr(char *dir, char *raddr, int len) 7197dd7cddfSDavid du Colombier { 7207dd7cddfSDavid du Colombier char buf[64]; 7217dd7cddfSDavid du Colombier int fd, n; 7227dd7cddfSDavid du Colombier 7237dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "%s/remote", dir); 7247dd7cddfSDavid du Colombier fd = open(buf, OREAD); 7257dd7cddfSDavid du Colombier if(fd < 0){ 7267dd7cddfSDavid du Colombier snprint(raddr, sizeof(raddr), "unknown"); 7277dd7cddfSDavid du Colombier return; 7287dd7cddfSDavid du Colombier } 7297dd7cddfSDavid du Colombier n = read(fd, raddr, len-1); 7307dd7cddfSDavid du Colombier close(fd); 7317dd7cddfSDavid du Colombier if(n <= 0){ 7327dd7cddfSDavid du Colombier snprint(raddr, sizeof(raddr), "unknown"); 7337dd7cddfSDavid du Colombier return; 7347dd7cddfSDavid du Colombier } 7357dd7cddfSDavid du Colombier if(n > 0) 7367dd7cddfSDavid du Colombier n--; 7377dd7cddfSDavid du Colombier raddr[n] = 0; 7383e12c5d1SDavid du Colombier } 739