121183Sdist /*
2*61458Sbostic * Copyright (c) 1983, 1993
3*61458Sbostic * The Regents of the University of California. All rights reserved.
433822Sbostic *
542673Sbostic * %sccs.include.redist.c%
621183Sdist */
721183Sdist
813609Ssam #ifndef lint
9*61458Sbostic static char copyright[] =
10*61458Sbostic "@(#) Copyright (c) 1983, 1993\n\
11*61458Sbostic The Regents of the University of California. All rights reserved.\n";
1233822Sbostic #endif /* not lint */
137772Ssam
1421183Sdist #ifndef lint
15*61458Sbostic static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 06/04/93";
1633822Sbostic #endif /* not lint */
1721183Sdist
187772Ssam /*
197772Ssam * Trivial file transfer protocol server.
2026108Sminshall *
2157963Sandrew * This version includes many modifications by Jim Guyton
2257963Sandrew * <guyton@rand-unix>.
237772Ssam */
2426108Sminshall
2557963Sandrew #include <sys/param.h>
269220Ssam #include <sys/ioctl.h>
2713609Ssam #include <sys/stat.h>
2857963Sandrew #include <sys/socket.h>
299220Ssam
309220Ssam #include <netinet/in.h>
3112217Ssam #include <arpa/tftp.h>
3257963Sandrew #include <arpa/inet.h>
3357963Sandrew
3457963Sandrew #include <ctype.h>
3557963Sandrew #include <errno.h>
3657963Sandrew #include <fcntl.h>
3746687Sbostic #include <netdb.h>
3842413Sbostic #include <setjmp.h>
3957963Sandrew #include <signal.h>
407772Ssam #include <stdio.h>
4157963Sandrew #include <stdlib.h>
4242413Sbostic #include <string.h>
4357963Sandrew #include <syslog.h>
4460039Storek #include <unistd.h>
459220Ssam
4660039Storek #include "tftpsubs.h"
4757963Sandrew
4813020Ssam #define TIMEOUT 5
4913020Ssam
5016372Skarels int peer;
5113020Ssam int rexmtval = TIMEOUT;
5213020Ssam int maxtimeout = 5*TIMEOUT;
5326108Sminshall
5426108Sminshall #define PKTSIZE SEGSIZE+4
5526108Sminshall char buf[PKTSIZE];
5626108Sminshall char ackbuf[PKTSIZE];
5716372Skarels struct sockaddr_in from;
5816372Skarels int fromlen;
597772Ssam
6060039Storek void tftp __P((struct tftphdr *, int));
6160039Storek
6257963Sandrew /*
6360039Storek * Null-terminated directory prefix list for absolute pathname requests and
6457963Sandrew * search list for relative pathname requests.
6557963Sandrew *
6657963Sandrew * MAXDIRS should be at least as large as the number of arguments that
6757963Sandrew * inetd allows (currently 20).
6857963Sandrew */
6957963Sandrew #define MAXDIRS 20
7057963Sandrew static struct dirlist {
7157963Sandrew char *name;
7257963Sandrew int len;
7357963Sandrew } dirs[MAXDIRS+1];
7457963Sandrew static int suppress_naks;
7557963Sandrew static int logging;
7635783Stef
7760039Storek static char *errtomsg __P((int));
7860039Storek static void nak __P((int));
7960039Storek static char *verifyhost __P((struct sockaddr_in *));
8057963Sandrew
8160039Storek int
main(argc,argv)8257963Sandrew main(argc, argv)
8357963Sandrew int argc;
8457963Sandrew char *argv[];
857772Ssam {
867772Ssam register struct tftphdr *tp;
8757963Sandrew register int n;
8857963Sandrew int ch, on;
8960039Storek struct sockaddr_in sin;
907772Ssam
9157963Sandrew openlog("tftpd", LOG_PID, LOG_FTP);
9257963Sandrew while ((ch = getopt(argc, argv, "ln")) != EOF) {
9357963Sandrew switch (ch) {
9457963Sandrew case 'l':
9557963Sandrew logging = 1;
9657963Sandrew break;
9757963Sandrew case 'n':
9857963Sandrew suppress_naks = 1;
9957963Sandrew break;
10057963Sandrew default:
10157963Sandrew syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
10257963Sandrew }
10357963Sandrew }
10457963Sandrew if (optind < argc) {
10557963Sandrew struct dirlist *dirp;
10657963Sandrew
10757963Sandrew /* Get list of directory prefixes. Skip relative pathnames. */
10857963Sandrew for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
10957963Sandrew optind++) {
11057963Sandrew if (argv[optind][0] == '/') {
11157963Sandrew dirp->name = argv[optind];
11257963Sandrew dirp->len = strlen(dirp->name);
11357963Sandrew dirp++;
11457963Sandrew }
11557963Sandrew }
11657963Sandrew }
11757963Sandrew
11857963Sandrew on = 1;
11928070Sminshall if (ioctl(0, FIONBIO, &on) < 0) {
12028070Sminshall syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
12128070Sminshall exit(1);
12228070Sminshall }
12316372Skarels fromlen = sizeof (from);
12416372Skarels n = recvfrom(0, buf, sizeof (buf), 0,
12546687Sbostic (struct sockaddr *)&from, &fromlen);
12616372Skarels if (n < 0) {
12728070Sminshall syslog(LOG_ERR, "recvfrom: %m\n");
1288385Ssam exit(1);
1298385Ssam }
13028070Sminshall /*
13128070Sminshall * Now that we have read the message out of the UDP
13228070Sminshall * socket, we fork and exit. Thus, inetd will go back
13328070Sminshall * to listening to the tftp port, and the next request
13428070Sminshall * to come in will start up a new instance of tftpd.
13528070Sminshall *
13628070Sminshall * We do this so that inetd can run tftpd in "wait" mode.
13728070Sminshall * The problem with tftpd running in "nowait" mode is that
13828070Sminshall * inetd may get one or more successful "selects" on the
13928070Sminshall * tftp port before we do our receive, so more than one
14028070Sminshall * instance of tftpd may be started up. Worse, if tftpd
14128070Sminshall * break before doing the above "recvfrom", inetd would
14228070Sminshall * spawn endless instances, clogging the system.
14328070Sminshall */
14428070Sminshall {
14528070Sminshall int pid;
14628070Sminshall int i, j;
14728070Sminshall
14828070Sminshall for (i = 1; i < 20; i++) {
14928070Sminshall pid = fork();
15028070Sminshall if (pid < 0) {
15128070Sminshall sleep(i);
15228070Sminshall /*
15328070Sminshall * flush out to most recently sent request.
15428070Sminshall *
15528070Sminshall * This may drop some request, but those
15628070Sminshall * will be resent by the clients when
15728070Sminshall * they timeout. The positive effect of
15828070Sminshall * this flush is to (try to) prevent more
15928070Sminshall * than one tftpd being started up to service
16028070Sminshall * a single request from a single client.
16128070Sminshall */
16228070Sminshall j = sizeof from;
16328070Sminshall i = recvfrom(0, buf, sizeof (buf), 0,
16446687Sbostic (struct sockaddr *)&from, &j);
16528070Sminshall if (i > 0) {
16628070Sminshall n = i;
16728070Sminshall fromlen = j;
16828070Sminshall }
16928070Sminshall } else {
17028070Sminshall break;
17128070Sminshall }
17228070Sminshall }
17328070Sminshall if (pid < 0) {
17428070Sminshall syslog(LOG_ERR, "fork: %m\n");
17528070Sminshall exit(1);
17628070Sminshall } else if (pid != 0) {
17728070Sminshall exit(0);
17828070Sminshall }
17928070Sminshall }
18016372Skarels from.sin_family = AF_INET;
18116372Skarels alarm(0);
18216372Skarels close(0);
18316372Skarels close(1);
18416372Skarels peer = socket(AF_INET, SOCK_DGRAM, 0);
18516372Skarels if (peer < 0) {
18628070Sminshall syslog(LOG_ERR, "socket: %m\n");
18716372Skarels exit(1);
1887772Ssam }
18960039Storek memset(&sin, 0, sizeof(sin));
19060039Storek sin.sin_family = AF_INET;
19146687Sbostic if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
19228070Sminshall syslog(LOG_ERR, "bind: %m\n");
19316372Skarels exit(1);
19416372Skarels }
19546687Sbostic if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
19628070Sminshall syslog(LOG_ERR, "connect: %m\n");
19716372Skarels exit(1);
1987772Ssam }
19916372Skarels tp = (struct tftphdr *)buf;
20016372Skarels tp->th_opcode = ntohs(tp->th_opcode);
20116372Skarels if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
20216372Skarels tftp(tp, n);
20316372Skarels exit(1);
2047772Ssam }
2057772Ssam
20660039Storek struct formats;
20760039Storek int validate_access __P((char **, int));
20860039Storek void sendfile __P((struct formats *));
20960039Storek void recvfile __P((struct formats *));
2107772Ssam
2117772Ssam struct formats {
2127772Ssam char *f_mode;
21360039Storek int (*f_validate) __P((char **, int));
21460039Storek void (*f_send) __P((struct formats *));
21560039Storek void (*f_recv) __P((struct formats *));
21626108Sminshall int f_convert;
2177772Ssam } formats[] = {
21826108Sminshall { "netascii", validate_access, sendfile, recvfile, 1 },
21926108Sminshall { "octet", validate_access, sendfile, recvfile, 0 },
2207772Ssam #ifdef notdef
22126108Sminshall { "mail", validate_user, sendmail, recvmail, 1 },
2227772Ssam #endif
2237772Ssam { 0 }
2247772Ssam };
2257772Ssam
2267772Ssam /*
2277772Ssam * Handle initial connection protocol.
2287772Ssam */
22960039Storek void
tftp(tp,size)23016372Skarels tftp(tp, size)
2317772Ssam struct tftphdr *tp;
2327772Ssam int size;
2337772Ssam {
2347772Ssam register char *cp;
2357772Ssam int first = 1, ecode;
2367772Ssam register struct formats *pf;
2377772Ssam char *filename, *mode;
2387772Ssam
2397772Ssam filename = cp = tp->th_stuff;
2407772Ssam again:
2417772Ssam while (cp < buf + size) {
2427772Ssam if (*cp == '\0')
2437772Ssam break;
2447772Ssam cp++;
2457772Ssam }
2467772Ssam if (*cp != '\0') {
2477772Ssam nak(EBADOP);
2487772Ssam exit(1);
2497772Ssam }
2507772Ssam if (first) {
2517772Ssam mode = ++cp;
2527772Ssam first = 0;
2537772Ssam goto again;
2547772Ssam }
2557772Ssam for (cp = mode; *cp; cp++)
2567772Ssam if (isupper(*cp))
2577772Ssam *cp = tolower(*cp);
2587772Ssam for (pf = formats; pf->f_mode; pf++)
2597772Ssam if (strcmp(pf->f_mode, mode) == 0)
2607772Ssam break;
2617772Ssam if (pf->f_mode == 0) {
2627772Ssam nak(EBADOP);
2637772Ssam exit(1);
2647772Ssam }
26557963Sandrew ecode = (*pf->f_validate)(&filename, tp->th_opcode);
26657963Sandrew if (logging) {
26757963Sandrew syslog(LOG_INFO, "%s: %s request for %s: %s",
26857963Sandrew verifyhost(&from),
26957963Sandrew tp->th_opcode == WRQ ? "write" : "read",
27057963Sandrew filename, errtomsg(ecode));
27157963Sandrew }
2727772Ssam if (ecode) {
27357963Sandrew /*
27457963Sandrew * Avoid storms of naks to a RRQ broadcast for a relative
27557963Sandrew * bootfile pathname from a diskless Sun.
27657963Sandrew */
27757963Sandrew if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
27857963Sandrew exit(0);
2797772Ssam nak(ecode);
2807772Ssam exit(1);
2817772Ssam }
2827772Ssam if (tp->th_opcode == WRQ)
2837772Ssam (*pf->f_recv)(pf);
2847772Ssam else
2857772Ssam (*pf->f_send)(pf);
2867772Ssam exit(0);
2877772Ssam }
2887772Ssam
28916372Skarels
29026108Sminshall FILE *file;
29126108Sminshall
2927772Ssam /*
2937772Ssam * Validate file access. Since we
2947772Ssam * have no uid or gid, for now require
2957772Ssam * file to exist and be publicly
2967772Ssam * readable/writable.
29735783Stef * If we were invoked with arguments
29835783Stef * from inetd then the file must also be
29935783Stef * in one of the given directory prefixes.
3007772Ssam * Note also, full path name must be
3017772Ssam * given as we have no login directory.
3027772Ssam */
30360039Storek int
validate_access(filep,mode)30457963Sandrew validate_access(filep, mode)
30557963Sandrew char **filep;
3067772Ssam int mode;
3077772Ssam {
3087772Ssam struct stat stbuf;
30926108Sminshall int fd;
31057963Sandrew struct dirlist *dirp;
31157963Sandrew static char pathname[MAXPATHLEN];
31257963Sandrew char *filename = *filep;
3137772Ssam
31441146Stef /*
31557963Sandrew * Prevent tricksters from getting around the directory restrictions
31641146Stef */
31757963Sandrew if (strstr(filename, "/../"))
31835783Stef return (EACCESS);
31957963Sandrew
32057963Sandrew if (*filename == '/') {
32157963Sandrew /*
32257963Sandrew * Allow the request if it's in one of the approved locations.
32360039Storek * Special case: check the null prefix ("/") by looking
32457963Sandrew * for length = 1 and relying on the arg. processing that
32557963Sandrew * it's a /.
32657963Sandrew */
32757963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) {
32857963Sandrew if (dirp->len == 1 ||
32957963Sandrew (!strncmp(filename, dirp->name, dirp->len) &&
33057963Sandrew filename[dirp->len] == '/'))
33157963Sandrew break;
33257963Sandrew }
33357963Sandrew /* If directory list is empty, allow access to any file */
33457963Sandrew if (dirp->name == NULL && dirp != dirs)
3357772Ssam return (EACCESS);
33657963Sandrew if (stat(filename, &stbuf) < 0)
33757963Sandrew return (errno == ENOENT ? ENOTFOUND : EACCESS);
33857963Sandrew if ((stbuf.st_mode & S_IFMT) != S_IFREG)
33957963Sandrew return (ENOTFOUND);
34057963Sandrew if (mode == RRQ) {
34157963Sandrew if ((stbuf.st_mode & S_IROTH) == 0)
34257963Sandrew return (EACCESS);
34357963Sandrew } else {
34457963Sandrew if ((stbuf.st_mode & S_IWOTH) == 0)
34557963Sandrew return (EACCESS);
34657963Sandrew }
3477772Ssam } else {
34857963Sandrew int err;
34957963Sandrew
35060039Storek /*
35157963Sandrew * Relative file name: search the approved locations for it.
35257963Sandrew * Don't allow write requests or ones that avoid directory
35357963Sandrew * restrictions.
35457963Sandrew */
35557963Sandrew
35657963Sandrew if (mode != RRQ || !strncmp(filename, "../", 3))
3577772Ssam return (EACCESS);
35857963Sandrew
35957963Sandrew /*
36057963Sandrew * If the file exists in one of the directories and isn't
36160039Storek * readable, continue looking. However, change the error code
36257963Sandrew * to give an indication that the file exists.
36357963Sandrew */
36457963Sandrew err = ENOTFOUND;
36557963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) {
36657963Sandrew sprintf(pathname, "%s/%s", dirp->name, filename);
36757963Sandrew if (stat(pathname, &stbuf) == 0 &&
36857963Sandrew (stbuf.st_mode & S_IFMT) == S_IFREG) {
36957963Sandrew if ((stbuf.st_mode & S_IROTH) != 0) {
37057963Sandrew break;
37157963Sandrew }
37257963Sandrew err = EACCESS;
37357963Sandrew }
37457963Sandrew }
37557963Sandrew if (dirp->name == NULL)
37657963Sandrew return (err);
37757963Sandrew *filep = filename = pathname;
3787772Ssam }
37926108Sminshall fd = open(filename, mode == RRQ ? 0 : 1);
3807772Ssam if (fd < 0)
3817772Ssam return (errno + 100);
38226108Sminshall file = fdopen(fd, (mode == RRQ)? "r":"w");
38326108Sminshall if (file == NULL) {
38426108Sminshall return errno+100;
38526108Sminshall }
3867772Ssam return (0);
3877772Ssam }
3887772Ssam
38913020Ssam int timeout;
39013020Ssam jmp_buf timeoutbuf;
3917772Ssam
39246687Sbostic void
timer()3937772Ssam timer()
3947772Ssam {
39513020Ssam
39613020Ssam timeout += rexmtval;
39713020Ssam if (timeout >= maxtimeout)
3987772Ssam exit(1);
39913020Ssam longjmp(timeoutbuf, 1);
4007772Ssam }
4017772Ssam
4027772Ssam /*
4037772Ssam * Send the requested file.
4047772Ssam */
40560039Storek void
sendfile(pf)4067772Ssam sendfile(pf)
40726108Sminshall struct formats *pf;
4087772Ssam {
40926108Sminshall struct tftphdr *dp, *r_init();
41026108Sminshall register struct tftphdr *ap; /* ack packet */
41160039Storek register int size, n;
41260039Storek volatile int block;
4137772Ssam
41413020Ssam signal(SIGALRM, timer);
41526108Sminshall dp = r_init();
41626108Sminshall ap = (struct tftphdr *)ackbuf;
41760039Storek block = 1;
4187772Ssam do {
41926108Sminshall size = readit(file, &dp, pf->f_convert);
4207772Ssam if (size < 0) {
4217772Ssam nak(errno + 100);
42226108Sminshall goto abort;
4237772Ssam }
42426108Sminshall dp->th_opcode = htons((u_short)DATA);
42526108Sminshall dp->th_block = htons((u_short)block);
4267772Ssam timeout = 0;
42760039Storek (void)setjmp(timeoutbuf);
42826108Sminshall
42926109Sminshall send_data:
43026108Sminshall if (send(peer, dp, size + 4, 0) != size + 4) {
43128070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n");
43226108Sminshall goto abort;
4337772Ssam }
43426108Sminshall read_ahead(file, pf->f_convert);
43526109Sminshall for ( ; ; ) {
43626108Sminshall alarm(rexmtval); /* read the ack */
43726108Sminshall n = recv(peer, ackbuf, sizeof (ackbuf), 0);
4387772Ssam alarm(0);
43913020Ssam if (n < 0) {
44028070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n");
44126108Sminshall goto abort;
44213020Ssam }
44326108Sminshall ap->th_opcode = ntohs((u_short)ap->th_opcode);
44426108Sminshall ap->th_block = ntohs((u_short)ap->th_block);
44526108Sminshall
44626108Sminshall if (ap->th_opcode == ERROR)
44726108Sminshall goto abort;
44860039Storek
44926109Sminshall if (ap->th_opcode == ACK) {
45060039Storek if (ap->th_block == block)
45126109Sminshall break;
45226115Sminshall /* Re-synchronize with the other side */
45326115Sminshall (void) synchnet(peer);
45460039Storek if (ap->th_block == (block -1))
45526109Sminshall goto send_data;
45626109Sminshall }
45726108Sminshall
45826109Sminshall }
4597772Ssam block++;
4607772Ssam } while (size == SEGSIZE);
46126108Sminshall abort:
46226108Sminshall (void) fclose(file);
4637772Ssam }
4647772Ssam
46546687Sbostic void
justquit()46626108Sminshall justquit()
46726108Sminshall {
46826108Sminshall exit(0);
46926108Sminshall }
47026108Sminshall
47126108Sminshall
4727772Ssam /*
4737772Ssam * Receive a file.
4747772Ssam */
47560039Storek void
recvfile(pf)4767772Ssam recvfile(pf)
47726108Sminshall struct formats *pf;
4787772Ssam {
47926108Sminshall struct tftphdr *dp, *w_init();
48026108Sminshall register struct tftphdr *ap; /* ack buffer */
48160039Storek register int n, size;
48260039Storek volatile int block;
4837772Ssam
48413020Ssam signal(SIGALRM, timer);
48526108Sminshall dp = w_init();
48626108Sminshall ap = (struct tftphdr *)ackbuf;
48760039Storek block = 0;
4887772Ssam do {
4897772Ssam timeout = 0;
49026108Sminshall ap->th_opcode = htons((u_short)ACK);
49126108Sminshall ap->th_block = htons((u_short)block);
4927772Ssam block++;
49313020Ssam (void) setjmp(timeoutbuf);
49426108Sminshall send_ack:
49526108Sminshall if (send(peer, ackbuf, 4, 0) != 4) {
49628070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n");
49713020Ssam goto abort;
4987772Ssam }
49926108Sminshall write_behind(file, pf->f_convert);
50026108Sminshall for ( ; ; ) {
50113020Ssam alarm(rexmtval);
50226108Sminshall n = recv(peer, dp, PKTSIZE, 0);
5037772Ssam alarm(0);
50426108Sminshall if (n < 0) { /* really? */
50528070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n");
50613020Ssam goto abort;
50713020Ssam }
50826108Sminshall dp->th_opcode = ntohs((u_short)dp->th_opcode);
50926108Sminshall dp->th_block = ntohs((u_short)dp->th_block);
51026108Sminshall if (dp->th_opcode == ERROR)
51113020Ssam goto abort;
51226108Sminshall if (dp->th_opcode == DATA) {
51326108Sminshall if (dp->th_block == block) {
51426108Sminshall break; /* normal */
51526108Sminshall }
51626115Sminshall /* Re-synchronize with the other side */
51726115Sminshall (void) synchnet(peer);
51826108Sminshall if (dp->th_block == (block-1))
51926108Sminshall goto send_ack; /* rexmit */
52026108Sminshall }
52126108Sminshall }
52226108Sminshall /* size = write(file, dp->th_data, n - 4); */
52326108Sminshall size = writeit(file, &dp, n - 4, pf->f_convert);
52426108Sminshall if (size != (n-4)) { /* ahem */
52526108Sminshall if (size < 0) nak(errno + 100);
52626108Sminshall else nak(ENOSPACE);
52713020Ssam goto abort;
5287772Ssam }
5297772Ssam } while (size == SEGSIZE);
53026108Sminshall write_behind(file, pf->f_convert);
53126108Sminshall (void) fclose(file); /* close data file */
53226108Sminshall
53326108Sminshall ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
53426108Sminshall ap->th_block = htons((u_short)(block));
53526108Sminshall (void) send(peer, ackbuf, 4, 0);
53626108Sminshall
53726108Sminshall signal(SIGALRM, justquit); /* just quit on timeout */
53826108Sminshall alarm(rexmtval);
53926108Sminshall n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
54026108Sminshall alarm(0);
54126108Sminshall if (n >= 4 && /* if read some data */
54226108Sminshall dp->th_opcode == DATA && /* and got a data block */
54326108Sminshall block == dp->th_block) { /* then my last ack was lost */
54426108Sminshall (void) send(peer, ackbuf, 4, 0); /* resend final ack */
54526108Sminshall }
54613020Ssam abort:
54726108Sminshall return;
5487772Ssam }
5497772Ssam
5507772Ssam struct errmsg {
5517772Ssam int e_code;
5527772Ssam char *e_msg;
5537772Ssam } errmsgs[] = {
5547772Ssam { EUNDEF, "Undefined error code" },
5557772Ssam { ENOTFOUND, "File not found" },
5567772Ssam { EACCESS, "Access violation" },
5577772Ssam { ENOSPACE, "Disk full or allocation exceeded" },
5587772Ssam { EBADOP, "Illegal TFTP operation" },
5597772Ssam { EBADID, "Unknown transfer ID" },
5607772Ssam { EEXISTS, "File already exists" },
5617772Ssam { ENOUSER, "No such user" },
5627772Ssam { -1, 0 }
5637772Ssam };
5647772Ssam
56557963Sandrew static char *
errtomsg(error)56657963Sandrew errtomsg(error)
56757963Sandrew int error;
56857963Sandrew {
56957963Sandrew static char buf[20];
57057963Sandrew register struct errmsg *pe;
57157963Sandrew if (error == 0)
57257963Sandrew return "success";
57357963Sandrew for (pe = errmsgs; pe->e_code >= 0; pe++)
57457963Sandrew if (pe->e_code == error)
57557963Sandrew return pe->e_msg;
57657963Sandrew sprintf(buf, "error %d", error);
57757963Sandrew return buf;
57857963Sandrew }
57957963Sandrew
5807772Ssam /*
5817772Ssam * Send a nak packet (error message).
5827772Ssam * Error code passed in is one of the
5837772Ssam * standard TFTP codes, or a UNIX errno
5847772Ssam * offset by 100.
5857772Ssam */
58660039Storek static void
nak(error)5877772Ssam nak(error)
5887772Ssam int error;
5897772Ssam {
5907772Ssam register struct tftphdr *tp;
5917772Ssam int length;
5927772Ssam register struct errmsg *pe;
5937772Ssam
5947772Ssam tp = (struct tftphdr *)buf;
5957772Ssam tp->th_opcode = htons((u_short)ERROR);
5967772Ssam tp->th_code = htons((u_short)error);
5977772Ssam for (pe = errmsgs; pe->e_code >= 0; pe++)
5987772Ssam if (pe->e_code == error)
5997772Ssam break;
60026108Sminshall if (pe->e_code < 0) {
60142413Sbostic pe->e_msg = strerror(error - 100);
60226108Sminshall tp->th_code = EUNDEF; /* set 'undef' errorcode */
60326108Sminshall }
6047772Ssam strcpy(tp->th_msg, pe->e_msg);
6057772Ssam length = strlen(pe->e_msg);
6067772Ssam tp->th_msg[length] = '\0';
6077772Ssam length += 5;
60816372Skarels if (send(peer, buf, length, 0) != length)
60928070Sminshall syslog(LOG_ERR, "nak: %m\n");
6107772Ssam }
61157963Sandrew
61257963Sandrew static char *
verifyhost(fromp)61357963Sandrew verifyhost(fromp)
61457963Sandrew struct sockaddr_in *fromp;
61557963Sandrew {
61657963Sandrew struct hostent *hp;
61757963Sandrew
61857963Sandrew hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr),
61957963Sandrew fromp->sin_family);
62057963Sandrew if (hp)
62157963Sandrew return hp->h_name;
62257963Sandrew else
62357963Sandrew return inet_ntoa(fromp->sin_addr);
62457963Sandrew }
625