]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/tftpd.c
fix fuckup
[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
15         Debug=          0,
16
17         Opsize=         sizeof(short),
18         Blksize=        sizeof(short),
19         Hdrsize=        Opsize + Blksize,
20
21         Ackerr=         -1,
22         Ackok=          0,
23         Ackrexmit=      1,
24
25         /* op codes */
26         Tftp_READ       = 1,
27         Tftp_WRITE      = 2,
28         Tftp_DATA       = 3,
29         Tftp_ACK        = 4,
30         Tftp_ERROR      = 5,
31         Tftp_OACK       = 6,            /* option acknowledge */
32
33         Errnotdef       = 0,            /* see textual error instead */
34         Errnotfound     = 1,
35         Errnoaccess     = 2,
36         Errdiskfull     = 3,
37         Errbadop        = 4,
38         Errbadtid       = 5,
39         Errexists       = 6,
40         Errnouser       = 7,
41         Errbadopt       = 8,            /* really bad option value */
42
43         Defsegsize      = 512,
44         Maxsegsize      = 65464,        /* from rfc2348 */
45
46         /*
47          * bandt (viaduct) tunnels use smaller mtu than ether's
48          * (1400 bytes for tcp mss of 1300 bytes).
49          */
50         Bandtmtu        = 1400,
51         /*
52          * maximum size of block's data content, excludes hdrs,
53          * notably IP/UDP and TFTP, using worst-case (IPv6) sizes.
54          */
55         Bandtblksz      = Bandtmtu - 40 - 8,
56         Bcavium         = 1432,         /* cavium's u-boot demands this size */
57 };
58
59 typedef struct Opt Opt;
60 struct Opt {
61         char    *name;
62         int     *valp;          /* set to client's value if within bounds */
63         int     min;
64         int     max;
65 };
66
67 int     dbg;
68 int     restricted;
69 int     pid;
70
71 /* options */
72 int     blksize = Defsegsize;           /* excluding 4-byte header */
73 int     timeout = 5;                    /* seconds */
74 int     tsize;
75 static Opt option[] = {
76         "timeout",      &timeout,       1,      255,
77         /* see "hack" below */
78         "blksize",      &blksize,       8,      Maxsegsize,
79         "tsize",        &tsize,         0,      ~0UL >> 1,
80 };
81
82 void    sendfile(int, char*, char*, int);
83 void    recvfile(int, char*, char*);
84 void    nak(int, int, char*);
85 void    ack(int, ushort);
86 void    clrcon(void);
87 void    setuser(void);
88 char*   sunkernel(char*);
89 void    remoteaddr(char*, char*, int);
90 void    doserve(int);
91
92 char    bigbuf[32768];
93 char    raddr[64];
94
95 char    *dir = "/lib/tftpd";
96 char    *dirsl;
97 int     dirsllen;
98 char    flog[] = "ipboot";
99 char    net[Maxpath];
100
101 static char *opnames[] = {
102 [Tftp_READ]     "read",
103 [Tftp_WRITE]    "write",
104 [Tftp_DATA]     "data",
105 [Tftp_ACK]      "ack",
106 [Tftp_ERROR]    "error",
107 [Tftp_OACK]     "oack",
108 };
109
110 void
111 usage(void)
112 {
113         fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
114                 argv0);
115         exits("usage");
116 }
117
118 void
119 main(int argc, char **argv)
120 {
121         char buf[64];
122         char adir[64], ldir[64];
123         int cfd, lcfd, dfd;
124         char *svc = "69";
125
126         setnetmtpt(net, sizeof net, nil);
127         ARGBEGIN{
128         case 'd':
129                 dbg++;
130                 break;
131         case 'h':
132                 dir = EARGF(usage());
133                 break;
134         case 'r':
135                 restricted = 1;
136                 break;
137         case 's':
138                 svc = EARGF(usage());
139                 break;
140         case 'x':
141                 setnetmtpt(net, sizeof net, EARGF(usage()));
142                 break;
143         default:
144                 usage();
145         }ARGEND
146
147         snprint(buf, sizeof buf, "%s/", dir);
148         dirsl = strdup(buf);
149         dirsllen = strlen(dirsl);
150
151         fmtinstall('E', eipfmt);
152         fmtinstall('I', eipfmt);
153
154         /*
155          * setuser calls newns, and typical /lib/namespace files contain
156          * "cd /usr/$user", so call setuser before chdir.
157          */
158         setuser();
159         if(chdir(dir) < 0)
160                 sysfatal("can't get to directory %s: %r", dir);
161
162         if(!dbg)
163                 switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
164                 case -1:
165                         sysfatal("fork: %r");
166                 case 0:
167                         break;
168                 default:
169                         exits(0);
170                 }
171
172         snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
173         cfd = announce(buf, adir);
174         if (cfd < 0)
175                 sysfatal("announcing on %s: %r", buf);
176         syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
177 //      setuser();
178         for(;;) {
179                 lcfd = listen(adir, ldir);
180                 if(lcfd < 0)
181                         sysfatal("listening on %s: %r", adir);
182
183                 switch(fork()) {
184                 case -1:
185                         sysfatal("fork: %r");
186                 case 0:
187                         dfd = accept(lcfd, ldir);
188                         if(dfd < 0)
189                                 exits(0);
190                         remoteaddr(ldir, raddr, sizeof(raddr));
191                         pid = getpid();
192                         syslog(0, flog, "tftp %d connection from %s dir %s",
193                                 pid, raddr, ldir);
194                         doserve(dfd);
195                         exits("done");
196                         break;
197                 default:
198                         close(lcfd);
199                         continue;
200                 }
201         }
202 }
203
204 static Opt *
205 handleopt(int fd, char *name, char *val)
206 {
207         int n;
208         Opt *op;
209
210         for (op = option; op < option + nelem(option); op++)
211                 if(cistrcmp(name, op->name) == 0) {
212                         n = strtol(val, nil, 10);
213                         if (n < op->min || n > op->max) {
214                                 nak(fd, Errbadopt, "option value out of range");
215                                 syslog(dbg, flog, "tftp bad option value from "
216                                         "client: %s %s", name, val);
217                                 sysfatal("bad option value from client: %s %s",
218                                         name, val);
219                         }
220                         *op->valp = n;
221                         /* incoming 0 for tsize is uninteresting */
222                         if(cistrcmp("tsize", op->name) != 0)
223                                 syslog(dbg, flog, "tftpd %d setting %s to client's %d",
224                                         pid, name, n);
225                         return op;
226                 }
227         return nil;
228 }
229
230 static vlong
231 filesize(char *file)
232 {
233         vlong size;
234         Dir *dp;
235
236         dp = dirstat(file);
237         if (dp == nil)
238                 return -1;
239         size = dp->length;
240         free(dp);
241         return size;
242 }
243
244 /* copy word into bp iff it fits before ep, returns bytes to advance bp. */
245 static int
246 emits(char *word, char *bp, char *ep)
247 {
248         int len;
249
250         len = strlen(word) + 1;
251         if (bp + len >= ep)
252                 return -1;
253         strcpy(bp, word);
254         return len;
255 }
256
257 /* format number into bp iff it fits before ep. */
258 static int
259 emitn(vlong n, char *bp, char *ep)
260 {
261         char numb[32];
262
263         snprint(numb, sizeof numb, "%lld", n);
264         return emits(numb, bp, ep);
265 }
266
267 /*
268  * send an OACK packet to respond to options.  bail early with -1 on error.
269  * p is the packet containing the options.
270  *
271  * hack: bandt (viaducts) uses smaller mtu than ether's
272  * (1400 bytes for tcp mss of 1300 bytes),
273  * so offer at most bandt's mtu minus headers,
274  * to avoid failure of pxe booting via viaduct.
275  * there's an exception for the cavium's u-boot.
276  */
277 static int
278 options(int fd, char *buf, int bufsz, char *file, ushort oper, char *p, int dlen)
279 {
280         int nmlen, vallen, olen, nopts;
281         vlong size;
282         char *val, *bp, *ep;
283         Opt *op;
284
285         buf[0] = 0;
286         buf[1] = Tftp_OACK;
287         bp = buf + Opsize;
288         ep = buf + bufsz;
289         nopts = 0;
290         for (; dlen > 0 && *p != '\0'; p = val + vallen, bp += olen) {
291                 nmlen = strlen(p) + 1;          /* include NUL */
292                 if (nmlen > dlen)
293                         break;
294                 dlen -= nmlen;
295                 val = p + nmlen;
296                 if (dlen <= 0 || *val == '\0')
297                         break;
298
299                 vallen = strlen(val) + 1;
300                 if (vallen > dlen)
301                         break;
302                 dlen -= vallen;
303
304                 olen = 0;
305                 op = handleopt(fd, p, val);
306                 if (op == nil)
307                         continue;
308
309                 nopts++;
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 fd, 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(fd, 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 fd, char *name, char *mode, int opts)
561 {
562         int file, block, ret, rexmit, n, txtry;
563         uchar buf[Maxsegsize+Hdrsize];
564         char errbuf[ERRMAX];
565
566         file = -1;
567         syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
568                 pid, name, mode, raddr);
569         name = sunkernel(name);
570         if(name == 0){
571                 nak(fd, 0, "not in our database");
572                 goto error;
573         }
574
575         notify(catcher);
576
577         file = open(name, OREAD);
578         if(file < 0) {
579                 errstr(errbuf, sizeof errbuf);
580                 nak(fd, 0, errbuf);
581                 goto error;
582         }
583         block = 0;
584         rexmit = Ackok;
585         n = 0;
586         /*
587          * if we sent an oack previously, wait for the client's ack or error.
588          * if we get no ack for our oack, it could be that we returned
589          * a tsize that the client can't handle, or it could be intel
590          * pxe just read-with-tsize to get size, couldn't be bothered to
591          * ack our oack and has just gone ahead and issued another read.
592          */
593         if(opts && awaitack(fd, 0) != Ackok)
594                 goto error;
595
596         for(txtry = 0; txtry < timeout;) {
597                 if(rexmit == Ackok) {
598                         block++;
599                         buf[0] = 0;
600                         buf[1] = Tftp_DATA;
601                         buf[2] = block>>8;
602                         buf[3] = block;
603                         n = read(file, buf+Hdrsize, blksize);
604                         if(n < 0) {
605                                 errstr(errbuf, sizeof errbuf);
606                                 nak(fd, 0, errbuf);
607                                 goto error;
608                         }
609                         txtry = 0;
610                 }
611                 else {
612                         syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
613                                 pid, Hdrsize+n, name, block, raddr);
614                         txtry++;
615                 }
616
617                 ret = write(fd, buf, Hdrsize+n);
618                 if(ret < Hdrsize+n) {
619                         syslog(dbg, flog,
620                                 "tftpd network write error on %s to %s: %r",
621                                 name, raddr);
622                         sysfatal("tftpd: network write error: %r");
623                 }
624                 if (Debug)
625                         syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
626
627                 rexmit = awaitack(fd, block);
628                 if (rexmit == Ackerr)
629                         break;
630                 if(ret != blksize+Hdrsize && rexmit == Ackok)
631                         break;
632         }
633         syslog(dbg, flog, "tftpd %d done sending file '%s' %s to %s",
634                 pid, name, mode, raddr);
635 error:
636         close(fd);
637         close(file);
638 }
639
640 void
641 recvfile(int fd, char *name, char *mode)
642 {
643         ushort op, block, inblock;
644         uchar buf[Maxsegsize+8];
645         char errbuf[ERRMAX];
646         int n, ret, file;
647
648         syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
649
650         file = create(name, OWRITE, 0666);
651         if(file < 0) {
652                 errstr(errbuf, sizeof errbuf);
653                 nak(fd, 0, errbuf);
654                 syslog(dbg, flog, "can't create %s: %s", name, errbuf);
655                 return;
656         }
657
658         block = 0;
659         ack(fd, block);
660         block++;
661
662         for (;;) {
663                 alarm(15000);
664                 n = read(fd, buf, blksize+8);
665                 alarm(0);
666                 if(n < 0) {
667                         syslog(dbg, flog, "tftpd: network error reading %s: %r",
668                                 name);
669                         goto error;
670                 }
671                 /*
672                  * NB: not `<='; just a header is legal and happens when
673                  * file being read is a multiple of segment-size bytes long.
674                  */
675                 if(n < Hdrsize) {
676                         syslog(dbg, flog,
677                                 "tftpd: short read from network, reading %s",
678                                 name);
679                         goto error;
680                 }
681                 op = buf[0]<<8|buf[1];
682                 if(op == Tftp_ERROR) {
683                         syslog(dbg, flog, "tftpd: tftp error reading %s", name);
684                         goto error;
685                 }
686
687                 n -= Hdrsize;
688                 inblock = buf[2]<<8|buf[3];
689                 if(op == Tftp_DATA) {
690                         if(inblock == block) {
691                                 ret = write(file, buf+Hdrsize, n);
692                                 if(ret != n) {
693                                         errstr(errbuf, sizeof errbuf);
694                                         nak(fd, 0, errbuf);
695                                         syslog(dbg, flog,
696                                             "tftpd: error writing %s: %s",
697                                                 name, errbuf);
698                                         goto error;
699                                 }
700                                 ack(fd, block);
701                                 block++;
702                         } else
703                                 ack(fd, 0xffff);        /* tell him to resend */
704                 }
705         }
706 error:
707         close(file);
708 }
709
710 void
711 ack(int fd, ushort block)
712 {
713         uchar ack[4];
714         int n;
715
716         ack[0] = 0;
717         ack[1] = Tftp_ACK;
718         ack[2] = block>>8;
719         ack[3] = block;
720
721         n = write(fd, ack, 4);
722         if(n < 4)
723                 sysfatal("network write: %r");
724 }
725
726 void
727 nak(int fd, int code, char *msg)
728 {
729         char buf[128];
730         int n;
731
732         n = 5 + strlen(msg);
733         if(n > sizeof(buf))
734                 n = sizeof(buf);
735         buf[0] = 0;
736         buf[1] = Tftp_ERROR;
737         buf[2] = 0;
738         buf[3] = code;
739         memmove(buf+4, msg, n - 5);
740         buf[n-1] = 0;
741         if(write(fd, buf, n) != n)
742                 sysfatal("write nak: %r");
743 }
744
745 void
746 setuser(void)
747 {
748         int fd;
749
750         fd = open("#c/user", OWRITE);
751         if(fd < 0 || write(fd, "none", strlen("none")) < 0)
752                 sysfatal("can't become none: %r");
753         close(fd);
754         if(newns("none", nil) < 0)
755                 sysfatal("can't build namespace: %r");
756 }
757
758 char*
759 lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
760 {
761         static Ndb *db;
762         char *attrs[1];
763         Ndbtuple *t;
764
765         if(db == nil)
766                 db = ndbopen(0);
767         if(db == nil)
768                 return nil;
769
770         if(sattr == nil)
771                 sattr = ipattr(sval);
772
773         attrs[0] = tattr;
774         t = ndbipinfo(db, sattr, sval, attrs, 1);
775         if(t == nil)
776                 return nil;
777         strncpy(tval, t->val, len);
778         tval[len-1] = 0;
779         ndbfree(t);
780         return tval;
781 }
782
783 /*
784  *  for sun kernel boots, replace the requested file name with
785  *  a one from our database.  If the database doesn't specify a file,
786  *  don't answer.
787  */
788 char*
789 sunkernel(char *name)
790 {
791         ulong addr;
792         uchar v4[IPv4addrlen];
793         uchar v6[IPaddrlen];
794         char buf[256];
795         char ipbuf[128];
796         char *suffix;
797
798         addr = strtoul(name, &suffix, 16);
799         if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
800                 return name;
801
802         v4[0] = addr>>24;
803         v4[1] = addr>>16;
804         v4[2] = addr>>8;
805         v4[3] = addr;
806         v4tov6(v6, v4);
807         sprint(ipbuf, "%I", v6);
808         return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
809 }
810
811 void
812 remoteaddr(char *dir, char *raddr, int len)
813 {
814         char buf[64];
815         int fd, n;
816
817         snprint(buf, sizeof(buf), "%s/remote", dir);
818         fd = open(buf, OREAD);
819         if(fd < 0){
820                 snprint(raddr, sizeof(raddr), "unknown");
821                 return;
822         }
823         n = read(fd, raddr, len-1);
824         close(fd);
825         if(n <= 0){
826                 snprint(raddr, sizeof(raddr), "unknown");
827                 return;
828         }
829         if(n > 0)
830                 n--;
831         raddr[n] = 0;
832 }