]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/tftpd.c
socksd: setnetmtpt
[plan9front.git] / sys / src / cmd / ip / tftpd.c
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 (bp + 3 >= ep)
344                 return -1;
345         *bp++ = '\0';
346         *bp++ = '\0';                   /* overkill */
347         *bp++ = '\0';
348
349         if (write(fd, buf, bp - buf) < bp - buf) {
350                 syslog(dbg, flog, "tftpd network write error on oack to %s: %r",
351                         raddr);
352                 sysfatal("tftpd: network write error: %r");
353         }
354         if(Debug)
355                 syslog(dbg, flog, "tftpd oack: options to %s", raddr);
356         return nopts;
357 }
358
359 static void
360 optlog(char *bytes, char *p, int dlen)
361 {
362         char *bp;
363
364         bp = bytes;
365         sprint(bp, "tftpd %d option bytes: ", dlen);
366         bp += strlen(bp);
367         for (; dlen > 0; dlen--, p++)
368                 *bp++ = *p? *p: ' ';
369         *bp = '\0';
370         syslog(dbg, flog, "%s", bytes);
371 }
372
373 /*
374  * replace one occurrence of %[ICE] with ip, cfgpxe name, or ether mac, resp.
375  * we can't easily use $ because u-boot has stranger quoting rules than sh.
376  */
377 char *
378 mapname(char *file)
379 {
380         int nf;
381         char *p, *newnm, *cur, *arpf, *ln, *remip, *bang;
382         char *fields[4];
383         Biobuf *arp;
384
385         p = strchr(file, '%');
386         if (p == nil || p[1] == '\0')
387                 return strdup(file);
388
389         remip = strdup(raddr);
390         newnm = mallocz(strlen(file) + Maxpath, 1);
391         if (remip == nil || newnm == nil)
392                 sysfatal("out of memory");
393
394         bang = strchr(remip, '!');
395         if (bang)
396                 *bang = '\0';                   /* remove !port */
397
398         memmove(newnm, file, p - file);         /* copy up to % */
399         cur = newnm + strlen(newnm);
400         switch(p[1]) {
401         case 'I':
402                 strcpy(cur, remip);             /* remote's IP */
403                 break;
404         case 'C':
405                 strcpy(cur, "/cfg/pxe/");
406                 cur += strlen(cur);
407                 /* fall through */
408         case 'E':
409                 /* look up remote's IP in /net/arp to get mac. */
410                 arpf = smprint("%s/arp", net);
411                 arp = Bopen(arpf, OREAD);
412                 free(arpf);
413                 if (arp == nil)
414                         break;
415                 /* read lines looking for remip in 3rd field of 4 */
416                 while ((ln = Brdline(arp, '\n')) != nil) {
417                         ln[Blinelen(arp)-1] = 0;
418                         nf = tokenize(ln, fields, nelem(fields));
419                         if (nf >= 4 && strcmp(fields[2], remip) == 0) {
420                                 strcpy(cur, fields[3]);
421                                 break;
422                         }
423                 }
424                 Bterm(arp);
425                 break;
426         }
427         strcat(newnm, p + 2);                   /* tail following %x */
428         free(remip);
429         return newnm;
430 }
431
432 void
433 doserve(int fd)
434 {
435         int dlen, opts;
436         char *mode, *p, *file;
437         short op;
438
439         dlen = read(fd, bigbuf, sizeof(bigbuf)-1);
440         if(dlen < 0)
441                 sysfatal("listen read: %r");
442
443         bigbuf[dlen] = '\0';
444         op = (bigbuf[0]<<8) | bigbuf[1];
445         dlen -= Opsize;
446         mode = file = bigbuf + Opsize;
447         while(*mode != '\0' && dlen--)
448                 mode++;
449         mode++;
450         p = mode;
451         while(*p && dlen--)
452                 p++;
453
454         file = mapname(file);   /* we don't free the result; minor leak */
455
456         if(dlen == 0) {
457                 nak(fd, 0, "bad tftpmode");
458                 close(fd);
459                 syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s",
460                         pid, mode, file, raddr);
461                 return;
462         }
463
464         if(op != Tftp_READ && op != Tftp_WRITE) {
465                 nak(fd, Errbadop, "Illegal TFTP operation");
466                 close(fd);
467                 syslog(dbg, flog, "tftpd %d bad request %d (%s) %s", pid, op,
468                         (op < nelem(opnames)? opnames[op]: "gok"), raddr);
469                 return;
470         }
471
472         if(restricted){
473                 if(file[0] == '#' || strncmp(file, "../", 3) == 0 ||
474                   strstr(file, "/../") != nil ||
475                   (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){
476                         nak(fd, Errnoaccess, "Permission denied");
477                         close(fd);
478                         syslog(dbg, flog, "tftpd %d bad request %d from %s file %s",
479                                 pid, op, raddr, file);
480                         return;
481                 }
482         }
483
484         /*
485          * options are supposed to be negotiated, but the cavium board's
486          * u-boot really wants us to use a block size of 1432 bytes and won't
487          * take `no' for an answer.
488          */
489         p++;                            /* skip NUL after mode */
490         dlen--;
491         opts = 0;
492         if(dlen > 0) {                  /* might have options */
493                 char bytes[32*1024];
494
495                 if(Debug)
496                         optlog(bytes, p, dlen);
497                 opts = options(fd, bytes, sizeof bytes, file, op, p, dlen);
498                 if (opts < 0)
499                         return;
500         }
501         if(op == Tftp_READ)
502                 sendfile(fd, file, mode, opts);
503         else
504                 recvfile(fd, file, mode);
505 }
506
507 void
508 catcher(void *junk, char *msg)
509 {
510         USED(junk);
511
512         if(strncmp(msg, "exit", 4) == 0)
513                 noted(NDFLT);
514         noted(NCONT);
515 }
516
517 static int
518 awaitack(int fd, int block)
519 {
520         int ackblock, al, rxl;
521         ushort op;
522         uchar ack[1024];
523
524         for(rxl = 0; rxl < 10; rxl++) {
525                 memset(ack, 0, Hdrsize);
526                 alarm(1000);
527                 al = read(fd, ack, sizeof(ack));
528                 alarm(0);
529                 if(al < 0) {
530                         if (Debug)
531                                 syslog(dbg, flog, "tftpd %d timed out "
532                                         "waiting for ack from %s", pid, raddr);
533                         return Ackrexmit;
534                 }
535                 op = ack[0]<<8|ack[1];
536                 if(op == Tftp_ERROR) {
537                         if (Debug)
538                                 syslog(dbg, flog, "tftpd %d got error "
539                                         "waiting for ack from %s", pid, raddr);
540                         return Ackerr;
541                 } else if(op != Tftp_ACK) {
542                         syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid,
543                                 (op < nelem(opnames)? opnames[op]: "gok"),
544                                 raddr);
545                         return Ackerr;
546                 }
547                 ackblock = ack[2]<<8|ack[3];
548                 if (Debug)
549                         syslog(dbg, flog, "tftpd %d read ack of %d bytes "
550                                 "for block %d", pid, al, ackblock);
551                 if(ackblock == block)
552                         return Ackok;           /* for block just sent */
553                 else if(ackblock == block + 1)  /* intel pxe eof bug */
554                         return Ackok;
555                 else if(ackblock == 0xffff)
556                         return Ackrexmit;
557                 else
558                         /* ack is for some other block; ignore it, try again */
559                         syslog(dbg, flog, "tftpd %d expected ack for block %d, "
560                                 "got %d", pid, block, ackblock);
561         }
562         return Ackrexmit;
563 }
564
565 void
566 sendfile(int fd, char *name, char *mode, int opts)
567 {
568         int file, block, ret, rexmit, n, txtry;
569         uchar buf[Maxsegsize+Hdrsize];
570         char errbuf[Maxerr];
571
572         file = -1;
573         syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
574                 pid, name, mode, raddr);
575         name = sunkernel(name);
576         if(name == 0){
577                 nak(fd, 0, "not in our database");
578                 goto error;
579         }
580
581         notify(catcher);
582
583         file = open(name, OREAD);
584         if(file < 0) {
585                 errstr(errbuf, sizeof errbuf);
586                 nak(fd, 0, errbuf);
587                 goto error;
588         }
589         block = 0;
590         rexmit = Ackok;
591         n = 0;
592         /*
593          * if we sent an oack previously, wait for the client's ack or error.
594          * if we get no ack for our oack, it could be that we returned
595          * a tsize that the client can't handle, or it could be intel
596          * pxe just read-with-tsize to get size, couldn't be bothered to
597          * ack our oack and has just gone ahead and issued another read.
598          */
599         if(opts && awaitack(fd, 0) != Ackok)
600                 goto error;
601
602         for(txtry = 0; txtry < timeout;) {
603                 if(rexmit == Ackok) {
604                         block++;
605                         buf[0] = 0;
606                         buf[1] = Tftp_DATA;
607                         buf[2] = block>>8;
608                         buf[3] = block;
609                         n = read(file, buf+Hdrsize, blksize);
610                         if(n < 0) {
611                                 errstr(errbuf, sizeof errbuf);
612                                 nak(fd, 0, errbuf);
613                                 goto error;
614                         }
615                         txtry = 0;
616                 }
617                 else {
618                         syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
619                                 pid, Hdrsize+n, name, block, raddr);
620                         txtry++;
621                 }
622
623                 ret = write(fd, buf, Hdrsize+n);
624                 if(ret < Hdrsize+n) {
625                         syslog(dbg, flog,
626                                 "tftpd network write error on %s to %s: %r",
627                                 name, raddr);
628                         sysfatal("tftpd: network write error: %r");
629                 }
630                 if (Debug)
631                         syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
632
633                 rexmit = awaitack(fd, block);
634                 if (rexmit == Ackerr)
635                         break;
636                 if(ret != blksize+Hdrsize && rexmit == Ackok)
637                         break;
638         }
639         syslog(dbg, flog, "tftpd %d done sending file '%s' %s to %s",
640                 pid, name, mode, raddr);
641 error:
642         close(fd);
643         close(file);
644 }
645
646 void
647 recvfile(int fd, 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(fd, 0, errbuf);
660                 syslog(dbg, flog, "can't create %s: %r", name);
661                 return;
662         }
663
664         block = 0;
665         ack(fd, block);
666         block++;
667
668         for (;;) {
669                 alarm(15000);
670                 n = read(fd, 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(fd, 0, errbuf);
701                                         syslog(dbg, flog,
702                                             "tftpd: error writing %s: %s",
703                                                 name, errbuf);
704                                         goto error;
705                                 }
706                                 ack(fd, block);
707                                 block++;
708                         } else
709                                 ack(fd, 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 }