xref: /plan9/sys/src/cmd/ip/tftpd.c (revision a1216cc64119db675aa140f55fbd73eb2414b763)
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 */
46a4796b5cSDavid du Colombier 
47a4796b5cSDavid du Colombier 	/*
48a4796b5cSDavid du Colombier 	 * bandt (viaduct) tunnels use smaller mtu than ether's
49a4796b5cSDavid du Colombier 	 * (1400 bytes for tcp mss of 1300 bytes).
50a4796b5cSDavid du Colombier 	 */
51a4796b5cSDavid du Colombier 	Bandtmtu	= 1400,
52a4796b5cSDavid du Colombier 	/*
53a4796b5cSDavid du Colombier 	 * maximum size of block's data content, excludes hdrs,
54a4796b5cSDavid du Colombier 	 * notably IP/UDP and TFTP, using worst-case (IPv6) sizes.
55a4796b5cSDavid du Colombier 	 */
56a4796b5cSDavid du Colombier 	Bandtblksz	= Bandtmtu - 40 - 8,
5798bee55eSDavid du Colombier 	Bcavium		= 1432,		/* cavium's u-boot demands this size */
583e5d0078SDavid du Colombier };
593e5d0078SDavid du Colombier 
603e5d0078SDavid du Colombier typedef struct Opt Opt;
613e5d0078SDavid du Colombier struct Opt {
623e5d0078SDavid du Colombier 	char	*name;
633e5d0078SDavid du Colombier 	int	*valp;		/* set to client's value if within bounds */
643e5d0078SDavid du Colombier 	int	min;
653e5d0078SDavid du Colombier 	int	max;
669a747e4fSDavid du Colombier };
679a747e4fSDavid du Colombier 
683e12c5d1SDavid du Colombier int 	dbg;
69219b2ee8SDavid du Colombier int	restricted;
703e5d0078SDavid du Colombier int	pid;
713e5d0078SDavid du Colombier 
723e5d0078SDavid du Colombier /* options */
73a4796b5cSDavid du Colombier int	blksize = Defsegsize;		/* excluding 4-byte header */
743e5d0078SDavid du Colombier int	timeout = 5;			/* seconds */
753e5d0078SDavid du Colombier int	tsize;
763e5d0078SDavid du Colombier static Opt option[] = {
773e5d0078SDavid du Colombier 	"timeout",	&timeout,	1,	255,
78a4796b5cSDavid du Colombier 	/* see "hack" below */
793e5d0078SDavid du Colombier 	"blksize",	&blksize,	8,	Maxsegsize,
803e5d0078SDavid du Colombier 	"tsize",	&tsize,		0,	~0UL >> 1,
813e5d0078SDavid du Colombier };
823e5d0078SDavid du Colombier 
833e5d0078SDavid du Colombier void	sendfile(int, char*, char*, int);
843e12c5d1SDavid du Colombier void	recvfile(int, char*, char*);
853e12c5d1SDavid du Colombier void	nak(int, int, char*);
863e12c5d1SDavid du Colombier void	ack(int, ushort);
873e12c5d1SDavid du Colombier void	clrcon(void);
883e12c5d1SDavid du Colombier void	setuser(void);
893e12c5d1SDavid du Colombier char*	sunkernel(char*);
907dd7cddfSDavid du Colombier void	remoteaddr(char*, char*, int);
917dd7cddfSDavid du Colombier void	doserve(int);
923e12c5d1SDavid du Colombier 
937dd7cddfSDavid du Colombier char	bigbuf[32768];
947dd7cddfSDavid du Colombier char	raddr[64];
953e12c5d1SDavid du Colombier 
963e12c5d1SDavid du Colombier char	*dir = "/lib/tftpd";
97219b2ee8SDavid du Colombier char	*dirsl;
98219b2ee8SDavid du Colombier int	dirsllen;
993e12c5d1SDavid du Colombier char	flog[] = "ipboot";
1009a747e4fSDavid du Colombier char	net[Maxpath];
1013e12c5d1SDavid du Colombier 
1023e5d0078SDavid du Colombier static char *opnames[] = {
1033e5d0078SDavid du Colombier [Tftp_READ]	"read",
1043e5d0078SDavid du Colombier [Tftp_WRITE]	"write",
1053e5d0078SDavid du Colombier [Tftp_DATA]	"data",
1063e5d0078SDavid du Colombier [Tftp_ACK]	"ack",
1073e5d0078SDavid du Colombier [Tftp_ERROR]	"error",
1083e5d0078SDavid du Colombier [Tftp_OACK]	"oack",
1093e12c5d1SDavid du Colombier };
1103e12c5d1SDavid du Colombier 
1113e12c5d1SDavid du Colombier void
usage(void)1127dd7cddfSDavid du Colombier usage(void)
1137dd7cddfSDavid du Colombier {
114c0eadb1cSDavid du Colombier 	fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
115c0eadb1cSDavid du Colombier 		argv0);
1167dd7cddfSDavid du Colombier 	exits("usage");
1177dd7cddfSDavid du Colombier }
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier void
main(int argc,char ** argv)1203e12c5d1SDavid du Colombier main(int argc, char **argv)
1213e12c5d1SDavid du Colombier {
1227dd7cddfSDavid du Colombier 	char buf[64];
1237dd7cddfSDavid du Colombier 	char adir[64], ldir[64];
1247dd7cddfSDavid du Colombier 	int cfd, lcfd, dfd;
125162f803dSDavid du Colombier 	char *svc = "69";
1263e12c5d1SDavid du Colombier 
127162f803dSDavid du Colombier 	setnetmtpt(net, sizeof net, nil);
1283e12c5d1SDavid du Colombier 	ARGBEGIN{
1293e12c5d1SDavid du Colombier 	case 'd':
1303e12c5d1SDavid du Colombier 		dbg++;
1313e12c5d1SDavid du Colombier 		break;
1323e12c5d1SDavid du Colombier 	case 'h':
133162f803dSDavid du Colombier 		dir = EARGF(usage());
1343e12c5d1SDavid du Colombier 		break;
135219b2ee8SDavid du Colombier 	case 'r':
136219b2ee8SDavid du Colombier 		restricted = 1;
137219b2ee8SDavid du Colombier 		break;
138c0eadb1cSDavid du Colombier 	case 's':
139c0eadb1cSDavid du Colombier 		svc = EARGF(usage());
140c0eadb1cSDavid du Colombier 		break;
1417dd7cddfSDavid du Colombier 	case 'x':
142162f803dSDavid du Colombier 		setnetmtpt(net, sizeof net, EARGF(usage()));
1437dd7cddfSDavid du Colombier 		break;
1443e12c5d1SDavid du Colombier 	default:
1457dd7cddfSDavid du Colombier 		usage();
1463e12c5d1SDavid du Colombier 	}ARGEND
1473e12c5d1SDavid du Colombier 
148219b2ee8SDavid du Colombier 	snprint(buf, sizeof buf, "%s/", dir);
149219b2ee8SDavid du Colombier 	dirsl = strdup(buf);
150219b2ee8SDavid du Colombier 	dirsllen = strlen(dirsl);
151219b2ee8SDavid du Colombier 
1529a747e4fSDavid du Colombier 	fmtinstall('E', eipfmt);
1539a747e4fSDavid du Colombier 	fmtinstall('I', eipfmt);
1543e12c5d1SDavid du Colombier 
15564eb6bc1SDavid du Colombier 	/*
15664eb6bc1SDavid du Colombier 	 * setuser calls newns, and typical /lib/namespace files contain
15764eb6bc1SDavid du Colombier 	 * "cd /usr/$user", so call setuser before chdir.
15864eb6bc1SDavid du Colombier 	 */
15964eb6bc1SDavid du Colombier 	setuser();
1603e12c5d1SDavid du Colombier 	if(chdir(dir) < 0)
1617dd7cddfSDavid du Colombier 		sysfatal("can't get to directory %s: %r", dir);
1623e12c5d1SDavid du Colombier 
1637dd7cddfSDavid du Colombier 	if(!dbg)
1643e12c5d1SDavid du Colombier 		switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
1653e12c5d1SDavid du Colombier 		case -1:
1667dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
1673e12c5d1SDavid du Colombier 		case 0:
1683e12c5d1SDavid du Colombier 			break;
1693e12c5d1SDavid du Colombier 		default:
1703e12c5d1SDavid du Colombier 			exits(0);
1713e12c5d1SDavid du Colombier 		}
1723e12c5d1SDavid du Colombier 
173c0eadb1cSDavid du Colombier 	snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
1747dd7cddfSDavid du Colombier 	cfd = announce(buf, adir);
175c0eadb1cSDavid du Colombier 	if (cfd < 0)
176c0eadb1cSDavid du Colombier 		sysfatal("announcing on %s: %r", buf);
177c0eadb1cSDavid du Colombier 	syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
17864eb6bc1SDavid du Colombier //	setuser();
1793e12c5d1SDavid du Colombier 	for(;;) {
1807dd7cddfSDavid du Colombier 		lcfd = listen(adir, ldir);
1817dd7cddfSDavid du Colombier 		if(lcfd < 0)
182c0eadb1cSDavid du Colombier 			sysfatal("listening on %s: %r", adir);
1837dd7cddfSDavid du Colombier 
1847dd7cddfSDavid du Colombier 		switch(fork()) {
1857dd7cddfSDavid du Colombier 		case -1:
1867dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
1877dd7cddfSDavid du Colombier 		case 0:
188c0eadb1cSDavid du Colombier 			dfd = accept(lcfd, ldir);
1897dd7cddfSDavid du Colombier 			if(dfd < 0)
1907dd7cddfSDavid du Colombier  				exits(0);
1917dd7cddfSDavid du Colombier 			remoteaddr(ldir, raddr, sizeof(raddr));
1923e5d0078SDavid du Colombier 			pid = getpid();
1933e5d0078SDavid du Colombier 			syslog(0, flog, "tftp %d connection from %s dir %s",
1943e5d0078SDavid du Colombier 				pid, raddr, ldir);
1957dd7cddfSDavid du Colombier 			doserve(dfd);
1967dd7cddfSDavid du Colombier 			exits("done");
1977dd7cddfSDavid du Colombier 			break;
1987dd7cddfSDavid du Colombier 		default:
1997dd7cddfSDavid du Colombier 			close(lcfd);
2007dd7cddfSDavid du Colombier 			continue;
2017dd7cddfSDavid du Colombier 		}
2027dd7cddfSDavid du Colombier 	}
2037dd7cddfSDavid du Colombier }
2047dd7cddfSDavid du Colombier 
2053e5d0078SDavid du Colombier static Opt *
handleopt(int fd,char * name,char * val)2063e5d0078SDavid du Colombier handleopt(int fd, char *name, char *val)
2073e5d0078SDavid du Colombier {
2083e5d0078SDavid du Colombier 	int n;
2093e5d0078SDavid du Colombier 	Opt *op;
2103e5d0078SDavid du Colombier 
2113e5d0078SDavid du Colombier 	for (op = option; op < option + nelem(option); op++)
2123e5d0078SDavid du Colombier 		if(cistrcmp(name, op->name) == 0) {
2133e5d0078SDavid du Colombier 			n = strtol(val, nil, 10);
2143e5d0078SDavid du Colombier 			if (n < op->min || n > op->max) {
2153e5d0078SDavid du Colombier 				nak(fd, Errbadopt, "option value out of range");
2163e5d0078SDavid du Colombier 				syslog(dbg, flog, "tftp bad option value from "
2173e5d0078SDavid du Colombier 					"client: %s %s", name, val);
2183e5d0078SDavid du Colombier 				sysfatal("bad option value from client: %s %s",
2193e5d0078SDavid du Colombier 					name, val);
2203e5d0078SDavid du Colombier 			}
2213e5d0078SDavid du Colombier 			*op->valp = n;
22265b2e598SDavid du Colombier 			/* incoming 0 for tsize is uninteresting */
22365b2e598SDavid du Colombier 			if(cistrcmp("tsize", op->name) != 0)
22465b2e598SDavid du Colombier 				syslog(dbg, flog, "tftpd %d setting %s to client's %d",
2253e5d0078SDavid du Colombier 					pid, name, n);
2263e5d0078SDavid du Colombier 			return op;
2273e5d0078SDavid du Colombier 		}
2283e5d0078SDavid du Colombier 	return nil;
2293e5d0078SDavid du Colombier }
2303e5d0078SDavid du Colombier 
2313e5d0078SDavid du Colombier static vlong
filesize(char * file)2323e5d0078SDavid du Colombier filesize(char *file)
2333e5d0078SDavid du Colombier {
2343e5d0078SDavid du Colombier 	vlong size;
2353e5d0078SDavid du Colombier 	Dir *dp;
2363e5d0078SDavid du Colombier 
2373e5d0078SDavid du Colombier 	dp = dirstat(file);
2383e5d0078SDavid du Colombier 	if (dp == nil)
23965b2e598SDavid du Colombier 		return -1;
2403e5d0078SDavid du Colombier 	size = dp->length;
2413e5d0078SDavid du Colombier 	free(dp);
2423e5d0078SDavid du Colombier 	return size;
2433e5d0078SDavid du Colombier }
2443e5d0078SDavid du Colombier 
24565b2e598SDavid du Colombier /* copy word into bp iff it fits before ep, returns bytes to advance bp. */
2463e5d0078SDavid du Colombier static int
emits(char * word,char * bp,char * ep)24765b2e598SDavid du Colombier emits(char *word, char *bp, char *ep)
2483e5d0078SDavid du Colombier {
24965b2e598SDavid du Colombier 	int len;
25065b2e598SDavid du Colombier 
25165b2e598SDavid du Colombier 	len = strlen(word) + 1;
25265b2e598SDavid du Colombier 	if (bp + len >= ep)
25365b2e598SDavid du Colombier 		return -1;
25465b2e598SDavid du Colombier 	strcpy(bp, word);
25565b2e598SDavid du Colombier 	return len;
25665b2e598SDavid du Colombier }
25765b2e598SDavid du Colombier 
25865b2e598SDavid du Colombier /* format number into bp iff it fits before ep. */
25965b2e598SDavid du Colombier static int
emitn(vlong n,char * bp,char * ep)26065b2e598SDavid du Colombier emitn(vlong n, char *bp, char *ep)
26165b2e598SDavid du Colombier {
26265b2e598SDavid du Colombier 	char numb[32];
26365b2e598SDavid du Colombier 
26465b2e598SDavid du Colombier 	snprint(numb, sizeof numb, "%lld", n);
26565b2e598SDavid du Colombier 	return emits(numb, bp, ep);
26665b2e598SDavid du Colombier }
26765b2e598SDavid du Colombier 
26865b2e598SDavid du Colombier /*
26965b2e598SDavid du Colombier  * send an OACK packet to respond to options.  bail early with -1 on error.
27065b2e598SDavid du Colombier  * p is the packet containing the options.
27165b2e598SDavid du Colombier  *
27265b2e598SDavid du Colombier  * hack: bandt (viaducts) uses smaller mtu than ether's
27365b2e598SDavid du Colombier  * (1400 bytes for tcp mss of 1300 bytes),
27465b2e598SDavid du Colombier  * so offer at most bandt's mtu minus headers,
27565b2e598SDavid du Colombier  * to avoid failure of pxe booting via viaduct.
27665b2e598SDavid du Colombier  * there's an exception for the cavium's u-boot.
27765b2e598SDavid du Colombier  */
27865b2e598SDavid du Colombier static int
options(int fd,char * buf,int bufsz,char * file,ushort oper,char * p,int dlen)27965b2e598SDavid du Colombier options(int fd, char *buf, int bufsz, char *file, ushort oper, char *p, int dlen)
28065b2e598SDavid du Colombier {
28165b2e598SDavid du Colombier 	int nmlen, vallen, olen, nopts;
2823e5d0078SDavid du Colombier 	vlong size;
28365b2e598SDavid du Colombier 	char *val, *bp, *ep;
2843e5d0078SDavid du Colombier 	Opt *op;
2853e5d0078SDavid du Colombier 
2863e5d0078SDavid du Colombier 	buf[0] = 0;
2873e5d0078SDavid du Colombier 	buf[1] = Tftp_OACK;
2883e5d0078SDavid du Colombier 	bp = buf + Opsize;
28965b2e598SDavid du Colombier 	ep = buf + bufsz;
2903e5d0078SDavid du Colombier 	nopts = 0;
29165b2e598SDavid du Colombier 	for (; dlen > 0 && *p != '\0'; p = val + vallen, bp += olen) {
2923e5d0078SDavid du Colombier 		nmlen = strlen(p) + 1;		/* include NUL */
2933e5d0078SDavid du Colombier 		if (nmlen > dlen)
2943e5d0078SDavid du Colombier 			break;
2953e5d0078SDavid du Colombier 		dlen -= nmlen;
2963e5d0078SDavid du Colombier 		val = p + nmlen;
2973e5d0078SDavid du Colombier 		if (dlen <= 0 || *val == '\0')
2983e5d0078SDavid du Colombier 			break;
2993e5d0078SDavid du Colombier 
3003e5d0078SDavid du Colombier 		vallen = strlen(val) + 1;
3013e5d0078SDavid du Colombier 		if (vallen > dlen)
3023e5d0078SDavid du Colombier 			break;
3033e5d0078SDavid du Colombier 		dlen -= vallen;
3043e5d0078SDavid du Colombier 
3053e5d0078SDavid du Colombier 		nopts++;
30665b2e598SDavid du Colombier 		olen = 0;
3073e5d0078SDavid du Colombier 		op = handleopt(fd, p, val);
30865b2e598SDavid du Colombier 		if (op == nil)
30965b2e598SDavid du Colombier 			continue;
31065b2e598SDavid du Colombier 
3113e5d0078SDavid du Colombier 		/* append OACK response to buf */
31265b2e598SDavid du Colombier 		nmlen = emits(p, bp, ep);	/* option name */
31365b2e598SDavid du Colombier 		if (nmlen < 0)
31465b2e598SDavid du Colombier 			return -1;
3153e5d0078SDavid du Colombier 		bp += nmlen;
31665b2e598SDavid du Colombier 
3173e5d0078SDavid du Colombier 		if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) {
3183e5d0078SDavid du Colombier 			size = filesize(file);
31965b2e598SDavid du Colombier 			if (size == -1) {
32065b2e598SDavid du Colombier 				nak(fd, Errnotfound, "no such file");
32165b2e598SDavid du Colombier 				syslog(dbg, flog, "tftpd tsize for "
32265b2e598SDavid du Colombier 					"non-existent file %s", file);
32365b2e598SDavid du Colombier 				// *op->valp = 0;
32465b2e598SDavid du Colombier 				// olen = emits("0", bp, ep);
32565b2e598SDavid du Colombier 				return -1;
32665b2e598SDavid du Colombier 			}
32765b2e598SDavid du Colombier 			*op->valp = size;
32865b2e598SDavid du Colombier 			olen = emitn(size, bp, ep);
3293e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d %s tsize is %,lld",
3303e5d0078SDavid du Colombier 				pid, file, size);
33165b2e598SDavid du Colombier 		} else if (oper == Tftp_READ && cistrcmp(p, "blksize") == 0 &&
33298bee55eSDavid du Colombier 		    blksize > Bandtblksz && blksize != Bcavium) {
33365b2e598SDavid du Colombier 			*op->valp = blksize = Bandtblksz;
33465b2e598SDavid du Colombier 			olen = emitn(blksize, bp, ep);
33565b2e598SDavid du Colombier 			syslog(dbg, flog, "tftpd %d overriding blksize to %d",
336a4796b5cSDavid du Colombier 				pid, blksize);
3373e5d0078SDavid du Colombier 		} else
33865b2e598SDavid du Colombier 			olen = emits(val, bp, ep);  /* use requested value */
3393e5d0078SDavid du Colombier 	}
3403e5d0078SDavid du Colombier 	if (nopts == 0)
3413e5d0078SDavid du Colombier 		return 0;		/* no options actually seen */
34265b2e598SDavid du Colombier 
3433e5d0078SDavid du Colombier 	if (write(fd, buf, bp - buf) < bp - buf) {
3443e5d0078SDavid du Colombier 		syslog(dbg, flog, "tftpd network write error on oack to %s: %r",
3453e5d0078SDavid du Colombier 			raddr);
3463e5d0078SDavid du Colombier 		sysfatal("tftpd: network write error: %r");
3473e5d0078SDavid du Colombier 	}
3483e5d0078SDavid du Colombier 	if(Debug)
3493e5d0078SDavid du Colombier 		syslog(dbg, flog, "tftpd oack: options to %s", raddr);
3503e5d0078SDavid du Colombier 	return nopts;
3513e5d0078SDavid du Colombier }
3523e5d0078SDavid du Colombier 
3533e5d0078SDavid du Colombier static void
optlog(char * bytes,char * p,int dlen)3543e5d0078SDavid du Colombier optlog(char *bytes, char *p, int dlen)
3553e5d0078SDavid du Colombier {
3563e5d0078SDavid du Colombier 	char *bp;
3573e5d0078SDavid du Colombier 
3583e5d0078SDavid du Colombier 	bp = bytes;
3593e5d0078SDavid du Colombier 	sprint(bp, "tftpd %d option bytes: ", dlen);
3603e5d0078SDavid du Colombier 	bp += strlen(bp);
3613e5d0078SDavid du Colombier 	for (; dlen > 0; dlen--, p++)
3623e5d0078SDavid du Colombier 		*bp++ = *p? *p: ' ';
3633e5d0078SDavid du Colombier 	*bp = '\0';
3643e5d0078SDavid du Colombier 	syslog(dbg, flog, "%s", bytes);
3653e5d0078SDavid du Colombier }
3663e5d0078SDavid du Colombier 
367a20d0e67SDavid du Colombier /*
368a20d0e67SDavid du Colombier  * replace one occurrence of %[ICE] with ip, cfgpxe name, or ether mac, resp.
369a20d0e67SDavid du Colombier  * we can't easily use $ because u-boot has stranger quoting rules than sh.
370a20d0e67SDavid du Colombier  */
371a20d0e67SDavid du Colombier char *
mapname(char * file)372a20d0e67SDavid du Colombier mapname(char *file)
373a20d0e67SDavid du Colombier {
374a20d0e67SDavid du Colombier 	int nf;
375a20d0e67SDavid du Colombier 	char *p, *newnm, *cur, *arpf, *ln, *remip, *bang;
376a20d0e67SDavid du Colombier 	char *fields[4];
377a20d0e67SDavid du Colombier 	Biobuf *arp;
378a20d0e67SDavid du Colombier 
379a20d0e67SDavid du Colombier 	p = strchr(file, '%');
380a20d0e67SDavid du Colombier 	if (p == nil || p[1] == '\0')
381a20d0e67SDavid du Colombier 		return strdup(file);
382a20d0e67SDavid du Colombier 
383a20d0e67SDavid du Colombier 	remip = strdup(raddr);
384a20d0e67SDavid du Colombier 	newnm = mallocz(strlen(file) + Maxpath, 1);
385a20d0e67SDavid du Colombier 	if (remip == nil || newnm == nil)
386a20d0e67SDavid du Colombier 		sysfatal("out of memory");
387a20d0e67SDavid du Colombier 
388a20d0e67SDavid du Colombier 	bang = strchr(remip, '!');
389a20d0e67SDavid du Colombier 	if (bang)
390a20d0e67SDavid du Colombier 		*bang = '\0';			/* remove !port */
391a20d0e67SDavid du Colombier 
392a20d0e67SDavid du Colombier 	memmove(newnm, file, p - file);		/* copy up to % */
393a20d0e67SDavid du Colombier 	cur = newnm + strlen(newnm);
394a20d0e67SDavid du Colombier 	switch(p[1]) {
395a20d0e67SDavid du Colombier 	case 'I':
396a20d0e67SDavid du Colombier 		strcpy(cur, remip);		/* remote's IP */
397a20d0e67SDavid du Colombier 		break;
398a20d0e67SDavid du Colombier 	case 'C':
399a20d0e67SDavid du Colombier 		strcpy(cur, "/cfg/pxe/");
400a20d0e67SDavid du Colombier 		cur += strlen(cur);
401a20d0e67SDavid du Colombier 		/* fall through */
402a20d0e67SDavid du Colombier 	case 'E':
403a20d0e67SDavid du Colombier 		/* look up remote's IP in /net/arp to get mac. */
404a20d0e67SDavid du Colombier 		arpf = smprint("%s/arp", net);
405a20d0e67SDavid du Colombier 		arp = Bopen(arpf, OREAD);
406a20d0e67SDavid du Colombier 		free(arpf);
407a20d0e67SDavid du Colombier 		if (arp == nil)
408a20d0e67SDavid du Colombier 			break;
409a20d0e67SDavid du Colombier 		/* read lines looking for remip in 3rd field of 4 */
410a20d0e67SDavid du Colombier 		while ((ln = Brdline(arp, '\n')) != nil) {
411a20d0e67SDavid du Colombier 			ln[Blinelen(arp)-1] = 0;
412a20d0e67SDavid du Colombier 			nf = tokenize(ln, fields, nelem(fields));
413a20d0e67SDavid du Colombier 			if (nf >= 4 && strcmp(fields[2], remip) == 0) {
414a20d0e67SDavid du Colombier 				strcpy(cur, fields[3]);
415a20d0e67SDavid du Colombier 				break;
416a20d0e67SDavid du Colombier 			}
417a20d0e67SDavid du Colombier 		}
418a20d0e67SDavid du Colombier 		Bterm(arp);
419a20d0e67SDavid du Colombier 		break;
420a20d0e67SDavid du Colombier 	}
421a20d0e67SDavid du Colombier 	strcat(newnm, p + 2);			/* tail following %x */
422a20d0e67SDavid du Colombier 	free(remip);
423a20d0e67SDavid du Colombier 	return newnm;
424a20d0e67SDavid du Colombier }
425a20d0e67SDavid du Colombier 
4267dd7cddfSDavid du Colombier void
doserve(int fd)4277dd7cddfSDavid du Colombier doserve(int fd)
4287dd7cddfSDavid du Colombier {
4293e5d0078SDavid du Colombier 	int dlen, opts;
4303e5d0078SDavid du Colombier 	char *mode, *p, *file;
4317dd7cddfSDavid du Colombier 	short op;
4327dd7cddfSDavid du Colombier 
4333e5d0078SDavid du Colombier 	dlen = read(fd, bigbuf, sizeof(bigbuf)-1);
4343e12c5d1SDavid du Colombier 	if(dlen < 0)
4357dd7cddfSDavid du Colombier 		sysfatal("listen read: %r");
4363e12c5d1SDavid du Colombier 
4373e5d0078SDavid du Colombier 	bigbuf[dlen] = '\0';
4387dd7cddfSDavid du Colombier 	op = (bigbuf[0]<<8) | bigbuf[1];
4393e5d0078SDavid du Colombier 	dlen -= Opsize;
4403e5d0078SDavid du Colombier 	mode = file = bigbuf + Opsize;
4413e12c5d1SDavid du Colombier 	while(*mode != '\0' && dlen--)
4423e12c5d1SDavid du Colombier 		mode++;
4433e12c5d1SDavid du Colombier 	mode++;
4443e12c5d1SDavid du Colombier 	p = mode;
4453e12c5d1SDavid du Colombier 	while(*p && dlen--)
4463e12c5d1SDavid du Colombier 		p++;
447a20d0e67SDavid du Colombier 
448a20d0e67SDavid du Colombier 	file = mapname(file);	/* we don't free the result; minor leak */
449a20d0e67SDavid du Colombier 
4503e12c5d1SDavid du Colombier 	if(dlen == 0) {
4517dd7cddfSDavid du Colombier 		nak(fd, 0, "bad tftpmode");
4527dd7cddfSDavid du Colombier 		close(fd);
4533e5d0078SDavid du Colombier 		syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s",
4543e5d0078SDavid du Colombier 			pid, mode, file, raddr);
4557dd7cddfSDavid du Colombier 		return;
4563e12c5d1SDavid du Colombier 	}
4573e12c5d1SDavid du Colombier 
4583e12c5d1SDavid du Colombier 	if(op != Tftp_READ && op != Tftp_WRITE) {
4593e5d0078SDavid du Colombier 		nak(fd, Errbadop, "Illegal TFTP operation");
4607dd7cddfSDavid du Colombier 		close(fd);
46165b2e598SDavid du Colombier 		syslog(dbg, flog, "tftpd %d bad request %d (%s) %s", pid, op,
46265b2e598SDavid du Colombier 			(op < nelem(opnames)? opnames[op]: "gok"), raddr);
4637dd7cddfSDavid du Colombier 		return;
4643e12c5d1SDavid du Colombier 	}
4657dd7cddfSDavid du Colombier 
466219b2ee8SDavid du Colombier 	if(restricted){
4673e5d0078SDavid du Colombier 		if(file[0] == '#' || strncmp(file, "../", 3) == 0 ||
4683e5d0078SDavid du Colombier 		  strstr(file, "/../") != nil ||
4693e5d0078SDavid du Colombier 		  (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){
4703e5d0078SDavid du Colombier 			nak(fd, Errnoaccess, "Permission denied");
4717dd7cddfSDavid du Colombier 			close(fd);
4723e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d bad request %d from %s file %s",
4733e5d0078SDavid du Colombier 				pid, op, raddr, file);
4747dd7cddfSDavid du Colombier 			return;
475219b2ee8SDavid du Colombier 		}
476219b2ee8SDavid du Colombier 	}
4777dd7cddfSDavid du Colombier 
4783e5d0078SDavid du Colombier 	/*
4793e5d0078SDavid du Colombier 	 * options are supposed to be negotiated, but the cavium board's
4803e5d0078SDavid du Colombier 	 * u-boot really wants us to use a block size of 1432 bytes and won't
4813e5d0078SDavid du Colombier 	 * take `no' for an answer.
4823e5d0078SDavid du Colombier 	 */
4833e5d0078SDavid du Colombier 	p++;				/* skip NUL after mode */
4843e5d0078SDavid du Colombier 	dlen--;
4853e5d0078SDavid du Colombier 	opts = 0;
4863e5d0078SDavid du Colombier 	if(dlen > 0) {			/* might have options */
4873e5d0078SDavid du Colombier 		char bytes[32*1024];
4883e5d0078SDavid du Colombier 
4893e5d0078SDavid du Colombier 		if(Debug)
4903e5d0078SDavid du Colombier 			optlog(bytes, p, dlen);
49165b2e598SDavid du Colombier 		opts = options(fd, bytes, sizeof bytes, file, op, p, dlen);
49265b2e598SDavid du Colombier 		if (opts < 0)
49365b2e598SDavid du Colombier 			return;
4943e5d0078SDavid du Colombier 	}
4953e12c5d1SDavid du Colombier 	if(op == Tftp_READ)
4963e5d0078SDavid du Colombier 		sendfile(fd, file, mode, opts);
4973e12c5d1SDavid du Colombier 	else
4983e5d0078SDavid du Colombier 		recvfile(fd, file, mode);
4993e12c5d1SDavid du Colombier }
5003e12c5d1SDavid du Colombier 
5013e12c5d1SDavid du Colombier void
catcher(void * junk,char * msg)5023e12c5d1SDavid du Colombier catcher(void *junk, char *msg)
5033e12c5d1SDavid du Colombier {
5043e12c5d1SDavid du Colombier 	USED(junk);
5053e12c5d1SDavid du Colombier 
5063e12c5d1SDavid du Colombier 	if(strncmp(msg, "exit", 4) == 0)
5073e12c5d1SDavid du Colombier 		noted(NDFLT);
5083e12c5d1SDavid du Colombier 	noted(NCONT);
5093e12c5d1SDavid du Colombier }
5103e12c5d1SDavid du Colombier 
5113e5d0078SDavid du Colombier static int
awaitack(int net,int block)512f15ef940SDavid du Colombier awaitack(int net, int block)
5133e12c5d1SDavid du Colombier {
5143e5d0078SDavid du Colombier 	int ackblock, al, rxl;
5153e5d0078SDavid du Colombier 	ushort op;
5163e12c5d1SDavid du Colombier 	uchar ack[1024];
5173e12c5d1SDavid du Colombier 
5183e5d0078SDavid du Colombier 	for(rxl = 0; rxl < 10; rxl++) {
5193e5d0078SDavid du Colombier 		memset(ack, 0, Hdrsize);
5203e5d0078SDavid du Colombier 		alarm(1000);
521f15ef940SDavid du Colombier 		al = read(net, ack, sizeof(ack));
5223e5d0078SDavid du Colombier 		alarm(0);
5233e5d0078SDavid du Colombier 		if(al < 0) {
5243e5d0078SDavid du Colombier 			if (Debug)
5253e5d0078SDavid du Colombier 				syslog(dbg, flog, "tftpd %d timed out "
5263e5d0078SDavid du Colombier 					"waiting for ack from %s", pid, raddr);
5273e5d0078SDavid du Colombier 			return Ackrexmit;
5283e5d0078SDavid du Colombier 		}
5293e5d0078SDavid du Colombier 		op = ack[0]<<8|ack[1];
5303e5d0078SDavid du Colombier 		if(op == Tftp_ERROR) {
5313e5d0078SDavid du Colombier 			if (Debug)
5323e5d0078SDavid du Colombier 				syslog(dbg, flog, "tftpd %d got error "
5333e5d0078SDavid du Colombier 					"waiting for ack from %s", pid, raddr);
5343e5d0078SDavid du Colombier 			return Ackerr;
5353e5d0078SDavid du Colombier 		} else if(op != Tftp_ACK) {
5363e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid,
5373e5d0078SDavid du Colombier 				(op < nelem(opnames)? opnames[op]: "gok"),
5383e5d0078SDavid du Colombier 				raddr);
5393e5d0078SDavid du Colombier 			return Ackerr;
5403e5d0078SDavid du Colombier 		}
5413e5d0078SDavid du Colombier 		ackblock = ack[2]<<8|ack[3];
5423e5d0078SDavid du Colombier 		if (Debug)
5433e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d read ack of %d bytes "
5443e5d0078SDavid du Colombier 				"for block %d", pid, al, ackblock);
5453e5d0078SDavid du Colombier 		if(ackblock == block)
5463e5d0078SDavid du Colombier 			return Ackok;		/* for block just sent */
5473e5d0078SDavid du Colombier 		else if(ackblock == block + 1)	/* intel pxe eof bug */
5483e5d0078SDavid du Colombier 			return Ackok;
5493e5d0078SDavid du Colombier 		else if(ackblock == 0xffff)
5503e5d0078SDavid du Colombier 			return Ackrexmit;
5513e5d0078SDavid du Colombier 		else
5523e5d0078SDavid du Colombier 			/* ack is for some other block; ignore it, try again */
5533e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d expected ack for block %d, "
5543e5d0078SDavid du Colombier 				"got %d", pid, block, ackblock);
5553e5d0078SDavid du Colombier 	}
5563e5d0078SDavid du Colombier 	return Ackrexmit;
5573e5d0078SDavid du Colombier }
5583e5d0078SDavid du Colombier 
5593e5d0078SDavid du Colombier void
sendfile(int net,char * name,char * mode,int opts)560f15ef940SDavid du Colombier sendfile(int net, char *name, char *mode, int opts)
5613e5d0078SDavid du Colombier {
562f15ef940SDavid du Colombier 	int file, block, ret, rexmit, n, txtry, failed;
5633e5d0078SDavid du Colombier 	uchar buf[Maxsegsize+Hdrsize];
5643e5d0078SDavid du Colombier 	char errbuf[Maxerr];
5653e5d0078SDavid du Colombier 
56622c8337bSDavid du Colombier 	file = -1;
567f15ef940SDavid du Colombier 	failed = 1;
5683e5d0078SDavid du Colombier 	syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
5693e5d0078SDavid du Colombier 		pid, name, mode, raddr);
5703e12c5d1SDavid du Colombier 	name = sunkernel(name);
5713e12c5d1SDavid du Colombier 	if(name == 0){
572f15ef940SDavid du Colombier 		nak(net, 0, "not in our database");
57322c8337bSDavid du Colombier 		goto error;
5743e12c5d1SDavid du Colombier 	}
5753e12c5d1SDavid du Colombier 
5763e12c5d1SDavid du Colombier 	notify(catcher);
5773e12c5d1SDavid du Colombier 
5783e12c5d1SDavid du Colombier 	file = open(name, OREAD);
5793e12c5d1SDavid du Colombier 	if(file < 0) {
5809a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
581f15ef940SDavid du Colombier 		nak(net, 0, errbuf);
58222c8337bSDavid du Colombier 		goto error;
5833e12c5d1SDavid du Colombier 	}
5843e12c5d1SDavid du Colombier 	block = 0;
5853e5d0078SDavid du Colombier 	rexmit = Ackok;
5863e12c5d1SDavid du Colombier 	n = 0;
5873e5d0078SDavid du Colombier 	/*
5883e5d0078SDavid du Colombier 	 * if we sent an oack previously, wait for the client's ack or error.
5893e5d0078SDavid du Colombier 	 * if we get no ack for our oack, it could be that we returned
5903e5d0078SDavid du Colombier 	 * a tsize that the client can't handle, or it could be intel
5913e5d0078SDavid du Colombier 	 * pxe just read-with-tsize to get size, couldn't be bothered to
5923e5d0078SDavid du Colombier 	 * ack our oack and has just gone ahead and issued another read.
5933e5d0078SDavid du Colombier 	 */
594f15ef940SDavid du Colombier 	if(opts && awaitack(net, 0) != Ackok)
5953e5d0078SDavid du Colombier 		goto error;
5963e5d0078SDavid du Colombier 
5973e5d0078SDavid du Colombier 	for(txtry = 0; txtry < timeout;) {
5983e5d0078SDavid du Colombier 		if(rexmit == Ackok) {
599*a1216cc6SDavid du Colombier 			/* block number wraparound for enormous hogs */
600*a1216cc6SDavid du Colombier 			if (block >= 65536)
601*a1216cc6SDavid du Colombier 				block = 0;
6023e12c5d1SDavid du Colombier 			block++;
6033e12c5d1SDavid du Colombier 			buf[0] = 0;
6043e12c5d1SDavid du Colombier 			buf[1] = Tftp_DATA;
6053e12c5d1SDavid du Colombier 			buf[2] = block>>8;
6063e12c5d1SDavid du Colombier 			buf[3] = block;
6073e5d0078SDavid du Colombier 			n = read(file, buf+Hdrsize, blksize);
6083e12c5d1SDavid du Colombier 			if(n < 0) {
6099a747e4fSDavid du Colombier 				errstr(errbuf, sizeof errbuf);
610f15ef940SDavid du Colombier 				nak(net, 0, errbuf);
61122c8337bSDavid du Colombier 				goto error;
6123e12c5d1SDavid du Colombier 			}
613219b2ee8SDavid du Colombier 			txtry = 0;
6143e12c5d1SDavid du Colombier 		}
6157dd7cddfSDavid du Colombier 		else {
6163e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
6173e5d0078SDavid du Colombier 				pid, Hdrsize+n, name, block, raddr);
6183e12c5d1SDavid du Colombier 			txtry++;
6197dd7cddfSDavid du Colombier 		}
6207dd7cddfSDavid du Colombier 
621f15ef940SDavid du Colombier 		ret = write(net, buf, Hdrsize+n);
6223e5d0078SDavid du Colombier 		if(ret < Hdrsize+n) {
6233e5d0078SDavid du Colombier 			syslog(dbg, flog,
6243e5d0078SDavid du Colombier 				"tftpd network write error on %s to %s: %r",
6253e5d0078SDavid du Colombier 				name, raddr);
6267dd7cddfSDavid du Colombier 			sysfatal("tftpd: network write error: %r");
6273e5d0078SDavid du Colombier 		}
6283e5d0078SDavid du Colombier 		if (Debug)
6293e5d0078SDavid du Colombier 			syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
6303e12c5d1SDavid du Colombier 
631f15ef940SDavid du Colombier 		rexmit = awaitack(net, block);
6323e5d0078SDavid du Colombier 		if (rexmit == Ackerr)
6333e12c5d1SDavid du Colombier 			break;
634f15ef940SDavid du Colombier 		if(ret != blksize+Hdrsize && rexmit == Ackok) {
635f15ef940SDavid du Colombier 			failed = 0;
6363e12c5d1SDavid du Colombier 			break;
6373e12c5d1SDavid du Colombier 		}
638f15ef940SDavid du Colombier 	}
6393e12c5d1SDavid du Colombier error:
640f15ef940SDavid du Colombier 	syslog(dbg, flog, "tftpd %d %s file '%s' %s to %s",
641f15ef940SDavid du Colombier 		pid, (failed? "failed to send": "sent"), name, mode, raddr);
642f15ef940SDavid du Colombier 	close(net);
6433e12c5d1SDavid du Colombier 	close(file);
6443e12c5d1SDavid du Colombier }
6453e12c5d1SDavid du Colombier 
6463e12c5d1SDavid du Colombier void
recvfile(int net,char * name,char * mode)647f15ef940SDavid du Colombier recvfile(int net, char *name, char *mode)
6483e12c5d1SDavid du Colombier {
6493e12c5d1SDavid du Colombier 	ushort op, block, inblock;
6503e5d0078SDavid du Colombier 	uchar buf[Maxsegsize+8];
6519a747e4fSDavid du Colombier 	char errbuf[Maxerr];
6523e12c5d1SDavid du Colombier 	int n, ret, file;
6533e12c5d1SDavid du Colombier 
6543e12c5d1SDavid du Colombier 	syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
6553e12c5d1SDavid du Colombier 
6563e12c5d1SDavid du Colombier 	file = create(name, OWRITE, 0666);
6573e12c5d1SDavid du Colombier 	if(file < 0) {
6589a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
659f15ef940SDavid du Colombier 		nak(net, 0, errbuf);
660162f803dSDavid du Colombier 		syslog(dbg, flog, "can't create %s: %r", name);
6613e12c5d1SDavid du Colombier 		return;
6623e12c5d1SDavid du Colombier 	}
6633e12c5d1SDavid du Colombier 
6643e12c5d1SDavid du Colombier 	block = 0;
665f15ef940SDavid du Colombier 	ack(net, block);
6663e12c5d1SDavid du Colombier 	block++;
6673e12c5d1SDavid du Colombier 
6683e12c5d1SDavid du Colombier 	for (;;) {
6693e12c5d1SDavid du Colombier 		alarm(15000);
670f15ef940SDavid du Colombier 		n = read(net, buf, blksize+8);
6713e12c5d1SDavid du Colombier 		alarm(0);
672162f803dSDavid du Colombier 		if(n < 0) {
673162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: network error reading %s: %r",
674162f803dSDavid du Colombier 				name);
6753e12c5d1SDavid du Colombier 			goto error;
676162f803dSDavid du Colombier 		}
67727308f26SDavid du Colombier 		/*
67827308f26SDavid du Colombier 		 * NB: not `<='; just a header is legal and happens when
67927308f26SDavid du Colombier 		 * file being read is a multiple of segment-size bytes long.
68027308f26SDavid du Colombier 		 */
68127308f26SDavid du Colombier 		if(n < Hdrsize) {
682162f803dSDavid du Colombier 			syslog(dbg, flog,
683162f803dSDavid du Colombier 				"tftpd: short read from network, reading %s",
684162f803dSDavid du Colombier 				name);
685162f803dSDavid du Colombier 			goto error;
686162f803dSDavid du Colombier 		}
6873e12c5d1SDavid du Colombier 		op = buf[0]<<8|buf[1];
688162f803dSDavid du Colombier 		if(op == Tftp_ERROR) {
689162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: tftp error reading %s", name);
6903e12c5d1SDavid du Colombier 			goto error;
691162f803dSDavid du Colombier 		}
6923e12c5d1SDavid du Colombier 
693162f803dSDavid du Colombier 		n -= Hdrsize;
6943e12c5d1SDavid du Colombier 		inblock = buf[2]<<8|buf[3];
6953e12c5d1SDavid du Colombier 		if(op == Tftp_DATA) {
6963e12c5d1SDavid du Colombier 			if(inblock == block) {
697162f803dSDavid du Colombier 				ret = write(file, buf+Hdrsize, n);
698162f803dSDavid du Colombier 				if(ret != n) {
6999a747e4fSDavid du Colombier 					errstr(errbuf, sizeof errbuf);
700f15ef940SDavid du Colombier 					nak(net, 0, errbuf);
701162f803dSDavid du Colombier 					syslog(dbg, flog,
702162f803dSDavid du Colombier 					    "tftpd: error writing %s: %s",
703162f803dSDavid du Colombier 						name, errbuf);
7043e12c5d1SDavid du Colombier 					goto error;
7053e12c5d1SDavid du Colombier 				}
706f15ef940SDavid du Colombier 				ack(net, block);
7073e12c5d1SDavid du Colombier 				block++;
70878307a5fSDavid du Colombier 			} else
709f15ef940SDavid du Colombier 				ack(net, 0xffff);	/* tell him to resend */
7103e12c5d1SDavid du Colombier 		}
7113e12c5d1SDavid du Colombier 	}
7123e12c5d1SDavid du Colombier error:
7133e12c5d1SDavid du Colombier 	close(file);
7143e12c5d1SDavid du Colombier }
7153e12c5d1SDavid du Colombier 
7163e12c5d1SDavid du Colombier void
ack(int fd,ushort block)7173e12c5d1SDavid du Colombier ack(int fd, ushort block)
7183e12c5d1SDavid du Colombier {
7193e12c5d1SDavid du Colombier 	uchar ack[4];
7203e12c5d1SDavid du Colombier 	int n;
7213e12c5d1SDavid du Colombier 
7223e12c5d1SDavid du Colombier 	ack[0] = 0;
7233e12c5d1SDavid du Colombier 	ack[1] = Tftp_ACK;
7243e12c5d1SDavid du Colombier 	ack[2] = block>>8;
7253e12c5d1SDavid du Colombier 	ack[3] = block;
7263e12c5d1SDavid du Colombier 
7273e12c5d1SDavid du Colombier 	n = write(fd, ack, 4);
728162f803dSDavid du Colombier 	if(n < 4)
7297dd7cddfSDavid du Colombier 		sysfatal("network write: %r");
7303e12c5d1SDavid du Colombier }
7313e12c5d1SDavid du Colombier 
7323e12c5d1SDavid du Colombier void
nak(int fd,int code,char * msg)7333e12c5d1SDavid du Colombier nak(int fd, int code, char *msg)
7343e12c5d1SDavid du Colombier {
7353e12c5d1SDavid du Colombier 	char buf[128];
7363e12c5d1SDavid du Colombier 	int n;
7373e12c5d1SDavid du Colombier 
7383e12c5d1SDavid du Colombier 	buf[0] = 0;
7393e12c5d1SDavid du Colombier 	buf[1] = Tftp_ERROR;
7403e12c5d1SDavid du Colombier 	buf[2] = 0;
7413e12c5d1SDavid du Colombier 	buf[3] = code;
7423e12c5d1SDavid du Colombier 	strcpy(buf+4, msg);
7433e12c5d1SDavid du Colombier 	n = strlen(msg) + 4 + 1;
7443e5d0078SDavid du Colombier 	if(write(fd, buf, n) < n)
7457dd7cddfSDavid du Colombier 		sysfatal("write nak: %r");
7463e12c5d1SDavid du Colombier }
7473e12c5d1SDavid du Colombier 
7483e12c5d1SDavid du Colombier void
setuser(void)7493e12c5d1SDavid du Colombier setuser(void)
7503e12c5d1SDavid du Colombier {
751c0eadb1cSDavid du Colombier 	int fd;
7523e12c5d1SDavid du Colombier 
753c0eadb1cSDavid du Colombier 	fd = open("#c/user", OWRITE);
754c0eadb1cSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
755c0eadb1cSDavid du Colombier 		sysfatal("can't become none: %r");
756c0eadb1cSDavid du Colombier 	close(fd);
757c0eadb1cSDavid du Colombier 	if(newns("none", nil) < 0)
758c0eadb1cSDavid du Colombier 		sysfatal("can't build namespace: %r");
7593e12c5d1SDavid du Colombier }
7603e12c5d1SDavid du Colombier 
7617dd7cddfSDavid du Colombier char*
lookup(char * sattr,char * sval,char * tattr,char * tval,int len)762da51d93aSDavid du Colombier lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
7637dd7cddfSDavid du Colombier {
7647dd7cddfSDavid du Colombier 	static Ndb *db;
7657dd7cddfSDavid du Colombier 	char *attrs[1];
7667dd7cddfSDavid du Colombier 	Ndbtuple *t;
7677dd7cddfSDavid du Colombier 
7687dd7cddfSDavid du Colombier 	if(db == nil)
7697dd7cddfSDavid du Colombier 		db = ndbopen(0);
7707dd7cddfSDavid du Colombier 	if(db == nil)
7717dd7cddfSDavid du Colombier 		return nil;
7727dd7cddfSDavid du Colombier 
7737dd7cddfSDavid du Colombier 	if(sattr == nil)
7747dd7cddfSDavid du Colombier 		sattr = ipattr(sval);
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier 	attrs[0] = tattr;
7777dd7cddfSDavid du Colombier 	t = ndbipinfo(db, sattr, sval, attrs, 1);
7787dd7cddfSDavid du Colombier 	if(t == nil)
7797dd7cddfSDavid du Colombier 		return nil;
780da51d93aSDavid du Colombier 	strncpy(tval, t->val, len);
781da51d93aSDavid du Colombier 	tval[len-1] = 0;
7827dd7cddfSDavid du Colombier 	ndbfree(t);
7837dd7cddfSDavid du Colombier 	return tval;
7847dd7cddfSDavid du Colombier }
7857dd7cddfSDavid du Colombier 
7863e12c5d1SDavid du Colombier /*
7873e12c5d1SDavid du Colombier  *  for sun kernel boots, replace the requested file name with
7883e12c5d1SDavid du Colombier  *  a one from our database.  If the database doesn't specify a file,
7893e12c5d1SDavid du Colombier  *  don't answer.
7903e12c5d1SDavid du Colombier  */
7913e12c5d1SDavid du Colombier char*
sunkernel(char * name)7923e12c5d1SDavid du Colombier sunkernel(char *name)
7933e12c5d1SDavid du Colombier {
7943e12c5d1SDavid du Colombier 	ulong addr;
7957dd7cddfSDavid du Colombier 	uchar v4[IPv4addrlen];
7967dd7cddfSDavid du Colombier 	uchar v6[IPaddrlen];
797da51d93aSDavid du Colombier 	char buf[256];
798da51d93aSDavid du Colombier 	char ipbuf[128];
79927e10919SDavid du Colombier 	char *suffix;
8003e12c5d1SDavid du Colombier 
80127e10919SDavid du Colombier 	addr = strtoul(name, &suffix, 16);
80227e10919SDavid du Colombier 	if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
8033e12c5d1SDavid du Colombier 		return name;
8043e12c5d1SDavid du Colombier 
8057dd7cddfSDavid du Colombier 	v4[0] = addr>>24;
8067dd7cddfSDavid du Colombier 	v4[1] = addr>>16;
8077dd7cddfSDavid du Colombier 	v4[2] = addr>>8;
8087dd7cddfSDavid du Colombier 	v4[3] = addr;
8097dd7cddfSDavid du Colombier 	v4tov6(v6, v4);
8107dd7cddfSDavid du Colombier 	sprint(ipbuf, "%I", v6);
811da51d93aSDavid du Colombier 	return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
8127dd7cddfSDavid du Colombier }
8137dd7cddfSDavid du Colombier 
8147dd7cddfSDavid du Colombier void
remoteaddr(char * dir,char * raddr,int len)8157dd7cddfSDavid du Colombier remoteaddr(char *dir, char *raddr, int len)
8167dd7cddfSDavid du Colombier {
8177dd7cddfSDavid du Colombier 	char buf[64];
8187dd7cddfSDavid du Colombier 	int fd, n;
8197dd7cddfSDavid du Colombier 
8207dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/remote", dir);
8217dd7cddfSDavid du Colombier 	fd = open(buf, OREAD);
8227dd7cddfSDavid du Colombier 	if(fd < 0){
8237dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
8247dd7cddfSDavid du Colombier 		return;
8257dd7cddfSDavid du Colombier 	}
8267dd7cddfSDavid du Colombier 	n = read(fd, raddr, len-1);
8277dd7cddfSDavid du Colombier 	close(fd);
8287dd7cddfSDavid du Colombier 	if(n <= 0){
8297dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
8307dd7cddfSDavid du Colombier 		return;
8317dd7cddfSDavid du Colombier 	}
8327dd7cddfSDavid du Colombier 	if(n > 0)
8337dd7cddfSDavid du Colombier 		n--;
8347dd7cddfSDavid du Colombier 	raddr[n] = 0;
8353e12c5d1SDavid du Colombier }
836