ref: 731aeeedb377c86eea8b0263b72190924fd90887
parent: 0d926a251269277214ab0ce5e84f5c4674576d6f
author: cinap_lenrek <[email protected]>
date: Fri Mar 9 01:06:33 EST 2012
tftpd: import from sources
--- a/sys/src/cmd/ip/tftpd.c
+++ b/sys/src/cmd/ip/tftpd.c
@@ -54,6 +54,7 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if(file < 0) {
errstr(errbuf, sizeof errbuf);
nak(fd, 0, errbuf);
- return;
+ goto error;
}
block = 0;
rexmit = Ackok;
@@ -519,7 +610,7 @@
if(n < 0) {
errstr(errbuf, sizeof errbuf);
nak(fd, 0, errbuf);
- return;
+ goto error;
}
txtry = 0;
}
@@ -545,6 +636,8 @@
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 @@
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);