diff options
| author | cinap_lenrek <cinap_lenrek@flatbox.9hal> | 2012-03-09 06:06:33 +0100 |
|---|---|---|
| committer | cinap_lenrek <cinap_lenrek@flatbox.9hal> | 2012-03-09 06:06:33 +0100 |
| commit | 731aeeedb377c86eea8b0263b72190924fd90887 (patch) | |
| tree | d68f1d4a36fef1a7c6fb4d82b30e83fc0b5e3652 | |
| parent | 0d926a251269277214ab0ce5e84f5c4674576d6f (diff) | |
| download | plan9front-731aeeedb377c86eea8b0263b72190924fd90887.tar.xz | |
tftpd: import from sources
| -rw-r--r-- | sys/src/cmd/ip/tftpd.c | 217 |
1 files changed, 157 insertions, 60 deletions
diff --git a/sys/src/cmd/ip/tftpd.c b/sys/src/cmd/ip/tftpd.c index c005f0128..c39f2173d 100644 --- a/sys/src/cmd/ip/tftpd.c +++ b/sys/src/cmd/ip/tftpd.c @@ -54,6 +54,7 @@ enum * notably IP/UDP and TFTP, using worst-case (IPv6) sizes. */ Bandtblksz = Bandtmtu - 40 - 8, + Bcavium = 1432, /* cavium's u-boot demands this size */ }; typedef struct Opt Opt; @@ -218,8 +219,10 @@ handleopt(int fd, char *name, char *val) name, val); } *op->valp = n; - syslog(dbg, flog, "tftpd %d setting %s to %d", - pid, name, n); + /* incoming 0 for tsize is uninteresting */ + if(cistrcmp("tsize", op->name) != 0) + syslog(dbg, flog, "tftpd %d setting %s to client's %d", + pid, name, n); return op; } return nil; @@ -233,25 +236,59 @@ filesize(char *file) dp = dirstat(file); if (dp == nil) - return 0; + return -1; size = dp->length; free(dp); return size; } +/* copy word into bp iff it fits before ep, returns bytes to advance bp. */ static int -options(int fd, char *buf, char *file, ushort oper, char *p, int dlen) +emits(char *word, char *bp, char *ep) { - int nmlen, vallen, nopts; + int len; + + len = strlen(word) + 1; + if (bp + len >= ep) + return -1; + strcpy(bp, word); + return len; +} + +/* format number into bp iff it fits before ep. */ +static int +emitn(vlong n, char *bp, char *ep) +{ + char numb[32]; + + snprint(numb, sizeof numb, "%lld", n); + return emits(numb, bp, ep); +} + +/* + * send an OACK packet to respond to options. bail early with -1 on error. + * p is the packet containing the options. + * + * hack: bandt (viaducts) uses smaller mtu than ether's + * (1400 bytes for tcp mss of 1300 bytes), + * so offer at most bandt's mtu minus headers, + * to avoid failure of pxe booting via viaduct. + * there's an exception for the cavium's u-boot. + */ +static int +options(int fd, char *buf, int bufsz, char *file, ushort oper, char *p, int dlen) +{ + int nmlen, vallen, olen, nopts; vlong size; - char *val, *bp; + char *val, *bp, *ep; Opt *op; buf[0] = 0; buf[1] = Tftp_OACK; bp = buf + Opsize; + ep = buf + bufsz; nopts = 0; - while (dlen > 0 && *p != '\0') { + for (; dlen > 0 && *p != '\0'; p = val + vallen, bp += olen) { nmlen = strlen(p) + 1; /* include NUL */ if (nmlen > dlen) break; @@ -266,42 +303,49 @@ options(int fd, char *buf, char *file, ushort oper, char *p, int dlen) dlen -= vallen; nopts++; + olen = 0; op = handleopt(fd, p, val); - if (op) { - /* append OACK response to buf */ - sprint(bp, "%s", p); - bp += nmlen; - if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) { - size = filesize(file); - sprint(bp, "%lld", size); - syslog(dbg, flog, "tftpd %d %s tsize is %,lld", - pid, file, size); + if (op == nil) + continue; + + /* append OACK response to buf */ + nmlen = emits(p, bp, ep); /* option name */ + if (nmlen < 0) + return -1; + bp += nmlen; + + if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) { + size = filesize(file); + if (size == -1) { + nak(fd, Errnotfound, "no such file"); + syslog(dbg, flog, "tftpd tsize for " + "non-existent file %s", file); + // *op->valp = 0; + // olen = emits("0", bp, ep); + return -1; } - /* - * hack: bandt (viaducts) uses smaller mtu than ether's - * (1400 bytes for tcp mss of 1300 bytes), - * so offer at most bandt's mtu minus headers, - * to avoid failure of pxe booting via viaduct. - */ - else if (oper == Tftp_READ && - cistrcmp(p, "blksize") == 0 && - blksize > Bandtblksz) { - blksize = Bandtblksz; - sprint(bp, "%d", blksize); - syslog(dbg, flog, - "tftpd %d overriding blksize to %d", - pid, blksize); - } else - strcpy(bp, val); /* use requested value */ - bp += strlen(bp) + 1; - } - p = val + vallen; + *op->valp = size; + olen = emitn(size, bp, ep); + syslog(dbg, flog, "tftpd %d %s tsize is %,lld", + pid, file, size); + } else if (oper == Tftp_READ && cistrcmp(p, "blksize") == 0 && + blksize > Bandtblksz && blksize != Bcavium) { + *op->valp = blksize = Bandtblksz; + olen = emitn(blksize, bp, ep); + syslog(dbg, flog, "tftpd %d overriding blksize to %d", + pid, blksize); + } else + olen = emits(val, bp, ep); /* use requested value */ } if (nopts == 0) return 0; /* no options actually seen */ + + if (bp + 3 >= ep) + return -1; *bp++ = '\0'; *bp++ = '\0'; /* overkill */ *bp++ = '\0'; + if (write(fd, buf, bp - buf) < bp - buf) { syslog(dbg, flog, "tftpd network write error on oack to %s: %r", raddr); @@ -312,25 +356,6 @@ options(int fd, char *buf, char *file, ushort oper, char *p, int dlen) return nopts; } -/* this doesn't stop the cavium from barging ahead */ -//static void -//sendnoopts(int fd, char *name) -//{ -// char buf[64]; -// -// memset(buf, 0, sizeof buf); -// buf[0] = 0; -// buf[1] = Tftp_OACK; -// -// if(write(fd, buf, sizeof buf) < sizeof buf) { -// syslog(dbg, flog, "tftpd network write error on %s oack to %s: %r", -// name, raddr); -// sysfatal("tftpd: network write error: %r"); -// } -// if(Debug) -// syslog(dbg, flog, "tftpd oack: no options to %s", raddr); -//} - static void optlog(char *bytes, char *p, int dlen) { @@ -345,6 +370,65 @@ optlog(char *bytes, char *p, int dlen) syslog(dbg, flog, "%s", bytes); } +/* + * replace one occurrence of %[ICE] with ip, cfgpxe name, or ether mac, resp. + * we can't easily use $ because u-boot has stranger quoting rules than sh. + */ +char * +mapname(char *file) +{ + int nf; + char *p, *newnm, *cur, *arpf, *ln, *remip, *bang; + char *fields[4]; + Biobuf *arp; + + p = strchr(file, '%'); + if (p == nil || p[1] == '\0') + return strdup(file); + + remip = strdup(raddr); + newnm = mallocz(strlen(file) + Maxpath, 1); + if (remip == nil || newnm == nil) + sysfatal("out of memory"); + + bang = strchr(remip, '!'); + if (bang) + *bang = '\0'; /* remove !port */ + + memmove(newnm, file, p - file); /* copy up to % */ + cur = newnm + strlen(newnm); + switch(p[1]) { + case 'I': + strcpy(cur, remip); /* remote's IP */ + break; + case 'C': + strcpy(cur, "/cfg/pxe/"); + cur += strlen(cur); + /* fall through */ + case 'E': + /* look up remote's IP in /net/arp to get mac. */ + arpf = smprint("%s/arp", net); + arp = Bopen(arpf, OREAD); + free(arpf); + if (arp == nil) + break; + /* read lines looking for remip in 3rd field of 4 */ + while ((ln = Brdline(arp, '\n')) != nil) { + ln[Blinelen(arp)-1] = 0; + nf = tokenize(ln, fields, nelem(fields)); + if (nf >= 4 && strcmp(fields[2], remip) == 0) { + strcpy(cur, fields[3]); + break; + } + } + Bterm(arp); + break; + } + strcat(newnm, p + 2); /* tail following %x */ + free(remip); + return newnm; +} + void doserve(int fd) { @@ -366,6 +450,9 @@ doserve(int fd) p = mode; while(*p && dlen--) p++; + + file = mapname(file); /* we don't free the result; minor leak */ + if(dlen == 0) { nak(fd, 0, "bad tftpmode"); close(fd); @@ -377,7 +464,8 @@ doserve(int fd) if(op != Tftp_READ && op != Tftp_WRITE) { nak(fd, Errbadop, "Illegal TFTP operation"); close(fd); - syslog(dbg, flog, "tftpd %d bad request %d %s", pid, op, raddr); + syslog(dbg, flog, "tftpd %d bad request %d (%s) %s", pid, op, + (op < nelem(opnames)? opnames[op]: "gok"), raddr); return; } @@ -406,7 +494,9 @@ doserve(int fd) if(Debug) optlog(bytes, p, dlen); - opts = options(fd, bytes, file, op, p, dlen); + opts = options(fd, bytes, sizeof bytes, file, op, p, dlen); + if (opts < 0) + return; } if(op == Tftp_READ) sendfile(fd, file, mode, opts); @@ -479,12 +569,13 @@ sendfile(int fd, char *name, char *mode, int opts) uchar buf[Maxsegsize+Hdrsize]; char errbuf[Maxerr]; + file = -1; syslog(dbg, flog, "tftpd %d send file '%s' %s to %s", pid, name, mode, raddr); name = sunkernel(name); if(name == 0){ nak(fd, 0, "not in our database"); - return; + goto error; } notify(catcher); @@ -493,7 +584,7 @@ sendfile(int fd, char *name, char *mode, int opts) if(file < 0) { errstr(errbuf, sizeof errbuf); nak(fd, 0, errbuf); - return; + goto error; } block = 0; rexmit = Ackok; @@ -519,7 +610,7 @@ sendfile(int fd, char *name, char *mode, int opts) if(n < 0) { errstr(errbuf, sizeof errbuf); nak(fd, 0, errbuf); - return; + goto error; } txtry = 0; } @@ -545,6 +636,8 @@ sendfile(int fd, char *name, char *mode, int opts) if(ret != blksize+Hdrsize && rexmit == Ackok) break; } + syslog(dbg, flog, "tftpd %d done sending file '%s' %s to %s", + pid, name, mode, raddr); error: close(fd); close(file); @@ -581,7 +674,11 @@ recvfile(int fd, char *name, char *mode) name); goto error; } - if(n <= Hdrsize) { + /* + * NB: not `<='; just a header is legal and happens when + * file being read is a multiple of segment-size bytes long. + */ + if(n < Hdrsize) { syslog(dbg, flog, "tftpd: short read from network, reading %s", name); |
