]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/tftpd.c
Import sources from 2011-03-30 iso image - lib
[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 };
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                         syslog(dbg, flog, "tftpd %d setting %s to %d",
222                                 pid, name, n);
223                         return op;
224                 }
225         return nil;
226 }
227
228 static vlong
229 filesize(char *file)
230 {
231         vlong size;
232         Dir *dp;
233
234         dp = dirstat(file);
235         if (dp == nil)
236                 return 0;
237         size = dp->length;
238         free(dp);
239         return size;
240 }
241
242 static int
243 options(int fd, char *buf, char *file, ushort oper, char *p, int dlen)
244 {
245         int nmlen, vallen, nopts;
246         vlong size;
247         char *val, *bp;
248         Opt *op;
249
250         buf[0] = 0;
251         buf[1] = Tftp_OACK;
252         bp = buf + Opsize;
253         nopts = 0;
254         while (dlen > 0 && *p != '\0') {
255                 nmlen = strlen(p) + 1;          /* include NUL */
256                 if (nmlen > dlen)
257                         break;
258                 dlen -= nmlen;
259                 val = p + nmlen;
260                 if (dlen <= 0 || *val == '\0')
261                         break;
262
263                 vallen = strlen(val) + 1;
264                 if (vallen > dlen)
265                         break;
266                 dlen -= vallen;
267
268                 nopts++;
269                 op = handleopt(fd, p, val);
270                 if (op) {
271                         /* append OACK response to buf */
272                         sprint(bp, "%s", p);
273                         bp += nmlen;
274                         if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) {
275                                 size = filesize(file);
276                                 sprint(bp, "%lld", size);
277                                 syslog(dbg, flog, "tftpd %d %s tsize is %,lld",
278                                         pid, file, size);
279                         }
280                         /*
281                          * hack: bandt (viaducts) uses smaller mtu than ether's
282                          * (1400 bytes for tcp mss of 1300 bytes),
283                          * so offer at most bandt's mtu minus headers,
284                          * to avoid failure of pxe booting via viaduct.
285                          */
286                         else if (oper == Tftp_READ &&
287                             cistrcmp(p, "blksize") == 0 &&
288                             blksize > Bandtblksz) {
289                                 blksize = Bandtblksz;
290                                 sprint(bp, "%d", blksize);
291                                 syslog(dbg, flog,
292                                         "tftpd %d overriding blksize to %d",
293                                         pid, blksize);
294                         } else
295                                 strcpy(bp, val);  /* use requested value */
296                         bp += strlen(bp) + 1;
297                 }
298                 p = val + vallen;
299         }
300         if (nopts == 0)
301                 return 0;               /* no options actually seen */
302         *bp++ = '\0';
303         *bp++ = '\0';                   /* overkill */
304         *bp++ = '\0';
305         if (write(fd, buf, bp - buf) < bp - buf) {
306                 syslog(dbg, flog, "tftpd network write error on oack to %s: %r",
307                         raddr);
308                 sysfatal("tftpd: network write error: %r");
309         }
310         if(Debug)
311                 syslog(dbg, flog, "tftpd oack: options to %s", raddr);
312         return nopts;
313 }
314
315 /* this doesn't stop the cavium from barging ahead */
316 //static void
317 //sendnoopts(int fd, char *name)
318 //{
319 //      char buf[64];
320 //
321 //      memset(buf, 0, sizeof buf);
322 //      buf[0] = 0;
323 //      buf[1] = Tftp_OACK;
324 //
325 //      if(write(fd, buf, sizeof buf) < sizeof buf) {
326 //              syslog(dbg, flog, "tftpd network write error on %s oack to %s: %r",
327 //                      name, raddr);
328 //              sysfatal("tftpd: network write error: %r");
329 //      }
330 //      if(Debug)
331 //              syslog(dbg, flog, "tftpd oack: no options to %s", raddr);
332 //}
333
334 static void
335 optlog(char *bytes, char *p, int dlen)
336 {
337         char *bp;
338
339         bp = bytes;
340         sprint(bp, "tftpd %d option bytes: ", dlen);
341         bp += strlen(bp);
342         for (; dlen > 0; dlen--, p++)
343                 *bp++ = *p? *p: ' ';
344         *bp = '\0';
345         syslog(dbg, flog, "%s", bytes);
346 }
347
348 void
349 doserve(int fd)
350 {
351         int dlen, opts;
352         char *mode, *p, *file;
353         short op;
354
355         dlen = read(fd, bigbuf, sizeof(bigbuf)-1);
356         if(dlen < 0)
357                 sysfatal("listen read: %r");
358
359         bigbuf[dlen] = '\0';
360         op = (bigbuf[0]<<8) | bigbuf[1];
361         dlen -= Opsize;
362         mode = file = bigbuf + Opsize;
363         while(*mode != '\0' && dlen--)
364                 mode++;
365         mode++;
366         p = mode;
367         while(*p && dlen--)
368                 p++;
369         if(dlen == 0) {
370                 nak(fd, 0, "bad tftpmode");
371                 close(fd);
372                 syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s",
373                         pid, mode, file, raddr);
374                 return;
375         }
376
377         if(op != Tftp_READ && op != Tftp_WRITE) {
378                 nak(fd, Errbadop, "Illegal TFTP operation");
379                 close(fd);
380                 syslog(dbg, flog, "tftpd %d bad request %d %s", pid, op, raddr);
381                 return;
382         }
383
384         if(restricted){
385                 if(file[0] == '#' || strncmp(file, "../", 3) == 0 ||
386                   strstr(file, "/../") != nil ||
387                   (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){
388                         nak(fd, Errnoaccess, "Permission denied");
389                         close(fd);
390                         syslog(dbg, flog, "tftpd %d bad request %d from %s file %s",
391                                 pid, op, raddr, file);
392                         return;
393                 }
394         }
395
396         /*
397          * options are supposed to be negotiated, but the cavium board's
398          * u-boot really wants us to use a block size of 1432 bytes and won't
399          * take `no' for an answer.
400          */
401         p++;                            /* skip NUL after mode */
402         dlen--;
403         opts = 0;
404         if(dlen > 0) {                  /* might have options */
405                 char bytes[32*1024];
406
407                 if(Debug)
408                         optlog(bytes, p, dlen);
409                 opts = options(fd, bytes, file, op, p, dlen);
410         }
411         if(op == Tftp_READ)
412                 sendfile(fd, file, mode, opts);
413         else
414                 recvfile(fd, file, mode);
415 }
416
417 void
418 catcher(void *junk, char *msg)
419 {
420         USED(junk);
421
422         if(strncmp(msg, "exit", 4) == 0)
423                 noted(NDFLT);
424         noted(NCONT);
425 }
426
427 static int
428 awaitack(int fd, int block)
429 {
430         int ackblock, al, rxl;
431         ushort op;
432         uchar ack[1024];
433
434         for(rxl = 0; rxl < 10; rxl++) {
435                 memset(ack, 0, Hdrsize);
436                 alarm(1000);
437                 al = read(fd, ack, sizeof(ack));
438                 alarm(0);
439                 if(al < 0) {
440                         if (Debug)
441                                 syslog(dbg, flog, "tftpd %d timed out "
442                                         "waiting for ack from %s", pid, raddr);
443                         return Ackrexmit;
444                 }
445                 op = ack[0]<<8|ack[1];
446                 if(op == Tftp_ERROR) {
447                         if (Debug)
448                                 syslog(dbg, flog, "tftpd %d got error "
449                                         "waiting for ack from %s", pid, raddr);
450                         return Ackerr;
451                 } else if(op != Tftp_ACK) {
452                         syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid,
453                                 (op < nelem(opnames)? opnames[op]: "gok"),
454                                 raddr);
455                         return Ackerr;
456                 }
457                 ackblock = ack[2]<<8|ack[3];
458                 if (Debug)
459                         syslog(dbg, flog, "tftpd %d read ack of %d bytes "
460                                 "for block %d", pid, al, ackblock);
461                 if(ackblock == block)
462                         return Ackok;           /* for block just sent */
463                 else if(ackblock == block + 1)  /* intel pxe eof bug */
464                         return Ackok;
465                 else if(ackblock == 0xffff)
466                         return Ackrexmit;
467                 else
468                         /* ack is for some other block; ignore it, try again */
469                         syslog(dbg, flog, "tftpd %d expected ack for block %d, "
470                                 "got %d", pid, block, ackblock);
471         }
472         return Ackrexmit;
473 }
474
475 void
476 sendfile(int fd, char *name, char *mode, int opts)
477 {
478         int file, block, ret, rexmit, n, txtry;
479         uchar buf[Maxsegsize+Hdrsize];
480         char errbuf[Maxerr];
481
482         syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
483                 pid, name, mode, raddr);
484         name = sunkernel(name);
485         if(name == 0){
486                 nak(fd, 0, "not in our database");
487                 return;
488         }
489
490         notify(catcher);
491
492         file = open(name, OREAD);
493         if(file < 0) {
494                 errstr(errbuf, sizeof errbuf);
495                 nak(fd, 0, errbuf);
496                 return;
497         }
498         block = 0;
499         rexmit = Ackok;
500         n = 0;
501         /*
502          * if we sent an oack previously, wait for the client's ack or error.
503          * if we get no ack for our oack, it could be that we returned
504          * a tsize that the client can't handle, or it could be intel
505          * pxe just read-with-tsize to get size, couldn't be bothered to
506          * ack our oack and has just gone ahead and issued another read.
507          */
508         if(opts && awaitack(fd, 0) != Ackok)
509                 goto error;
510
511         for(txtry = 0; txtry < timeout;) {
512                 if(rexmit == Ackok) {
513                         block++;
514                         buf[0] = 0;
515                         buf[1] = Tftp_DATA;
516                         buf[2] = block>>8;
517                         buf[3] = block;
518                         n = read(file, buf+Hdrsize, blksize);
519                         if(n < 0) {
520                                 errstr(errbuf, sizeof errbuf);
521                                 nak(fd, 0, errbuf);
522                                 return;
523                         }
524                         txtry = 0;
525                 }
526                 else {
527                         syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
528                                 pid, Hdrsize+n, name, block, raddr);
529                         txtry++;
530                 }
531
532                 ret = write(fd, buf, Hdrsize+n);
533                 if(ret < Hdrsize+n) {
534                         syslog(dbg, flog,
535                                 "tftpd network write error on %s to %s: %r",
536                                 name, raddr);
537                         sysfatal("tftpd: network write error: %r");
538                 }
539                 if (Debug)
540                         syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
541
542                 rexmit = awaitack(fd, block);
543                 if (rexmit == Ackerr)
544                         break;
545                 if(ret != blksize+Hdrsize && rexmit == Ackok)
546                         break;
547         }
548 error:
549         close(fd);
550         close(file);
551 }
552
553 void
554 recvfile(int fd, char *name, char *mode)
555 {
556         ushort op, block, inblock;
557         uchar buf[Maxsegsize+8];
558         char errbuf[Maxerr];
559         int n, ret, file;
560
561         syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
562
563         file = create(name, OWRITE, 0666);
564         if(file < 0) {
565                 errstr(errbuf, sizeof errbuf);
566                 nak(fd, 0, errbuf);
567                 syslog(dbg, flog, "can't create %s: %r", name);
568                 return;
569         }
570
571         block = 0;
572         ack(fd, block);
573         block++;
574
575         for (;;) {
576                 alarm(15000);
577                 n = read(fd, buf, blksize+8);
578                 alarm(0);
579                 if(n < 0) {
580                         syslog(dbg, flog, "tftpd: network error reading %s: %r",
581                                 name);
582                         goto error;
583                 }
584                 if(n <= Hdrsize) {
585                         syslog(dbg, flog,
586                                 "tftpd: short read from network, reading %s",
587                                 name);
588                         goto error;
589                 }
590                 op = buf[0]<<8|buf[1];
591                 if(op == Tftp_ERROR) {
592                         syslog(dbg, flog, "tftpd: tftp error reading %s", name);
593                         goto error;
594                 }
595
596                 n -= Hdrsize;
597                 inblock = buf[2]<<8|buf[3];
598                 if(op == Tftp_DATA) {
599                         if(inblock == block) {
600                                 ret = write(file, buf+Hdrsize, n);
601                                 if(ret != n) {
602                                         errstr(errbuf, sizeof errbuf);
603                                         nak(fd, 0, errbuf);
604                                         syslog(dbg, flog,
605                                             "tftpd: error writing %s: %s",
606                                                 name, errbuf);
607                                         goto error;
608                                 }
609                                 ack(fd, block);
610                                 block++;
611                         } else
612                                 ack(fd, 0xffff);        /* tell him to resend */
613                 }
614         }
615 error:
616         close(file);
617 }
618
619 void
620 ack(int fd, ushort block)
621 {
622         uchar ack[4];
623         int n;
624
625         ack[0] = 0;
626         ack[1] = Tftp_ACK;
627         ack[2] = block>>8;
628         ack[3] = block;
629
630         n = write(fd, ack, 4);
631         if(n < 4)
632                 sysfatal("network write: %r");
633 }
634
635 void
636 nak(int fd, int code, char *msg)
637 {
638         char buf[128];
639         int n;
640
641         buf[0] = 0;
642         buf[1] = Tftp_ERROR;
643         buf[2] = 0;
644         buf[3] = code;
645         strcpy(buf+4, msg);
646         n = strlen(msg) + 4 + 1;
647         if(write(fd, buf, n) < n)
648                 sysfatal("write nak: %r");
649 }
650
651 void
652 setuser(void)
653 {
654         int fd;
655
656         fd = open("#c/user", OWRITE);
657         if(fd < 0 || write(fd, "none", strlen("none")) < 0)
658                 sysfatal("can't become none: %r");
659         close(fd);
660         if(newns("none", nil) < 0)
661                 sysfatal("can't build namespace: %r");
662 }
663
664 char*
665 lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
666 {
667         static Ndb *db;
668         char *attrs[1];
669         Ndbtuple *t;
670
671         if(db == nil)
672                 db = ndbopen(0);
673         if(db == nil)
674                 return nil;
675
676         if(sattr == nil)
677                 sattr = ipattr(sval);
678
679         attrs[0] = tattr;
680         t = ndbipinfo(db, sattr, sval, attrs, 1);
681         if(t == nil)
682                 return nil;
683         strncpy(tval, t->val, len);
684         tval[len-1] = 0;
685         ndbfree(t);
686         return tval;
687 }
688
689 /*
690  *  for sun kernel boots, replace the requested file name with
691  *  a one from our database.  If the database doesn't specify a file,
692  *  don't answer.
693  */
694 char*
695 sunkernel(char *name)
696 {
697         ulong addr;
698         uchar v4[IPv4addrlen];
699         uchar v6[IPaddrlen];
700         char buf[256];
701         char ipbuf[128];
702         char *suffix;
703
704         addr = strtoul(name, &suffix, 16);
705         if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
706                 return name;
707
708         v4[0] = addr>>24;
709         v4[1] = addr>>16;
710         v4[2] = addr>>8;
711         v4[3] = addr;
712         v4tov6(v6, v4);
713         sprint(ipbuf, "%I", v6);
714         return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
715 }
716
717 void
718 remoteaddr(char *dir, char *raddr, int len)
719 {
720         char buf[64];
721         int fd, n;
722
723         snprint(buf, sizeof(buf), "%s/remote", dir);
724         fd = open(buf, OREAD);
725         if(fd < 0){
726                 snprint(raddr, sizeof(raddr), "unknown");
727                 return;
728         }
729         n = read(fd, raddr, len-1);
730         close(fd);
731         if(n <= 0){
732                 snprint(raddr, sizeof(raddr), "unknown");
733                 return;
734         }
735         if(n > 0)
736                 n--;
737         raddr[n] = 0;
738 }