Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_tlsv12/sys/src/cmd/ip/tftpd.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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 *
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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*
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*
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
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
}