121183Sdist /* 2*57963Sandrew * 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[] = 10*57963Sandrew "@(#) 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*57963Sandrew static char sccsid[] = "@(#)tftpd.c 5.14 (Berkeley) 02/12/93"; 1633822Sbostic #endif /* not lint */ 1721183Sdist 187772Ssam /* 197772Ssam * Trivial file transfer protocol server. 2026108Sminshall * 21*57963Sandrew * This version includes many modifications by Jim Guyton 22*57963Sandrew * <guyton@rand-unix>. 237772Ssam */ 2426108Sminshall 25*57963Sandrew #include <sys/param.h> 269220Ssam #include <sys/ioctl.h> 2713609Ssam #include <sys/stat.h> 28*57963Sandrew #include <sys/socket.h> 299220Ssam 309220Ssam #include <netinet/in.h> 3112217Ssam #include <arpa/tftp.h> 32*57963Sandrew #include <arpa/inet.h> 33*57963Sandrew 34*57963Sandrew #include <ctype.h> 35*57963Sandrew #include <errno.h> 36*57963Sandrew #include <fcntl.h> 3746687Sbostic #include <netdb.h> 3842413Sbostic #include <setjmp.h> 39*57963Sandrew #include <signal.h> 407772Ssam #include <stdio.h> 41*57963Sandrew #include <stdlib.h> 4242413Sbostic #include <string.h> 43*57963Sandrew #include <syslog.h> 449220Ssam 45*57963Sandrew #include "pathnames.h" 46*57963Sandrew 4713020Ssam #define TIMEOUT 5 4813020Ssam 498385Ssam struct sockaddr_in sin = { AF_INET }; 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 60*57963Sandrew /* 61*57963Sandrew * Null-terminated directory prefix list for absolute pathname requests and 62*57963Sandrew * search list for relative pathname requests. 63*57963Sandrew * 64*57963Sandrew * MAXDIRS should be at least as large as the number of arguments that 65*57963Sandrew * inetd allows (currently 20). 66*57963Sandrew */ 67*57963Sandrew #define MAXDIRS 20 68*57963Sandrew static struct dirlist { 69*57963Sandrew char *name; 70*57963Sandrew int len; 71*57963Sandrew } dirs[MAXDIRS+1]; 72*57963Sandrew static int suppress_naks; 73*57963Sandrew static int logging; 7435783Stef 75*57963Sandrew static char *errtomsg(); 76*57963Sandrew static char *verifyhost(); 77*57963Sandrew 78*57963Sandrew main(argc, argv) 79*57963Sandrew int argc; 80*57963Sandrew char *argv[]; 817772Ssam { 827772Ssam register struct tftphdr *tp; 83*57963Sandrew register int n; 84*57963Sandrew int ch, on; 857772Ssam 86*57963Sandrew openlog("tftpd", LOG_PID, LOG_FTP); 87*57963Sandrew while ((ch = getopt(argc, argv, "ln")) != EOF) { 88*57963Sandrew switch (ch) { 89*57963Sandrew case 'l': 90*57963Sandrew logging = 1; 91*57963Sandrew break; 92*57963Sandrew case 'n': 93*57963Sandrew suppress_naks = 1; 94*57963Sandrew break; 95*57963Sandrew default: 96*57963Sandrew syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 97*57963Sandrew } 98*57963Sandrew } 99*57963Sandrew if (optind < argc) { 100*57963Sandrew struct dirlist *dirp; 101*57963Sandrew 102*57963Sandrew /* Get list of directory prefixes. Skip relative pathnames. */ 103*57963Sandrew for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 104*57963Sandrew optind++) { 105*57963Sandrew if (argv[optind][0] == '/') { 106*57963Sandrew dirp->name = argv[optind]; 107*57963Sandrew dirp->len = strlen(dirp->name); 108*57963Sandrew dirp++; 109*57963Sandrew } 110*57963Sandrew } 111*57963Sandrew } 112*57963Sandrew 113*57963Sandrew on = 1; 11428070Sminshall if (ioctl(0, FIONBIO, &on) < 0) { 11528070Sminshall syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 11628070Sminshall exit(1); 11728070Sminshall } 11816372Skarels fromlen = sizeof (from); 11916372Skarels n = recvfrom(0, buf, sizeof (buf), 0, 12046687Sbostic (struct sockaddr *)&from, &fromlen); 12116372Skarels if (n < 0) { 12228070Sminshall syslog(LOG_ERR, "recvfrom: %m\n"); 1238385Ssam exit(1); 1248385Ssam } 12528070Sminshall /* 12628070Sminshall * Now that we have read the message out of the UDP 12728070Sminshall * socket, we fork and exit. Thus, inetd will go back 12828070Sminshall * to listening to the tftp port, and the next request 12928070Sminshall * to come in will start up a new instance of tftpd. 13028070Sminshall * 13128070Sminshall * We do this so that inetd can run tftpd in "wait" mode. 13228070Sminshall * The problem with tftpd running in "nowait" mode is that 13328070Sminshall * inetd may get one or more successful "selects" on the 13428070Sminshall * tftp port before we do our receive, so more than one 13528070Sminshall * instance of tftpd may be started up. Worse, if tftpd 13628070Sminshall * break before doing the above "recvfrom", inetd would 13728070Sminshall * spawn endless instances, clogging the system. 13828070Sminshall */ 13928070Sminshall { 14028070Sminshall int pid; 14128070Sminshall int i, j; 14228070Sminshall 14328070Sminshall for (i = 1; i < 20; i++) { 14428070Sminshall pid = fork(); 14528070Sminshall if (pid < 0) { 14628070Sminshall sleep(i); 14728070Sminshall /* 14828070Sminshall * flush out to most recently sent request. 14928070Sminshall * 15028070Sminshall * This may drop some request, but those 15128070Sminshall * will be resent by the clients when 15228070Sminshall * they timeout. The positive effect of 15328070Sminshall * this flush is to (try to) prevent more 15428070Sminshall * than one tftpd being started up to service 15528070Sminshall * a single request from a single client. 15628070Sminshall */ 15728070Sminshall j = sizeof from; 15828070Sminshall i = recvfrom(0, buf, sizeof (buf), 0, 15946687Sbostic (struct sockaddr *)&from, &j); 16028070Sminshall if (i > 0) { 16128070Sminshall n = i; 16228070Sminshall fromlen = j; 16328070Sminshall } 16428070Sminshall } else { 16528070Sminshall break; 16628070Sminshall } 16728070Sminshall } 16828070Sminshall if (pid < 0) { 16928070Sminshall syslog(LOG_ERR, "fork: %m\n"); 17028070Sminshall exit(1); 17128070Sminshall } else if (pid != 0) { 17228070Sminshall exit(0); 17328070Sminshall } 17428070Sminshall } 17516372Skarels from.sin_family = AF_INET; 17616372Skarels alarm(0); 17716372Skarels close(0); 17816372Skarels close(1); 17916372Skarels peer = socket(AF_INET, SOCK_DGRAM, 0); 18016372Skarels if (peer < 0) { 18128070Sminshall syslog(LOG_ERR, "socket: %m\n"); 18216372Skarels exit(1); 1837772Ssam } 18446687Sbostic if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 18528070Sminshall syslog(LOG_ERR, "bind: %m\n"); 18616372Skarels exit(1); 18716372Skarels } 18846687Sbostic if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 18928070Sminshall syslog(LOG_ERR, "connect: %m\n"); 19016372Skarels exit(1); 1917772Ssam } 19216372Skarels tp = (struct tftphdr *)buf; 19316372Skarels tp->th_opcode = ntohs(tp->th_opcode); 19416372Skarels if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 19516372Skarels tftp(tp, n); 19616372Skarels exit(1); 1977772Ssam } 1987772Ssam 1997772Ssam int validate_access(); 2007772Ssam int sendfile(), recvfile(); 2017772Ssam 2027772Ssam struct formats { 2037772Ssam char *f_mode; 2047772Ssam int (*f_validate)(); 2057772Ssam int (*f_send)(); 2067772Ssam int (*f_recv)(); 20726108Sminshall int f_convert; 2087772Ssam } formats[] = { 20926108Sminshall { "netascii", validate_access, sendfile, recvfile, 1 }, 21026108Sminshall { "octet", validate_access, sendfile, recvfile, 0 }, 2117772Ssam #ifdef notdef 21226108Sminshall { "mail", validate_user, sendmail, recvmail, 1 }, 2137772Ssam #endif 2147772Ssam { 0 } 2157772Ssam }; 2167772Ssam 2177772Ssam /* 2187772Ssam * Handle initial connection protocol. 2197772Ssam */ 22016372Skarels tftp(tp, size) 2217772Ssam struct tftphdr *tp; 2227772Ssam int size; 2237772Ssam { 2247772Ssam register char *cp; 2257772Ssam int first = 1, ecode; 2267772Ssam register struct formats *pf; 2277772Ssam char *filename, *mode; 2287772Ssam 2297772Ssam filename = cp = tp->th_stuff; 2307772Ssam again: 2317772Ssam while (cp < buf + size) { 2327772Ssam if (*cp == '\0') 2337772Ssam break; 2347772Ssam cp++; 2357772Ssam } 2367772Ssam if (*cp != '\0') { 2377772Ssam nak(EBADOP); 2387772Ssam exit(1); 2397772Ssam } 2407772Ssam if (first) { 2417772Ssam mode = ++cp; 2427772Ssam first = 0; 2437772Ssam goto again; 2447772Ssam } 2457772Ssam for (cp = mode; *cp; cp++) 2467772Ssam if (isupper(*cp)) 2477772Ssam *cp = tolower(*cp); 2487772Ssam for (pf = formats; pf->f_mode; pf++) 2497772Ssam if (strcmp(pf->f_mode, mode) == 0) 2507772Ssam break; 2517772Ssam if (pf->f_mode == 0) { 2527772Ssam nak(EBADOP); 2537772Ssam exit(1); 2547772Ssam } 255*57963Sandrew ecode = (*pf->f_validate)(&filename, tp->th_opcode); 256*57963Sandrew if (logging) { 257*57963Sandrew syslog(LOG_INFO, "%s: %s request for %s: %s", 258*57963Sandrew verifyhost(&from), 259*57963Sandrew tp->th_opcode == WRQ ? "write" : "read", 260*57963Sandrew filename, errtomsg(ecode)); 261*57963Sandrew } 2627772Ssam if (ecode) { 263*57963Sandrew /* 264*57963Sandrew * Avoid storms of naks to a RRQ broadcast for a relative 265*57963Sandrew * bootfile pathname from a diskless Sun. 266*57963Sandrew */ 267*57963Sandrew if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 268*57963Sandrew exit(0); 2697772Ssam nak(ecode); 2707772Ssam exit(1); 2717772Ssam } 2727772Ssam if (tp->th_opcode == WRQ) 2737772Ssam (*pf->f_recv)(pf); 2747772Ssam else 2757772Ssam (*pf->f_send)(pf); 2767772Ssam exit(0); 2777772Ssam } 2787772Ssam 27916372Skarels 28026108Sminshall FILE *file; 28126108Sminshall 2827772Ssam /* 2837772Ssam * Validate file access. Since we 2847772Ssam * have no uid or gid, for now require 2857772Ssam * file to exist and be publicly 2867772Ssam * readable/writable. 28735783Stef * If we were invoked with arguments 28835783Stef * from inetd then the file must also be 28935783Stef * in one of the given directory prefixes. 2907772Ssam * Note also, full path name must be 2917772Ssam * given as we have no login directory. 2927772Ssam */ 293*57963Sandrew validate_access(filep, mode) 294*57963Sandrew char **filep; 2957772Ssam int mode; 2967772Ssam { 2977772Ssam struct stat stbuf; 29826108Sminshall int fd; 299*57963Sandrew struct dirlist *dirp; 300*57963Sandrew static char pathname[MAXPATHLEN]; 301*57963Sandrew char *filename = *filep; 3027772Ssam 30341146Stef /* 304*57963Sandrew * Prevent tricksters from getting around the directory restrictions 30541146Stef */ 306*57963Sandrew if (strstr(filename, "/../")) 30735783Stef return (EACCESS); 308*57963Sandrew 309*57963Sandrew if (*filename == '/') { 310*57963Sandrew /* 311*57963Sandrew * Allow the request if it's in one of the approved locations. 312*57963Sandrew * Special case: check the null prefix ("/") by looking 313*57963Sandrew * for length = 1 and relying on the arg. processing that 314*57963Sandrew * it's a /. 315*57963Sandrew */ 316*57963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) { 317*57963Sandrew if (dirp->len == 1 || 318*57963Sandrew (!strncmp(filename, dirp->name, dirp->len) && 319*57963Sandrew filename[dirp->len] == '/')) 320*57963Sandrew break; 321*57963Sandrew } 322*57963Sandrew /* If directory list is empty, allow access to any file */ 323*57963Sandrew if (dirp->name == NULL && dirp != dirs) 3247772Ssam return (EACCESS); 325*57963Sandrew if (stat(filename, &stbuf) < 0) 326*57963Sandrew return (errno == ENOENT ? ENOTFOUND : EACCESS); 327*57963Sandrew if ((stbuf.st_mode & S_IFMT) != S_IFREG) 328*57963Sandrew return (ENOTFOUND); 329*57963Sandrew if (mode == RRQ) { 330*57963Sandrew if ((stbuf.st_mode & S_IROTH) == 0) 331*57963Sandrew return (EACCESS); 332*57963Sandrew } else { 333*57963Sandrew if ((stbuf.st_mode & S_IWOTH) == 0) 334*57963Sandrew return (EACCESS); 335*57963Sandrew } 3367772Ssam } else { 337*57963Sandrew int err; 338*57963Sandrew 339*57963Sandrew /* 340*57963Sandrew * Relative file name: search the approved locations for it. 341*57963Sandrew * Don't allow write requests or ones that avoid directory 342*57963Sandrew * restrictions. 343*57963Sandrew */ 344*57963Sandrew 345*57963Sandrew if (mode != RRQ || !strncmp(filename, "../", 3)) 3467772Ssam return (EACCESS); 347*57963Sandrew 348*57963Sandrew /* 349*57963Sandrew * If the file exists in one of the directories and isn't 350*57963Sandrew * readable, continue looking. However, change the error code 351*57963Sandrew * to give an indication that the file exists. 352*57963Sandrew */ 353*57963Sandrew err = ENOTFOUND; 354*57963Sandrew for (dirp = dirs; dirp->name != NULL; dirp++) { 355*57963Sandrew sprintf(pathname, "%s/%s", dirp->name, filename); 356*57963Sandrew if (stat(pathname, &stbuf) == 0 && 357*57963Sandrew (stbuf.st_mode & S_IFMT) == S_IFREG) { 358*57963Sandrew if ((stbuf.st_mode & S_IROTH) != 0) { 359*57963Sandrew break; 360*57963Sandrew } 361*57963Sandrew err = EACCESS; 362*57963Sandrew } 363*57963Sandrew } 364*57963Sandrew if (dirp->name == NULL) 365*57963Sandrew return (err); 366*57963Sandrew *filep = filename = pathname; 3677772Ssam } 36826108Sminshall fd = open(filename, mode == RRQ ? 0 : 1); 3697772Ssam if (fd < 0) 3707772Ssam return (errno + 100); 37126108Sminshall file = fdopen(fd, (mode == RRQ)? "r":"w"); 37226108Sminshall if (file == NULL) { 37326108Sminshall return errno+100; 37426108Sminshall } 3757772Ssam return (0); 3767772Ssam } 3777772Ssam 37813020Ssam int timeout; 37913020Ssam jmp_buf timeoutbuf; 3807772Ssam 38146687Sbostic void 3827772Ssam timer() 3837772Ssam { 38413020Ssam 38513020Ssam timeout += rexmtval; 38613020Ssam if (timeout >= maxtimeout) 3877772Ssam exit(1); 38813020Ssam longjmp(timeoutbuf, 1); 3897772Ssam } 3907772Ssam 3917772Ssam /* 3927772Ssam * Send the requested file. 3937772Ssam */ 3947772Ssam sendfile(pf) 39526108Sminshall struct formats *pf; 3967772Ssam { 39726108Sminshall struct tftphdr *dp, *r_init(); 39826108Sminshall register struct tftphdr *ap; /* ack packet */ 3997772Ssam register int block = 1, size, n; 4007772Ssam 40113020Ssam signal(SIGALRM, timer); 40226108Sminshall dp = r_init(); 40326108Sminshall ap = (struct tftphdr *)ackbuf; 4047772Ssam do { 40526108Sminshall size = readit(file, &dp, pf->f_convert); 4067772Ssam if (size < 0) { 4077772Ssam nak(errno + 100); 40826108Sminshall goto abort; 4097772Ssam } 41026108Sminshall dp->th_opcode = htons((u_short)DATA); 41126108Sminshall dp->th_block = htons((u_short)block); 4127772Ssam timeout = 0; 41313020Ssam (void) setjmp(timeoutbuf); 41426108Sminshall 41526109Sminshall send_data: 41626108Sminshall if (send(peer, dp, size + 4, 0) != size + 4) { 41728070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n"); 41826108Sminshall goto abort; 4197772Ssam } 42026108Sminshall read_ahead(file, pf->f_convert); 42126109Sminshall for ( ; ; ) { 42226108Sminshall alarm(rexmtval); /* read the ack */ 42326108Sminshall n = recv(peer, ackbuf, sizeof (ackbuf), 0); 4247772Ssam alarm(0); 42513020Ssam if (n < 0) { 42628070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n"); 42726108Sminshall goto abort; 42813020Ssam } 42926108Sminshall ap->th_opcode = ntohs((u_short)ap->th_opcode); 43026108Sminshall ap->th_block = ntohs((u_short)ap->th_block); 43126108Sminshall 43226108Sminshall if (ap->th_opcode == ERROR) 43326108Sminshall goto abort; 43426109Sminshall 43526109Sminshall if (ap->th_opcode == ACK) { 43626109Sminshall if (ap->th_block == block) { 43726109Sminshall break; 43826109Sminshall } 43926115Sminshall /* Re-synchronize with the other side */ 44026115Sminshall (void) synchnet(peer); 44126109Sminshall if (ap->th_block == (block -1)) { 44226109Sminshall goto send_data; 44326109Sminshall } 44426109Sminshall } 44526108Sminshall 44626109Sminshall } 4477772Ssam block++; 4487772Ssam } while (size == SEGSIZE); 44926108Sminshall abort: 45026108Sminshall (void) fclose(file); 4517772Ssam } 4527772Ssam 45346687Sbostic void 45426108Sminshall justquit() 45526108Sminshall { 45626108Sminshall exit(0); 45726108Sminshall } 45826108Sminshall 45926108Sminshall 4607772Ssam /* 4617772Ssam * Receive a file. 4627772Ssam */ 4637772Ssam recvfile(pf) 46426108Sminshall struct formats *pf; 4657772Ssam { 46626108Sminshall struct tftphdr *dp, *w_init(); 46726108Sminshall register struct tftphdr *ap; /* ack buffer */ 4687772Ssam register int block = 0, n, size; 4697772Ssam 47013020Ssam signal(SIGALRM, timer); 47126108Sminshall dp = w_init(); 47226108Sminshall ap = (struct tftphdr *)ackbuf; 4737772Ssam do { 4747772Ssam timeout = 0; 47526108Sminshall ap->th_opcode = htons((u_short)ACK); 47626108Sminshall ap->th_block = htons((u_short)block); 4777772Ssam block++; 47813020Ssam (void) setjmp(timeoutbuf); 47926108Sminshall send_ack: 48026108Sminshall if (send(peer, ackbuf, 4, 0) != 4) { 48128070Sminshall syslog(LOG_ERR, "tftpd: write: %m\n"); 48213020Ssam goto abort; 4837772Ssam } 48426108Sminshall write_behind(file, pf->f_convert); 48526108Sminshall for ( ; ; ) { 48613020Ssam alarm(rexmtval); 48726108Sminshall n = recv(peer, dp, PKTSIZE, 0); 4887772Ssam alarm(0); 48926108Sminshall if (n < 0) { /* really? */ 49028070Sminshall syslog(LOG_ERR, "tftpd: read: %m\n"); 49113020Ssam goto abort; 49213020Ssam } 49326108Sminshall dp->th_opcode = ntohs((u_short)dp->th_opcode); 49426108Sminshall dp->th_block = ntohs((u_short)dp->th_block); 49526108Sminshall if (dp->th_opcode == ERROR) 49613020Ssam goto abort; 49726108Sminshall if (dp->th_opcode == DATA) { 49826108Sminshall if (dp->th_block == block) { 49926108Sminshall break; /* normal */ 50026108Sminshall } 50126115Sminshall /* Re-synchronize with the other side */ 50226115Sminshall (void) synchnet(peer); 50326108Sminshall if (dp->th_block == (block-1)) 50426108Sminshall goto send_ack; /* rexmit */ 50526108Sminshall } 50626108Sminshall } 50726108Sminshall /* size = write(file, dp->th_data, n - 4); */ 50826108Sminshall size = writeit(file, &dp, n - 4, pf->f_convert); 50926108Sminshall if (size != (n-4)) { /* ahem */ 51026108Sminshall if (size < 0) nak(errno + 100); 51126108Sminshall else nak(ENOSPACE); 51213020Ssam goto abort; 5137772Ssam } 5147772Ssam } while (size == SEGSIZE); 51526108Sminshall write_behind(file, pf->f_convert); 51626108Sminshall (void) fclose(file); /* close data file */ 51726108Sminshall 51826108Sminshall ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 51926108Sminshall ap->th_block = htons((u_short)(block)); 52026108Sminshall (void) send(peer, ackbuf, 4, 0); 52126108Sminshall 52226108Sminshall signal(SIGALRM, justquit); /* just quit on timeout */ 52326108Sminshall alarm(rexmtval); 52426108Sminshall n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 52526108Sminshall alarm(0); 52626108Sminshall if (n >= 4 && /* if read some data */ 52726108Sminshall dp->th_opcode == DATA && /* and got a data block */ 52826108Sminshall block == dp->th_block) { /* then my last ack was lost */ 52926108Sminshall (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 53026108Sminshall } 53113020Ssam abort: 53226108Sminshall return; 5337772Ssam } 5347772Ssam 5357772Ssam struct errmsg { 5367772Ssam int e_code; 5377772Ssam char *e_msg; 5387772Ssam } errmsgs[] = { 5397772Ssam { EUNDEF, "Undefined error code" }, 5407772Ssam { ENOTFOUND, "File not found" }, 5417772Ssam { EACCESS, "Access violation" }, 5427772Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 5437772Ssam { EBADOP, "Illegal TFTP operation" }, 5447772Ssam { EBADID, "Unknown transfer ID" }, 5457772Ssam { EEXISTS, "File already exists" }, 5467772Ssam { ENOUSER, "No such user" }, 5477772Ssam { -1, 0 } 5487772Ssam }; 5497772Ssam 550*57963Sandrew static char * 551*57963Sandrew errtomsg(error) 552*57963Sandrew int error; 553*57963Sandrew { 554*57963Sandrew static char buf[20]; 555*57963Sandrew register struct errmsg *pe; 556*57963Sandrew if (error == 0) 557*57963Sandrew return "success"; 558*57963Sandrew for (pe = errmsgs; pe->e_code >= 0; pe++) 559*57963Sandrew if (pe->e_code == error) 560*57963Sandrew return pe->e_msg; 561*57963Sandrew sprintf(buf, "error %d", error); 562*57963Sandrew return buf; 563*57963Sandrew } 564*57963Sandrew 5657772Ssam /* 5667772Ssam * Send a nak packet (error message). 5677772Ssam * Error code passed in is one of the 5687772Ssam * standard TFTP codes, or a UNIX errno 5697772Ssam * offset by 100. 5707772Ssam */ 5717772Ssam nak(error) 5727772Ssam int error; 5737772Ssam { 5747772Ssam register struct tftphdr *tp; 5757772Ssam int length; 5767772Ssam register struct errmsg *pe; 5777772Ssam 5787772Ssam tp = (struct tftphdr *)buf; 5797772Ssam tp->th_opcode = htons((u_short)ERROR); 5807772Ssam tp->th_code = htons((u_short)error); 5817772Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 5827772Ssam if (pe->e_code == error) 5837772Ssam break; 58426108Sminshall if (pe->e_code < 0) { 58542413Sbostic pe->e_msg = strerror(error - 100); 58626108Sminshall tp->th_code = EUNDEF; /* set 'undef' errorcode */ 58726108Sminshall } 5887772Ssam strcpy(tp->th_msg, pe->e_msg); 5897772Ssam length = strlen(pe->e_msg); 5907772Ssam tp->th_msg[length] = '\0'; 5917772Ssam length += 5; 59216372Skarels if (send(peer, buf, length, 0) != length) 59328070Sminshall syslog(LOG_ERR, "nak: %m\n"); 5947772Ssam } 595*57963Sandrew 596*57963Sandrew static char * 597*57963Sandrew verifyhost(fromp) 598*57963Sandrew struct sockaddr_in *fromp; 599*57963Sandrew { 600*57963Sandrew struct hostent *hp; 601*57963Sandrew 602*57963Sandrew hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), 603*57963Sandrew fromp->sin_family); 604*57963Sandrew if (hp) 605*57963Sandrew return hp->h_name; 606*57963Sandrew else 607*57963Sandrew return inet_ntoa(fromp->sin_addr); 608*57963Sandrew } 609