tftpd.c revision 24193
167754Smsmith/* 267754Smsmith * Copyright (c) 1983, 1993 367754Smsmith * The Regents of the University of California. All rights reserved. 470243Smsmith * 567754Smsmith * Redistribution and use in source and binary forms, with or without 667754Smsmith * modification, are permitted provided that the following conditions 767754Smsmith * are met: 867754Smsmith * 1. Redistributions of source code must retain the above copyright 967754Smsmith * notice, this list of conditions and the following disclaimer. 1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1167754Smsmith * notice, this list of conditions and the following disclaimer in the 12202771Sjkim * documentation and/or other materials provided with the distribution. 1370243Smsmith * 3. All advertising materials mentioning features or use of this software 1467754Smsmith * must display the following acknowledgement: 1567754Smsmith * This product includes software developed by the University of 1667754Smsmith * California, Berkeley and its contributors. 1767754Smsmith * 4. Neither the name of the University nor the names of its contributors 1867754Smsmith * may be used to endorse or promote products derived from this software 1967754Smsmith * without specific prior written permission. 2067754Smsmith * 2167754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2267754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2367754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2467754Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2567754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2667754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2767754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2867754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2967754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3067754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3167754Smsmith * SUCH DAMAGE. 3267754Smsmith * 3367754Smsmith * $Id: tftpd.c,v 1.7 1997/02/22 14:22:36 peter Exp $ 3467754Smsmith */ 3567754Smsmith 3667754Smsmith#ifndef lint 3767754Smsmithstatic char copyright[] = 3867754Smsmith"@(#) Copyright (c) 1983, 1993\n\ 3967754Smsmith The Regents of the University of California. All rights reserved.\n"; 4067754Smsmith#endif /* not lint */ 4167754Smsmith 4267754Smsmith#ifndef lint 4367754Smsmithstatic char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 4467754Smsmith#endif /* not lint */ 4567754Smsmith 4667754Smsmith/* 4767754Smsmith * Trivial file transfer protocol server. 4867754Smsmith * 4967754Smsmith * This version includes many modifications by Jim Guyton 5067754Smsmith * <guyton@rand-unix>. 5167754Smsmith */ 5267754Smsmith 5367754Smsmith#include <sys/param.h> 5467754Smsmith#include <sys/ioctl.h> 5567754Smsmith#include <sys/stat.h> 5667754Smsmith#include <sys/socket.h> 5767754Smsmith#include <sys/types.h> 5867754Smsmith 5967754Smsmith#include <netinet/in.h> 6067754Smsmith#include <arpa/tftp.h> 6167754Smsmith#include <arpa/inet.h> 6267754Smsmith 6367754Smsmith#include <ctype.h> 6467754Smsmith#include <errno.h> 6567754Smsmith#include <fcntl.h> 6667754Smsmith#include <netdb.h> 6767754Smsmith#include <setjmp.h> 6867754Smsmith#include <signal.h> 6967754Smsmith#include <stdio.h> 7067754Smsmith#include <stdlib.h> 7167754Smsmith#include <string.h> 7267754Smsmith#include <syslog.h> 7367754Smsmith#include <unistd.h> 7467754Smsmith#include <pwd.h> 7567754Smsmith 7667754Smsmith#include "tftpsubs.h" 7767754Smsmith 7867754Smsmith#define TIMEOUT 5 7967754Smsmith 8067754Smsmithint peer; 8167754Smsmithint rexmtval = TIMEOUT; 8267754Smsmithint maxtimeout = 5*TIMEOUT; 8367754Smsmith 8467754Smsmith#define PKTSIZE SEGSIZE+4 8567754Smsmithchar buf[PKTSIZE]; 8667754Smsmithchar ackbuf[PKTSIZE]; 8767754Smsmithstruct sockaddr_in from; 8867754Smsmithint fromlen; 8967754Smsmith 9067754Smsmithvoid tftp __P((struct tftphdr *, int)); 9167754Smsmith 9267754Smsmith/* 9367754Smsmith * Null-terminated directory prefix list for absolute pathname requests and 9467754Smsmith * search list for relative pathname requests. 9567754Smsmith * 9667754Smsmith * MAXDIRS should be at least as large as the number of arguments that 9767754Smsmith * inetd allows (currently 20). 9867754Smsmith */ 9967754Smsmith#define MAXDIRS 20 10067754Smsmithstatic struct dirlist { 10167754Smsmith char *name; 10267754Smsmith int len; 10367754Smsmith} dirs[MAXDIRS+1]; 10467754Smsmithstatic int suppress_naks; 10567754Smsmithstatic int logging; 10667754Smsmith 10767754Smsmithstatic char *errtomsg __P((int)); 10867754Smsmithstatic void nak __P((int)); 10967754Smsmithstatic char *verifyhost __P((struct sockaddr_in *)); 11067754Smsmith 11167754Smsmithint 11267754Smsmithmain(argc, argv) 11367754Smsmith int argc; 11467754Smsmith char *argv[]; 11567754Smsmith{ 11667754Smsmith register struct tftphdr *tp; 11767754Smsmith register int n; 11867754Smsmith int ch, on; 11967754Smsmith struct sockaddr_in sin; 12067754Smsmith char *chroot_dir = NULL; 121193267Sjkim struct passwd *nobody; 122193267Sjkim 123206117Sjkim openlog("tftpd", LOG_PID, LOG_FTP); 124193267Sjkim while ((ch = getopt(argc, argv, "lns:")) != EOF) { 125193341Sjkim switch (ch) { 126193341Sjkim case 'l': 12767754Smsmith logging = 1; 128193267Sjkim break; 129193267Sjkim case 'n': 130193267Sjkim suppress_naks = 1; 131193267Sjkim break; 132193267Sjkim case 's': 13377424Smsmith chroot_dir = optarg; 134193267Sjkim break; 135193267Sjkim default: 136193267Sjkim syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 137193267Sjkim } 138193267Sjkim } 139193267Sjkim if (optind < argc) { 140193267Sjkim struct dirlist *dirp; 141193267Sjkim 142193267Sjkim /* Get list of directory prefixes. Skip relative pathnames. */ 143193267Sjkim for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 144193267Sjkim optind++) { 145193267Sjkim if (argv[optind][0] == '/') { 146193267Sjkim dirp->name = argv[optind]; 147193267Sjkim dirp->len = strlen(dirp->name); 148204773Sjkim dirp++; 149206117Sjkim } 150193267Sjkim } 151193267Sjkim } 152151937Sjkim else if (chroot_dir) { 15367754Smsmith dirs->name = "/"; 15467754Smsmith dirs->len = 1; 15567754Smsmith } 156167802Sjkim 157167802Sjkim on = 1; 158167802Sjkim if (ioctl(0, FIONBIO, &on) < 0) { 159167802Sjkim syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 160167802Sjkim exit(1); 161167802Sjkim } 16267754Smsmith fromlen = sizeof (from); 16367754Smsmith n = recvfrom(0, buf, sizeof (buf), 0, 16467754Smsmith (struct sockaddr *)&from, &fromlen); 16567754Smsmith if (n < 0) { 16667754Smsmith syslog(LOG_ERR, "recvfrom: %m\n"); 16767754Smsmith exit(1); 16867754Smsmith } 16967754Smsmith /* 170100966Siwasaki * Now that we have read the message out of the UDP 171100966Siwasaki * socket, we fork and exit. Thus, inetd will go back 172100966Siwasaki * to listening to the tftp port, and the next request 173100966Siwasaki * to come in will start up a new instance of tftpd. 17467754Smsmith * 17567754Smsmith * We do this so that inetd can run tftpd in "wait" mode. 17667754Smsmith * The problem with tftpd running in "nowait" mode is that 17767754Smsmith * inetd may get one or more successful "selects" on the 17880062Smsmith * tftp port before we do our receive, so more than one 17980062Smsmith * instance of tftpd may be started up. Worse, if tftpd 18080062Smsmith * break before doing the above "recvfrom", inetd would 18180062Smsmith * spawn endless instances, clogging the system. 18267754Smsmith */ 18367754Smsmith { 18467754Smsmith int pid; 18567754Smsmith int i, j; 18667754Smsmith 18767754Smsmith for (i = 1; i < 20; i++) { 18867754Smsmith pid = fork(); 18967754Smsmith if (pid < 0) { 19080062Smsmith sleep(i); 19167754Smsmith /* 19267754Smsmith * flush out to most recently sent request. 193193267Sjkim * 194193267Sjkim * This may drop some request, but those 195193267Sjkim * will be resent by the clients when 196193267Sjkim * they timeout. The positive effect of 19780062Smsmith * this flush is to (try to) prevent more 19867754Smsmith * than one tftpd being started up to service 19980062Smsmith * a single request from a single client. 20067754Smsmith */ 20187031Smsmith j = sizeof from; 20287031Smsmith i = recvfrom(0, buf, sizeof (buf), 0, 20387031Smsmith (struct sockaddr *)&from, &j); 20467754Smsmith if (i > 0) { 20587031Smsmith n = i; 20667754Smsmith fromlen = j; 207151937Sjkim } 20877424Smsmith } else { 20977424Smsmith break; 21077424Smsmith } 21177424Smsmith } 21277424Smsmith if (pid < 0) { 21377424Smsmith syslog(LOG_ERR, "fork: %m\n"); 21477424Smsmith exit(1); 21577424Smsmith } else if (pid != 0) { 21677424Smsmith exit(0); 21777424Smsmith } 21877424Smsmith } 21977424Smsmith 22077424Smsmith /* 22177424Smsmith * Since we exit here, we should do that only after the above 22277424Smsmith * recvfrom to keep inetd from constantly forking should there 22367754Smsmith * be a problem. See the above comment about system clogging. 22467754Smsmith */ 22567754Smsmith if (chroot_dir) { 226167802Sjkim /* Must get this before chroot because /etc might go away */ 227167802Sjkim if ((nobody = getpwnam("nobody")) == NULL) { 228167802Sjkim syslog(LOG_ERR, "nobody: no such user"); 229167802Sjkim exit(1); 23067754Smsmith } 231193267Sjkim if (chroot(chroot_dir)) { 23267754Smsmith syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); 23367754Smsmith exit(1); 23467754Smsmith } 23580062Smsmith chdir( "/" ); 23667754Smsmith setuid(nobody->pw_uid); 23767754Smsmith } 23867754Smsmith 239167802Sjkim from.sin_family = AF_INET; 240193267Sjkim alarm(0); 24167754Smsmith close(0); 24267754Smsmith close(1); 24367754Smsmith peer = socket(AF_INET, SOCK_DGRAM, 0); 24467754Smsmith if (peer < 0) { 245167802Sjkim syslog(LOG_ERR, "socket: %m\n"); 246193267Sjkim exit(1); 247167802Sjkim } 24867754Smsmith memset(&sin, 0, sizeof(sin)); 24978986Smsmith sin.sin_family = AF_INET; 250167802Sjkim if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 251193267Sjkim syslog(LOG_ERR, "bind: %m\n"); 252167802Sjkim exit(1); 25367754Smsmith } 254193267Sjkim if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 255193267Sjkim syslog(LOG_ERR, "connect: %m\n"); 256193267Sjkim exit(1); 257193267Sjkim } 25878986Smsmith tp = (struct tftphdr *)buf; 259193267Sjkim tp->th_opcode = ntohs(tp->th_opcode); 260193267Sjkim if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 261193267Sjkim tftp(tp, n); 262193267Sjkim exit(1); 263193267Sjkim} 26467754Smsmith 26567754Smsmithstruct formats; 26667754Smsmithint validate_access __P((char **, int)); 26767754Smsmithvoid sendfile __P((struct formats *)); 26867754Smsmithvoid recvfile __P((struct formats *)); 26967754Smsmith 27067754Smsmithstruct formats { 27167754Smsmith char *f_mode; 272199337Sjkim int (*f_validate) __P((char **, int)); 273199337Sjkim void (*f_send) __P((struct formats *)); 27467754Smsmith void (*f_recv) __P((struct formats *)); 275114237Snjl int f_convert; 27667754Smsmith} formats[] = { 27767754Smsmith { "netascii", validate_access, sendfile, recvfile, 1 }, 27867754Smsmith { "octet", validate_access, sendfile, recvfile, 0 }, 279114237Snjl#ifdef notdef 28077424Smsmith { "mail", validate_user, sendmail, recvmail, 1 }, 28167754Smsmith#endif 28267754Smsmith { 0 } 28367754Smsmith}; 28467754Smsmith 28567754Smsmith/* 28667754Smsmith * Handle initial connection protocol. 28767754Smsmith */ 28867754Smsmithvoid 28967754Smsmithtftp(tp, size) 29067754Smsmith struct tftphdr *tp; 29167754Smsmith int size; 29267754Smsmith{ 29367754Smsmith register char *cp; 29467754Smsmith int first = 1, ecode; 29567754Smsmith register struct formats *pf; 29687031Smsmith char *filename, *mode; 29787031Smsmith 29887031Smsmith filename = cp = tp->th_stuff; 29987031Smsmithagain: 30087031Smsmith while (cp < buf + size) { 30167754Smsmith if (*cp == '\0') 30287031Smsmith break; 30387031Smsmith cp++; 30487031Smsmith } 30587031Smsmith if (*cp != '\0') { 30687031Smsmith nak(EBADOP); 30787031Smsmith exit(1); 30887031Smsmith } 30987031Smsmith if (first) { 31087031Smsmith mode = ++cp; 31187031Smsmith first = 0; 31287031Smsmith goto again; 313151937Sjkim } 314151937Sjkim for (cp = mode; *cp; cp++) 315151937Sjkim if (isupper(*cp)) 316151937Sjkim *cp = tolower(*cp); 317151937Sjkim for (pf = formats; pf->f_mode; pf++) 318151937Sjkim if (strcmp(pf->f_mode, mode) == 0) 31987031Smsmith break; 320151937Sjkim if (pf->f_mode == 0) { 32167754Smsmith nak(EBADOP); 32267754Smsmith exit(1); 32367754Smsmith } 32467754Smsmith ecode = (*pf->f_validate)(&filename, tp->th_opcode); 32567754Smsmith if (logging) { 32667754Smsmith syslog(LOG_INFO, "%s: %s request for %s: %s", 32767754Smsmith verifyhost(&from), 32867754Smsmith tp->th_opcode == WRQ ? "write" : "read", 32967754Smsmith filename, errtomsg(ecode)); 33067754Smsmith } 33167754Smsmith if (ecode) { 33299679Siwasaki /* 33399679Siwasaki * Avoid storms of naks to a RRQ broadcast for a relative 33499679Siwasaki * bootfile pathname from a diskless Sun. 33599679Siwasaki */ 33699679Siwasaki if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 33799679Siwasaki exit(0); 33899679Siwasaki nak(ecode); 33999679Siwasaki exit(1); 34067754Smsmith } 341117521Snjl if (tp->th_opcode == WRQ) 342197104Sjkim (*pf->f_recv)(pf); 34367754Smsmith else 34467754Smsmith (*pf->f_send)(pf); 345193267Sjkim exit(0); 346193267Sjkim} 347193267Sjkim 348193267Sjkim 34967754SmsmithFILE *file; 35067754Smsmith 35167754Smsmith/* 35267754Smsmith * Validate file access. Since we 35367754Smsmith * have no uid or gid, for now require 35467754Smsmith * file to exist and be publicly 35567754Smsmith * readable/writable. 35667754Smsmith * If we were invoked with arguments 35767754Smsmith * from inetd then the file must also be 35867754Smsmith * in one of the given directory prefixes. 35967754Smsmith * Note also, full path name must be 36067754Smsmith * given as we have no login directory. 36167754Smsmith */ 36267754Smsmithint 36367754Smsmithvalidate_access(filep, mode) 36467754Smsmith char **filep; 36567754Smsmith int mode; 36667754Smsmith{ 367193267Sjkim struct stat stbuf; 36867754Smsmith int fd; 36967754Smsmith struct dirlist *dirp; 370193267Sjkim static char pathname[MAXPATHLEN]; 371193267Sjkim char *filename = *filep; 372193267Sjkim 373193267Sjkim /* 374193267Sjkim * Prevent tricksters from getting around the directory restrictions 37567754Smsmith */ 37667754Smsmith if (strstr(filename, "/../")) 37777424Smsmith return (EACCESS); 37867754Smsmith 37967754Smsmith if (*filename == '/') { 38067754Smsmith /* 38167754Smsmith * Allow the request if it's in one of the approved locations. 38267754Smsmith * Special case: check the null prefix ("/") by looking 38377424Smsmith * for length = 1 and relying on the arg. processing that 38467754Smsmith * it's a /. 38567754Smsmith */ 38667754Smsmith for (dirp = dirs; dirp->name != NULL; dirp++) { 38767754Smsmith if (dirp->len == 1 || 38867754Smsmith (!strncmp(filename, dirp->name, dirp->len) && 38977424Smsmith filename[dirp->len] == '/')) 39067754Smsmith break; 39167754Smsmith } 39267754Smsmith /* If directory list is empty, allow access to any file */ 39367754Smsmith if (dirp->name == NULL && dirp != dirs) 39467754Smsmith return (EACCESS); 39567754Smsmith if (stat(filename, &stbuf) < 0) 39677424Smsmith return (errno == ENOENT ? ENOTFOUND : EACCESS); 39767754Smsmith if ((stbuf.st_mode & S_IFMT) != S_IFREG) 39867754Smsmith return (ENOTFOUND); 39967754Smsmith if (mode == RRQ) { 40067754Smsmith if ((stbuf.st_mode & S_IROTH) == 0) 40177424Smsmith return (EACCESS); 40277424Smsmith } else { 40377424Smsmith if ((stbuf.st_mode & S_IWOTH) == 0) 40467754Smsmith return (EACCESS); 40567754Smsmith } 40667754Smsmith } else { 40767754Smsmith int err; 40867754Smsmith 40977424Smsmith /* 41077424Smsmith * Relative file name: search the approved locations for it. 41167754Smsmith * Don't allow write requests that avoid directory 41267754Smsmith * restrictions. 41367754Smsmith */ 414117521Snjl 41567754Smsmith if (!strncmp(filename, "../", 3)) 41667754Smsmith return (EACCESS); 417129684Snjl 41867754Smsmith /* 41967754Smsmith * If the file exists in one of the directories and isn't 42067754Smsmith * readable, continue looking. However, change the error code 421193267Sjkim * to give an indication that the file exists. 422193267Sjkim */ 423193267Sjkim err = ENOTFOUND; 424193267Sjkim for (dirp = dirs; dirp->name != NULL; dirp++) { 425193267Sjkim snprintf(pathname, sizeof(pathname), "%s/%s", 426193267Sjkim dirp->name, filename); 427138287Smarks if (stat(pathname, &stbuf) == 0 && 428138287Smarks (stbuf.st_mode & S_IFMT) == S_IFREG) { 429138287Smarks if ((stbuf.st_mode & S_IROTH) != 0) { 430138287Smarks break; 431138287Smarks } 432138287Smarks err = EACCESS; 433138287Smarks } 434138287Smarks } 43567754Smsmith if (dirp->name == NULL) 436107325Siwasaki return (err); 43791116Smsmith *filep = filename = pathname; 43867754Smsmith } 43967754Smsmith fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); 44067754Smsmith if (fd < 0) 44191116Smsmith return (errno + 100); 44267754Smsmith file = fdopen(fd, (mode == RRQ)? "r":"w"); 44367754Smsmith if (file == NULL) { 44467754Smsmith return errno+100; 445117521Snjl } 44684491Smsmith return (0); 44767754Smsmith} 44867754Smsmith 44967754Smsmithint timeout; 450117521Snjljmp_buf timeoutbuf; 45184491Smsmith 45267754Smsmithvoid 45367754Smsmithtimer() 45467754Smsmith{ 455117521Snjl 45667754Smsmith timeout += rexmtval; 45767754Smsmith if (timeout >= maxtimeout) 45867754Smsmith exit(1); 459117521Snjl longjmp(timeoutbuf, 1); 46067754Smsmith} 46167754Smsmith 462193267Sjkim/* 463193267Sjkim * Send the requested file. 464193267Sjkim */ 465193267Sjkimvoid 466117521Snjlsendfile(pf) 467206117Sjkim struct formats *pf; 468129684Snjl{ 469129684Snjl struct tftphdr *dp, *r_init(); 470206117Sjkim register struct tftphdr *ap; /* ack packet */ 471129684Snjl register int size, n; 472129684Snjl volatile int block; 473117521Snjl 474117521Snjl signal(SIGALRM, timer); 475117521Snjl dp = r_init(); 476206117Sjkim ap = (struct tftphdr *)ackbuf; 477117521Snjl block = 1; 478117521Snjl do { 479117521Snjl size = readit(file, &dp, pf->f_convert); 480117521Snjl if (size < 0) { 481117521Snjl nak(errno + 100); 482206117Sjkim goto abort; 483117521Snjl } 484117521Snjl dp->th_opcode = htons((u_short)DATA); 485117521Snjl dp->th_block = htons((u_short)block); 486117521Snjl timeout = 0; 487206117Sjkim (void)setjmp(timeoutbuf); 488117521Snjl 489117521Snjlsend_data: 490117521Snjl if (send(peer, dp, size + 4, 0) != size + 4) { 491117521Snjl syslog(LOG_ERR, "tftpd: write: %m\n"); 492117521Snjl goto abort; 493117521Snjl } 494117521Snjl read_ahead(file, pf->f_convert); 495117521Snjl for ( ; ; ) { 496193267Sjkim alarm(rexmtval); /* read the ack */ 497193267Sjkim n = recv(peer, ackbuf, sizeof (ackbuf), 0); 498193267Sjkim alarm(0); 499193267Sjkim if (n < 0) { 500193267Sjkim syslog(LOG_ERR, "tftpd: read: %m\n"); 501193267Sjkim goto abort; 502193267Sjkim } 503193267Sjkim ap->th_opcode = ntohs((u_short)ap->th_opcode); 504193267Sjkim ap->th_block = ntohs((u_short)ap->th_block); 505193267Sjkim 506193267Sjkim if (ap->th_opcode == ERROR) 507193267Sjkim goto abort; 508193267Sjkim 509117521Snjl if (ap->th_opcode == ACK) { 510117521Snjl if (ap->th_block == block) 511117521Snjl break; 512117521Snjl /* Re-synchronize with the other side */ 513151937Sjkim (void) synchnet(peer); 514117521Snjl if (ap->th_block == (block -1)) 515117521Snjl goto send_data; 516117521Snjl } 517117521Snjl 518117521Snjl } 519117521Snjl block++; 52067754Smsmith } while (size == SEGSIZE); 52167754Smsmithabort: 52267754Smsmith (void) fclose(file); 523114237Snjl} 524114237Snjl 525114237Snjlvoid 526114237Snjljustquit() 527114237Snjl{ 528167802Sjkim exit(0); 529167802Sjkim} 530167802Sjkim 531167802Sjkim 532167802Sjkim/* 533167802Sjkim * Receive a file. 534114237Snjl */ 53567754Smsmithvoid 53667754Smsmithrecvfile(pf) 53767754Smsmith struct formats *pf; 53867754Smsmith{ 53967754Smsmith struct tftphdr *dp, *w_init(); 54067754Smsmith register struct tftphdr *ap; /* ack buffer */ 54167754Smsmith register int n, size; 54267754Smsmith volatile int block; 54367754Smsmith 54467754Smsmith signal(SIGALRM, timer); 54567754Smsmith dp = w_init(); 546114237Snjl ap = (struct tftphdr *)ackbuf; 547167802Sjkim block = 0; 548167802Sjkim do { 549167802Sjkim timeout = 0; 550167802Sjkim ap->th_opcode = htons((u_short)ACK); 551114237Snjl ap->th_block = htons((u_short)block); 552114237Snjl block++; 55367754Smsmith (void) setjmp(timeoutbuf); 55467754Smsmithsend_ack: 55567754Smsmith if (send(peer, ackbuf, 4, 0) != 4) { 55667754Smsmith syslog(LOG_ERR, "tftpd: write: %m\n"); 55767754Smsmith goto abort; 55867754Smsmith } 55967754Smsmith write_behind(file, pf->f_convert); 56067754Smsmith for ( ; ; ) { 56167754Smsmith alarm(rexmtval); 562114237Snjl n = recv(peer, dp, PKTSIZE, 0); 563114237Snjl alarm(0); 564114237Snjl if (n < 0) { /* really? */ 565114237Snjl syslog(LOG_ERR, "tftpd: read: %m\n"); 56667754Smsmith goto abort; 567193267Sjkim } 56867754Smsmith dp->th_opcode = ntohs((u_short)dp->th_opcode); 56967754Smsmith dp->th_block = ntohs((u_short)dp->th_block); 57067754Smsmith if (dp->th_opcode == ERROR) 57167754Smsmith goto abort; 572193267Sjkim if (dp->th_opcode == DATA) { 573193267Sjkim if (dp->th_block == block) { 574193267Sjkim break; /* normal */ 575193267Sjkim } 576193267Sjkim /* Re-synchronize with the other side */ 577197104Sjkim (void) synchnet(peer); 578193267Sjkim if (dp->th_block == (block-1)) 579193267Sjkim goto send_ack; /* rexmit */ 580193267Sjkim } 581193267Sjkim } 582197104Sjkim /* size = write(file, dp->th_data, n - 4); */ 583193267Sjkim size = writeit(file, &dp, n - 4, pf->f_convert); 584193267Sjkim if (size != (n-4)) { /* ahem */ 585193267Sjkim if (size < 0) nak(errno + 100); 586193267Sjkim else nak(ENOSPACE); 58799679Siwasaki goto abort; 588167802Sjkim } 58999679Siwasaki } while (size == SEGSIZE); 59099679Siwasaki write_behind(file, pf->f_convert); 591193267Sjkim (void) fclose(file); /* close data file */ 59299679Siwasaki 593167802Sjkim ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 59499679Siwasaki ap->th_block = htons((u_short)(block)); 59599679Siwasaki (void) send(peer, ackbuf, 4, 0); 59699679Siwasaki 59799679Siwasaki signal(SIGALRM, justquit); /* just quit on timeout */ 59899679Siwasaki alarm(rexmtval); 59999679Siwasaki n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 60087031Smsmith alarm(0); 60167754Smsmith if (n >= 4 && /* if read some data */ 60287031Smsmith dp->th_opcode == DATA && /* and got a data block */ 60399679Siwasaki block == dp->th_block) { /* then my last ack was lost */ 60487031Smsmith (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 60587031Smsmith } 60671867Smsmithabort: 60799679Siwasaki return; 60871867Smsmith} 60971867Smsmith 610114237Snjlstruct errmsg { 611114237Snjl int e_code; 612114237Snjl char *e_msg; 613114237Snjl} errmsgs[] = { 61482367Smsmith { EUNDEF, "Undefined error code" }, 615193267Sjkim { ENOTFOUND, "File not found" }, 616193267Sjkim { EACCESS, "Access violation" }, 617193267Sjkim { ENOSPACE, "Disk full or allocation exceeded" }, 618193267Sjkim { EBADOP, "Illegal TFTP operation" }, 619193267Sjkim { EBADID, "Unknown transfer ID" }, 62067754Smsmith { EEXISTS, "File already exists" }, 621193267Sjkim { ENOUSER, "No such user" }, 622193267Sjkim { -1, 0 } 623193267Sjkim}; 624193267Sjkim 625193267Sjkimstatic char * 62699679Siwasakierrtomsg(error) 627193267Sjkim int error; 628193267Sjkim{ 629193267Sjkim static char buf[20]; 630193267Sjkim register struct errmsg *pe; 631193267Sjkim if (error == 0) 632193267Sjkim return "success"; 633193267Sjkim for (pe = errmsgs; pe->e_code >= 0; pe++) 634193267Sjkim if (pe->e_code == error) 635193267Sjkim return pe->e_msg; 636193267Sjkim snprintf(buf, sizeof(buf), "error %d", error); 637193267Sjkim return buf; 638193267Sjkim} 639193267Sjkim 640193267Sjkim/* 641193267Sjkim * Send a nak packet (error message). 642193267Sjkim * Error code passed in is one of the 643193267Sjkim * standard TFTP codes, or a UNIX errno 644193267Sjkim * offset by 100. 645193267Sjkim */ 646193267Sjkimstatic void 647193267Sjkimnak(error) 648193267Sjkim int error; 649193267Sjkim{ 650193267Sjkim register struct tftphdr *tp; 651193267Sjkim int length; 652193267Sjkim register struct errmsg *pe; 653193267Sjkim 654193267Sjkim tp = (struct tftphdr *)buf; 655193267Sjkim tp->th_opcode = htons((u_short)ERROR); 656193267Sjkim tp->th_code = htons((u_short)error); 657193267Sjkim for (pe = errmsgs; pe->e_code >= 0; pe++) 658193267Sjkim if (pe->e_code == error) 659193267Sjkim break; 660193267Sjkim if (pe->e_code < 0) { 661193267Sjkim pe->e_msg = strerror(error - 100); 662193267Sjkim tp->th_code = EUNDEF; /* set 'undef' errorcode */ 663193267Sjkim } 664193267Sjkim strcpy(tp->th_msg, pe->e_msg); 665193267Sjkim length = strlen(pe->e_msg); 666193267Sjkim tp->th_msg[length] = '\0'; 667193267Sjkim length += 5; 668193267Sjkim if (send(peer, buf, length, 0) != length) 669193267Sjkim syslog(LOG_ERR, "nak: %m\n"); 670193267Sjkim} 671193267Sjkim 672193267Sjkimstatic char * 673193267Sjkimverifyhost(fromp) 674193267Sjkim struct sockaddr_in *fromp; 675193267Sjkim{ 676193267Sjkim struct hostent *hp; 677193267Sjkim 678193267Sjkim hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), 679193267Sjkim fromp->sin_family); 680193267Sjkim if (hp) 681193267Sjkim return hp->h_name; 682193267Sjkim else 683193267Sjkim return inet_ntoa(fromp->sin_addr); 684193267Sjkim} 685193267Sjkim