xref: /plan9/sys/src/cmd/ip/tftpd.c (revision a1216cc64119db675aa140f55fbd73eb2414b763)
1 /*
2  * tftpd - tftp service, see /lib/rfc/rfc783 (now rfc1350 + 234[789])
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <auth.h>
7 #include <bio.h>
8 #include <ip.h>
9 #include <ndb.h>
10 
11 enum
12 {
13 	Maxpath=	128,
14 	Maxerr=		256,
15 
16 	Debug=		0,
17 
18 	Opsize=		sizeof(short),
19 	Blksize=	sizeof(short),
20 	Hdrsize=	Opsize + Blksize,
21 
22 	Ackerr=		-1,
23 	Ackok=		0,
24 	Ackrexmit=	1,
25 
26 	/* op codes */
27 	Tftp_READ	= 1,
28 	Tftp_WRITE	= 2,
29 	Tftp_DATA	= 3,
30 	Tftp_ACK	= 4,
31 	Tftp_ERROR	= 5,
32 	Tftp_OACK	= 6,		/* option acknowledge */
33 
34 	Errnotdef	= 0,		/* see textual error instead */
35 	Errnotfound	= 1,
36 	Errnoaccess	= 2,
37 	Errdiskfull	= 3,
38 	Errbadop	= 4,
39 	Errbadtid	= 5,
40 	Errexists	= 6,
41 	Errnouser	= 7,
42 	Errbadopt	= 8,		/* really bad option value */
43 
44 	Defsegsize	= 512,
45 	Maxsegsize	= 65464,	/* from rfc2348 */
46 
47 	/*
48 	 * bandt (viaduct) tunnels use smaller mtu than ether's
49 	 * (1400 bytes for tcp mss of 1300 bytes).
50 	 */
51 	Bandtmtu	= 1400,
52 	/*
53 	 * maximum size of block's data content, excludes hdrs,
54 	 * notably IP/UDP and TFTP, using worst-case (IPv6) sizes.
55 	 */
56 	Bandtblksz	= Bandtmtu - 40 - 8,
57 	Bcavium		= 1432,		/* cavium's u-boot demands this size */
58 };
59 
60 typedef struct Opt Opt;
61 struct Opt {
62 	char	*name;
63 	int	*valp;		/* set to client's value if within bounds */
64 	int	min;
65 	int	max;
66 };
67 
68 int 	dbg;
69 int	restricted;
70 int	pid;
71 
72 /* options */
73 int	blksize = Defsegsize;		/* excluding 4-byte header */
74 int	timeout = 5;			/* seconds */
75 int	tsize;
76 static Opt option[] = {
77 	"timeout",	&timeout,	1,	255,
78 	/* see "hack" below */
79 	"blksize",	&blksize,	8,	Maxsegsize,
80 	"tsize",	&tsize,		0,	~0UL >> 1,
81 };
82 
83 void	sendfile(int, char*, char*, int);
84 void	recvfile(int, char*, char*);
85 void	nak(int, int, char*);
86 void	ack(int, ushort);
87 void	clrcon(void);
88 void	setuser(void);
89 char*	sunkernel(char*);
90 void	remoteaddr(char*, char*, int);
91 void	doserve(int);
92 
93 char	bigbuf[32768];
94 char	raddr[64];
95 
96 char	*dir = "/lib/tftpd";
97 char	*dirsl;
98 int	dirsllen;
99 char	flog[] = "ipboot";
100 char	net[Maxpath];
101 
102 static char *opnames[] = {
103 [Tftp_READ]	"read",
104 [Tftp_WRITE]	"write",
105 [Tftp_DATA]	"data",
106 [Tftp_ACK]	"ack",
107 [Tftp_ERROR]	"error",
108 [Tftp_OACK]	"oack",
109 };
110 
111 void
usage(void)112 usage(void)
113 {
114 	fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
115 		argv0);
116 	exits("usage");
117 }
118 
119 void
main(int argc,char ** argv)120 main(int argc, char **argv)
121 {
122 	char buf[64];
123 	char adir[64], ldir[64];
124 	int cfd, lcfd, dfd;
125 	char *svc = "69";
126 
127 	setnetmtpt(net, sizeof net, nil);
128 	ARGBEGIN{
129 	case 'd':
130 		dbg++;
131 		break;
132 	case 'h':
133 		dir = EARGF(usage());
134 		break;
135 	case 'r':
136 		restricted = 1;
137 		break;
138 	case 's':
139 		svc = EARGF(usage());
140 		break;
141 	case 'x':
142 		setnetmtpt(net, sizeof net, EARGF(usage()));
143 		break;
144 	default:
145 		usage();
146 	}ARGEND
147 
148 	snprint(buf, sizeof buf, "%s/", dir);
149 	dirsl = strdup(buf);
150 	dirsllen = strlen(dirsl);
151 
152 	fmtinstall('E', eipfmt);
153 	fmtinstall('I', eipfmt);
154 
155 	/*
156 	 * setuser calls newns, and typical /lib/namespace files contain
157 	 * "cd /usr/$user", so call setuser before chdir.
158 	 */
159 	setuser();
160 	if(chdir(dir) < 0)
161 		sysfatal("can't get to directory %s: %r", dir);
162 
163 	if(!dbg)
164 		switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
165 		case -1:
166 			sysfatal("fork: %r");
167 		case 0:
168 			break;
169 		default:
170 			exits(0);
171 		}
172 
173 	snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
174 	cfd = announce(buf, adir);
175 	if (cfd < 0)
176 		sysfatal("announcing on %s: %r", buf);
177 	syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
178 //	setuser();
179 	for(;;) {
180 		lcfd = listen(adir, ldir);
181 		if(lcfd < 0)
182 			sysfatal("listening on %s: %r", adir);
183 
184 		switch(fork()) {
185 		case -1:
186 			sysfatal("fork: %r");
187 		case 0:
188 			dfd = accept(lcfd, ldir);
189 			if(dfd < 0)
190  				exits(0);
191 			remoteaddr(ldir, raddr, sizeof(raddr));
192 			pid = getpid();
193 			syslog(0, flog, "tftp %d connection from %s dir %s",
194 				pid, raddr, ldir);
195 			doserve(dfd);
196 			exits("done");
197 			break;
198 		default:
199 			close(lcfd);
200 			continue;
201 		}
202 	}
203 }
204 
205 static Opt *
handleopt(int fd,char * name,char * val)206 handleopt(int fd, char *name, char *val)
207 {
208 	int n;
209 	Opt *op;
210 
211 	for (op = option; op < option + nelem(option); op++)
212 		if(cistrcmp(name, op->name) == 0) {
213 			n = strtol(val, nil, 10);
214 			if (n < op->min || n > op->max) {
215 				nak(fd, Errbadopt, "option value out of range");
216 				syslog(dbg, flog, "tftp bad option value from "
217 					"client: %s %s", name, val);
218 				sysfatal("bad option value from client: %s %s",
219 					name, val);
220 			}
221 			*op->valp = n;
222 			/* incoming 0 for tsize is uninteresting */
223 			if(cistrcmp("tsize", op->name) != 0)
224 				syslog(dbg, flog, "tftpd %d setting %s to client's %d",
225 					pid, name, n);
226 			return op;
227 		}
228 	return nil;
229 }
230 
231 static vlong
filesize(char * file)232 filesize(char *file)
233 {
234 	vlong size;
235 	Dir *dp;
236 
237 	dp = dirstat(file);
238 	if (dp == nil)
239 		return -1;
240 	size = dp->length;
241 	free(dp);
242 	return size;
243 }
244 
245 /* copy word into bp iff it fits before ep, returns bytes to advance bp. */
246 static int
emits(char * word,char * bp,char * ep)247 emits(char *word, char *bp, char *ep)
248 {
249 	int len;
250 
251 	len = strlen(word) + 1;
252 	if (bp + len >= ep)
253 		return -1;
254 	strcpy(bp, word);
255 	return len;
256 }
257 
258 /* format number into bp iff it fits before ep. */
259 static int
emitn(vlong n,char * bp,char * ep)260 emitn(vlong n, char *bp, char *ep)
261 {
262 	char numb[32];
263 
264 	snprint(numb, sizeof numb, "%lld", n);
265 	return emits(numb, bp, ep);
266 }
267 
268 /*
269  * send an OACK packet to respond to options.  bail early with -1 on error.
270  * p is the packet containing the options.
271  *
272  * hack: bandt (viaducts) uses smaller mtu than ether's
273  * (1400 bytes for tcp mss of 1300 bytes),
274  * so offer at most bandt's mtu minus headers,
275  * to avoid failure of pxe booting via viaduct.
276  * there's an exception for the cavium's u-boot.
277  */
278 static int
options(int fd,char * buf,int bufsz,char * file,ushort oper,char * p,int dlen)279 options(int fd, char *buf, int bufsz, char *file, ushort oper, char *p, int dlen)
280 {
281 	int nmlen, vallen, olen, nopts;
282 	vlong size;
283 	char *val, *bp, *ep;
284 	Opt *op;
285 
286 	buf[0] = 0;
287 	buf[1] = Tftp_OACK;
288 	bp = buf + Opsize;
289 	ep = buf + bufsz;
290 	nopts = 0;
291 	for (; dlen > 0 && *p != '\0'; p = val + vallen, bp += olen) {
292 		nmlen = strlen(p) + 1;		/* include NUL */
293 		if (nmlen > dlen)
294 			break;
295 		dlen -= nmlen;
296 		val = p + nmlen;
297 		if (dlen <= 0 || *val == '\0')
298 			break;
299 
300 		vallen = strlen(val) + 1;
301 		if (vallen > dlen)
302 			break;
303 		dlen -= vallen;
304 
305 		nopts++;
306 		olen = 0;
307 		op = handleopt(fd, p, val);
308 		if (op == nil)
309 			continue;
310 
311 		/* append OACK response to buf */
312 		nmlen = emits(p, bp, ep);	/* option name */
313 		if (nmlen < 0)
314 			return -1;
315 		bp += nmlen;
316 
317 		if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) {
318 			size = filesize(file);
319 			if (size == -1) {
320 				nak(fd, Errnotfound, "no such file");
321 				syslog(dbg, flog, "tftpd tsize for "
322 					"non-existent file %s", file);
323 				// *op->valp = 0;
324 				// olen = emits("0", bp, ep);
325 				return -1;
326 			}
327 			*op->valp = size;
328 			olen = emitn(size, bp, ep);
329 			syslog(dbg, flog, "tftpd %d %s tsize is %,lld",
330 				pid, file, size);
331 		} else if (oper == Tftp_READ && cistrcmp(p, "blksize") == 0 &&
332 		    blksize > Bandtblksz && blksize != Bcavium) {
333 			*op->valp = blksize = Bandtblksz;
334 			olen = emitn(blksize, bp, ep);
335 			syslog(dbg, flog, "tftpd %d overriding blksize to %d",
336 				pid, blksize);
337 		} else
338 			olen = emits(val, bp, ep);  /* use requested value */
339 	}
340 	if (nopts == 0)
341 		return 0;		/* no options actually seen */
342 
343 	if (write(fd, buf, bp - buf) < bp - buf) {
344 		syslog(dbg, flog, "tftpd network write error on oack to %s: %r",
345 			raddr);
346 		sysfatal("tftpd: network write error: %r");
347 	}
348 	if(Debug)
349 		syslog(dbg, flog, "tftpd oack: options to %s", raddr);
350 	return nopts;
351 }
352 
353 static void
optlog(char * bytes,char * p,int dlen)354 optlog(char *bytes, char *p, int dlen)
355 {
356 	char *bp;
357 
358 	bp = bytes;
359 	sprint(bp, "tftpd %d option bytes: ", dlen);
360 	bp += strlen(bp);
361 	for (; dlen > 0; dlen--, p++)
362 		*bp++ = *p? *p: ' ';
363 	*bp = '\0';
364 	syslog(dbg, flog, "%s", bytes);
365 }
366 
367 /*
368  * replace one occurrence of %[ICE] with ip, cfgpxe name, or ether mac, resp.
369  * we can't easily use $ because u-boot has stranger quoting rules than sh.
370  */
371 char *
mapname(char * file)372 mapname(char *file)
373 {
374 	int nf;
375 	char *p, *newnm, *cur, *arpf, *ln, *remip, *bang;
376 	char *fields[4];
377 	Biobuf *arp;
378 
379 	p = strchr(file, '%');
380 	if (p == nil || p[1] == '\0')
381 		return strdup(file);
382 
383 	remip = strdup(raddr);
384 	newnm = mallocz(strlen(file) + Maxpath, 1);
385 	if (remip == nil || newnm == nil)
386 		sysfatal("out of memory");
387 
388 	bang = strchr(remip, '!');
389 	if (bang)
390 		*bang = '\0';			/* remove !port */
391 
392 	memmove(newnm, file, p - file);		/* copy up to % */
393 	cur = newnm + strlen(newnm);
394 	switch(p[1]) {
395 	case 'I':
396 		strcpy(cur, remip);		/* remote's IP */
397 		break;
398 	case 'C':
399 		strcpy(cur, "/cfg/pxe/");
400 		cur += strlen(cur);
401 		/* fall through */
402 	case 'E':
403 		/* look up remote's IP in /net/arp to get mac. */
404 		arpf = smprint("%s/arp", net);
405 		arp = Bopen(arpf, OREAD);
406 		free(arpf);
407 		if (arp == nil)
408 			break;
409 		/* read lines looking for remip in 3rd field of 4 */
410 		while ((ln = Brdline(arp, '\n')) != nil) {
411 			ln[Blinelen(arp)-1] = 0;
412 			nf = tokenize(ln, fields, nelem(fields));
413 			if (nf >= 4 && strcmp(fields[2], remip) == 0) {
414 				strcpy(cur, fields[3]);
415 				break;
416 			}
417 		}
418 		Bterm(arp);
419 		break;
420 	}
421 	strcat(newnm, p + 2);			/* tail following %x */
422 	free(remip);
423 	return newnm;
424 }
425 
426 void
doserve(int fd)427 doserve(int fd)
428 {
429 	int dlen, opts;
430 	char *mode, *p, *file;
431 	short op;
432 
433 	dlen = read(fd, bigbuf, sizeof(bigbuf)-1);
434 	if(dlen < 0)
435 		sysfatal("listen read: %r");
436 
437 	bigbuf[dlen] = '\0';
438 	op = (bigbuf[0]<<8) | bigbuf[1];
439 	dlen -= Opsize;
440 	mode = file = bigbuf + Opsize;
441 	while(*mode != '\0' && dlen--)
442 		mode++;
443 	mode++;
444 	p = mode;
445 	while(*p && dlen--)
446 		p++;
447 
448 	file = mapname(file);	/* we don't free the result; minor leak */
449 
450 	if(dlen == 0) {
451 		nak(fd, 0, "bad tftpmode");
452 		close(fd);
453 		syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s",
454 			pid, mode, file, raddr);
455 		return;
456 	}
457 
458 	if(op != Tftp_READ && op != Tftp_WRITE) {
459 		nak(fd, Errbadop, "Illegal TFTP operation");
460 		close(fd);
461 		syslog(dbg, flog, "tftpd %d bad request %d (%s) %s", pid, op,
462 			(op < nelem(opnames)? opnames[op]: "gok"), raddr);
463 		return;
464 	}
465 
466 	if(restricted){
467 		if(file[0] == '#' || strncmp(file, "../", 3) == 0 ||
468 		  strstr(file, "/../") != nil ||
469 		  (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){
470 			nak(fd, Errnoaccess, "Permission denied");
471 			close(fd);
472 			syslog(dbg, flog, "tftpd %d bad request %d from %s file %s",
473 				pid, op, raddr, file);
474 			return;
475 		}
476 	}
477 
478 	/*
479 	 * options are supposed to be negotiated, but the cavium board's
480 	 * u-boot really wants us to use a block size of 1432 bytes and won't
481 	 * take `no' for an answer.
482 	 */
483 	p++;				/* skip NUL after mode */
484 	dlen--;
485 	opts = 0;
486 	if(dlen > 0) {			/* might have options */
487 		char bytes[32*1024];
488 
489 		if(Debug)
490 			optlog(bytes, p, dlen);
491 		opts = options(fd, bytes, sizeof bytes, file, op, p, dlen);
492 		if (opts < 0)
493 			return;
494 	}
495 	if(op == Tftp_READ)
496 		sendfile(fd, file, mode, opts);
497 	else
498 		recvfile(fd, file, mode);
499 }
500 
501 void
catcher(void * junk,char * msg)502 catcher(void *junk, char *msg)
503 {
504 	USED(junk);
505 
506 	if(strncmp(msg, "exit", 4) == 0)
507 		noted(NDFLT);
508 	noted(NCONT);
509 }
510 
511 static int
awaitack(int net,int block)512 awaitack(int net, int block)
513 {
514 	int ackblock, al, rxl;
515 	ushort op;
516 	uchar ack[1024];
517 
518 	for(rxl = 0; rxl < 10; rxl++) {
519 		memset(ack, 0, Hdrsize);
520 		alarm(1000);
521 		al = read(net, ack, sizeof(ack));
522 		alarm(0);
523 		if(al < 0) {
524 			if (Debug)
525 				syslog(dbg, flog, "tftpd %d timed out "
526 					"waiting for ack from %s", pid, raddr);
527 			return Ackrexmit;
528 		}
529 		op = ack[0]<<8|ack[1];
530 		if(op == Tftp_ERROR) {
531 			if (Debug)
532 				syslog(dbg, flog, "tftpd %d got error "
533 					"waiting for ack from %s", pid, raddr);
534 			return Ackerr;
535 		} else if(op != Tftp_ACK) {
536 			syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid,
537 				(op < nelem(opnames)? opnames[op]: "gok"),
538 				raddr);
539 			return Ackerr;
540 		}
541 		ackblock = ack[2]<<8|ack[3];
542 		if (Debug)
543 			syslog(dbg, flog, "tftpd %d read ack of %d bytes "
544 				"for block %d", pid, al, ackblock);
545 		if(ackblock == block)
546 			return Ackok;		/* for block just sent */
547 		else if(ackblock == block + 1)	/* intel pxe eof bug */
548 			return Ackok;
549 		else if(ackblock == 0xffff)
550 			return Ackrexmit;
551 		else
552 			/* ack is for some other block; ignore it, try again */
553 			syslog(dbg, flog, "tftpd %d expected ack for block %d, "
554 				"got %d", pid, block, ackblock);
555 	}
556 	return Ackrexmit;
557 }
558 
559 void
sendfile(int net,char * name,char * mode,int opts)560 sendfile(int net, char *name, char *mode, int opts)
561 {
562 	int file, block, ret, rexmit, n, txtry, failed;
563 	uchar buf[Maxsegsize+Hdrsize];
564 	char errbuf[Maxerr];
565 
566 	file = -1;
567 	failed = 1;
568 	syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
569 		pid, name, mode, raddr);
570 	name = sunkernel(name);
571 	if(name == 0){
572 		nak(net, 0, "not in our database");
573 		goto error;
574 	}
575 
576 	notify(catcher);
577 
578 	file = open(name, OREAD);
579 	if(file < 0) {
580 		errstr(errbuf, sizeof errbuf);
581 		nak(net, 0, errbuf);
582 		goto error;
583 	}
584 	block = 0;
585 	rexmit = Ackok;
586 	n = 0;
587 	/*
588 	 * if we sent an oack previously, wait for the client's ack or error.
589 	 * if we get no ack for our oack, it could be that we returned
590 	 * a tsize that the client can't handle, or it could be intel
591 	 * pxe just read-with-tsize to get size, couldn't be bothered to
592 	 * ack our oack and has just gone ahead and issued another read.
593 	 */
594 	if(opts && awaitack(net, 0) != Ackok)
595 		goto error;
596 
597 	for(txtry = 0; txtry < timeout;) {
598 		if(rexmit == Ackok) {
599 			/* block number wraparound for enormous hogs */
600 			if (block >= 65536)
601 				block = 0;
602 			block++;
603 			buf[0] = 0;
604 			buf[1] = Tftp_DATA;
605 			buf[2] = block>>8;
606 			buf[3] = block;
607 			n = read(file, buf+Hdrsize, blksize);
608 			if(n < 0) {
609 				errstr(errbuf, sizeof errbuf);
610 				nak(net, 0, errbuf);
611 				goto error;
612 			}
613 			txtry = 0;
614 		}
615 		else {
616 			syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
617 				pid, Hdrsize+n, name, block, raddr);
618 			txtry++;
619 		}
620 
621 		ret = write(net, buf, Hdrsize+n);
622 		if(ret < Hdrsize+n) {
623 			syslog(dbg, flog,
624 				"tftpd network write error on %s to %s: %r",
625 				name, raddr);
626 			sysfatal("tftpd: network write error: %r");
627 		}
628 		if (Debug)
629 			syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
630 
631 		rexmit = awaitack(net, block);
632 		if (rexmit == Ackerr)
633 			break;
634 		if(ret != blksize+Hdrsize && rexmit == Ackok) {
635 			failed = 0;
636 			break;
637 		}
638 	}
639 error:
640 	syslog(dbg, flog, "tftpd %d %s file '%s' %s to %s",
641 		pid, (failed? "failed to send": "sent"), name, mode, raddr);
642 	close(net);
643 	close(file);
644 }
645 
646 void
recvfile(int net,char * name,char * mode)647 recvfile(int net, char *name, char *mode)
648 {
649 	ushort op, block, inblock;
650 	uchar buf[Maxsegsize+8];
651 	char errbuf[Maxerr];
652 	int n, ret, file;
653 
654 	syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
655 
656 	file = create(name, OWRITE, 0666);
657 	if(file < 0) {
658 		errstr(errbuf, sizeof errbuf);
659 		nak(net, 0, errbuf);
660 		syslog(dbg, flog, "can't create %s: %r", name);
661 		return;
662 	}
663 
664 	block = 0;
665 	ack(net, block);
666 	block++;
667 
668 	for (;;) {
669 		alarm(15000);
670 		n = read(net, buf, blksize+8);
671 		alarm(0);
672 		if(n < 0) {
673 			syslog(dbg, flog, "tftpd: network error reading %s: %r",
674 				name);
675 			goto error;
676 		}
677 		/*
678 		 * NB: not `<='; just a header is legal and happens when
679 		 * file being read is a multiple of segment-size bytes long.
680 		 */
681 		if(n < Hdrsize) {
682 			syslog(dbg, flog,
683 				"tftpd: short read from network, reading %s",
684 				name);
685 			goto error;
686 		}
687 		op = buf[0]<<8|buf[1];
688 		if(op == Tftp_ERROR) {
689 			syslog(dbg, flog, "tftpd: tftp error reading %s", name);
690 			goto error;
691 		}
692 
693 		n -= Hdrsize;
694 		inblock = buf[2]<<8|buf[3];
695 		if(op == Tftp_DATA) {
696 			if(inblock == block) {
697 				ret = write(file, buf+Hdrsize, n);
698 				if(ret != n) {
699 					errstr(errbuf, sizeof errbuf);
700 					nak(net, 0, errbuf);
701 					syslog(dbg, flog,
702 					    "tftpd: error writing %s: %s",
703 						name, errbuf);
704 					goto error;
705 				}
706 				ack(net, block);
707 				block++;
708 			} else
709 				ack(net, 0xffff);	/* tell him to resend */
710 		}
711 	}
712 error:
713 	close(file);
714 }
715 
716 void
ack(int fd,ushort block)717 ack(int fd, ushort block)
718 {
719 	uchar ack[4];
720 	int n;
721 
722 	ack[0] = 0;
723 	ack[1] = Tftp_ACK;
724 	ack[2] = block>>8;
725 	ack[3] = block;
726 
727 	n = write(fd, ack, 4);
728 	if(n < 4)
729 		sysfatal("network write: %r");
730 }
731 
732 void
nak(int fd,int code,char * msg)733 nak(int fd, int code, char *msg)
734 {
735 	char buf[128];
736 	int n;
737 
738 	buf[0] = 0;
739 	buf[1] = Tftp_ERROR;
740 	buf[2] = 0;
741 	buf[3] = code;
742 	strcpy(buf+4, msg);
743 	n = strlen(msg) + 4 + 1;
744 	if(write(fd, buf, n) < n)
745 		sysfatal("write nak: %r");
746 }
747 
748 void
setuser(void)749 setuser(void)
750 {
751 	int fd;
752 
753 	fd = open("#c/user", OWRITE);
754 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
755 		sysfatal("can't become none: %r");
756 	close(fd);
757 	if(newns("none", nil) < 0)
758 		sysfatal("can't build namespace: %r");
759 }
760 
761 char*
lookup(char * sattr,char * sval,char * tattr,char * tval,int len)762 lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
763 {
764 	static Ndb *db;
765 	char *attrs[1];
766 	Ndbtuple *t;
767 
768 	if(db == nil)
769 		db = ndbopen(0);
770 	if(db == nil)
771 		return nil;
772 
773 	if(sattr == nil)
774 		sattr = ipattr(sval);
775 
776 	attrs[0] = tattr;
777 	t = ndbipinfo(db, sattr, sval, attrs, 1);
778 	if(t == nil)
779 		return nil;
780 	strncpy(tval, t->val, len);
781 	tval[len-1] = 0;
782 	ndbfree(t);
783 	return tval;
784 }
785 
786 /*
787  *  for sun kernel boots, replace the requested file name with
788  *  a one from our database.  If the database doesn't specify a file,
789  *  don't answer.
790  */
791 char*
sunkernel(char * name)792 sunkernel(char *name)
793 {
794 	ulong addr;
795 	uchar v4[IPv4addrlen];
796 	uchar v6[IPaddrlen];
797 	char buf[256];
798 	char ipbuf[128];
799 	char *suffix;
800 
801 	addr = strtoul(name, &suffix, 16);
802 	if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
803 		return name;
804 
805 	v4[0] = addr>>24;
806 	v4[1] = addr>>16;
807 	v4[2] = addr>>8;
808 	v4[3] = addr;
809 	v4tov6(v6, v4);
810 	sprint(ipbuf, "%I", v6);
811 	return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
812 }
813 
814 void
remoteaddr(char * dir,char * raddr,int len)815 remoteaddr(char *dir, char *raddr, int len)
816 {
817 	char buf[64];
818 	int fd, n;
819 
820 	snprint(buf, sizeof(buf), "%s/remote", dir);
821 	fd = open(buf, OREAD);
822 	if(fd < 0){
823 		snprint(raddr, sizeof(raddr), "unknown");
824 		return;
825 	}
826 	n = read(fd, raddr, len-1);
827 	close(fd);
828 	if(n <= 0){
829 		snprint(raddr, sizeof(raddr), "unknown");
830 		return;
831 	}
832 	if(n > 0)
833 		n--;
834 	raddr[n] = 0;
835 }
836