Ditch the sysvinit stuff
[profile/ivi/iputils.git] / tftpd.c
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)tftpd.c     5.13 (Berkeley) 2/26/91";*/
42 /*static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $";*/
43 #endif /* not lint */
44
45 /*
46  * Trivial file transfer protocol server.
47  *
48  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
49  */
50
51 #include <sys/types.h>
52 #include <sys/ioctl.h>
53 #include <sys/stat.h>
54 #include <unistd.h>
55 #include <signal.h>
56 #include <fcntl.h>
57
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <netdb.h>
61
62 #include <setjmp.h>
63 #include <syslog.h>
64 #include <stdio.h>
65 #include <errno.h>
66 #include <ctype.h>
67 #include <string.h>
68 #include <stdlib.h>
69
70 #include "tftp.h"
71
72 #ifndef MSG_CONFIRM
73 #define MSG_CONFIRM 0
74 #warning Please, upgrade kernel, otherwise this tftpd has no advantages.
75 #endif
76
77 #define TIMEOUT         5
78
79 int     peer;
80 int     rexmtval = TIMEOUT;
81 int     maxtimeout = 5*TIMEOUT;
82
83 #define PKTSIZE SEGSIZE+4
84 char    buf[PKTSIZE];
85 char    ackbuf[PKTSIZE];
86 union {
87         struct  sockaddr     sa;
88         struct  sockaddr_in  sin;
89         struct  sockaddr_in6 sin6;
90 } from;
91 socklen_t       fromlen;
92
93 #define MAXARG  1
94 char    *dirs[MAXARG+1];
95
96 void tftp(struct tftphdr *tp, int size) __attribute__((noreturn));
97 void nak(int error);
98 int validate_access(char *filename, int mode);
99
100 struct formats;
101
102 void sendfile(struct formats *pf);
103 void recvfile(struct formats *pf);
104
105
106 int main(int ac, char **av)
107 {
108         register struct tftphdr *tp;
109         register int n = 0;
110         int on = 1;
111
112         /* Sanity. If parent forgot to setuid() on us. */
113         if (geteuid() == 0) {
114                 setgid(65534);
115                 setuid(65534);
116         }
117
118         ac--; av++;
119         while (ac-- > 0 && n < MAXARG)
120                 dirs[n++] = *av++;
121
122         openlog("tftpd", LOG_PID, LOG_DAEMON);
123         if (ioctl(0, FIONBIO, &on) < 0) {
124                 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
125                 exit(1);
126         }
127         fromlen = sizeof (from);
128         n = recvfrom(0, buf, sizeof (buf), 0,
129             (struct sockaddr *)&from, &fromlen);
130         if (n < 0) {
131                 if (errno != EAGAIN)
132                         syslog(LOG_ERR, "recvfrom: %m\n");
133                 exit(1);
134         }
135         /*
136          * Now that we have read the message out of the UDP
137          * socket, we fork and exit.  Thus, inetd will go back
138          * to listening to the tftp port, and the next request
139          * to come in will start up a new instance of tftpd.
140          *
141          * We do this so that inetd can run tftpd in "wait" mode.
142          * The problem with tftpd running in "nowait" mode is that
143          * inetd may get one or more successful "selects" on the
144          * tftp port before we do our receive, so more than one
145          * instance of tftpd may be started up.  Worse, if tftpd
146          * break before doing the above "recvfrom", inetd would
147          * spawn endless instances, clogging the system.
148          */
149         {
150                 int pid;
151                 int i;
152                 socklen_t j;
153
154                 for (i = 1; i < 20; i++) {
155                     pid = fork();
156                     if (pid < 0) {
157                                 sleep(i);
158                                 /*
159                                  * flush out to most recently sent request.
160                                  *
161                                  * This may drop some request, but those
162                                  * will be resent by the clients when
163                                  * they timeout.  The positive effect of
164                                  * this flush is to (try to) prevent more
165                                  * than one tftpd being started up to service
166                                  * a single request from a single client.
167                                  */
168                                 j = sizeof from;
169                                 i = recvfrom(0, buf, sizeof (buf), 0,
170                                     (struct sockaddr *)&from, &j);
171                                 if (i > 0) {
172                                         n = i;
173                                         fromlen = j;
174                                 }
175                     } else {
176                                 break;
177                     }
178                 }
179                 if (pid < 0) {
180                         syslog(LOG_ERR, "fork: %m\n");
181                         exit(1);
182                 } else if (pid != 0) {
183                         exit(0);
184                 }
185         }
186         alarm(0);
187         close(0);
188         close(1);
189         peer = socket(from.sa.sa_family, SOCK_DGRAM, 0);
190         if (peer < 0) {
191                 syslog(LOG_ERR, "socket: %m\n");
192                 exit(1);
193         }
194         if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
195                 syslog(LOG_ERR, "connect: %m\n");
196                 exit(1);
197         }
198         tp = (struct tftphdr *)buf;
199         tp->th_opcode = ntohs(tp->th_opcode);
200         if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
201                 tftp(tp, n);
202         exit(1);
203 }
204
205 struct formats {
206         char    *f_mode;
207         int     (*f_validate)(char *filename, int mode);
208         void    (*f_send)(struct formats*);
209         void    (*f_recv)(struct formats*);
210         int     f_convert;
211 } formats[] = {
212         { "netascii",   validate_access,        sendfile,       recvfile, 1 },
213         { "octet",      validate_access,        sendfile,       recvfile, 0 },
214 #ifdef notdef
215         { "mail",       validate_user,          sendmail,       recvmail, 1 },
216 #endif
217         { 0 }
218 };
219
220 /*
221  * Handle initial connection protocol.
222  */
223 void tftp(struct tftphdr *tp, int size)
224 {
225         register char *cp;
226         int first = 1, ecode;
227         register struct formats *pf;
228         char *filename, *mode = NULL;
229
230         filename = cp = tp->th_stuff;
231 again:
232         while (cp < buf + size) {
233                 if (*cp == '\0')
234                         break;
235                 cp++;
236         }
237         if (*cp != '\0') {
238                 nak(EBADOP);
239                 exit(1);
240         }
241         if (first) {
242                 mode = ++cp;
243                 first = 0;
244                 goto again;
245         }
246         for (cp = mode; *cp; cp++)
247                 if (isupper(*cp))
248                         *cp = tolower(*cp);
249         for (pf = formats; pf->f_mode; pf++)
250                 if (strcmp(pf->f_mode, mode) == 0)
251                         break;
252         if (pf->f_mode == 0) {
253                 nak(EBADOP);
254                 exit(1);
255         }
256         ecode = (*pf->f_validate)(filename, tp->th_opcode);
257         if (ecode) {
258                 nak(ecode);
259                 exit(1);
260         }
261         if (tp->th_opcode == WRQ)
262                 (*pf->f_recv)(pf);
263         else
264                 (*pf->f_send)(pf);
265         exit(0);
266 }
267
268
269 FILE *file;
270
271 /*
272  * Validate file access.  Since we
273  * have no uid or gid, for now require
274  * file to exist and be publicly
275  * readable/writable.
276  * If we were invoked with arguments
277  * from inetd then the file must also be
278  * in one of the given directory prefixes.
279  * Note also, full path name must be
280  * given as we have no login directory.
281  */
282 int validate_access(char *filename, int mode)
283 {
284         struct stat stbuf;
285         int    fd;
286         char  *cp;
287         char   fnamebuf[1024+512];
288
289         for (cp = filename; *cp; cp++) {
290                 if(*cp == '.' && (cp == filename || strncmp(cp-1, "/../", 4) == 0)) {
291                         syslog(LOG_ERR, "bad path %s", filename);
292                         return(EACCESS);
293                 }
294         }
295
296         if (*filename == '/')
297                 filename++;
298
299         if (!*dirs) {
300                 syslog(LOG_ERR, "no dirs");
301                 return EACCESS;
302         }
303         snprintf(fnamebuf, sizeof(fnamebuf)-1, "%s/%s", *dirs, filename);
304         filename = fnamebuf;
305
306         if (stat(filename, &stbuf) < 0) {
307                 syslog(LOG_ERR, "stat %s : %m", filename);
308                 return (errno == ENOENT ? ENOTFOUND : EACCESS);
309         }
310         if (mode == RRQ) {
311                 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) {
312                         syslog(LOG_ERR, "not readable %s", filename);
313                         return (EACCESS);
314                 }
315         } else {
316                 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) {
317                         syslog(LOG_ERR, "not writable %s", filename);
318                         return (EACCESS);
319                 }
320         }
321         fd = open(filename, mode == RRQ ? 0 : 1);
322         if (fd < 0) {
323                 syslog(LOG_ERR, "cannot open %s: %m", filename);
324                 return (errno + 100);
325         }
326         file = fdopen(fd, (mode == RRQ)? "r":"w");
327         if (file == NULL) {
328                 return errno+100;
329         }
330         return (0);
331 }
332
333 int     confirmed;
334 int     timeout;
335 jmp_buf timeoutbuf;
336
337 void timer(int signo)
338 {
339         confirmed = 0;
340         timeout += rexmtval;
341         if (timeout >= maxtimeout)
342                 exit(1);
343         longjmp(timeoutbuf, 1);
344 }
345
346 /*
347  * Send the requested file.
348  */
349 void sendfile(struct formats *pf)
350 {
351         struct tftphdr *dp;
352         register struct tftphdr *ap;    /* ack packet */
353         volatile int block = 1;
354         int size, n;
355
356         confirmed = 0;
357         signal(SIGALRM, timer);
358         dp = r_init();
359         ap = (struct tftphdr *)ackbuf;
360         do {
361                 size = readit(file, &dp, pf->f_convert);
362                 if (size < 0) {
363                         nak(errno + 100);
364                         goto abort;
365                 }
366                 dp->th_opcode = htons((u_short)DATA);
367                 dp->th_block = htons((u_short)block);
368                 timeout = 0;
369                 (void) setjmp(timeoutbuf);
370
371 send_data:
372                 if (send(peer, dp, size + 4, confirmed) != size + 4) {
373                         syslog(LOG_ERR, "tftpd: write: %m\n");
374                         goto abort;
375                 }
376                 confirmed = 0;
377                 read_ahead(file, pf->f_convert);
378                 for ( ; ; ) {
379                         alarm(rexmtval);        /* read the ack */
380                         n = recv(peer, ackbuf, sizeof (ackbuf), 0);
381                         alarm(0);
382                         if (n < 0) {
383                                 syslog(LOG_ERR, "tftpd: read: %m\n");
384                                 goto abort;
385                         }
386                         ap->th_opcode = ntohs((u_short)ap->th_opcode);
387                         ap->th_block = ntohs((u_short)ap->th_block);
388
389                         if (ap->th_opcode == ERROR)
390                                 goto abort;
391
392                         if (ap->th_opcode == ACK) {
393                                 if (ap->th_block == block) {
394                                         confirmed = MSG_CONFIRM;
395                                         break;
396                                 }
397                                 /* Re-synchronize with the other side */
398                                 synchnet(peer);
399                                 if (ap->th_block == (block -1)) {
400                                         goto send_data;
401                                 }
402                         }
403
404                 }
405                 block++;
406         } while (size == SEGSIZE);
407 abort:
408         (void) fclose(file);
409 }
410
411 void justquit(int signo)
412 {
413         exit(0);
414 }
415
416
417 /*
418  * Receive a file.
419  */
420 void recvfile(struct formats *pf)
421 {
422         struct tftphdr *dp;
423         register struct tftphdr *ap;    /* ack buffer */
424         volatile int block = 0, n, size;
425
426         confirmed = 0;
427         signal(SIGALRM, timer);
428         dp = w_init();
429         ap = (struct tftphdr *)ackbuf;
430         do {
431                 timeout = 0;
432                 ap->th_opcode = htons((u_short)ACK);
433                 ap->th_block = htons((u_short)block);
434                 block++;
435                 (void) setjmp(timeoutbuf);
436 send_ack:
437                 if (send(peer, ackbuf, 4, confirmed) != 4) {
438                         syslog(LOG_ERR, "tftpd: write: %m\n");
439                         goto abort;
440                 }
441                 confirmed = 0;
442                 write_behind(file, pf->f_convert);
443                 for ( ; ; ) {
444                         alarm(rexmtval);
445                         n = recv(peer, dp, PKTSIZE, 0);
446                         alarm(0);
447                         if (n < 0) {            /* really? */
448                                 syslog(LOG_ERR, "tftpd: read: %m\n");
449                                 goto abort;
450                         }
451                         dp->th_opcode = ntohs((u_short)dp->th_opcode);
452                         dp->th_block = ntohs((u_short)dp->th_block);
453                         if (dp->th_opcode == ERROR)
454                                 goto abort;
455                         if (dp->th_opcode == DATA) {
456                                 if (dp->th_block == block) {
457                                         confirmed = MSG_CONFIRM;
458                                         break;   /* normal */
459                                 }
460                                 /* Re-synchronize with the other side */
461                                 (void) synchnet(peer);
462                                 if (dp->th_block == (block-1))
463                                         goto send_ack;          /* rexmit */
464                         }
465                 }
466                 /*  size = write(file, dp->th_data, n - 4); */
467                 size = writeit(file, &dp, n - 4, pf->f_convert);
468                 if (size != (n-4)) {                    /* ahem */
469                         if (size < 0) nak(errno + 100);
470                         else nak(ENOSPACE);
471                         goto abort;
472                 }
473         } while (size == SEGSIZE);
474         write_behind(file, pf->f_convert);
475         (void) fclose(file);            /* close data file */
476
477         ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
478         ap->th_block = htons((u_short)(block));
479         (void) send(peer, ackbuf, 4, confirmed);
480
481         signal(SIGALRM, justquit);      /* just quit on timeout */
482         alarm(rexmtval);
483         n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
484         alarm(0);
485         if (n >= 4 &&                   /* if read some data */
486             dp->th_opcode == DATA &&    /* and got a data block */
487             block == dp->th_block) {    /* then my last ack was lost */
488                 (void) send(peer, ackbuf, 4, 0);     /* resend final ack */
489         }
490 abort:
491         return;
492 }
493
494 struct errmsg {
495         int     e_code;
496         char    *e_msg;
497 } errmsgs[] = {
498         { EUNDEF,       "Undefined error code" },
499         { ENOTFOUND,    "File not found" },
500         { EACCESS,      "Access violation" },
501         { ENOSPACE,     "Disk full or allocation exceeded" },
502         { EBADOP,       "Illegal TFTP operation" },
503         { EBADID,       "Unknown transfer ID" },
504         { EEXISTS,      "File already exists" },
505         { ENOUSER,      "No such user" },
506         { -1,           0 }
507 };
508
509 /*
510  * Send a nak packet (error message).
511  * Error code passed in is one of the
512  * standard TFTP codes, or a UNIX errno
513  * offset by 100.
514  */
515 void nak(int error)
516 {
517         register struct tftphdr *tp;
518         int length;
519         register struct errmsg *pe;
520
521         tp = (struct tftphdr *)buf;
522         tp->th_opcode = htons((u_short)ERROR);
523         tp->th_code = htons((u_short)error);
524         for (pe = errmsgs; pe->e_code >= 0; pe++)
525                 if (pe->e_code == error)
526                         break;
527         if (pe->e_code < 0) {
528                 pe->e_msg = strerror(error - 100);
529                 tp->th_code = EUNDEF;   /* set 'undef' errorcode */
530         }
531         strcpy(tp->th_msg, pe->e_msg);
532         length = strlen(pe->e_msg);
533         tp->th_msg[length] = '\0';
534         length += 5;
535         if (send(peer, buf, length, 0) != length)
536                 syslog(LOG_ERR, "nak: %m\n");
537 }