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