121183Sdist /* 257963Sandrew * Copyright (c) 1983, 1993 Regents of the University of California. 333822Sbostic * All rights reserved. 433822Sbostic * 542673Sbostic * %sccs.include.redist.c% 621183Sdist */ 721183Sdist 813609Ssam #ifndef lint 921183Sdist char copyright[] = 1057963Sandrew "@(#) Copyright (c) 1983, 1993 Regents of the University of California.\n\ 1121183Sdist All rights reserved.\n"; 1233822Sbostic #endif /* not lint */ 137772Ssam 1421183Sdist #ifndef lint 15*60039Storek static char sccsid[] = "@(#)tftpd.c 5.15 (Berkeley) 05/16/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> 44*60039Storek #include <unistd.h> 459220Ssam 46*60039Storek #include "tftpsubs.h" 4757963Sandrew #include "pathnames.h" 4857963Sandrew 4913020Ssam #define TIMEOUT 5 5013020Ssam 5116372Skarels int peer; 5213020Ssam int rexmtval = TIMEOUT; 5313020Ssam int maxtimeout = 5*TIMEOUT; 5426108Sminshall 5526108Sminshall #define PKTSIZE SEGSIZE+4 5626108Sminshall char buf[PKTSIZE]; 5726108Sminshall char ackbuf[PKTSIZE]; 5816372Skarels struct sockaddr_in from; 5916372Skarels int fromlen; 607772Ssam 61*60039Storek void tftp __P((struct tftphdr *, int)); 62*60039Storek 6357963Sandrew /* 64*60039Storek * Null-terminated directory prefix list for absolute pathname requests and 6557963Sandrew * search list for relative pathname requests. 6657963Sandrew * 6757963Sandrew * MAXDIRS should be at least as large as the number of arguments that 6857963Sandrew * inetd allows (currently 20). 6957963Sandrew */ 7057963Sandrew #define MAXDIRS 20 7157963Sandrew static struct dirlist { 7257963Sandrew char *name; 7357963Sandrew int len; 7457963Sandrew } dirs[MAXDIRS+1]; 7557963Sandrew static int suppress_naks; 7657963Sandrew static int logging; 7735783Stef 78*60039Storek static char *errtomsg __P((int)); 79*60039Storek static void nak __P((int)); 80*60039Storek static char *verifyhost __P((struct sockaddr_in *)); 8157963Sandrew 82*60039Storek int 8357963Sandrew main(argc, argv) 8457963Sandrew int argc; 8557963Sandrew char *argv[]; 867772Ssam { 877772Ssam register struct tftphdr *tp; 8857963Sandrew register int n; 8957963Sandrew int ch, on; 90*60039Storek struct sockaddr_in sin; 917772Ssam 9257963Sandrew openlog("tftpd", LOG_PID, LOG_FTP); 9357963Sandrew while ((ch = getopt(argc, argv, "ln")) != EOF) { 9457963Sandrew switch (ch) { 9557963Sandrew case 'l': 9657963Sandrew logging = 1; 9757963Sandrew break; 9857963Sandrew case 'n': 9957963Sandrew suppress_naks = 1; 10057963Sandrew break; 10157963Sandrew default: 10257963Sandrew syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 10357963Sandrew } 10457963Sandrew } 10557963Sandrew if (optind < argc) { 10657963Sandrew struct dirlist *dirp; 10757963Sandrew 10857963Sandrew /* Get list of directory prefixes. Skip relative pathnames. */ 10957963Sandrew for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 11057963Sandrew optind++) { 11157963Sandrew if (argv[optind][0] == '/') { 11257963Sandrew dirp->name = argv[optind]; 11357963Sandrew dirp->len = strlen(dirp->name); 11457963Sandrew dirp++; 11557963Sandrew } 11657963Sandrew } 11757963Sandrew } 11857963Sandrew 11957963Sandrew on = 1; 12028070Sminshall if (ioctl(0, FIONBIO, &on) < 0) { 12128070Sminshall syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 12228070Sminshall exit(1); 12328070Sminshall } 12416372Skarels fromlen = sizeof (from); 12516372Skarels n = recvfrom(0, buf, sizeof (buf), 0, 12646687Sbostic (struct sockaddr *)&from, &fromlen); 12716372Skarels if (n < 0) { 12828070Sminshall syslog(LOG_ERR, "recvfrom: %m\n"); 1298385Ssam exit(1); 1308385Ssam } 13128070Sminshall /* 13228070Sminshall * Now that we have read the message out of the UDP 13328070Sminshall * socket, we fork and exit. Thus, inetd will go back 13428070Sminshall * to listening to the tftp port, and the next request 13528070Sminshall * to come in will start up a new instance of tftpd. 13628070Sminshall * 13728070Sminshall * We do this so that inetd can run tftpd in "wait" mode. 13828070Sminshall * The problem with tftpd running in "nowait" mode is that 13928070Sminshall * inetd may get one or more successful "selects" on the 14028070Sminshall * tftp port before we do our receive, so more than one 14128070Sminshall * instance of tftpd may be started up. Worse, if tftpd 14228070Sminshall * break before doing the above "recvfrom", inetd would 14328070Sminshall * spawn endless instances, clogging the system. 14428070Sminshall */ 14528070Sminshall { 14628070Sminshall int pid; 14728070Sminshall int i, j; 14828070Sminshall 14928070Sminshall for (i = 1; i < 20; i++) { 15028070Sminshall pid = fork(); 15128070Sminshall if (pid < 0) { 15228070Sminshall sleep(i); 15328070Sminshall /* 15428070Sminshall * flush out to most recently sent request. 15528070Sminshall * 15628070Sminshall * This may drop some request, but those 15728070Sminshall * will be resent by the clients when 15828070Sminshall * they timeout. The positive effect of 15928070Sminshall * this flush is to (try to) prevent more 16028070Sminshall * than one tftpd being started up to service 16128070Sminshall * a single request from a single client. 16228070Sminshall */ 16328070Sminshall j = sizeof from; 16428070Sminshall i = recvfrom(0, buf, sizeof (buf), 0, 16546687Sbostic (struct sockaddr *)&from, &j); 16628070Sminshall if (i > 0) { 16728070Sminshall n = i; 16828070Sminshall fromlen = j; 16928070Sminshall } 17028070Sminshall } else { 17128070Sminshall break; 17228070Sminshall } 17328070Sminshall } 17428070Sminshall if (pid < 0) { 17528070Sminshall syslog(LOG_ERR, "fork: %m\n"); 17628070Sminshall exit(1); 17728070Sminshall } else if (pid != 0) { 17828070Sminshall exit(0); 17928070Sminshall } 18028070Sminshall } 18116372Skarels from.sin_family = AF_INET; 18216372Skarels alarm(0); 18316372Skarels close(0); 18416372Skarels close(1); 18516372Skarels peer = socket(AF_INET, SOCK_DGRAM, 0); 18616372Skarels if (peer < 0) { 18728070Sminshall syslog(LOG_ERR, "socket: %m\n"); 18816372Skarels exit(1); 1897772Ssam } 190*60039Storek memset(&sin, 0, sizeof(sin)); 191*60039Storek sin.sin_family = AF_INET; 19246687Sbostic if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 19328070Sminshall syslog(LOG_ERR, "bind: %m\n"); 19416372Skarels exit(1); 19516372Skarels } 19646687Sbostic if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 19728070Sminshall syslog(LOG_ERR, "connect: %m\n"); 19816372Skarels exit(1); 1997772Ssam } 20016372Skarels tp = (struct tftphdr *)buf; 20116372Skarels tp->th_opcode = ntohs(tp->th_opcode); 20216372Skarels if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 20316372Skarels tftp(tp, n); 20416372Skarels exit(1); 2057772Ssam } 2067772Ssam 207*60039Storek struct formats; 208*60039Storek int validate_access __P((char **, int)); 209*60039Storek void sendfile __P((struct formats *)); 210*60039Storek void recvfile __P((struct formats *)); 2117772Ssam 2127772Ssam struct formats { 2137772Ssam char *f_mode; 214*60039Storek int (*f_validate) __P((char **, int)); 215*60039Storek void (*f_send) __P((struct formats *)); 216*60039Storek void (*f_recv) __P((struct formats *)); 21726108Sminshall int f_convert; 2187772Ssam } formats[] = { 21926108Sminshall { "netascii", validate_access, sendfile, recvfile, 1 }, 22026108Sminshall { "octet", validate_access, sendfile, recvfile, 0 }, 2217772Ssam #ifdef notdef 22226108Sminshall { "mail", validate_user, sendmail, recvmail, 1 }, 2237772Ssam #endif 2247772Ssam { 0 } 2257772Ssam }; 2267772Ssam 2277772Ssam /* 2287772Ssam * Handle initial connection protocol. 2297772Ssam */ 230*60039Storek void 23116372Skarels tftp(tp, size) 2327772Ssam struct tftphdr *tp; 2337772Ssam int size; 2347772Ssam { 2357772Ssam register char *cp; 2367772Ssam int first = 1, ecode; 2377772Ssam register struct formats *pf; 2387772Ssam char *filename, *mode; 2397772Ssam 2407772Ssam filename = cp = tp->th_stuff; 2417772Ssam again: 2427772Ssam while (cp < buf + size) { 2437772Ssam if (*cp == '\0') 2447772Ssam break; 2457772Ssam cp++; 2467772Ssam } 2477772Ssam if (*cp != '\0') { 2487772Ssam nak(EBADOP); 2497772Ssam exit(1); 2507772Ssam } 2517772Ssam if (first) { 2527772Ssam mode = ++cp; 2537772Ssam first = 0; 2547772Ssam goto again; 2557772Ssam } 2567772Ssam for (cp = mode; *cp; cp++) 2577772Ssam if (isupper(*cp)) 2587772Ssam *cp = tolower(*cp); 2597772Ssam for (pf = formats; pf->f_mode; pf++) 2607772Ssam if (strcmp(pf->f_mode, mode) == 0) 2617772Ssam break; 2627772Ssam if (pf->f_mode == 0) { 2637772Ssam nak(EBADOP); 2647772Ssam exit(1); 2657772Ssam } 26657963Sandrew ecode = (*pf->f_validate)(&filename, tp->th_opcode); 26757963Sandrew if (logging) { 26857963Sandrew syslog(LOG_INFO, "%s: %s request for %s: %s", 26957963Sandrew verifyhost(&from), 27057963Sandrew tp->th_opcode == WRQ ? "write" : "read", 27157963Sandrew filename, errtomsg(ecode)); 27257963Sandrew } 2737772Ssam if (ecode) { 27457963Sandrew /* 27557963Sandrew * Avoid storms of naks to a RRQ broadcast for a relative 27657963Sandrew * bootfile pathname from a diskless Sun. 27757963Sandrew */ 27857963Sandrew if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 27957963Sandrew exit(0); 2807772Ssam nak(ecode); 2817772Ssam exit(1); 2827772Ssam } 2837772Ssam if (tp->th_opcode == WRQ) 2847772Ssam (*pf->f_recv)(pf); 2857772Ssam else 2867772Ssam (*pf->f_send)(pf); 2877772Ssam exit(0); 2887772Ssam } 2897772Ssam 29016372Skarels 29126108Sminshall FILE *file; 29226108Sminshall 2937772Ssam /* 2947772Ssam * Validate file access. Since we 2957772Ssam * have no uid or gid, for now require 2967772Ssam * file to exist and be publicly 2977772Ssam * readable/writable. 29835783Stef * If we were invoked with arguments 29935783Stef * from inetd then the file must also be 30035783Stef * in one of the given directory prefixes. 3017772Ssam * Note also, full path name must be 3027772Ssam * given as we have no login directory. 3037772Ssam */ 304*60039Storek int 30557963Sandrew validate_access(filep, mode) 30657963Sandrew char **filep; 3077772Ssam int mode; 3087772Ssam { 3097772Ssam struct stat stbuf; 31026108Sminshall int fd; 31157963Sandrew struct dirlist *dirp; 31257963Sandrew static char pathname[MAXPATHLEN]; 31357963Sandrew char *filename = *filep; 3147772Ssam 31541146Stef /* 31657963Sandrew * Prevent tricksters from getting around the directory restrictions 31741146Stef */ 31857963Sandrew if (strstr(filename, "/../")) 31935783Stef return (EACCESS); 32057963Sandrew 32157963Sandrew if (*filename == '/') { 32257963Sandrew /* 32357963Sandrew * Allow the request if it's in one of the approved locations. 324*60039Storek * Special case: check the null prefix ("/") by looking 32557963Sandrew * for length = 1 and relying on the arg. processing that 32657963Sandrew * it's a /. 32757963Sandrew */ 32857963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) { 32957963Sandrew if (dirp->len == 1 || 33057963Sandrew (!strncmp(filename, dirp->name, dirp->len) && 33157963Sandrew filename[dirp->len] == '/')) 33257963Sandrew break; 33357963Sandrew } 33457963Sandrew /* If directory list is empty, allow access to any file */ 33557963Sandrew if (dirp->name == NULL && dirp != dirs) 3367772Ssam return (EACCESS); 33757963Sandrew if (stat(filename, &stbuf) < 0) 33857963Sandrew return (errno == ENOENT ? ENOTFOUND : EACCESS); 33957963Sandrew if ((stbuf.st_mode & S_IFMT) != S_IFREG) 34057963Sandrew return (ENOTFOUND); 34157963Sandrew if (mode == RRQ) { 34257963Sandrew if ((stbuf.st_mode & S_IROTH) == 0) 34357963Sandrew return (EACCESS); 34457963Sandrew } else { 34557963Sandrew if ((stbuf.st_mode & S_IWOTH) == 0) 34657963Sandrew return (EACCESS); 34757963Sandrew } 3487772Ssam } else { 34957963Sandrew int err; 35057963Sandrew 351*60039Storek /* 35257963Sandrew * Relative file name: search the approved locations for it. 35357963Sandrew * Don't allow write requests or ones that avoid directory 35457963Sandrew * restrictions. 35557963Sandrew */ 35657963Sandrew 35757963Sandrew if (mode != RRQ || !strncmp(filename, "../", 3)) 3587772Ssam return (EACCESS); 35957963Sandrew 36057963Sandrew /* 36157963Sandrew * If the file exists in one of the directories and isn't 362*60039Storek * readable, continue looking. However, change the error code 36357963Sandrew * to give an indication that the file exists. 36457963Sandrew */ 36557963Sandrew err = ENOTFOUND; 36657963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) { 36757963Sandrew sprintf(pathname, "%s/%s", dirp->name, filename); 36857963Sandrew if (stat(pathname, &stbuf) == 0 && 36957963Sandrew (stbuf.st_mode & S_IFMT) == S_IFREG) { 37057963Sandrew if ((stbuf.st_mode & S_IROTH) != 0) { 37157963Sandrew break; 37257963Sandrew } 37357963Sandrew err = EACCESS; 37457963Sandrew } 37557963Sandrew } 37657963Sandrew if (dirp->name == NULL) 37757963Sandrew return (err); 37857963Sandrew *filep = filename = pathname; 3797772Ssam } 38026108Sminshall fd = open(filename, mode == RRQ ? 0 : 1); 3817772Ssam if (fd < 0) 3827772Ssam return (errno + 100); 38326108Sminshall file = fdopen(fd, (mode == RRQ)? "r":"w"); 38426108Sminshall if (file == NULL) { 38526108Sminshall return errno+100; 38626108Sminshall } 3877772Ssam return (0); 3887772Ssam } 3897772Ssam 39013020Ssam int timeout; 39113020Ssam jmp_buf timeoutbuf; 3927772Ssam 39346687Sbostic void 3947772Ssam timer() 3957772Ssam { 39613020Ssam 39713020Ssam timeout += rexmtval; 39813020Ssam if (timeout >= maxtimeout) 3997772Ssam exit(1); 40013020Ssam longjmp(timeoutbuf, 1); 4017772Ssam } 4027772Ssam 4037772Ssam /* 4047772Ssam * Send the requested file. 4057772Ssam */ 406*60039Storek void 4077772Ssam sendfile(pf) 40826108Sminshall struct formats *pf; 4097772Ssam { 41026108Sminshall struct tftphdr *dp, *r_init(); 41126108Sminshall register struct tftphdr *ap; /* ack packet */ 412*60039Storek register int size, n; 413*60039Storek volatile int block; 4147772Ssam 41513020Ssam signal(SIGALRM, timer); 41626108Sminshall dp = r_init(); 41726108Sminshall ap = (struct tftphdr *)ackbuf; 418*60039Storek block = 1; 4197772Ssam do { 42026108Sminshall size = readit(file, &dp, pf->f_convert); 4217772Ssam if (size < 0) { 4227772Ssam nak(errno + 100); 42326108Sminshall goto abort; 4247772Ssam } 42526108Sminshall dp->th_opcode = htons((u_short)DATA); 42626108Sminshall dp->th_block = htons((u_short)block); 4277772Ssam timeout = 0; 428*60039Storek (void)setjmp(timeoutbuf); 42926108Sminshall 43026109Sminshall send_data: 43126108Sminshall if (send(peer, dp, size + 4, 0) != size + 4) { 43228070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n"); 43326108Sminshall goto abort; 4347772Ssam } 43526108Sminshall read_ahead(file, pf->f_convert); 43626109Sminshall for ( ; ; ) { 43726108Sminshall alarm(rexmtval); /* read the ack */ 43826108Sminshall n = recv(peer, ackbuf, sizeof (ackbuf), 0); 4397772Ssam alarm(0); 44013020Ssam if (n < 0) { 44128070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n"); 44226108Sminshall goto abort; 44313020Ssam } 44426108Sminshall ap->th_opcode = ntohs((u_short)ap->th_opcode); 44526108Sminshall ap->th_block = ntohs((u_short)ap->th_block); 44626108Sminshall 44726108Sminshall if (ap->th_opcode == ERROR) 44826108Sminshall goto abort; 449*60039Storek 45026109Sminshall if (ap->th_opcode == ACK) { 451*60039Storek if (ap->th_block == block) 45226109Sminshall break; 45326115Sminshall /* Re-synchronize with the other side */ 45426115Sminshall (void) synchnet(peer); 455*60039Storek if (ap->th_block == (block -1)) 45626109Sminshall goto send_data; 45726109Sminshall } 45826108Sminshall 45926109Sminshall } 4607772Ssam block++; 4617772Ssam } while (size == SEGSIZE); 46226108Sminshall abort: 46326108Sminshall (void) fclose(file); 4647772Ssam } 4657772Ssam 46646687Sbostic void 46726108Sminshall justquit() 46826108Sminshall { 46926108Sminshall exit(0); 47026108Sminshall } 47126108Sminshall 47226108Sminshall 4737772Ssam /* 4747772Ssam * Receive a file. 4757772Ssam */ 476*60039Storek void 4777772Ssam recvfile(pf) 47826108Sminshall struct formats *pf; 4797772Ssam { 48026108Sminshall struct tftphdr *dp, *w_init(); 48126108Sminshall register struct tftphdr *ap; /* ack buffer */ 482*60039Storek register int n, size; 483*60039Storek volatile int block; 4847772Ssam 48513020Ssam signal(SIGALRM, timer); 48626108Sminshall dp = w_init(); 48726108Sminshall ap = (struct tftphdr *)ackbuf; 488*60039Storek block = 0; 4897772Ssam do { 4907772Ssam timeout = 0; 49126108Sminshall ap->th_opcode = htons((u_short)ACK); 49226108Sminshall ap->th_block = htons((u_short)block); 4937772Ssam block++; 49413020Ssam (void) setjmp(timeoutbuf); 49526108Sminshall send_ack: 49626108Sminshall if (send(peer, ackbuf, 4, 0) != 4) { 49728070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n"); 49813020Ssam goto abort; 4997772Ssam } 50026108Sminshall write_behind(file, pf->f_convert); 50126108Sminshall for ( ; ; ) { 50213020Ssam alarm(rexmtval); 50326108Sminshall n = recv(peer, dp, PKTSIZE, 0); 5047772Ssam alarm(0); 50526108Sminshall if (n < 0) { /* really? */ 50628070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n"); 50713020Ssam goto abort; 50813020Ssam } 50926108Sminshall dp->th_opcode = ntohs((u_short)dp->th_opcode); 51026108Sminshall dp->th_block = ntohs((u_short)dp->th_block); 51126108Sminshall if (dp->th_opcode == ERROR) 51213020Ssam goto abort; 51326108Sminshall if (dp->th_opcode == DATA) { 51426108Sminshall if (dp->th_block == block) { 51526108Sminshall break; /* normal */ 51626108Sminshall } 51726115Sminshall /* Re-synchronize with the other side */ 51826115Sminshall (void) synchnet(peer); 51926108Sminshall if (dp->th_block == (block-1)) 52026108Sminshall goto send_ack; /* rexmit */ 52126108Sminshall } 52226108Sminshall } 52326108Sminshall /* size = write(file, dp->th_data, n - 4); */ 52426108Sminshall size = writeit(file, &dp, n - 4, pf->f_convert); 52526108Sminshall if (size != (n-4)) { /* ahem */ 52626108Sminshall if (size < 0) nak(errno + 100); 52726108Sminshall else nak(ENOSPACE); 52813020Ssam goto abort; 5297772Ssam } 5307772Ssam } while (size == SEGSIZE); 53126108Sminshall write_behind(file, pf->f_convert); 53226108Sminshall (void) fclose(file); /* close data file */ 53326108Sminshall 53426108Sminshall ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 53526108Sminshall ap->th_block = htons((u_short)(block)); 53626108Sminshall (void) send(peer, ackbuf, 4, 0); 53726108Sminshall 53826108Sminshall signal(SIGALRM, justquit); /* just quit on timeout */ 53926108Sminshall alarm(rexmtval); 54026108Sminshall n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 54126108Sminshall alarm(0); 54226108Sminshall if (n >= 4 && /* if read some data */ 54326108Sminshall dp->th_opcode == DATA && /* and got a data block */ 54426108Sminshall block == dp->th_block) { /* then my last ack was lost */ 54526108Sminshall (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 54626108Sminshall } 54713020Ssam abort: 54826108Sminshall return; 5497772Ssam } 5507772Ssam 5517772Ssam struct errmsg { 5527772Ssam int e_code; 5537772Ssam char *e_msg; 5547772Ssam } errmsgs[] = { 5557772Ssam { EUNDEF, "Undefined error code" }, 5567772Ssam { ENOTFOUND, "File not found" }, 5577772Ssam { EACCESS, "Access violation" }, 5587772Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 5597772Ssam { EBADOP, "Illegal TFTP operation" }, 5607772Ssam { EBADID, "Unknown transfer ID" }, 5617772Ssam { EEXISTS, "File already exists" }, 5627772Ssam { ENOUSER, "No such user" }, 5637772Ssam { -1, 0 } 5647772Ssam }; 5657772Ssam 56657963Sandrew static char * 56757963Sandrew errtomsg(error) 56857963Sandrew int error; 56957963Sandrew { 57057963Sandrew static char buf[20]; 57157963Sandrew register struct errmsg *pe; 57257963Sandrew if (error == 0) 57357963Sandrew return "success"; 57457963Sandrew for (pe = errmsgs; pe->e_code >= 0; pe++) 57557963Sandrew if (pe->e_code == error) 57657963Sandrew return pe->e_msg; 57757963Sandrew sprintf(buf, "error %d", error); 57857963Sandrew return buf; 57957963Sandrew } 58057963Sandrew 5817772Ssam /* 5827772Ssam * Send a nak packet (error message). 5837772Ssam * Error code passed in is one of the 5847772Ssam * standard TFTP codes, or a UNIX errno 5857772Ssam * offset by 100. 5867772Ssam */ 587*60039Storek static void 5887772Ssam nak(error) 5897772Ssam int error; 5907772Ssam { 5917772Ssam register struct tftphdr *tp; 5927772Ssam int length; 5937772Ssam register struct errmsg *pe; 5947772Ssam 5957772Ssam tp = (struct tftphdr *)buf; 5967772Ssam tp->th_opcode = htons((u_short)ERROR); 5977772Ssam tp->th_code = htons((u_short)error); 5987772Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 5997772Ssam if (pe->e_code == error) 6007772Ssam break; 60126108Sminshall if (pe->e_code < 0) { 60242413Sbostic pe->e_msg = strerror(error - 100); 60326108Sminshall tp->th_code = EUNDEF; /* set 'undef' errorcode */ 60426108Sminshall } 6057772Ssam strcpy(tp->th_msg, pe->e_msg); 6067772Ssam length = strlen(pe->e_msg); 6077772Ssam tp->th_msg[length] = '\0'; 6087772Ssam length += 5; 60916372Skarels if (send(peer, buf, length, 0) != length) 61028070Sminshall syslog(LOG_ERR, "nak: %m\n"); 6117772Ssam } 61257963Sandrew 61357963Sandrew static char * 61457963Sandrew verifyhost(fromp) 61557963Sandrew struct sockaddr_in *fromp; 61657963Sandrew { 61757963Sandrew struct hostent *hp; 61857963Sandrew 61957963Sandrew hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), 62057963Sandrew fromp->sin_family); 62157963Sandrew if (hp) 62257963Sandrew return hp->h_name; 62357963Sandrew else 62457963Sandrew return inet_ntoa(fromp->sin_addr); 62557963Sandrew } 626