1162f803dSDavid du Colombier /* 2*3e5d0078SDavid 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, 15*3e5d0078SDavid du Colombier 16*3e5d0078SDavid du Colombier Debug= 0, 17*3e5d0078SDavid du Colombier 18*3e5d0078SDavid du Colombier Opsize= sizeof(short), 19*3e5d0078SDavid du Colombier Blksize= sizeof(short), 20*3e5d0078SDavid du Colombier Hdrsize= Opsize + Blksize, 21*3e5d0078SDavid du Colombier 22*3e5d0078SDavid du Colombier Ackerr= -1, 23*3e5d0078SDavid du Colombier Ackok= 0, 24*3e5d0078SDavid du Colombier Ackrexmit= 1, 25*3e5d0078SDavid du Colombier 26*3e5d0078SDavid du Colombier /* op codes */ 27*3e5d0078SDavid du Colombier Tftp_READ = 1, 28*3e5d0078SDavid du Colombier Tftp_WRITE = 2, 29*3e5d0078SDavid du Colombier Tftp_DATA = 3, 30*3e5d0078SDavid du Colombier Tftp_ACK = 4, 31*3e5d0078SDavid du Colombier Tftp_ERROR = 5, 32*3e5d0078SDavid du Colombier Tftp_OACK = 6, /* option acknowledge */ 33*3e5d0078SDavid du Colombier 34*3e5d0078SDavid du Colombier Errnotdef = 0, /* see textual error instead */ 35*3e5d0078SDavid du Colombier Errnotfound = 1, 36*3e5d0078SDavid du Colombier Errnoaccess = 2, 37*3e5d0078SDavid du Colombier Errdiskfull = 3, 38*3e5d0078SDavid du Colombier Errbadop = 4, 39*3e5d0078SDavid du Colombier Errbadtid = 5, 40*3e5d0078SDavid du Colombier Errexists = 6, 41*3e5d0078SDavid du Colombier Errnouser = 7, 42*3e5d0078SDavid du Colombier Errbadopt = 8, /* really bad option value */ 43*3e5d0078SDavid du Colombier 44*3e5d0078SDavid du Colombier Defsegsize = 512, 45*3e5d0078SDavid du Colombier Maxsegsize = 65464, /* from rfc2348 */ 46*3e5d0078SDavid du Colombier }; 47*3e5d0078SDavid du Colombier 48*3e5d0078SDavid du Colombier typedef struct Opt Opt; 49*3e5d0078SDavid du Colombier struct Opt { 50*3e5d0078SDavid du Colombier char *name; 51*3e5d0078SDavid du Colombier int *valp; /* set to client's value if within bounds */ 52*3e5d0078SDavid du Colombier int min; 53*3e5d0078SDavid du Colombier int max; 549a747e4fSDavid du Colombier }; 559a747e4fSDavid du Colombier 563e12c5d1SDavid du Colombier int dbg; 57219b2ee8SDavid du Colombier int restricted; 58*3e5d0078SDavid du Colombier int pid; 59*3e5d0078SDavid du Colombier 60*3e5d0078SDavid du Colombier /* options */ 61*3e5d0078SDavid du Colombier int blksize = Defsegsize; 62*3e5d0078SDavid du Colombier int timeout = 5; /* seconds */ 63*3e5d0078SDavid du Colombier int tsize; 64*3e5d0078SDavid du Colombier static Opt option[] = { 65*3e5d0078SDavid du Colombier "timeout", &timeout, 1, 255, 66*3e5d0078SDavid du Colombier "blksize", &blksize, 8, Maxsegsize, 67*3e5d0078SDavid du Colombier "tsize", &tsize, 0, ~0UL >> 1, 68*3e5d0078SDavid du Colombier }; 69*3e5d0078SDavid du Colombier 70*3e5d0078SDavid du Colombier void sendfile(int, char*, char*, int); 713e12c5d1SDavid du Colombier void recvfile(int, char*, char*); 723e12c5d1SDavid du Colombier void nak(int, int, char*); 733e12c5d1SDavid du Colombier void ack(int, ushort); 743e12c5d1SDavid du Colombier void clrcon(void); 753e12c5d1SDavid du Colombier void setuser(void); 763e12c5d1SDavid du Colombier char* sunkernel(char*); 777dd7cddfSDavid du Colombier void remoteaddr(char*, char*, int); 787dd7cddfSDavid du Colombier void doserve(int); 793e12c5d1SDavid du Colombier 807dd7cddfSDavid du Colombier char bigbuf[32768]; 817dd7cddfSDavid du Colombier char raddr[64]; 823e12c5d1SDavid du Colombier 833e12c5d1SDavid du Colombier char *dir = "/lib/tftpd"; 84219b2ee8SDavid du Colombier char *dirsl; 85219b2ee8SDavid du Colombier int dirsllen; 863e12c5d1SDavid du Colombier char flog[] = "ipboot"; 879a747e4fSDavid du Colombier char net[Maxpath]; 883e12c5d1SDavid du Colombier 89*3e5d0078SDavid du Colombier static char *opnames[] = { 90*3e5d0078SDavid du Colombier [Tftp_READ] "read", 91*3e5d0078SDavid du Colombier [Tftp_WRITE] "write", 92*3e5d0078SDavid du Colombier [Tftp_DATA] "data", 93*3e5d0078SDavid du Colombier [Tftp_ACK] "ack", 94*3e5d0078SDavid du Colombier [Tftp_ERROR] "error", 95*3e5d0078SDavid du Colombier [Tftp_OACK] "oack", 963e12c5d1SDavid du Colombier }; 973e12c5d1SDavid du Colombier 983e12c5d1SDavid du Colombier void 997dd7cddfSDavid du Colombier usage(void) 1007dd7cddfSDavid du Colombier { 101c0eadb1cSDavid du Colombier fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n", 102c0eadb1cSDavid du Colombier argv0); 1037dd7cddfSDavid du Colombier exits("usage"); 1047dd7cddfSDavid du Colombier } 1057dd7cddfSDavid du Colombier 1067dd7cddfSDavid du Colombier void 1073e12c5d1SDavid du Colombier main(int argc, char **argv) 1083e12c5d1SDavid du Colombier { 1097dd7cddfSDavid du Colombier char buf[64]; 1107dd7cddfSDavid du Colombier char adir[64], ldir[64]; 1117dd7cddfSDavid du Colombier int cfd, lcfd, dfd; 112162f803dSDavid du Colombier char *svc = "69"; 1133e12c5d1SDavid du Colombier 114162f803dSDavid du Colombier setnetmtpt(net, sizeof net, nil); 1153e12c5d1SDavid du Colombier ARGBEGIN{ 1163e12c5d1SDavid du Colombier case 'd': 1173e12c5d1SDavid du Colombier dbg++; 1183e12c5d1SDavid du Colombier break; 1193e12c5d1SDavid du Colombier case 'h': 120162f803dSDavid du Colombier dir = EARGF(usage()); 1213e12c5d1SDavid du Colombier break; 122219b2ee8SDavid du Colombier case 'r': 123219b2ee8SDavid du Colombier restricted = 1; 124219b2ee8SDavid du Colombier break; 125c0eadb1cSDavid du Colombier case 's': 126c0eadb1cSDavid du Colombier svc = EARGF(usage()); 127c0eadb1cSDavid du Colombier break; 1287dd7cddfSDavid du Colombier case 'x': 129162f803dSDavid du Colombier setnetmtpt(net, sizeof net, EARGF(usage())); 1307dd7cddfSDavid du Colombier break; 1313e12c5d1SDavid du Colombier default: 1327dd7cddfSDavid du Colombier usage(); 1333e12c5d1SDavid du Colombier }ARGEND 1343e12c5d1SDavid du Colombier 135219b2ee8SDavid du Colombier snprint(buf, sizeof buf, "%s/", dir); 136219b2ee8SDavid du Colombier dirsl = strdup(buf); 137219b2ee8SDavid du Colombier dirsllen = strlen(dirsl); 138219b2ee8SDavid du Colombier 1399a747e4fSDavid du Colombier fmtinstall('E', eipfmt); 1409a747e4fSDavid du Colombier fmtinstall('I', eipfmt); 1413e12c5d1SDavid du Colombier 14264eb6bc1SDavid du Colombier /* 14364eb6bc1SDavid du Colombier * setuser calls newns, and typical /lib/namespace files contain 14464eb6bc1SDavid du Colombier * "cd /usr/$user", so call setuser before chdir. 14564eb6bc1SDavid du Colombier */ 14664eb6bc1SDavid du Colombier setuser(); 1473e12c5d1SDavid du Colombier if(chdir(dir) < 0) 1487dd7cddfSDavid du Colombier sysfatal("can't get to directory %s: %r", dir); 1493e12c5d1SDavid du Colombier 1507dd7cddfSDavid du Colombier if(!dbg) 1513e12c5d1SDavid du Colombier switch(rfork(RFNOTEG|RFPROC|RFFDG)) { 1523e12c5d1SDavid du Colombier case -1: 1537dd7cddfSDavid du Colombier sysfatal("fork: %r"); 1543e12c5d1SDavid du Colombier case 0: 1553e12c5d1SDavid du Colombier break; 1563e12c5d1SDavid du Colombier default: 1573e12c5d1SDavid du Colombier exits(0); 1583e12c5d1SDavid du Colombier } 1593e12c5d1SDavid du Colombier 160c0eadb1cSDavid du Colombier snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc); 1617dd7cddfSDavid du Colombier cfd = announce(buf, adir); 162c0eadb1cSDavid du Colombier if (cfd < 0) 163c0eadb1cSDavid du Colombier sysfatal("announcing on %s: %r", buf); 164c0eadb1cSDavid du Colombier syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir); 16564eb6bc1SDavid du Colombier // setuser(); 1663e12c5d1SDavid du Colombier for(;;) { 1677dd7cddfSDavid du Colombier lcfd = listen(adir, ldir); 1687dd7cddfSDavid du Colombier if(lcfd < 0) 169c0eadb1cSDavid du Colombier sysfatal("listening on %s: %r", adir); 1707dd7cddfSDavid du Colombier 1717dd7cddfSDavid du Colombier switch(fork()) { 1727dd7cddfSDavid du Colombier case -1: 1737dd7cddfSDavid du Colombier sysfatal("fork: %r"); 1747dd7cddfSDavid du Colombier case 0: 175c0eadb1cSDavid du Colombier dfd = accept(lcfd, ldir); 1767dd7cddfSDavid du Colombier if(dfd < 0) 1777dd7cddfSDavid du Colombier exits(0); 1787dd7cddfSDavid du Colombier remoteaddr(ldir, raddr, sizeof(raddr)); 179*3e5d0078SDavid du Colombier pid = getpid(); 180*3e5d0078SDavid du Colombier syslog(0, flog, "tftp %d connection from %s dir %s", 181*3e5d0078SDavid du Colombier pid, raddr, ldir); 1827dd7cddfSDavid du Colombier doserve(dfd); 1837dd7cddfSDavid du Colombier exits("done"); 1847dd7cddfSDavid du Colombier break; 1857dd7cddfSDavid du Colombier default: 1867dd7cddfSDavid du Colombier close(lcfd); 1877dd7cddfSDavid du Colombier continue; 1887dd7cddfSDavid du Colombier } 1897dd7cddfSDavid du Colombier } 1907dd7cddfSDavid du Colombier } 1917dd7cddfSDavid du Colombier 192*3e5d0078SDavid du Colombier static Opt * 193*3e5d0078SDavid du Colombier handleopt(int fd, char *name, char *val) 194*3e5d0078SDavid du Colombier { 195*3e5d0078SDavid du Colombier int n; 196*3e5d0078SDavid du Colombier Opt *op; 197*3e5d0078SDavid du Colombier 198*3e5d0078SDavid du Colombier for (op = option; op < option + nelem(option); op++) 199*3e5d0078SDavid du Colombier if(cistrcmp(name, op->name) == 0) { 200*3e5d0078SDavid du Colombier n = strtol(val, nil, 10); 201*3e5d0078SDavid du Colombier if (n < op->min || n > op->max) { 202*3e5d0078SDavid du Colombier nak(fd, Errbadopt, "option value out of range"); 203*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftp bad option value from " 204*3e5d0078SDavid du Colombier "client: %s %s", name, val); 205*3e5d0078SDavid du Colombier sysfatal("bad option value from client: %s %s", 206*3e5d0078SDavid du Colombier name, val); 207*3e5d0078SDavid du Colombier } 208*3e5d0078SDavid du Colombier *op->valp = n; 209*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d setting %s to %d", 210*3e5d0078SDavid du Colombier pid, name, n); 211*3e5d0078SDavid du Colombier return op; 212*3e5d0078SDavid du Colombier } 213*3e5d0078SDavid du Colombier return nil; 214*3e5d0078SDavid du Colombier } 215*3e5d0078SDavid du Colombier 216*3e5d0078SDavid du Colombier static vlong 217*3e5d0078SDavid du Colombier filesize(char *file) 218*3e5d0078SDavid du Colombier { 219*3e5d0078SDavid du Colombier vlong size; 220*3e5d0078SDavid du Colombier Dir *dp; 221*3e5d0078SDavid du Colombier 222*3e5d0078SDavid du Colombier dp = dirstat(file); 223*3e5d0078SDavid du Colombier if (dp == nil) 224*3e5d0078SDavid du Colombier return 0; 225*3e5d0078SDavid du Colombier size = dp->length; 226*3e5d0078SDavid du Colombier free(dp); 227*3e5d0078SDavid du Colombier return size; 228*3e5d0078SDavid du Colombier } 229*3e5d0078SDavid du Colombier 230*3e5d0078SDavid du Colombier static int 231*3e5d0078SDavid du Colombier options(int fd, char *buf, char *file, ushort oper, char *p, int dlen) 232*3e5d0078SDavid du Colombier { 233*3e5d0078SDavid du Colombier int nmlen, vallen, nopts; 234*3e5d0078SDavid du Colombier vlong size; 235*3e5d0078SDavid du Colombier char *val, *bp; 236*3e5d0078SDavid du Colombier Opt *op; 237*3e5d0078SDavid du Colombier 238*3e5d0078SDavid du Colombier buf[0] = 0; 239*3e5d0078SDavid du Colombier buf[1] = Tftp_OACK; 240*3e5d0078SDavid du Colombier bp = buf + Opsize; 241*3e5d0078SDavid du Colombier nopts = 0; 242*3e5d0078SDavid du Colombier while (dlen > 0 && *p != '\0') { 243*3e5d0078SDavid du Colombier nmlen = strlen(p) + 1; /* include NUL */ 244*3e5d0078SDavid du Colombier if (nmlen > dlen) 245*3e5d0078SDavid du Colombier break; 246*3e5d0078SDavid du Colombier dlen -= nmlen; 247*3e5d0078SDavid du Colombier val = p + nmlen; 248*3e5d0078SDavid du Colombier if (dlen <= 0 || *val == '\0') 249*3e5d0078SDavid du Colombier break; 250*3e5d0078SDavid du Colombier 251*3e5d0078SDavid du Colombier vallen = strlen(val) + 1; 252*3e5d0078SDavid du Colombier if (vallen > dlen) 253*3e5d0078SDavid du Colombier break; 254*3e5d0078SDavid du Colombier dlen -= vallen; 255*3e5d0078SDavid du Colombier 256*3e5d0078SDavid du Colombier nopts++; 257*3e5d0078SDavid du Colombier op = handleopt(fd, p, val); 258*3e5d0078SDavid du Colombier if (op) { 259*3e5d0078SDavid du Colombier /* append OACK response to buf */ 260*3e5d0078SDavid du Colombier sprint(bp, "%s", p); 261*3e5d0078SDavid du Colombier bp += nmlen; 262*3e5d0078SDavid du Colombier if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) { 263*3e5d0078SDavid du Colombier size = filesize(file); 264*3e5d0078SDavid du Colombier sprint(bp, "%lld", size); 265*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d %s tsize is %,lld", 266*3e5d0078SDavid du Colombier pid, file, size); 267*3e5d0078SDavid du Colombier } else 268*3e5d0078SDavid du Colombier sprint(bp, "%s", val); /* use value asked for */ 269*3e5d0078SDavid du Colombier bp += strlen(bp) + 1; 270*3e5d0078SDavid du Colombier } 271*3e5d0078SDavid du Colombier p = val + vallen; 272*3e5d0078SDavid du Colombier } 273*3e5d0078SDavid du Colombier if (nopts == 0) 274*3e5d0078SDavid du Colombier return 0; /* no options actually seen */ 275*3e5d0078SDavid du Colombier *bp++ = '\0'; 276*3e5d0078SDavid du Colombier *bp++ = '\0'; /* overkill */ 277*3e5d0078SDavid du Colombier *bp++ = '\0'; 278*3e5d0078SDavid du Colombier if (write(fd, buf, bp - buf) < bp - buf) { 279*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd network write error on oack to %s: %r", 280*3e5d0078SDavid du Colombier raddr); 281*3e5d0078SDavid du Colombier sysfatal("tftpd: network write error: %r"); 282*3e5d0078SDavid du Colombier } 283*3e5d0078SDavid du Colombier if(Debug) 284*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd oack: options to %s", raddr); 285*3e5d0078SDavid du Colombier return nopts; 286*3e5d0078SDavid du Colombier } 287*3e5d0078SDavid du Colombier 288*3e5d0078SDavid du Colombier /* this doesn't stop the cavium from barging ahead */ 289*3e5d0078SDavid du Colombier //static void 290*3e5d0078SDavid du Colombier //sendnoopts(int fd, char *name) 291*3e5d0078SDavid du Colombier //{ 292*3e5d0078SDavid du Colombier // char buf[64]; 293*3e5d0078SDavid du Colombier // 294*3e5d0078SDavid du Colombier // memset(buf, 0, sizeof buf); 295*3e5d0078SDavid du Colombier // buf[0] = 0; 296*3e5d0078SDavid du Colombier // buf[1] = Tftp_OACK; 297*3e5d0078SDavid du Colombier // 298*3e5d0078SDavid du Colombier // if(write(fd, buf, sizeof buf) < sizeof buf) { 299*3e5d0078SDavid du Colombier // syslog(dbg, flog, "tftpd network write error on %s oack to %s: %r", 300*3e5d0078SDavid du Colombier // name, raddr); 301*3e5d0078SDavid du Colombier // sysfatal("tftpd: network write error: %r"); 302*3e5d0078SDavid du Colombier // } 303*3e5d0078SDavid du Colombier // if(Debug) 304*3e5d0078SDavid du Colombier // syslog(dbg, flog, "tftpd oack: no options to %s", raddr); 305*3e5d0078SDavid du Colombier //} 306*3e5d0078SDavid du Colombier 307*3e5d0078SDavid du Colombier static void 308*3e5d0078SDavid du Colombier optlog(char *bytes, char *p, int dlen) 309*3e5d0078SDavid du Colombier { 310*3e5d0078SDavid du Colombier char *bp; 311*3e5d0078SDavid du Colombier 312*3e5d0078SDavid du Colombier bp = bytes; 313*3e5d0078SDavid du Colombier sprint(bp, "tftpd %d option bytes: ", dlen); 314*3e5d0078SDavid du Colombier bp += strlen(bp); 315*3e5d0078SDavid du Colombier for (; dlen > 0; dlen--, p++) 316*3e5d0078SDavid du Colombier *bp++ = *p? *p: ' '; 317*3e5d0078SDavid du Colombier *bp = '\0'; 318*3e5d0078SDavid du Colombier syslog(dbg, flog, "%s", bytes); 319*3e5d0078SDavid du Colombier } 320*3e5d0078SDavid du Colombier 3217dd7cddfSDavid du Colombier void 3227dd7cddfSDavid du Colombier doserve(int fd) 3237dd7cddfSDavid du Colombier { 324*3e5d0078SDavid du Colombier int dlen, opts; 325*3e5d0078SDavid du Colombier char *mode, *p, *file; 3267dd7cddfSDavid du Colombier short op; 3277dd7cddfSDavid du Colombier 328*3e5d0078SDavid du Colombier dlen = read(fd, bigbuf, sizeof(bigbuf)-1); 3293e12c5d1SDavid du Colombier if(dlen < 0) 3307dd7cddfSDavid du Colombier sysfatal("listen read: %r"); 3313e12c5d1SDavid du Colombier 332*3e5d0078SDavid du Colombier bigbuf[dlen] = '\0'; 3337dd7cddfSDavid du Colombier op = (bigbuf[0]<<8) | bigbuf[1]; 334*3e5d0078SDavid du Colombier dlen -= Opsize; 335*3e5d0078SDavid du Colombier mode = file = bigbuf + Opsize; 3363e12c5d1SDavid du Colombier while(*mode != '\0' && dlen--) 3373e12c5d1SDavid du Colombier mode++; 3383e12c5d1SDavid du Colombier mode++; 3393e12c5d1SDavid du Colombier p = mode; 3403e12c5d1SDavid du Colombier while(*p && dlen--) 3413e12c5d1SDavid du Colombier p++; 3423e12c5d1SDavid du Colombier if(dlen == 0) { 3437dd7cddfSDavid du Colombier nak(fd, 0, "bad tftpmode"); 3447dd7cddfSDavid du Colombier close(fd); 345*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s", 346*3e5d0078SDavid du Colombier pid, mode, file, raddr); 3477dd7cddfSDavid du Colombier return; 3483e12c5d1SDavid du Colombier } 3493e12c5d1SDavid du Colombier 3503e12c5d1SDavid du Colombier if(op != Tftp_READ && op != Tftp_WRITE) { 351*3e5d0078SDavid du Colombier nak(fd, Errbadop, "Illegal TFTP operation"); 3527dd7cddfSDavid du Colombier close(fd); 353*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad request %d %s", pid, op, raddr); 3547dd7cddfSDavid du Colombier return; 3553e12c5d1SDavid du Colombier } 3567dd7cddfSDavid du Colombier 357219b2ee8SDavid du Colombier if(restricted){ 358*3e5d0078SDavid du Colombier if(file[0] == '#' || strncmp(file, "../", 3) == 0 || 359*3e5d0078SDavid du Colombier strstr(file, "/../") != nil || 360*3e5d0078SDavid du Colombier (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){ 361*3e5d0078SDavid du Colombier nak(fd, Errnoaccess, "Permission denied"); 3627dd7cddfSDavid du Colombier close(fd); 363*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d bad request %d from %s file %s", 364*3e5d0078SDavid du Colombier pid, op, raddr, file); 3657dd7cddfSDavid du Colombier return; 366219b2ee8SDavid du Colombier } 367219b2ee8SDavid du Colombier } 3687dd7cddfSDavid du Colombier 369*3e5d0078SDavid du Colombier /* 370*3e5d0078SDavid du Colombier * options are supposed to be negotiated, but the cavium board's 371*3e5d0078SDavid du Colombier * u-boot really wants us to use a block size of 1432 bytes and won't 372*3e5d0078SDavid du Colombier * take `no' for an answer. 373*3e5d0078SDavid du Colombier */ 374*3e5d0078SDavid du Colombier p++; /* skip NUL after mode */ 375*3e5d0078SDavid du Colombier dlen--; 376*3e5d0078SDavid du Colombier opts = 0; 377*3e5d0078SDavid du Colombier if(dlen > 0) { /* might have options */ 378*3e5d0078SDavid du Colombier char bytes[32*1024]; 379*3e5d0078SDavid du Colombier 380*3e5d0078SDavid du Colombier if(Debug) 381*3e5d0078SDavid du Colombier optlog(bytes, p, dlen); 382*3e5d0078SDavid du Colombier opts = options(fd, bytes, file, op, p, dlen); 383*3e5d0078SDavid du Colombier } 3843e12c5d1SDavid du Colombier if(op == Tftp_READ) 385*3e5d0078SDavid du Colombier sendfile(fd, file, mode, opts); 3863e12c5d1SDavid du Colombier else 387*3e5d0078SDavid du Colombier recvfile(fd, file, mode); 3883e12c5d1SDavid du Colombier } 3893e12c5d1SDavid du Colombier 3903e12c5d1SDavid du Colombier void 3913e12c5d1SDavid du Colombier catcher(void *junk, char *msg) 3923e12c5d1SDavid du Colombier { 3933e12c5d1SDavid du Colombier USED(junk); 3943e12c5d1SDavid du Colombier 3953e12c5d1SDavid du Colombier if(strncmp(msg, "exit", 4) == 0) 3963e12c5d1SDavid du Colombier noted(NDFLT); 3973e12c5d1SDavid du Colombier noted(NCONT); 3983e12c5d1SDavid du Colombier } 3993e12c5d1SDavid du Colombier 400*3e5d0078SDavid du Colombier static int 401*3e5d0078SDavid du Colombier awaitack(int fd, int block) 4023e12c5d1SDavid du Colombier { 403*3e5d0078SDavid du Colombier int ackblock, al, rxl; 404*3e5d0078SDavid du Colombier ushort op; 4053e12c5d1SDavid du Colombier uchar ack[1024]; 4063e12c5d1SDavid du Colombier 407*3e5d0078SDavid du Colombier for(rxl = 0; rxl < 10; rxl++) { 408*3e5d0078SDavid du Colombier memset(ack, 0, Hdrsize); 409*3e5d0078SDavid du Colombier alarm(1000); 410*3e5d0078SDavid du Colombier al = read(fd, ack, sizeof(ack)); 411*3e5d0078SDavid du Colombier alarm(0); 412*3e5d0078SDavid du Colombier if(al < 0) { 413*3e5d0078SDavid du Colombier if (Debug) 414*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d timed out " 415*3e5d0078SDavid du Colombier "waiting for ack from %s", pid, raddr); 416*3e5d0078SDavid du Colombier return Ackrexmit; 417*3e5d0078SDavid du Colombier } 418*3e5d0078SDavid du Colombier op = ack[0]<<8|ack[1]; 419*3e5d0078SDavid du Colombier if(op == Tftp_ERROR) { 420*3e5d0078SDavid du Colombier if (Debug) 421*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d got error " 422*3e5d0078SDavid du Colombier "waiting for ack from %s", pid, raddr); 423*3e5d0078SDavid du Colombier return Ackerr; 424*3e5d0078SDavid du Colombier } else if(op != Tftp_ACK) { 425*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid, 426*3e5d0078SDavid du Colombier (op < nelem(opnames)? opnames[op]: "gok"), 427*3e5d0078SDavid du Colombier raddr); 428*3e5d0078SDavid du Colombier return Ackerr; 429*3e5d0078SDavid du Colombier } 430*3e5d0078SDavid du Colombier ackblock = ack[2]<<8|ack[3]; 431*3e5d0078SDavid du Colombier if (Debug) 432*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d read ack of %d bytes " 433*3e5d0078SDavid du Colombier "for block %d", pid, al, ackblock); 434*3e5d0078SDavid du Colombier if(ackblock == block) 435*3e5d0078SDavid du Colombier return Ackok; /* for block just sent */ 436*3e5d0078SDavid du Colombier else if(ackblock == block + 1) /* intel pxe eof bug */ 437*3e5d0078SDavid du Colombier return Ackok; 438*3e5d0078SDavid du Colombier else if(ackblock == 0xffff) 439*3e5d0078SDavid du Colombier return Ackrexmit; 440*3e5d0078SDavid du Colombier else 441*3e5d0078SDavid du Colombier /* ack is for some other block; ignore it, try again */ 442*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d expected ack for block %d, " 443*3e5d0078SDavid du Colombier "got %d", pid, block, ackblock); 444*3e5d0078SDavid du Colombier } 445*3e5d0078SDavid du Colombier return Ackrexmit; 446*3e5d0078SDavid du Colombier } 447*3e5d0078SDavid du Colombier 448*3e5d0078SDavid du Colombier void 449*3e5d0078SDavid du Colombier sendfile(int fd, char *name, char *mode, int opts) 450*3e5d0078SDavid du Colombier { 451*3e5d0078SDavid du Colombier int file, block, ret, rexmit, n, txtry; 452*3e5d0078SDavid du Colombier uchar buf[Maxsegsize+Hdrsize]; 453*3e5d0078SDavid du Colombier char errbuf[Maxerr]; 454*3e5d0078SDavid du Colombier 455*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d send file '%s' %s to %s", 456*3e5d0078SDavid du Colombier pid, name, mode, raddr); 4573e12c5d1SDavid du Colombier name = sunkernel(name); 4583e12c5d1SDavid du Colombier if(name == 0){ 4593e12c5d1SDavid du Colombier nak(fd, 0, "not in our database"); 4603e12c5d1SDavid du Colombier return; 4613e12c5d1SDavid du Colombier } 4623e12c5d1SDavid du Colombier 4633e12c5d1SDavid du Colombier notify(catcher); 4643e12c5d1SDavid du Colombier 4653e12c5d1SDavid du Colombier file = open(name, OREAD); 4663e12c5d1SDavid du Colombier if(file < 0) { 4679a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 4683e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 4693e12c5d1SDavid du Colombier return; 4703e12c5d1SDavid du Colombier } 4713e12c5d1SDavid du Colombier block = 0; 472*3e5d0078SDavid du Colombier rexmit = Ackok; 4733e12c5d1SDavid du Colombier n = 0; 474*3e5d0078SDavid du Colombier /* 475*3e5d0078SDavid du Colombier * if we sent an oack previously, wait for the client's ack or error. 476*3e5d0078SDavid du Colombier * if we get no ack for our oack, it could be that we returned 477*3e5d0078SDavid du Colombier * a tsize that the client can't handle, or it could be intel 478*3e5d0078SDavid du Colombier * pxe just read-with-tsize to get size, couldn't be bothered to 479*3e5d0078SDavid du Colombier * ack our oack and has just gone ahead and issued another read. 480*3e5d0078SDavid du Colombier */ 481*3e5d0078SDavid du Colombier if(opts && awaitack(fd, 0) != Ackok) 482*3e5d0078SDavid du Colombier goto error; 483*3e5d0078SDavid du Colombier 484*3e5d0078SDavid du Colombier for(txtry = 0; txtry < timeout;) { 485*3e5d0078SDavid du Colombier if(rexmit == Ackok) { 4863e12c5d1SDavid du Colombier block++; 4873e12c5d1SDavid du Colombier buf[0] = 0; 4883e12c5d1SDavid du Colombier buf[1] = Tftp_DATA; 4893e12c5d1SDavid du Colombier buf[2] = block>>8; 4903e12c5d1SDavid du Colombier buf[3] = block; 491*3e5d0078SDavid du Colombier n = read(file, buf+Hdrsize, blksize); 4923e12c5d1SDavid du Colombier if(n < 0) { 4939a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 4943e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 4953e12c5d1SDavid du Colombier return; 4963e12c5d1SDavid du Colombier } 497219b2ee8SDavid du Colombier txtry = 0; 4983e12c5d1SDavid du Colombier } 4997dd7cddfSDavid du Colombier else { 500*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s", 501*3e5d0078SDavid du Colombier pid, Hdrsize+n, name, block, raddr); 5023e12c5d1SDavid du Colombier txtry++; 5037dd7cddfSDavid du Colombier } 5047dd7cddfSDavid du Colombier 505*3e5d0078SDavid du Colombier ret = write(fd, buf, Hdrsize+n); 506*3e5d0078SDavid du Colombier if(ret < Hdrsize+n) { 507*3e5d0078SDavid du Colombier syslog(dbg, flog, 508*3e5d0078SDavid du Colombier "tftpd network write error on %s to %s: %r", 509*3e5d0078SDavid du Colombier name, raddr); 5107dd7cddfSDavid du Colombier sysfatal("tftpd: network write error: %r"); 511*3e5d0078SDavid du Colombier } 512*3e5d0078SDavid du Colombier if (Debug) 513*3e5d0078SDavid du Colombier syslog(dbg, flog, "tftpd %d sent block %d", pid, block); 5143e12c5d1SDavid du Colombier 515*3e5d0078SDavid du Colombier rexmit = awaitack(fd, block); 516*3e5d0078SDavid du Colombier if (rexmit == Ackerr) 5173e12c5d1SDavid du Colombier break; 518*3e5d0078SDavid du Colombier if(ret != blksize+Hdrsize && rexmit == Ackok) 5193e12c5d1SDavid du Colombier break; 5203e12c5d1SDavid du Colombier } 5213e12c5d1SDavid du Colombier error: 5223e12c5d1SDavid du Colombier close(fd); 5233e12c5d1SDavid du Colombier close(file); 5243e12c5d1SDavid du Colombier } 5253e12c5d1SDavid du Colombier 5263e12c5d1SDavid du Colombier void 5273e12c5d1SDavid du Colombier recvfile(int fd, char *name, char *mode) 5283e12c5d1SDavid du Colombier { 5293e12c5d1SDavid du Colombier ushort op, block, inblock; 530*3e5d0078SDavid du Colombier uchar buf[Maxsegsize+8]; 5319a747e4fSDavid du Colombier char errbuf[Maxerr]; 5323e12c5d1SDavid du Colombier int n, ret, file; 5333e12c5d1SDavid du Colombier 5343e12c5d1SDavid du Colombier syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr); 5353e12c5d1SDavid du Colombier 5363e12c5d1SDavid du Colombier file = create(name, OWRITE, 0666); 5373e12c5d1SDavid du Colombier if(file < 0) { 5389a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 5393e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 540162f803dSDavid du Colombier syslog(dbg, flog, "can't create %s: %r", name); 5413e12c5d1SDavid du Colombier return; 5423e12c5d1SDavid du Colombier } 5433e12c5d1SDavid du Colombier 5443e12c5d1SDavid du Colombier block = 0; 5453e12c5d1SDavid du Colombier ack(fd, block); 5463e12c5d1SDavid du Colombier block++; 5473e12c5d1SDavid du Colombier 5483e12c5d1SDavid du Colombier for (;;) { 5493e12c5d1SDavid du Colombier alarm(15000); 550*3e5d0078SDavid du Colombier n = read(fd, buf, blksize+8); 5513e12c5d1SDavid du Colombier alarm(0); 552162f803dSDavid du Colombier if(n < 0) { 553162f803dSDavid du Colombier syslog(dbg, flog, "tftpd: network error reading %s: %r", 554162f803dSDavid du Colombier name); 5553e12c5d1SDavid du Colombier goto error; 556162f803dSDavid du Colombier } 557162f803dSDavid du Colombier if(n <= Hdrsize) { 558162f803dSDavid du Colombier syslog(dbg, flog, 559162f803dSDavid du Colombier "tftpd: short read from network, reading %s", 560162f803dSDavid du Colombier name); 561162f803dSDavid du Colombier goto error; 562162f803dSDavid du Colombier } 5633e12c5d1SDavid du Colombier op = buf[0]<<8|buf[1]; 564162f803dSDavid du Colombier if(op == Tftp_ERROR) { 565162f803dSDavid du Colombier syslog(dbg, flog, "tftpd: tftp error reading %s", name); 5663e12c5d1SDavid du Colombier goto error; 567162f803dSDavid du Colombier } 5683e12c5d1SDavid du Colombier 569162f803dSDavid du Colombier n -= Hdrsize; 5703e12c5d1SDavid du Colombier inblock = buf[2]<<8|buf[3]; 5713e12c5d1SDavid du Colombier if(op == Tftp_DATA) { 5723e12c5d1SDavid du Colombier if(inblock == block) { 573162f803dSDavid du Colombier ret = write(file, buf+Hdrsize, n); 574162f803dSDavid du Colombier if(ret != n) { 5759a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf); 5763e12c5d1SDavid du Colombier nak(fd, 0, errbuf); 577162f803dSDavid du Colombier syslog(dbg, flog, 578162f803dSDavid du Colombier "tftpd: error writing %s: %s", 579162f803dSDavid du Colombier name, errbuf); 5803e12c5d1SDavid du Colombier goto error; 5813e12c5d1SDavid du Colombier } 5823e12c5d1SDavid du Colombier ack(fd, block); 5833e12c5d1SDavid du Colombier block++; 58478307a5fSDavid du Colombier } else 58578307a5fSDavid du Colombier ack(fd, 0xffff); /* tell him to resend */ 5863e12c5d1SDavid du Colombier } 5873e12c5d1SDavid du Colombier } 5883e12c5d1SDavid du Colombier error: 5893e12c5d1SDavid du Colombier close(file); 5903e12c5d1SDavid du Colombier } 5913e12c5d1SDavid du Colombier 5923e12c5d1SDavid du Colombier void 5933e12c5d1SDavid du Colombier ack(int fd, ushort block) 5943e12c5d1SDavid du Colombier { 5953e12c5d1SDavid du Colombier uchar ack[4]; 5963e12c5d1SDavid du Colombier int n; 5973e12c5d1SDavid du Colombier 5983e12c5d1SDavid du Colombier ack[0] = 0; 5993e12c5d1SDavid du Colombier ack[1] = Tftp_ACK; 6003e12c5d1SDavid du Colombier ack[2] = block>>8; 6013e12c5d1SDavid du Colombier ack[3] = block; 6023e12c5d1SDavid du Colombier 6033e12c5d1SDavid du Colombier n = write(fd, ack, 4); 604162f803dSDavid du Colombier if(n < 4) 6057dd7cddfSDavid du Colombier sysfatal("network write: %r"); 6063e12c5d1SDavid du Colombier } 6073e12c5d1SDavid du Colombier 6083e12c5d1SDavid du Colombier void 6093e12c5d1SDavid du Colombier nak(int fd, int code, char *msg) 6103e12c5d1SDavid du Colombier { 6113e12c5d1SDavid du Colombier char buf[128]; 6123e12c5d1SDavid du Colombier int n; 6133e12c5d1SDavid du Colombier 6143e12c5d1SDavid du Colombier buf[0] = 0; 6153e12c5d1SDavid du Colombier buf[1] = Tftp_ERROR; 6163e12c5d1SDavid du Colombier buf[2] = 0; 6173e12c5d1SDavid du Colombier buf[3] = code; 6183e12c5d1SDavid du Colombier strcpy(buf+4, msg); 6193e12c5d1SDavid du Colombier n = strlen(msg) + 4 + 1; 620*3e5d0078SDavid du Colombier if(write(fd, buf, n) < n) 6217dd7cddfSDavid du Colombier sysfatal("write nak: %r"); 6223e12c5d1SDavid du Colombier } 6233e12c5d1SDavid du Colombier 6243e12c5d1SDavid du Colombier void 6253e12c5d1SDavid du Colombier setuser(void) 6263e12c5d1SDavid du Colombier { 627c0eadb1cSDavid du Colombier int fd; 6283e12c5d1SDavid du Colombier 629c0eadb1cSDavid du Colombier fd = open("#c/user", OWRITE); 630c0eadb1cSDavid du Colombier if(fd < 0 || write(fd, "none", strlen("none")) < 0) 631c0eadb1cSDavid du Colombier sysfatal("can't become none: %r"); 632c0eadb1cSDavid du Colombier close(fd); 633c0eadb1cSDavid du Colombier if(newns("none", nil) < 0) 634c0eadb1cSDavid du Colombier sysfatal("can't build namespace: %r"); 6353e12c5d1SDavid du Colombier } 6363e12c5d1SDavid du Colombier 6377dd7cddfSDavid du Colombier char* 638da51d93aSDavid du Colombier lookup(char *sattr, char *sval, char *tattr, char *tval, int len) 6397dd7cddfSDavid du Colombier { 6407dd7cddfSDavid du Colombier static Ndb *db; 6417dd7cddfSDavid du Colombier char *attrs[1]; 6427dd7cddfSDavid du Colombier Ndbtuple *t; 6437dd7cddfSDavid du Colombier 6447dd7cddfSDavid du Colombier if(db == nil) 6457dd7cddfSDavid du Colombier db = ndbopen(0); 6467dd7cddfSDavid du Colombier if(db == nil) 6477dd7cddfSDavid du Colombier return nil; 6487dd7cddfSDavid du Colombier 6497dd7cddfSDavid du Colombier if(sattr == nil) 6507dd7cddfSDavid du Colombier sattr = ipattr(sval); 6517dd7cddfSDavid du Colombier 6527dd7cddfSDavid du Colombier attrs[0] = tattr; 6537dd7cddfSDavid du Colombier t = ndbipinfo(db, sattr, sval, attrs, 1); 6547dd7cddfSDavid du Colombier if(t == nil) 6557dd7cddfSDavid du Colombier return nil; 656da51d93aSDavid du Colombier strncpy(tval, t->val, len); 657da51d93aSDavid du Colombier tval[len-1] = 0; 6587dd7cddfSDavid du Colombier ndbfree(t); 6597dd7cddfSDavid du Colombier return tval; 6607dd7cddfSDavid du Colombier } 6617dd7cddfSDavid du Colombier 6623e12c5d1SDavid du Colombier /* 6633e12c5d1SDavid du Colombier * for sun kernel boots, replace the requested file name with 6643e12c5d1SDavid du Colombier * a one from our database. If the database doesn't specify a file, 6653e12c5d1SDavid du Colombier * don't answer. 6663e12c5d1SDavid du Colombier */ 6673e12c5d1SDavid du Colombier char* 6683e12c5d1SDavid du Colombier sunkernel(char *name) 6693e12c5d1SDavid du Colombier { 6703e12c5d1SDavid du Colombier ulong addr; 6717dd7cddfSDavid du Colombier uchar v4[IPv4addrlen]; 6727dd7cddfSDavid du Colombier uchar v6[IPaddrlen]; 673da51d93aSDavid du Colombier char buf[256]; 674da51d93aSDavid du Colombier char ipbuf[128]; 67527e10919SDavid du Colombier char *suffix; 6763e12c5d1SDavid du Colombier 67727e10919SDavid du Colombier addr = strtoul(name, &suffix, 16); 67827e10919SDavid du Colombier if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0)) 6793e12c5d1SDavid du Colombier return name; 6803e12c5d1SDavid du Colombier 6817dd7cddfSDavid du Colombier v4[0] = addr>>24; 6827dd7cddfSDavid du Colombier v4[1] = addr>>16; 6837dd7cddfSDavid du Colombier v4[2] = addr>>8; 6847dd7cddfSDavid du Colombier v4[3] = addr; 6857dd7cddfSDavid du Colombier v4tov6(v6, v4); 6867dd7cddfSDavid du Colombier sprint(ipbuf, "%I", v6); 687da51d93aSDavid du Colombier return lookup("ip", ipbuf, "bootf", buf, sizeof buf); 6887dd7cddfSDavid du Colombier } 6897dd7cddfSDavid du Colombier 6907dd7cddfSDavid du Colombier void 6917dd7cddfSDavid du Colombier remoteaddr(char *dir, char *raddr, int len) 6927dd7cddfSDavid du Colombier { 6937dd7cddfSDavid du Colombier char buf[64]; 6947dd7cddfSDavid du Colombier int fd, n; 6957dd7cddfSDavid du Colombier 6967dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "%s/remote", dir); 6977dd7cddfSDavid du Colombier fd = open(buf, OREAD); 6987dd7cddfSDavid du Colombier if(fd < 0){ 6997dd7cddfSDavid du Colombier snprint(raddr, sizeof(raddr), "unknown"); 7007dd7cddfSDavid du Colombier return; 7017dd7cddfSDavid du Colombier } 7027dd7cddfSDavid du Colombier n = read(fd, raddr, len-1); 7037dd7cddfSDavid du Colombier close(fd); 7047dd7cddfSDavid du Colombier if(n <= 0){ 7057dd7cddfSDavid du Colombier snprint(raddr, sizeof(raddr), "unknown"); 7067dd7cddfSDavid du Colombier return; 7077dd7cddfSDavid du Colombier } 7087dd7cddfSDavid du Colombier if(n > 0) 7097dd7cddfSDavid du Colombier n--; 7107dd7cddfSDavid du Colombier raddr[n] = 0; 7113e12c5d1SDavid du Colombier } 712