xref: /plan9/sys/src/cmd/ip/tftpd.c (revision 162f803d803e7af3469d29bde78fb975a68a2888)
1*162f803dSDavid du Colombier /*
2*162f803dSDavid du Colombier  * tftpd - tftp service, see /lib/rfc/rfc783
3*162f803dSDavid 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;
62*162f803dSDavid du Colombier 	char *svc = "69";
633e12c5d1SDavid du Colombier 
64*162f803dSDavid 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':
70*162f803dSDavid 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':
79*162f803dSDavid 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 
923e12c5d1SDavid du Colombier 	if(chdir(dir) < 0)
937dd7cddfSDavid du Colombier 		sysfatal("can't get to directory %s: %r", dir);
943e12c5d1SDavid du Colombier 
957dd7cddfSDavid du Colombier 	if(!dbg)
963e12c5d1SDavid du Colombier 		switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
973e12c5d1SDavid du Colombier 		case -1:
987dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
993e12c5d1SDavid du Colombier 		case 0:
1003e12c5d1SDavid du Colombier 			break;
1013e12c5d1SDavid du Colombier 		default:
1023e12c5d1SDavid du Colombier 			exits(0);
1033e12c5d1SDavid du Colombier 		}
1043e12c5d1SDavid du Colombier 
105c0eadb1cSDavid du Colombier 	snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
1067dd7cddfSDavid du Colombier 	cfd = announce(buf, adir);
107c0eadb1cSDavid du Colombier 	if (cfd < 0)
108c0eadb1cSDavid du Colombier 		sysfatal("announcing on %s: %r", buf);
109c0eadb1cSDavid du Colombier 	syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
11027e10919SDavid du Colombier 	setuser();
1113e12c5d1SDavid du Colombier 	for(;;) {
1127dd7cddfSDavid du Colombier 		lcfd = listen(adir, ldir);
1137dd7cddfSDavid du Colombier 		if(lcfd < 0)
114c0eadb1cSDavid du Colombier 			sysfatal("listening on %s: %r", adir);
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 		switch(fork()) {
1177dd7cddfSDavid du Colombier 		case -1:
1187dd7cddfSDavid du Colombier 			sysfatal("fork: %r");
1197dd7cddfSDavid du Colombier 		case 0:
120c0eadb1cSDavid du Colombier 			dfd = accept(lcfd, ldir);
1217dd7cddfSDavid du Colombier 			if(dfd < 0)
1227dd7cddfSDavid du Colombier  				exits(0);
1237dd7cddfSDavid du Colombier 			remoteaddr(ldir, raddr, sizeof(raddr));
124c0eadb1cSDavid du Colombier 			syslog(0, flog, "tftp connection from %s dir %s",
125c0eadb1cSDavid du Colombier 				raddr, ldir);
1267dd7cddfSDavid du Colombier 			doserve(dfd);
1277dd7cddfSDavid du Colombier 			exits("done");
1287dd7cddfSDavid du Colombier 			break;
1297dd7cddfSDavid du Colombier 		default:
1307dd7cddfSDavid du Colombier 			close(lcfd);
1317dd7cddfSDavid du Colombier 			continue;
1327dd7cddfSDavid du Colombier 		}
1337dd7cddfSDavid du Colombier 	}
1347dd7cddfSDavid du Colombier }
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier void
1377dd7cddfSDavid du Colombier doserve(int fd)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier 	int dlen;
1407dd7cddfSDavid du Colombier 	char *mode, *p;
1417dd7cddfSDavid du Colombier 	short op;
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier 	dlen = read(fd, bigbuf, sizeof(bigbuf));
1443e12c5d1SDavid du Colombier 	if(dlen < 0)
1457dd7cddfSDavid du Colombier 		sysfatal("listen read: %r");
1463e12c5d1SDavid du Colombier 
1477dd7cddfSDavid du Colombier 	op = (bigbuf[0]<<8) | bigbuf[1];
1483e12c5d1SDavid du Colombier 	dlen -= 2;
1497dd7cddfSDavid du Colombier 	mode = bigbuf+2;
1503e12c5d1SDavid du Colombier 	while(*mode != '\0' && dlen--)
1513e12c5d1SDavid du Colombier 		mode++;
1523e12c5d1SDavid du Colombier 	mode++;
1533e12c5d1SDavid du Colombier 	p = mode;
1543e12c5d1SDavid du Colombier 	while(*p && dlen--)
1553e12c5d1SDavid du Colombier 		p++;
1563e12c5d1SDavid du Colombier 	if(dlen == 0) {
1577dd7cddfSDavid du Colombier 		nak(fd, 0, "bad tftpmode");
1587dd7cddfSDavid du Colombier 		close(fd);
159c0eadb1cSDavid du Colombier 		syslog(dbg, flog, "bad mode from %s", raddr);
1607dd7cddfSDavid du Colombier 		return;
1613e12c5d1SDavid du Colombier 	}
1623e12c5d1SDavid du Colombier 
1633e12c5d1SDavid du Colombier 	if(op != Tftp_READ && op != Tftp_WRITE) {
1647dd7cddfSDavid du Colombier 		nak(fd, 4, "Illegal TFTP operation");
1657dd7cddfSDavid du Colombier 		close(fd);
1663e12c5d1SDavid du Colombier 		syslog(dbg, flog, "bad request %d %s", op, raddr);
1677dd7cddfSDavid du Colombier 		return;
1683e12c5d1SDavid du Colombier 	}
1697dd7cddfSDavid du Colombier 
170219b2ee8SDavid du Colombier 	if(restricted){
17167031067SDavid du Colombier 		if(bigbuf[2] == '#' ||
17267031067SDavid du Colombier 		  strncmp(bigbuf+2, "../", 3)==0 || strstr(bigbuf+2, "/../") ||
1737dd7cddfSDavid du Colombier 		  (bigbuf[2] == '/' && strncmp(bigbuf+2, dirsl, dirsllen)!=0)){
1747dd7cddfSDavid du Colombier 			nak(fd, 4, "Permission denied");
1757dd7cddfSDavid du Colombier 			close(fd);
17639734e7eSDavid du Colombier 			syslog(dbg, flog, "bad request %d from %s file %s", op, raddr, bigbuf+2);
1777dd7cddfSDavid du Colombier 			return;
178219b2ee8SDavid du Colombier 		}
179219b2ee8SDavid du Colombier 	}
1807dd7cddfSDavid du Colombier 
1813e12c5d1SDavid du Colombier 	if(op == Tftp_READ)
1827dd7cddfSDavid du Colombier 		sendfile(fd, bigbuf+2, mode);
1833e12c5d1SDavid du Colombier 	else
1847dd7cddfSDavid du Colombier 		recvfile(fd, bigbuf+2, mode);
1853e12c5d1SDavid du Colombier }
1863e12c5d1SDavid du Colombier 
1873e12c5d1SDavid du Colombier void
1883e12c5d1SDavid du Colombier catcher(void *junk, char *msg)
1893e12c5d1SDavid du Colombier {
1903e12c5d1SDavid du Colombier 	USED(junk);
1913e12c5d1SDavid du Colombier 
1923e12c5d1SDavid du Colombier 	if(strncmp(msg, "exit", 4) == 0)
1933e12c5d1SDavid du Colombier 		noted(NDFLT);
1943e12c5d1SDavid du Colombier 	noted(NCONT);
1953e12c5d1SDavid du Colombier }
1963e12c5d1SDavid du Colombier 
1973e12c5d1SDavid du Colombier void
1983e12c5d1SDavid du Colombier sendfile(int fd, char *name, char *mode)
1993e12c5d1SDavid du Colombier {
2003e12c5d1SDavid du Colombier 	int file;
2013e12c5d1SDavid du Colombier 	uchar buf[Segsize+4];
2023e12c5d1SDavid du Colombier 	uchar ack[1024];
2039a747e4fSDavid du Colombier 	char errbuf[Maxerr];
2043e12c5d1SDavid du Colombier 	int ackblock, block, ret;
2053e12c5d1SDavid du Colombier 	int rexmit, n, al, txtry, rxl;
2063e12c5d1SDavid du Colombier 	short op;
2073e12c5d1SDavid du Colombier 
2083e12c5d1SDavid du Colombier 	syslog(dbg, flog, "send file '%s' %s to %s", name, mode, raddr);
2093e12c5d1SDavid du Colombier 	name = sunkernel(name);
2103e12c5d1SDavid du Colombier 	if(name == 0){
2113e12c5d1SDavid du Colombier 		nak(fd, 0, "not in our database");
2123e12c5d1SDavid du Colombier 		return;
2133e12c5d1SDavid du Colombier 	}
2143e12c5d1SDavid du Colombier 
2153e12c5d1SDavid du Colombier 	notify(catcher);
2163e12c5d1SDavid du Colombier 
2173e12c5d1SDavid du Colombier 	file = open(name, OREAD);
2183e12c5d1SDavid du Colombier 	if(file < 0) {
2199a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
2203e12c5d1SDavid du Colombier 		nak(fd, 0, errbuf);
2213e12c5d1SDavid du Colombier 		return;
2223e12c5d1SDavid du Colombier 	}
2233e12c5d1SDavid du Colombier 	block = 0;
2243e12c5d1SDavid du Colombier 	rexmit = 0;
2253e12c5d1SDavid du Colombier 	n = 0;
226219b2ee8SDavid du Colombier 	for(txtry = 0; txtry < 5;) {
2273e12c5d1SDavid du Colombier 		if(rexmit == 0) {
2283e12c5d1SDavid du Colombier 			block++;
2293e12c5d1SDavid du Colombier 			buf[0] = 0;
2303e12c5d1SDavid du Colombier 			buf[1] = Tftp_DATA;
2313e12c5d1SDavid du Colombier 			buf[2] = block>>8;
2323e12c5d1SDavid du Colombier 			buf[3] = block;
2333e12c5d1SDavid du Colombier 			n = read(file, buf+4, Segsize);
2343e12c5d1SDavid du Colombier 			if(n < 0) {
2359a747e4fSDavid du Colombier 				errstr(errbuf, sizeof errbuf);
2363e12c5d1SDavid du Colombier 				nak(fd, 0, errbuf);
2373e12c5d1SDavid du Colombier 				return;
2383e12c5d1SDavid du Colombier 			}
239219b2ee8SDavid du Colombier 			txtry = 0;
2403e12c5d1SDavid du Colombier 		}
2417dd7cddfSDavid du Colombier 		else {
242*162f803dSDavid du Colombier 			syslog(dbg, flog, "rexmit %d %s:%d to %s",
243*162f803dSDavid du Colombier 				4+n, name, block, raddr);
2443e12c5d1SDavid du Colombier 			txtry++;
2457dd7cddfSDavid du Colombier 		}
2467dd7cddfSDavid du Colombier 
2473e12c5d1SDavid du Colombier 		ret = write(fd, buf, 4+n);
2483e12c5d1SDavid du Colombier 		if(ret < 0)
2497dd7cddfSDavid du Colombier 			sysfatal("tftpd: network write error: %r");
2503e12c5d1SDavid du Colombier 
2513e12c5d1SDavid du Colombier 		for(rxl = 0; rxl < 10; rxl++) {
2523e12c5d1SDavid du Colombier 			rexmit = 0;
253219b2ee8SDavid du Colombier 			alarm(500);
2543e12c5d1SDavid du Colombier 			al = read(fd, ack, sizeof(ack));
2553e12c5d1SDavid du Colombier 			alarm(0);
2563e12c5d1SDavid du Colombier 			if(al < 0) {
2573e12c5d1SDavid du Colombier 				rexmit = 1;
2583e12c5d1SDavid du Colombier 				break;
2593e12c5d1SDavid du Colombier 			}
2603e12c5d1SDavid du Colombier 			op = ack[0]<<8|ack[1];
2613e12c5d1SDavid du Colombier 			if(op == Tftp_ERROR)
2623e12c5d1SDavid du Colombier 				goto error;
2633e12c5d1SDavid du Colombier 			ackblock = ack[2]<<8|ack[3];
2643e12c5d1SDavid du Colombier 			if(ackblock == block)
2653e12c5d1SDavid du Colombier 				break;
2663e12c5d1SDavid du Colombier 			if(ackblock == 0xffff) {
2673e12c5d1SDavid du Colombier 				rexmit = 1;
2683e12c5d1SDavid du Colombier 				break;
2693e12c5d1SDavid du Colombier 			}
2703e12c5d1SDavid du Colombier 		}
2713e12c5d1SDavid du Colombier 		if(ret != Segsize+4 && rexmit == 0)
2723e12c5d1SDavid du Colombier 			break;
2733e12c5d1SDavid du Colombier 	}
2743e12c5d1SDavid du Colombier error:
2753e12c5d1SDavid du Colombier 	close(fd);
2763e12c5d1SDavid du Colombier 	close(file);
2773e12c5d1SDavid du Colombier }
2783e12c5d1SDavid du Colombier 
279*162f803dSDavid du Colombier enum { Hdrsize = 2 * sizeof(short), };		/* op, block */
280*162f803dSDavid du Colombier 
2813e12c5d1SDavid du Colombier void
2823e12c5d1SDavid du Colombier recvfile(int fd, char *name, char *mode)
2833e12c5d1SDavid du Colombier {
2843e12c5d1SDavid du Colombier 	ushort op, block, inblock;
2853e12c5d1SDavid du Colombier 	uchar buf[Segsize+8];
2869a747e4fSDavid du Colombier 	char errbuf[Maxerr];
2873e12c5d1SDavid du Colombier 	int n, ret, file;
2883e12c5d1SDavid du Colombier 
2893e12c5d1SDavid du Colombier 	syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
2903e12c5d1SDavid du Colombier 
2913e12c5d1SDavid du Colombier 	file = create(name, OWRITE, 0666);
2923e12c5d1SDavid du Colombier 	if(file < 0) {
2939a747e4fSDavid du Colombier 		errstr(errbuf, sizeof errbuf);
2943e12c5d1SDavid du Colombier 		nak(fd, 0, errbuf);
295*162f803dSDavid du Colombier 		syslog(dbg, flog, "can't create %s: %r", name);
2963e12c5d1SDavid du Colombier 		return;
2973e12c5d1SDavid du Colombier 	}
2983e12c5d1SDavid du Colombier 
2993e12c5d1SDavid du Colombier 	block = 0;
3003e12c5d1SDavid du Colombier 	ack(fd, block);
3013e12c5d1SDavid du Colombier 	block++;
3023e12c5d1SDavid du Colombier 
3033e12c5d1SDavid du Colombier 	for (;;) {
3043e12c5d1SDavid du Colombier 		alarm(15000);
3053e12c5d1SDavid du Colombier 		n = read(fd, buf, sizeof(buf));
3063e12c5d1SDavid du Colombier 		alarm(0);
307*162f803dSDavid du Colombier 		if(n < 0) {
308*162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: network error reading %s: %r",
309*162f803dSDavid du Colombier 				name);
3103e12c5d1SDavid du Colombier 			goto error;
311*162f803dSDavid du Colombier 		}
312*162f803dSDavid du Colombier 		if(n <= Hdrsize) {
313*162f803dSDavid du Colombier 			syslog(dbg, flog,
314*162f803dSDavid du Colombier 				"tftpd: short read from network, reading %s",
315*162f803dSDavid du Colombier 				name);
316*162f803dSDavid du Colombier 			goto error;
317*162f803dSDavid du Colombier 		}
3183e12c5d1SDavid du Colombier 		op = buf[0]<<8|buf[1];
319*162f803dSDavid du Colombier 		if(op == Tftp_ERROR) {
320*162f803dSDavid du Colombier 			syslog(dbg, flog, "tftpd: tftp error reading %s", name);
3213e12c5d1SDavid du Colombier 			goto error;
322*162f803dSDavid du Colombier 		}
3233e12c5d1SDavid du Colombier 
324*162f803dSDavid du Colombier 		n -= Hdrsize;
3253e12c5d1SDavid du Colombier 		inblock = buf[2]<<8|buf[3];
3263e12c5d1SDavid du Colombier 		if(op == Tftp_DATA) {
3273e12c5d1SDavid du Colombier 			if(inblock == block) {
328*162f803dSDavid du Colombier 				ret = write(file, buf+Hdrsize, n);
329*162f803dSDavid du Colombier 				if(ret != n) {
3309a747e4fSDavid du Colombier 					errstr(errbuf, sizeof errbuf);
3313e12c5d1SDavid du Colombier 					nak(fd, 0, errbuf);
332*162f803dSDavid du Colombier 					syslog(dbg, flog,
333*162f803dSDavid du Colombier 					    "tftpd: error writing %s: %s",
334*162f803dSDavid du Colombier 						name, errbuf);
3353e12c5d1SDavid du Colombier 					goto error;
3363e12c5d1SDavid du Colombier 				}
3373e12c5d1SDavid du Colombier 				ack(fd, block);
3383e12c5d1SDavid du Colombier 				block++;
3393e12c5d1SDavid du Colombier 			}
3403e12c5d1SDavid du Colombier 			ack(fd, 0xffff);
3413e12c5d1SDavid du Colombier 		}
3423e12c5d1SDavid du Colombier 	}
3433e12c5d1SDavid du Colombier error:
3443e12c5d1SDavid du Colombier 	close(file);
3453e12c5d1SDavid du Colombier }
3463e12c5d1SDavid du Colombier 
3473e12c5d1SDavid du Colombier void
3483e12c5d1SDavid du Colombier ack(int fd, ushort block)
3493e12c5d1SDavid du Colombier {
3503e12c5d1SDavid du Colombier 	uchar ack[4];
3513e12c5d1SDavid du Colombier 	int n;
3523e12c5d1SDavid du Colombier 
3533e12c5d1SDavid du Colombier 	ack[0] = 0;
3543e12c5d1SDavid du Colombier 	ack[1] = Tftp_ACK;
3553e12c5d1SDavid du Colombier 	ack[2] = block>>8;
3563e12c5d1SDavid du Colombier 	ack[3] = block;
3573e12c5d1SDavid du Colombier 
3583e12c5d1SDavid du Colombier 	n = write(fd, ack, 4);
359*162f803dSDavid du Colombier 	if(n < 4)
3607dd7cddfSDavid du Colombier 		sysfatal("network write: %r");
3613e12c5d1SDavid du Colombier }
3623e12c5d1SDavid du Colombier 
3633e12c5d1SDavid du Colombier void
3643e12c5d1SDavid du Colombier nak(int fd, int code, char *msg)
3653e12c5d1SDavid du Colombier {
3663e12c5d1SDavid du Colombier 	char buf[128];
3673e12c5d1SDavid du Colombier 	int n;
3683e12c5d1SDavid du Colombier 
3693e12c5d1SDavid du Colombier 	buf[0] = 0;
3703e12c5d1SDavid du Colombier 	buf[1] = Tftp_ERROR;
3713e12c5d1SDavid du Colombier 	buf[2] = 0;
3723e12c5d1SDavid du Colombier 	buf[3] = code;
3733e12c5d1SDavid du Colombier 	strcpy(buf+4, msg);
3743e12c5d1SDavid du Colombier 	n = strlen(msg) + 4 + 1;
3753e12c5d1SDavid du Colombier 	n = write(fd, buf, n);
3763e12c5d1SDavid du Colombier 	if(n < 0)
3777dd7cddfSDavid du Colombier 		sysfatal("write nak: %r");
3783e12c5d1SDavid du Colombier }
3793e12c5d1SDavid du Colombier 
3803e12c5d1SDavid du Colombier void
3813e12c5d1SDavid du Colombier setuser(void)
3823e12c5d1SDavid du Colombier {
383c0eadb1cSDavid du Colombier 	int fd;
3843e12c5d1SDavid du Colombier 
385c0eadb1cSDavid du Colombier 	fd = open("#c/user", OWRITE);
386c0eadb1cSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
387c0eadb1cSDavid du Colombier 		sysfatal("can't become none: %r");
388c0eadb1cSDavid du Colombier 	close(fd);
389c0eadb1cSDavid du Colombier 	if(newns("none", nil) < 0)
390c0eadb1cSDavid du Colombier 		sysfatal("can't build namespace: %r");
3913e12c5d1SDavid du Colombier }
3923e12c5d1SDavid du Colombier 
3937dd7cddfSDavid du Colombier char*
394da51d93aSDavid du Colombier lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
3957dd7cddfSDavid du Colombier {
3967dd7cddfSDavid du Colombier 	static Ndb *db;
3977dd7cddfSDavid du Colombier 	char *attrs[1];
3987dd7cddfSDavid du Colombier 	Ndbtuple *t;
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier 	if(db == nil)
4017dd7cddfSDavid du Colombier 		db = ndbopen(0);
4027dd7cddfSDavid du Colombier 	if(db == nil)
4037dd7cddfSDavid du Colombier 		return nil;
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier 	if(sattr == nil)
4067dd7cddfSDavid du Colombier 		sattr = ipattr(sval);
4077dd7cddfSDavid du Colombier 
4087dd7cddfSDavid du Colombier 	attrs[0] = tattr;
4097dd7cddfSDavid du Colombier 	t = ndbipinfo(db, sattr, sval, attrs, 1);
4107dd7cddfSDavid du Colombier 	if(t == nil)
4117dd7cddfSDavid du Colombier 		return nil;
412da51d93aSDavid du Colombier 	strncpy(tval, t->val, len);
413da51d93aSDavid du Colombier 	tval[len-1] = 0;
4147dd7cddfSDavid du Colombier 	ndbfree(t);
4157dd7cddfSDavid du Colombier 	return tval;
4167dd7cddfSDavid du Colombier }
4177dd7cddfSDavid du Colombier 
4183e12c5d1SDavid du Colombier /*
4193e12c5d1SDavid du Colombier  *  for sun kernel boots, replace the requested file name with
4203e12c5d1SDavid du Colombier  *  a one from our database.  If the database doesn't specify a file,
4213e12c5d1SDavid du Colombier  *  don't answer.
4223e12c5d1SDavid du Colombier  */
4233e12c5d1SDavid du Colombier char*
4243e12c5d1SDavid du Colombier sunkernel(char *name)
4253e12c5d1SDavid du Colombier {
4263e12c5d1SDavid du Colombier 	ulong addr;
4277dd7cddfSDavid du Colombier 	uchar v4[IPv4addrlen];
4287dd7cddfSDavid du Colombier 	uchar v6[IPaddrlen];
429da51d93aSDavid du Colombier 	char buf[256];
430da51d93aSDavid du Colombier 	char ipbuf[128];
43127e10919SDavid du Colombier 	char *suffix;
4323e12c5d1SDavid du Colombier 
43327e10919SDavid du Colombier 	addr = strtoul(name, &suffix, 16);
43427e10919SDavid du Colombier 	if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
4353e12c5d1SDavid du Colombier 		return name;
4363e12c5d1SDavid du Colombier 
4377dd7cddfSDavid du Colombier 	v4[0] = addr>>24;
4387dd7cddfSDavid du Colombier 	v4[1] = addr>>16;
4397dd7cddfSDavid du Colombier 	v4[2] = addr>>8;
4407dd7cddfSDavid du Colombier 	v4[3] = addr;
4417dd7cddfSDavid du Colombier 	v4tov6(v6, v4);
4427dd7cddfSDavid du Colombier 	sprint(ipbuf, "%I", v6);
443da51d93aSDavid du Colombier 	return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
4447dd7cddfSDavid du Colombier }
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier void
4477dd7cddfSDavid du Colombier remoteaddr(char *dir, char *raddr, int len)
4487dd7cddfSDavid du Colombier {
4497dd7cddfSDavid du Colombier 	char buf[64];
4507dd7cddfSDavid du Colombier 	int fd, n;
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/remote", dir);
4537dd7cddfSDavid du Colombier 	fd = open(buf, OREAD);
4547dd7cddfSDavid du Colombier 	if(fd < 0){
4557dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
4567dd7cddfSDavid du Colombier 		return;
4577dd7cddfSDavid du Colombier 	}
4587dd7cddfSDavid du Colombier 	n = read(fd, raddr, len-1);
4597dd7cddfSDavid du Colombier 	close(fd);
4607dd7cddfSDavid du Colombier 	if(n <= 0){
4617dd7cddfSDavid du Colombier 		snprint(raddr, sizeof(raddr), "unknown");
4627dd7cddfSDavid du Colombier 		return;
4637dd7cddfSDavid du Colombier 	}
4647dd7cddfSDavid du Colombier 	if(n > 0)
4657dd7cddfSDavid du Colombier 		n--;
4667dd7cddfSDavid du Colombier 	raddr[n] = 0;
4673e12c5d1SDavid du Colombier }
468