]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/tftpd/tftpd.c
This commit was generated by cvs2svn to compensate for changes in r94209,
[FreeBSD/FreeBSD.git] / libexec / tftpd / tftpd.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  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 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)tftpd.c     8.1 (Berkeley) 6/4/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47
48 /*
49  * Trivial file transfer protocol server.
50  *
51  * This version includes many modifications by Jim Guyton
52  * <guyton@rand-unix>.
53  */
54
55 #include <sys/param.h>
56 #include <sys/ioctl.h>
57 #include <sys/stat.h>
58 #include <sys/socket.h>
59 #include <sys/types.h>
60
61 #include <netinet/in.h>
62 #include <arpa/tftp.h>
63 #include <arpa/inet.h>
64
65 #include <ctype.h>
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <libutil.h>
69 #include <netdb.h>
70 #include <pwd.h>
71 #include <setjmp.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <syslog.h>
77 #include <unistd.h>
78
79 #include "tftpsubs.h"
80
81 #define TIMEOUT         5
82 #define MAX_TIMEOUTS    5
83
84 int     peer;
85 int     rexmtval = TIMEOUT;
86 int     max_rexmtval = 2*TIMEOUT;
87
88 #define PKTSIZE SEGSIZE+4
89 char    buf[PKTSIZE];
90 char    ackbuf[PKTSIZE];
91 struct  sockaddr_in from;
92 int     fromlen;
93
94 void    tftp(struct tftphdr *, int);
95
96 /*
97  * Null-terminated directory prefix list for absolute pathname requests and
98  * search list for relative pathname requests.
99  *
100  * MAXDIRS should be at least as large as the number of arguments that
101  * inetd allows (currently 20).
102  */
103 #define MAXDIRS 20
104 static struct dirlist {
105         char    *name;
106         int     len;
107 } dirs[MAXDIRS+1];
108 static int      suppress_naks;
109 static int      logging;
110 static int      ipchroot;
111
112 static char *errtomsg(int);
113 static void  nak(int);
114 static void  oack();
115
116 int
117 main(int argc, char *argv[])
118 {
119         struct tftphdr *tp;
120         int n;
121         int ch, on;
122         struct sockaddr_in sin;
123         char *chroot_dir = NULL;
124         struct passwd *nobody;
125         char *chuser = "nobody";
126
127         openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
128         while ((ch = getopt(argc, argv, "cClns:u:")) != -1) {
129                 switch (ch) {
130                 case 'c':
131                         ipchroot = 1;
132                         break;
133                 case 'C':
134                         ipchroot = 2;
135                         break;
136                 case 'l':
137                         logging = 1;
138                         break;
139                 case 'n':
140                         suppress_naks = 1;
141                         break;
142                 case 's':
143                         chroot_dir = optarg;
144                         break;
145                 case 'u':
146                         chuser = optarg;
147                         break;
148                 default:
149                         syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
150                 }
151         }
152         if (optind < argc) {
153                 struct dirlist *dirp;
154
155                 /* Get list of directory prefixes. Skip relative pathnames. */
156                 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
157                      optind++) {
158                         if (argv[optind][0] == '/') {
159                                 dirp->name = argv[optind];
160                                 dirp->len  = strlen(dirp->name);
161                                 dirp++;
162                         }
163                 }
164         }
165         else if (chroot_dir) {
166                 dirs->name = "/";
167                 dirs->len = 1;
168         }
169         if (ipchroot && chroot_dir == NULL) {
170                 syslog(LOG_ERR, "-c requires -s");
171                 exit(1);
172         }
173
174         on = 1;
175         if (ioctl(0, FIONBIO, &on) < 0) {
176                 syslog(LOG_ERR, "ioctl(FIONBIO): %m");
177                 exit(1);
178         }
179         fromlen = sizeof (from);
180         n = recvfrom(0, buf, sizeof (buf), 0,
181             (struct sockaddr *)&from, &fromlen);
182         if (n < 0) {
183                 syslog(LOG_ERR, "recvfrom: %m");
184                 exit(1);
185         }
186         /*
187          * Now that we have read the message out of the UDP
188          * socket, we fork and exit.  Thus, inetd will go back
189          * to listening to the tftp port, and the next request
190          * to come in will start up a new instance of tftpd.
191          *
192          * We do this so that inetd can run tftpd in "wait" mode.
193          * The problem with tftpd running in "nowait" mode is that
194          * inetd may get one or more successful "selects" on the
195          * tftp port before we do our receive, so more than one
196          * instance of tftpd may be started up.  Worse, if tftpd
197          * break before doing the above "recvfrom", inetd would
198          * spawn endless instances, clogging the system.
199          */
200         {
201                 int pid;
202                 int i, j;
203
204                 for (i = 1; i < 20; i++) {
205                     pid = fork();
206                     if (pid < 0) {
207                                 sleep(i);
208                                 /*
209                                  * flush out to most recently sent request.
210                                  *
211                                  * This may drop some request, but those
212                                  * will be resent by the clients when
213                                  * they timeout.  The positive effect of
214                                  * this flush is to (try to) prevent more
215                                  * than one tftpd being started up to service
216                                  * a single request from a single client.
217                                  */
218                                 j = sizeof from;
219                                 i = recvfrom(0, buf, sizeof (buf), 0,
220                                     (struct sockaddr *)&from, &j);
221                                 if (i > 0) {
222                                         n = i;
223                                         fromlen = j;
224                                 }
225                     } else {
226                                 break;
227                     }
228                 }
229                 if (pid < 0) {
230                         syslog(LOG_ERR, "fork: %m");
231                         exit(1);
232                 } else if (pid != 0) {
233                         exit(0);
234                 }
235         }
236
237         /*
238          * Since we exit here, we should do that only after the above
239          * recvfrom to keep inetd from constantly forking should there
240          * be a problem.  See the above comment about system clogging.
241          */
242         if (chroot_dir) {
243                 if (ipchroot) {
244                         char *tempchroot;
245                         struct stat sb;
246                         int statret;
247
248                         tempchroot = inet_ntoa(from.sin_addr);
249                         asprintf(&tempchroot, "%s/%s", chroot_dir, tempchroot);
250                         statret = stat(tempchroot, &sb);
251                         if ((sb.st_mode & S_IFDIR) &&
252                             (statret == 0 || (statret == -1 && ipchroot == 1)))
253                                 chroot_dir = tempchroot;
254                 }
255                 /* Must get this before chroot because /etc might go away */
256                 if ((nobody = getpwnam(chuser)) == NULL) {
257                         syslog(LOG_ERR, "%s: no such user", chuser);
258                         exit(1);
259                 }
260                 if (chroot(chroot_dir)) {
261                         syslog(LOG_ERR, "chroot: %s: %m", chroot_dir);
262                         exit(1);
263                 }
264                 chdir( "/" );
265                 setuid(nobody->pw_uid);
266                 setgroups(1, &nobody->pw_gid);
267         }
268
269         from.sin_family = AF_INET;
270         alarm(0);
271         close(0);
272         close(1);
273         peer = socket(AF_INET, SOCK_DGRAM, 0);
274         if (peer < 0) {
275                 syslog(LOG_ERR, "socket: %m");
276                 exit(1);
277         }
278         memset(&sin, 0, sizeof(sin));
279         sin.sin_family = AF_INET;
280         if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
281                 syslog(LOG_ERR, "bind: %m");
282                 exit(1);
283         }
284         if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
285                 syslog(LOG_ERR, "connect: %m");
286                 exit(1);
287         }
288         tp = (struct tftphdr *)buf;
289         tp->th_opcode = ntohs(tp->th_opcode);
290         if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
291                 tftp(tp, n);
292         exit(1);
293 }
294
295 struct formats;
296 int     validate_access(char **, int);
297 void    xmitfile(struct formats *);
298 void    recvfile(struct formats *);
299
300 struct formats {
301         char    *f_mode;
302         int     (*f_validate)(char **, int);
303         void    (*f_send)(struct formats *);
304         void    (*f_recv)(struct formats *);
305         int     f_convert;
306 } formats[] = {
307         { "netascii",   validate_access,        xmitfile,       recvfile, 1 },
308         { "octet",      validate_access,        xmitfile,       recvfile, 0 },
309 #ifdef notdef
310         { "mail",       validate_user,          sendmail,       recvmail, 1 },
311 #endif
312         { 0 }
313 };
314
315 struct options {
316         char    *o_type;
317         char    *o_request;
318         int     o_reply;        /* turn into union if need be */
319 } options[] = {
320         { "tsize" },            /* OPT_TSIZE */
321         { "timeout" },          /* OPT_TIMEOUT */
322         { NULL }
323 };
324
325 enum opt_enum {
326         OPT_TSIZE = 0,
327         OPT_TIMEOUT,
328 };
329
330 /*
331  * Handle initial connection protocol.
332  */
333 void
334 tftp(struct tftphdr *tp, int size)
335 {
336         char *cp;
337         int i, first = 1, has_options = 0, ecode;
338         struct formats *pf;
339         char *filename, *mode, *option, *ccp;
340
341         filename = cp = tp->th_stuff;
342 again:
343         while (cp < buf + size) {
344                 if (*cp == '\0')
345                         break;
346                 cp++;
347         }
348         if (*cp != '\0') {
349                 nak(EBADOP);
350                 exit(1);
351         }
352         if (first) {
353                 mode = ++cp;
354                 first = 0;
355                 goto again;
356         }
357         for (cp = mode; *cp; cp++)
358                 if (isupper(*cp))
359                         *cp = tolower(*cp);
360         for (pf = formats; pf->f_mode; pf++)
361                 if (strcmp(pf->f_mode, mode) == 0)
362                         break;
363         if (pf->f_mode == 0) {
364                 nak(EBADOP);
365                 exit(1);
366         }
367         while (++cp < buf + size) {
368                 for (i = 2, ccp = cp; i > 0; ccp++) {
369                         if (ccp >= buf + size) {
370                                 /*
371                                  * Don't reject the request, just stop trying
372                                  * to parse the option and get on with it.
373                                  * Some Apple OpenFirmware versions have
374                                  * trailing garbage on the end of otherwise
375                                  * valid requests.
376                                  */
377                                 goto option_fail;
378                         } else if (*ccp == '\0')
379                                 i--;
380                 }
381                 for (option = cp; *cp; cp++)
382                         if (isupper(*cp))
383                                 *cp = tolower(*cp);
384                 for (i = 0; options[i].o_type != NULL; i++)
385                         if (strcmp(option, options[i].o_type) == 0) {
386                                 options[i].o_request = ++cp;
387                                 has_options = 1;
388                         }
389                 cp = ccp-1;
390         }
391
392 option_fail:
393         if (options[OPT_TIMEOUT].o_request) {
394                 int to = atoi(options[OPT_TIMEOUT].o_request);
395                 if (to < 1 || to > 255) {
396                         nak(EBADOP);
397                         exit(1);
398                 }
399                 else if (to <= max_rexmtval)
400                         options[OPT_TIMEOUT].o_reply = rexmtval = to;
401                 else
402                         options[OPT_TIMEOUT].o_request = NULL;
403         }
404
405         ecode = (*pf->f_validate)(&filename, tp->th_opcode);
406         if (has_options)
407                 oack();
408         if (logging) {
409                 char host[MAXHOSTNAMELEN];
410
411                 realhostname(host, sizeof(host) - 1, &from.sin_addr);
412                 host[sizeof(host) - 1] = '\0';
413                 syslog(LOG_INFO, "%s: %s request for %s: %s", host,
414                         tp->th_opcode == WRQ ? "write" : "read",
415                         filename, errtomsg(ecode));
416         }
417         if (ecode) {
418                 /*
419                  * Avoid storms of naks to a RRQ broadcast for a relative
420                  * bootfile pathname from a diskless Sun.
421                  */
422                 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
423                         exit(0);
424                 nak(ecode);
425                 exit(1);
426         }
427         if (tp->th_opcode == WRQ)
428                 (*pf->f_recv)(pf);
429         else
430                 (*pf->f_send)(pf);
431         exit(0);
432 }
433
434
435 FILE *file;
436
437 /*
438  * Validate file access.  Since we
439  * have no uid or gid, for now require
440  * file to exist and be publicly
441  * readable/writable.
442  * If we were invoked with arguments
443  * from inetd then the file must also be
444  * in one of the given directory prefixes.
445  * Note also, full path name must be
446  * given as we have no login directory.
447  */
448 int
449 validate_access(char **filep, int mode)
450 {
451         struct stat stbuf;
452         int     fd;
453         struct dirlist *dirp;
454         static char pathname[MAXPATHLEN];
455         char *filename = *filep;
456
457         /*
458          * Prevent tricksters from getting around the directory restrictions
459          */
460         if (strstr(filename, "/../"))
461                 return (EACCESS);
462
463         if (*filename == '/') {
464                 /*
465                  * Allow the request if it's in one of the approved locations.
466                  * Special case: check the null prefix ("/") by looking
467                  * for length = 1 and relying on the arg. processing that
468                  * it's a /.
469                  */
470                 for (dirp = dirs; dirp->name != NULL; dirp++) {
471                         if (dirp->len == 1 ||
472                             (!strncmp(filename, dirp->name, dirp->len) &&
473                              filename[dirp->len] == '/'))
474                                     break;
475                 }
476                 /* If directory list is empty, allow access to any file */
477                 if (dirp->name == NULL && dirp != dirs)
478                         return (EACCESS);
479                 if (stat(filename, &stbuf) < 0)
480                         return (errno == ENOENT ? ENOTFOUND : EACCESS);
481                 if ((stbuf.st_mode & S_IFMT) != S_IFREG)
482                         return (ENOTFOUND);
483                 if (mode == RRQ) {
484                         if ((stbuf.st_mode & S_IROTH) == 0)
485                                 return (EACCESS);
486                 } else {
487                         if ((stbuf.st_mode & S_IWOTH) == 0)
488                                 return (EACCESS);
489                 }
490         } else {
491                 int err;
492
493                 /*
494                  * Relative file name: search the approved locations for it.
495                  * Don't allow write requests that avoid directory
496                  * restrictions.
497                  */
498
499                 if (!strncmp(filename, "../", 3))
500                         return (EACCESS);
501
502                 /*
503                  * If the file exists in one of the directories and isn't
504                  * readable, continue looking. However, change the error code
505                  * to give an indication that the file exists.
506                  */
507                 err = ENOTFOUND;
508                 for (dirp = dirs; dirp->name != NULL; dirp++) {
509                         snprintf(pathname, sizeof(pathname), "%s/%s",
510                                 dirp->name, filename);
511                         if (stat(pathname, &stbuf) == 0 &&
512                             (stbuf.st_mode & S_IFMT) == S_IFREG) {
513                                 if ((stbuf.st_mode & S_IROTH) != 0) {
514                                         break;
515                                 }
516                                 err = EACCESS;
517                         }
518                 }
519                 if (dirp->name == NULL)
520                         return (err);
521                 *filep = filename = pathname;
522         }
523         if (options[OPT_TSIZE].o_request) {
524                 if (mode == RRQ) 
525                         options[OPT_TSIZE].o_reply = stbuf.st_size;
526                 else
527                         /* XXX Allows writes of all sizes. */
528                         options[OPT_TSIZE].o_reply =
529                                 atoi(options[OPT_TSIZE].o_request);
530         }
531         fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC);
532         if (fd < 0)
533                 return (errno + 100);
534         file = fdopen(fd, (mode == RRQ)? "r":"w");
535         if (file == NULL) {
536                 return errno+100;
537         }
538         return (0);
539 }
540
541 int     timeouts;
542 jmp_buf timeoutbuf;
543
544 void
545 timer(int sig __unused)
546 {
547         if (++timeouts > MAX_TIMEOUTS)
548                 exit(1);
549         longjmp(timeoutbuf, 1);
550 }
551
552 /*
553  * Send the requested file.
554  */
555 void
556 xmitfile(struct formats *pf)
557 {
558         struct tftphdr *dp, *r_init();
559         struct tftphdr *ap;    /* ack packet */
560         int size, n;
561         volatile unsigned short block;
562
563         signal(SIGALRM, timer);
564         dp = r_init();
565         ap = (struct tftphdr *)ackbuf;
566         block = 1;
567         do {
568                 size = readit(file, &dp, pf->f_convert);
569                 if (size < 0) {
570                         nak(errno + 100);
571                         goto abort;
572                 }
573                 dp->th_opcode = htons((u_short)DATA);
574                 dp->th_block = htons((u_short)block);
575                 timeouts = 0;
576                 (void)setjmp(timeoutbuf);
577
578 send_data:
579                 if (send(peer, dp, size + 4, 0) != size + 4) {
580                         syslog(LOG_ERR, "write: %m");
581                         goto abort;
582                 }
583                 read_ahead(file, pf->f_convert);
584                 for ( ; ; ) {
585                         alarm(rexmtval);        /* read the ack */
586                         n = recv(peer, ackbuf, sizeof (ackbuf), 0);
587                         alarm(0);
588                         if (n < 0) {
589                                 syslog(LOG_ERR, "read: %m");
590                                 goto abort;
591                         }
592                         ap->th_opcode = ntohs((u_short)ap->th_opcode);
593                         ap->th_block = ntohs((u_short)ap->th_block);
594
595                         if (ap->th_opcode == ERROR)
596                                 goto abort;
597
598                         if (ap->th_opcode == ACK) {
599                                 if (ap->th_block == block)
600                                         break;
601                                 /* Re-synchronize with the other side */
602                                 (void) synchnet(peer);
603                                 if (ap->th_block == (block -1))
604                                         goto send_data;
605                         }
606
607                 }
608                 block++;
609         } while (size == SEGSIZE);
610 abort:
611         (void) fclose(file);
612 }
613
614 void
615 justquit(int sig __unused)
616 {
617         exit(0);
618 }
619
620
621 /*
622  * Receive a file.
623  */
624 void
625 recvfile(struct formats *pf)
626 {
627         struct tftphdr *dp, *w_init();
628         struct tftphdr *ap;    /* ack buffer */
629         int n, size;
630         volatile unsigned short block;
631
632         signal(SIGALRM, timer);
633         dp = w_init();
634         ap = (struct tftphdr *)ackbuf;
635         block = 0;
636         do {
637                 timeouts = 0;
638                 ap->th_opcode = htons((u_short)ACK);
639                 ap->th_block = htons((u_short)block);
640                 block++;
641                 (void) setjmp(timeoutbuf);
642 send_ack:
643                 if (send(peer, ackbuf, 4, 0) != 4) {
644                         syslog(LOG_ERR, "write: %m");
645                         goto abort;
646                 }
647                 write_behind(file, pf->f_convert);
648                 for ( ; ; ) {
649                         alarm(rexmtval);
650                         n = recv(peer, dp, PKTSIZE, 0);
651                         alarm(0);
652                         if (n < 0) {            /* really? */
653                                 syslog(LOG_ERR, "read: %m");
654                                 goto abort;
655                         }
656                         dp->th_opcode = ntohs((u_short)dp->th_opcode);
657                         dp->th_block = ntohs((u_short)dp->th_block);
658                         if (dp->th_opcode == ERROR)
659                                 goto abort;
660                         if (dp->th_opcode == DATA) {
661                                 if (dp->th_block == block) {
662                                         break;   /* normal */
663                                 }
664                                 /* Re-synchronize with the other side */
665                                 (void) synchnet(peer);
666                                 if (dp->th_block == (block-1))
667                                         goto send_ack;          /* rexmit */
668                         }
669                 }
670                 /*  size = write(file, dp->th_data, n - 4); */
671                 size = writeit(file, &dp, n - 4, pf->f_convert);
672                 if (size != (n-4)) {                    /* ahem */
673                         if (size < 0) nak(errno + 100);
674                         else nak(ENOSPACE);
675                         goto abort;
676                 }
677         } while (size == SEGSIZE);
678         write_behind(file, pf->f_convert);
679         (void) fclose(file);            /* close data file */
680
681         ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
682         ap->th_block = htons((u_short)(block));
683         (void) send(peer, ackbuf, 4, 0);
684
685         signal(SIGALRM, justquit);      /* just quit on timeout */
686         alarm(rexmtval);
687         n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
688         alarm(0);
689         if (n >= 4 &&                   /* if read some data */
690             dp->th_opcode == DATA &&    /* and got a data block */
691             block == dp->th_block) {    /* then my last ack was lost */
692                 (void) send(peer, ackbuf, 4, 0);     /* resend final ack */
693         }
694 abort:
695         return;
696 }
697
698 struct errmsg {
699         int     e_code;
700         char    *e_msg;
701 } errmsgs[] = {
702         { EUNDEF,       "Undefined error code" },
703         { ENOTFOUND,    "File not found" },
704         { EACCESS,      "Access violation" },
705         { ENOSPACE,     "Disk full or allocation exceeded" },
706         { EBADOP,       "Illegal TFTP operation" },
707         { EBADID,       "Unknown transfer ID" },
708         { EEXISTS,      "File already exists" },
709         { ENOUSER,      "No such user" },
710         { EOPTNEG,      "Option negotiation" },
711         { -1,           0 }
712 };
713
714 static char *
715 errtomsg(int error)
716 {
717         static char buf[20];
718         struct errmsg *pe;
719         if (error == 0)
720                 return "success";
721         for (pe = errmsgs; pe->e_code >= 0; pe++)
722                 if (pe->e_code == error)
723                         return pe->e_msg;
724         snprintf(buf, sizeof(buf), "error %d", error);
725         return buf;
726 }
727
728 /*
729  * Send a nak packet (error message).
730  * Error code passed in is one of the
731  * standard TFTP codes, or a UNIX errno
732  * offset by 100.
733  */
734 static void
735 nak(int error)
736 {
737         struct tftphdr *tp;
738         int length;
739         struct errmsg *pe;
740
741         tp = (struct tftphdr *)buf;
742         tp->th_opcode = htons((u_short)ERROR);
743         tp->th_code = htons((u_short)error);
744         for (pe = errmsgs; pe->e_code >= 0; pe++)
745                 if (pe->e_code == error)
746                         break;
747         if (pe->e_code < 0) {
748                 pe->e_msg = strerror(error - 100);
749                 tp->th_code = EUNDEF;   /* set 'undef' errorcode */
750         }
751         strcpy(tp->th_msg, pe->e_msg);
752         length = strlen(pe->e_msg);
753         tp->th_msg[length] = '\0';
754         length += 5;
755         if (send(peer, buf, length, 0) != length)
756                 syslog(LOG_ERR, "nak: %m");
757 }
758
759 /*
760  * Send an oack packet (option acknowledgement).
761  */
762 static void
763 oack(void)
764 {
765         struct tftphdr *tp, *ap;
766         int size, i, n;
767         char *bp;
768
769         tp = (struct tftphdr *)buf;
770         bp = buf + 2;
771         size = sizeof(buf) - 2;
772         tp->th_opcode = htons((u_short)OACK);
773         for (i = 0; options[i].o_type != NULL; i++) {
774                 if (options[i].o_request) {
775                         n = snprintf(bp, size, "%s%c%d", options[i].o_type,
776                                      0, options[i].o_reply);
777                         bp += n+1;
778                         size -= n+1;
779                         if (size < 0) {
780                                 syslog(LOG_ERR, "oack: buffer overflow");
781                                 exit(1);
782                         }
783                 }
784         }
785         size = bp - buf;
786         ap = (struct tftphdr *)ackbuf;
787         signal(SIGALRM, timer);
788         timeouts = 0;
789
790         (void)setjmp(timeoutbuf);
791         if (send(peer, buf, size, 0) != size) {
792                 syslog(LOG_INFO, "oack: %m");
793                 exit(1);
794         }
795
796         for (;;) {
797                 alarm(rexmtval);
798                 n = recv(peer, ackbuf, sizeof (ackbuf), 0);
799                 alarm(0);
800                 if (n < 0) {
801                         syslog(LOG_ERR, "recv: %m");
802                         exit(1);
803                 }
804                 ap->th_opcode = ntohs((u_short)ap->th_opcode);
805                 ap->th_block = ntohs((u_short)ap->th_block);
806                 if (ap->th_opcode == ERROR)
807                         exit(1);
808                 if (ap->th_opcode == ACK && ap->th_block == 0)
809                         break;
810         }
811 }