xref: /plan9/sys/src/cmd/ip/tftpd.c (revision 64eb6bc1458d88e1f6d7adf0ceec4bc93b60c1e7)
1162f803dSDavid du Colombier /*
2162f803dSDavid du Colombier  * tftpd - tftp service, see /lib/rfc/rfc783
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,
159a747e4fSDavid du Colombier };
169a747e4fSDavid du Colombier 
173e12c5d1SDavid du Colombier int 	dbg;
18219b2ee8SDavid du Colombier int	restricted;
193e12c5d1SDavid du Colombier void	sendfile(int, char*, char*);
203e12c5d1SDavid du Colombier void	recvfile(int, char*, char*);
213e12c5d1SDavid du Colombier void	nak(int, int, char*);
223e12c5d1SDavid du Colombier void	ack(int, ushort);
233e12c5d1SDavid du Colombier void	clrcon(void);
243e12c5d1SDavid du Colombier void	setuser(void);
253e12c5d1SDavid du Colombier char*	sunkernel(char*);
267dd7cddfSDavid du Colombier void	remoteaddr(char*, char*, int);
277dd7cddfSDavid du Colombier void	doserve(int);
283e12c5d1SDavid du Colombier 
297dd7cddfSDavid du Colombier char	bigbuf[32768];
307dd7cddfSDavid du Colombier char	raddr[64];
313e12c5d1SDavid du Colombier 
323e12c5d1SDavid du Colombier char	*dir = "/lib/tftpd";
33219b2ee8SDavid du Colombier char	*dirsl;
34219b2ee8SDavid du Colombier int	dirsllen;
353e12c5d1SDavid du Colombier char	flog[] = "ipboot";
369a747e4fSDavid du Colombier char	net[Maxpath];
373e12c5d1SDavid du Colombier 
383e12c5d1SDavid du Colombier enum
393e12c5d1SDavid du Colombier {
403e12c5d1SDavid du Colombier 	Tftp_READ	= 1,
413e12c5d1SDavid du Colombier 	Tftp_WRITE	= 2,
423e12c5d1SDavid du Colombier 	Tftp_DATA	= 3,
433e12c5d1SDavid du Colombier 	Tftp_ACK	= 4,
443e12c5d1SDavid du Colombier 	Tftp_ERROR	= 5,
453e12c5d1SDavid du Colombier 	Segsize		= 512,
463e12c5d1SDavid du Colombier };
473e12c5d1SDavid du Colombier 
483e12c5d1SDavid du Colombier void
497dd7cddfSDavid du Colombier usage(void)
507dd7cddfSDavid du Colombier {
51c0eadb1cSDavid du Colombier 	fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
52c0eadb1cSDavid du Colombier 		argv0);
537dd7cddfSDavid du Colombier 	exits("usage");
547dd7cddfSDavid du Colombier }
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier void
573e12c5d1SDavid du Colombier main(int argc, char **argv)
583e12c5d1SDavid du Colombier {
597dd7cddfSDavid du Colombier 	char buf[64];
607dd7cddfSDavid du Colombier 	char adir[64], ldir[64];
617dd7cddfSDavid du Colombier 	int cfd, lcfd, dfd;
62162f803dSDavid du Colombier 	char *svc = "69";
633e12c5d1SDavid du Colombier 
64162f803dSDavid du Colombier 	setnetmtpt(net, sizeof net, nil);
653e12c5d1SDavid du Colombier 	ARGBEGIN{
663e12c5d1SDavid du Colombier 	case 'd':
673e12c5d1SDavid du Colombier 		dbg++;
683e12c5d1SDavid du Colombier 		break;
693e12c5d1SDavid du Colombier 	case 'h':
70162f803dSDavid du Colombier 		dir = EARGF(usage());
713e12c5d1SDavid du Colombier 		break;
72219b2ee8SDavid du Colombier 	case 'r':
73219b2ee8SDavid du Colombier 		restricted = 1;
74219b2ee8SDavid du Colombier 		break;
75c0eadb1cSDavid du Colombier 	case 's':
76c0eadb1cSDavid du Colombier 		svc = EARGF(usage());
77c0eadb1cSDavid du Colombier 		break;
787dd7cddfSDavid du Colombier 	case 'x':
79162f803dSDavid du Colombier 		setnetmtpt(net, sizeof net, EARGF(usage()));
807dd7cddfSDavid du Colombier 		break;
813e12c5d1SDavid du Colombier 	default:
827dd7cddfSDavid du Colombier 		usage();
833e12c5d1SDavid du Colombier 	}ARGEND
843e12c5d1SDavid du Colombier 
85219b2ee8SDavid du Colombier 	snprint(buf, sizeof buf, "%s/", dir);
86219b2ee8SDavid du Colombier 	dirsl = strdup(buf);
87219b2ee8SDavid du Colombier 	dirsllen = strlen(dirsl);
88219b2ee8SDavid du Colombier 
899a747e4fSDavid du Colombier 	fmtinstall('E', eipfmt);
909a747e4fSDavid du Colombier 	fmtinstall('I', eipfmt);
913e12c5d1SDavid du Colombier 
92*64eb6bc1SDavid du Colombier 	/*
93*64eb6bc1SDavid du Colombier 	 * setuser calls newns, and typical /lib/namespace files contain
94*64eb6bc1SDavid du Colombier 	 * "cd /usr/$user", so call setuser before chdir.
95*64eb6bc1SDavid du Colombier 	 */
96*64eb6bc1SDavid du Colombier 	setuser();
973e12c5d1SDavid du Colombier 	if(chdir(dir) < 0)
987dd7cddfSDavid du Colombier 		sysfatal("can't get to directory %s: %r", dir);
993e12c5d1SDavid du Colombier 
1007dd7cddfSDavid du Colombier 	if(!dbg)
1013e12c5d1SDavid du Colombier 		switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
1023e12c5d1SDavid du Colombier 		case -1:
1037dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
1043e12c5d1SDavid du Colombier 		case 0:
1053e12c5d1SDavid du Colombier 			break;
1063e12c5d1SDavid du Colombier 		default:
1073e12c5d1SDavid du Colombier 			exits(0);
1083e12c5d1SDavid du Colombier 		}
1093e12c5d1SDavid du Colombier 
110c0eadb1cSDavid du Colombier 	snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
1117dd7cddfSDavid du Colombier 	cfd = announce(buf, adir);
112c0eadb1cSDavid du Colombier 	if (cfd < 0)
113c0eadb1cSDavid du Colombier 		sysfatal("announcing on %s: %r", buf);
114c0eadb1cSDavid du Colombier 	syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
115*64eb6bc1SDavid du Colombier //	setuser();
1163e12c5d1SDavid du Colombier 	for(;;) {
1177dd7cddfSDavid du Colombier 		lcfd = listen(adir, ldir);
1187dd7cddfSDavid du Colombier 		if(lcfd < 0)
119c0eadb1cSDavid du Colombier 			sysfatal("listening on %s: %r", adir);
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 		switch(fork()) {
1227dd7cddfSDavid du Colombier 		case -1:
1237dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
1247dd7cddfSDavid du Colombier 		case 0:
125c0eadb1cSDavid du Colombier 			dfd = accept(lcfd, ldir);
1267dd7cddfSDavid du Colombier 			if(dfd < 0)
1277dd7cddfSDavid du Colombier  				exits(0);
1287dd7cddfSDavid du Colombier 			remoteaddr(ldir, raddr, sizeof(raddr));
129c0eadb1cSDavid du Colombier 			syslog(0, flog, "tftp connection from %s dir %s",
130c0eadb1cSDavid du Colombier 				raddr, ldir);
1317dd7cddfSDavid du Colombier 			doserve(dfd);
1327dd7cddfSDavid du Colombier 			exits("done");
1337dd7cddfSDavid du Colombier 			break;
1347dd7cddfSDavid du Colombier 		default:
1357dd7cddfSDavid du Colombier 			close(lcfd);
1367dd7cddfSDavid du Colombier 			continue;
1377dd7cddfSDavid du Colombier 		}
1387dd7cddfSDavid du Colombier 	}
1397dd7cddfSDavid du Colombier }
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier void
1427dd7cddfSDavid du Colombier doserve(int fd)
1437dd7cddfSDavid du Colombier {
1447dd7cddfSDavid du Colombier 	int dlen;
1457dd7cddfSDavid du Colombier 	char *mode, *p;
1467dd7cddfSDavid du Colombier 	short op;
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier 	dlen = read(fd, bigbuf, sizeof(bigbuf));
1493e12c5d1SDavid du Colombier 	if(dlen < 0)
1507dd7cddfSDavid du Colombier 		sysfatal("listen read: %r");
1513e12c5d1SDavid du Colombier 
1527dd7cddfSDavid du Colombier 	op = (bigbuf[0]<<8) | bigbuf[1];
1533e12c5d1SDavid du Colombier 	dlen -= 2;
1547dd7cddfSDavid du Colombier 	mode = bigbuf+2;
1553e12c5d1SDavid du Colombier 	while(*mode != '\0' && dlen--)
1563e12c5d1SDavid du Colombier 		mode++;
1573e12c5d1SDavid du Colombier 	mode++;
1583e12c5d1SDavid du Colombier 	p = mode;
1593e12c5d1SDavid du Colombier 	while(*p && dlen--)
1603e12c5d1SDavid du Colombier 		p++;
1613e12c5d1SDavid du Colombier 	if(dlen == 0) {
1627dd7cddfSDavid du Colombier 		nak(fd, 0, "bad tftpmode");
1637dd7cddfSDavid du Colombier 		close(fd);
164c0eadb1cSDavid du Colombier 		syslog(dbg, flog, "bad mode from %s", raddr);
1657dd7cddfSDavid du Colombier 		return;
1663e12c5d1SDavid du Colombier 	}
1673e12c5d1SDavid du Colombier 
1683e12c5d1SDavid du Colombier 	if(op != Tftp_READ && op != Tftp_WRITE) {
1697dd7cddfSDavid du Colombier 		nak(fd, 4, "Illegal TFTP operation");
1707dd7cddfSDavid du Colombier 		close(fd);
1713e12c5d1SDavid du Colombier 		syslog(dbg, flog, "bad request %d %s", op, raddr);
1727dd7cddfSDavid du Colombier 		return;
1733e12c5d1SDavid du Colombier 	}
1747dd7cddfSDavid du Colombier 
175219b2ee8SDavid du Colombier 	if(restricted){
17667031067SDavid du Colombier 		if(bigbuf[2] == '#' ||
17767031067SDavid du Colombier 		  strncmp(bigbuf+2, "../", 3)==0 || strstr(bigbuf+2, "/../") ||
1787dd7cddfSDavid du Colombier 		  (bigbuf[2] == '/' && strncmp(bigbuf+2, dirsl, dirsllen)!=0)){
1797dd7cddfSDavid du Colombier 			nak(fd, 4, "Permission denied");
1807dd7cddfSDavid du Colombier 			close(fd);
18139734e7eSDavid du Colombier 			syslog(dbg, flog, "bad request %d from %s file %s", op, raddr, bigbuf+2);
1827dd7cddfSDavid du Colombier 			return;
183219b2ee8SDavid du Colombier 		}
184219b2ee8SDavid du Colombier 	}
1857dd7cddfSDavid du Colombier 
1863e12c5d1SDavid du Colombier 	if(op == Tftp_READ)
1877dd7cddfSDavid du Colombier 		sendfile(fd, bigbuf+2, mode);
1883e12c5d1SDavid du Colombier 	else
1897dd7cddfSDavid du Colombier 		recvfile(fd, bigbuf+2, mode);
1903e12c5d1SDavid du Colombier }
1913e12c5d1SDavid du Colombier 
1923e12c5d1SDavid du Colombier void
1933e12c5d1SDavid du Colombier catcher(void *junk, char *msg)
1943e12c5d1SDavid du Colombier {
1953e12c5d1SDavid du Colombier 	USED(junk);
1963e12c5d1SDavid du Colombier 
1973e12c5d1SDavid du Colombier 	if(strncmp(msg, "exit", 4) == 0)
1983e12c5d1SDavid du Colombier 		noted(NDFLT);
1993e12c5d1SDavid du Colombier 	noted(NCONT);
2003e12c5d1SDavid du Colombier }
2013e12c5d1SDavid du Colombier 
2023e12c5d1SDavid du Colombier void
2033e12c5d1SDavid du Colombier sendfile(int fd, char *name, char *mode)
2043e12c5d1SDavid du Colombier {
2053e12c5d1SDavid du Colombier 	int file;
2063e12c5d1SDavid du Colombier 	uchar buf[Segsize+4];
2073e12c5d1SDavid du Colombier 	uchar ack[1024];
2089a747e4fSDavid du Colombier 	char errbuf[Maxerr];
2093e12c5d1SDavid du Colombier 	int ackblock, block, ret;
2103e12c5d1SDavid du Colombier 	int rexmit, n, al, txtry, rxl;
2113e12c5d1SDavid du Colombier 	short op;
2123e12c5d1SDavid du Colombier 
2133e12c5d1SDavid du Colombier 	syslog(dbg, flog, "send file '%s' %s to %s", name, mode, raddr);
2143e12c5d1SDavid du Colombier 	name = sunkernel(name);
2153e12c5d1SDavid du Colombier 	if(name == 0){
2163e12c5d1SDavid du Colombier 		nak(fd, 0, "not in our database");
2173e12c5d1SDavid du Colombier 		return;
2183e12c5d1SDavid du Colombier 	}
2193e12c5d1SDavid du Colombier 
2203e12c5d1SDavid du Colombier 	notify(catcher);
2213e12c5d1SDavid du Colombier 
2223e12c5d1SDavid du Colombier 	file = open(name, OREAD);
2233e12c5d1SDavid du Colombier 	if(file < 0) {
2249a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
2253e12c5d1SDavid du Colombier 		nak(fd, 0, errbuf);
2263e12c5d1SDavid du Colombier 		return;
2273e12c5d1SDavid du Colombier 	}
2283e12c5d1SDavid du Colombier 	block = 0;
2293e12c5d1SDavid du Colombier 	rexmit = 0;
2303e12c5d1SDavid du Colombier 	n = 0;
231219b2ee8SDavid du Colombier 	for(txtry = 0; txtry < 5;) {
2323e12c5d1SDavid du Colombier 		if(rexmit == 0) {
2333e12c5d1SDavid du Colombier 			block++;
2343e12c5d1SDavid du Colombier 			buf[0] = 0;
2353e12c5d1SDavid du Colombier 			buf[1] = Tftp_DATA;
2363e12c5d1SDavid du Colombier 			buf[2] = block>>8;
2373e12c5d1SDavid du Colombier 			buf[3] = block;
2383e12c5d1SDavid du Colombier 			n = read(file, buf+4, Segsize);
2393e12c5d1SDavid du Colombier 			if(n < 0) {
2409a747e4fSDavid du Colombier 				errstr(errbuf, sizeof errbuf);
2413e12c5d1SDavid du Colombier 				nak(fd, 0, errbuf);
2423e12c5d1SDavid du Colombier 				return;
2433e12c5d1SDavid du Colombier 			}
244219b2ee8SDavid du Colombier 			txtry = 0;
2453e12c5d1SDavid du Colombier 		}
2467dd7cddfSDavid du Colombier 		else {
247162f803dSDavid du Colombier 			syslog(dbg, flog, "rexmit %d %s:%d to %s",
248162f803dSDavid du Colombier 				4+n, name, block, raddr);
2493e12c5d1SDavid du Colombier 			txtry++;
2507dd7cddfSDavid du Colombier 		}
2517dd7cddfSDavid du Colombier 
2523e12c5d1SDavid du Colombier 		ret = write(fd, buf, 4+n);
2537ec5746aSDavid du Colombier 		if(ret < 4+n)
2547dd7cddfSDavid du Colombier 			sysfatal("tftpd: network write error: %r");
2553e12c5d1SDavid du Colombier 
2563e12c5d1SDavid du Colombier 		for(rxl = 0; rxl < 10; rxl++) {
2573e12c5d1SDavid du Colombier 			rexmit = 0;
258*64eb6bc1SDavid du Colombier 			alarm(1000);
2593e12c5d1SDavid du Colombier 			al = read(fd, ack, sizeof(ack));
2603e12c5d1SDavid du Colombier 			alarm(0);
2613e12c5d1SDavid du Colombier 			if(al < 0) {
2623e12c5d1SDavid du Colombier 				rexmit = 1;
2633e12c5d1SDavid du Colombier 				break;
2643e12c5d1SDavid du Colombier 			}
2653e12c5d1SDavid du Colombier 			op = ack[0]<<8|ack[1];
2663e12c5d1SDavid du Colombier 			if(op == Tftp_ERROR)
2673e12c5d1SDavid du Colombier 				goto error;
2683e12c5d1SDavid du Colombier 			ackblock = ack[2]<<8|ack[3];
2693e12c5d1SDavid du Colombier 			if(ackblock == block)
2703e12c5d1SDavid du Colombier 				break;
2713e12c5d1SDavid du Colombier 			if(ackblock == 0xffff) {
2723e12c5d1SDavid du Colombier 				rexmit = 1;
2733e12c5d1SDavid du Colombier 				break;
2743e12c5d1SDavid du Colombier 			}
2753e12c5d1SDavid du Colombier 		}
2763e12c5d1SDavid du Colombier 		if(ret != Segsize+4 && rexmit == 0)
2773e12c5d1SDavid du Colombier 			break;
2783e12c5d1SDavid du Colombier 	}
2793e12c5d1SDavid du Colombier error:
2803e12c5d1SDavid du Colombier 	close(fd);
2813e12c5d1SDavid du Colombier 	close(file);
2823e12c5d1SDavid du Colombier }
2833e12c5d1SDavid du Colombier 
284162f803dSDavid du Colombier enum { Hdrsize = 2 * sizeof(short), };		/* op, block */
285162f803dSDavid du Colombier 
2863e12c5d1SDavid du Colombier void
2873e12c5d1SDavid du Colombier recvfile(int fd, char *name, char *mode)
2883e12c5d1SDavid du Colombier {
2893e12c5d1SDavid du Colombier 	ushort op, block, inblock;
2903e12c5d1SDavid du Colombier 	uchar buf[Segsize+8];
2919a747e4fSDavid du Colombier 	char errbuf[Maxerr];
2923e12c5d1SDavid du Colombier 	int n, ret, file;
2933e12c5d1SDavid du Colombier 
2943e12c5d1SDavid du Colombier 	syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
2953e12c5d1SDavid du Colombier 
2963e12c5d1SDavid du Colombier 	file = create(name, OWRITE, 0666);
2973e12c5d1SDavid du Colombier 	if(file < 0) {
2989a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
2993e12c5d1SDavid du Colombier 		nak(fd, 0, errbuf);
300162f803dSDavid du Colombier 		syslog(dbg, flog, "can't create %s: %r", name);
3013e12c5d1SDavid du Colombier 		return;
3023e12c5d1SDavid du Colombier 	}
3033e12c5d1SDavid du Colombier 
3043e12c5d1SDavid du Colombier 	block = 0;
3053e12c5d1SDavid du Colombier 	ack(fd, block);
3063e12c5d1SDavid du Colombier 	block++;
3073e12c5d1SDavid du Colombier 
3083e12c5d1SDavid du Colombier 	for (;;) {
3093e12c5d1SDavid du Colombier 		alarm(15000);
3103e12c5d1SDavid du Colombier 		n = read(fd, buf, sizeof(buf));
3113e12c5d1SDavid du Colombier 		alarm(0);
312162f803dSDavid du Colombier 		if(n < 0) {
313162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: network error reading %s: %r",
314162f803dSDavid du Colombier 				name);
3153e12c5d1SDavid du Colombier 			goto error;
316162f803dSDavid du Colombier 		}
317162f803dSDavid du Colombier 		if(n <= Hdrsize) {
318162f803dSDavid du Colombier 			syslog(dbg, flog,
319162f803dSDavid du Colombier 				"tftpd: short read from network, reading %s",
320162f803dSDavid du Colombier 				name);
321162f803dSDavid du Colombier 			goto error;
322162f803dSDavid du Colombier 		}
3233e12c5d1SDavid du Colombier 		op = buf[0]<<8|buf[1];
324162f803dSDavid du Colombier 		if(op == Tftp_ERROR) {
325162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: tftp error reading %s", name);
3263e12c5d1SDavid du Colombier 			goto error;
327162f803dSDavid du Colombier 		}
3283e12c5d1SDavid du Colombier 
329162f803dSDavid du Colombier 		n -= Hdrsize;
3303e12c5d1SDavid du Colombier 		inblock = buf[2]<<8|buf[3];
3313e12c5d1SDavid du Colombier 		if(op == Tftp_DATA) {
3323e12c5d1SDavid du Colombier 			if(inblock == block) {
333162f803dSDavid du Colombier 				ret = write(file, buf+Hdrsize, n);
334162f803dSDavid du Colombier 				if(ret != n) {
3359a747e4fSDavid du Colombier 					errstr(errbuf, sizeof errbuf);
3363e12c5d1SDavid du Colombier 					nak(fd, 0, errbuf);
337162f803dSDavid du Colombier 					syslog(dbg, flog,
338162f803dSDavid du Colombier 					    "tftpd: error writing %s: %s",
339162f803dSDavid du Colombier 						name, errbuf);
3403e12c5d1SDavid du Colombier 					goto error;
3413e12c5d1SDavid du Colombier 				}
3423e12c5d1SDavid du Colombier 				ack(fd, block);
3433e12c5d1SDavid du Colombier 				block++;
34478307a5fSDavid du Colombier 			} else
34578307a5fSDavid du Colombier 				ack(fd, 0xffff);	/* tell him to resend */
3463e12c5d1SDavid du Colombier 		}
3473e12c5d1SDavid du Colombier 	}
3483e12c5d1SDavid du Colombier error:
3493e12c5d1SDavid du Colombier 	close(file);
3503e12c5d1SDavid du Colombier }
3513e12c5d1SDavid du Colombier 
3523e12c5d1SDavid du Colombier void
3533e12c5d1SDavid du Colombier ack(int fd, ushort block)
3543e12c5d1SDavid du Colombier {
3553e12c5d1SDavid du Colombier 	uchar ack[4];
3563e12c5d1SDavid du Colombier 	int n;
3573e12c5d1SDavid du Colombier 
3583e12c5d1SDavid du Colombier 	ack[0] = 0;
3593e12c5d1SDavid du Colombier 	ack[1] = Tftp_ACK;
3603e12c5d1SDavid du Colombier 	ack[2] = block>>8;
3613e12c5d1SDavid du Colombier 	ack[3] = block;
3623e12c5d1SDavid du Colombier 
3633e12c5d1SDavid du Colombier 	n = write(fd, ack, 4);
364162f803dSDavid du Colombier 	if(n < 4)
3657dd7cddfSDavid du Colombier 		sysfatal("network write: %r");
3663e12c5d1SDavid du Colombier }
3673e12c5d1SDavid du Colombier 
3683e12c5d1SDavid du Colombier void
3693e12c5d1SDavid du Colombier nak(int fd, int code, char *msg)
3703e12c5d1SDavid du Colombier {
3713e12c5d1SDavid du Colombier 	char buf[128];
3723e12c5d1SDavid du Colombier 	int n;
3733e12c5d1SDavid du Colombier 
3743e12c5d1SDavid du Colombier 	buf[0] = 0;
3753e12c5d1SDavid du Colombier 	buf[1] = Tftp_ERROR;
3763e12c5d1SDavid du Colombier 	buf[2] = 0;
3773e12c5d1SDavid du Colombier 	buf[3] = code;
3783e12c5d1SDavid du Colombier 	strcpy(buf+4, msg);
3793e12c5d1SDavid du Colombier 	n = strlen(msg) + 4 + 1;
3803e12c5d1SDavid du Colombier 	n = write(fd, buf, n);
3813e12c5d1SDavid du Colombier 	if(n < 0)
3827dd7cddfSDavid du Colombier 		sysfatal("write nak: %r");
3833e12c5d1SDavid du Colombier }
3843e12c5d1SDavid du Colombier 
3853e12c5d1SDavid du Colombier void
3863e12c5d1SDavid du Colombier setuser(void)
3873e12c5d1SDavid du Colombier {
388c0eadb1cSDavid du Colombier 	int fd;
3893e12c5d1SDavid du Colombier 
390c0eadb1cSDavid du Colombier 	fd = open("#c/user", OWRITE);
391c0eadb1cSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
392c0eadb1cSDavid du Colombier 		sysfatal("can't become none: %r");
393c0eadb1cSDavid du Colombier 	close(fd);
394c0eadb1cSDavid du Colombier 	if(newns("none", nil) < 0)
395c0eadb1cSDavid du Colombier 		sysfatal("can't build namespace: %r");
3963e12c5d1SDavid du Colombier }
3973e12c5d1SDavid du Colombier 
3987dd7cddfSDavid du Colombier char*
399da51d93aSDavid du Colombier lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
4007dd7cddfSDavid du Colombier {
4017dd7cddfSDavid du Colombier 	static Ndb *db;
4027dd7cddfSDavid du Colombier 	char *attrs[1];
4037dd7cddfSDavid du Colombier 	Ndbtuple *t;
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier 	if(db == nil)
4067dd7cddfSDavid du Colombier 		db = ndbopen(0);
4077dd7cddfSDavid du Colombier 	if(db == nil)
4087dd7cddfSDavid du Colombier 		return nil;
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 	if(sattr == nil)
4117dd7cddfSDavid du Colombier 		sattr = ipattr(sval);
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier 	attrs[0] = tattr;
4147dd7cddfSDavid du Colombier 	t = ndbipinfo(db, sattr, sval, attrs, 1);
4157dd7cddfSDavid du Colombier 	if(t == nil)
4167dd7cddfSDavid du Colombier 		return nil;
417da51d93aSDavid du Colombier 	strncpy(tval, t->val, len);
418da51d93aSDavid du Colombier 	tval[len-1] = 0;
4197dd7cddfSDavid du Colombier 	ndbfree(t);
4207dd7cddfSDavid du Colombier 	return tval;
4217dd7cddfSDavid du Colombier }
4227dd7cddfSDavid du Colombier 
4233e12c5d1SDavid du Colombier /*
4243e12c5d1SDavid du Colombier  *  for sun kernel boots, replace the requested file name with
4253e12c5d1SDavid du Colombier  *  a one from our database.  If the database doesn't specify a file,
4263e12c5d1SDavid du Colombier  *  don't answer.
4273e12c5d1SDavid du Colombier  */
4283e12c5d1SDavid du Colombier char*
4293e12c5d1SDavid du Colombier sunkernel(char *name)
4303e12c5d1SDavid du Colombier {
4313e12c5d1SDavid du Colombier 	ulong addr;
4327dd7cddfSDavid du Colombier 	uchar v4[IPv4addrlen];
4337dd7cddfSDavid du Colombier 	uchar v6[IPaddrlen];
434da51d93aSDavid du Colombier 	char buf[256];
435da51d93aSDavid du Colombier 	char ipbuf[128];
43627e10919SDavid du Colombier 	char *suffix;
4373e12c5d1SDavid du Colombier 
43827e10919SDavid du Colombier 	addr = strtoul(name, &suffix, 16);
43927e10919SDavid du Colombier 	if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
4403e12c5d1SDavid du Colombier 		return name;
4413e12c5d1SDavid du Colombier 
4427dd7cddfSDavid du Colombier 	v4[0] = addr>>24;
4437dd7cddfSDavid du Colombier 	v4[1] = addr>>16;
4447dd7cddfSDavid du Colombier 	v4[2] = addr>>8;
4457dd7cddfSDavid du Colombier 	v4[3] = addr;
4467dd7cddfSDavid du Colombier 	v4tov6(v6, v4);
4477dd7cddfSDavid du Colombier 	sprint(ipbuf, "%I", v6);
448da51d93aSDavid du Colombier 	return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
4497dd7cddfSDavid du Colombier }
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier void
4527dd7cddfSDavid du Colombier remoteaddr(char *dir, char *raddr, int len)
4537dd7cddfSDavid du Colombier {
4547dd7cddfSDavid du Colombier 	char buf[64];
4557dd7cddfSDavid du Colombier 	int fd, n;
4567dd7cddfSDavid du Colombier 
4577dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/remote", dir);
4587dd7cddfSDavid du Colombier 	fd = open(buf, OREAD);
4597dd7cddfSDavid du Colombier 	if(fd < 0){
4607dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
4617dd7cddfSDavid du Colombier 		return;
4627dd7cddfSDavid du Colombier 	}
4637dd7cddfSDavid du Colombier 	n = read(fd, raddr, len-1);
4647dd7cddfSDavid du Colombier 	close(fd);
4657dd7cddfSDavid du Colombier 	if(n <= 0){
4667dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
4677dd7cddfSDavid du Colombier 		return;
4687dd7cddfSDavid du Colombier 	}
4697dd7cddfSDavid du Colombier 	if(n > 0)
4707dd7cddfSDavid du Colombier 		n--;
4717dd7cddfSDavid du Colombier 	raddr[n] = 0;
4723e12c5d1SDavid du Colombier }
473