shithub: riscv

Download patch

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);