tftpd.c revision 86765
1204431Sraj/* 2204431Sraj * Copyright (c) 1983, 1993 3204431Sraj * The Regents of the University of California. All rights reserved. 4204431Sraj * 5204431Sraj * Redistribution and use in source and binary forms, with or without 6204431Sraj * modification, are permitted provided that the following conditions 7204431Sraj * are met: 8204431Sraj * 1. Redistributions of source code must retain the above copyright 9204431Sraj * notice, this list of conditions and the following disclaimer. 10204431Sraj * 2. Redistributions in binary form must reproduce the above copyright 11204431Sraj * notice, this list of conditions and the following disclaimer in the 12204431Sraj * documentation and/or other materials provided with the distribution. 13204431Sraj * 3. All advertising materials mentioning features or use of this software 14204431Sraj * must display the following acknowledgement: 15204431Sraj * This product includes software developed by the University of 16204431Sraj * California, Berkeley and its contributors. 17204431Sraj * 4. Neither the name of the University nor the names of its contributors 18204431Sraj * may be used to endorse or promote products derived from this software 19204431Sraj * without specific prior written permission. 20204431Sraj * 21204431Sraj * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22204431Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23204431Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24204431Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25204431Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26238742Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27204431Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28204431Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29261215Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30204431Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31204431Sraj * SUCH DAMAGE. 32204431Sraj */ 33204431Sraj 34261215Simp#ifndef lint 35204431Srajstatic const char copyright[] = 36238742Simp"@(#) Copyright (c) 1983, 1993\n\ 37238742Simp The Regents of the University of California. All rights reserved.\n"; 38238742Simp#endif /* not lint */ 39204431Sraj 40204431Sraj#ifndef lint 41204431Sraj#if 0 42204431Srajstatic char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 43204431Sraj#endif 44204431Srajstatic const char rcsid[] = 45204431Sraj "$FreeBSD: head/libexec/tftpd/tftpd.c 86765 2001-11-22 05:08:35Z benno $"; 46204431Sraj#endif /* not lint */ 47204431Sraj 48204431Sraj/* 49204431Sraj * Trivial file transfer protocol server. 50204431Sraj * 51204431Sraj * This version includes many modifications by Jim Guyton 52204431Sraj * <guyton@rand-unix>. 53204431Sraj */ 54204431Sraj 55204431Sraj#include <sys/param.h> 56204431Sraj#include <sys/ioctl.h> 57261215Simp#include <sys/stat.h> 58204431Sraj#include <sys/socket.h> 59261215Simp#include <sys/types.h> 60204431Sraj 61204431Sraj#include <netinet/in.h> 62204431Sraj#include <arpa/tftp.h> 63204431Sraj#include <arpa/inet.h> 64204431Sraj 65204431Sraj#include <ctype.h> 66204431Sraj#include <errno.h> 67204431Sraj#include <fcntl.h> 68204431Sraj#include <libutil.h> 69204431Sraj#include <netdb.h> 70204431Sraj#include <pwd.h> 71204431Sraj#include <setjmp.h> 72204433Sraj#include <signal.h> 73204433Sraj#include <stdio.h> 74204433Sraj#include <stdlib.h> 75204433Sraj#include <string.h> 76204433Sraj#include <syslog.h> 77204433Sraj#include <unistd.h> 78204433Sraj 79204431Sraj#include "tftpsubs.h" 80204431Sraj 81204431Sraj#define TIMEOUT 5 82204431Sraj#define MAX_TIMEOUTS 5 83204431Sraj 84204431Srajint peer; 85204431Srajint rexmtval = TIMEOUT; 86204431Srajint max_rexmtval = 2*TIMEOUT; 87204431Sraj 88204431Sraj#define PKTSIZE SEGSIZE+4 89204431Srajchar buf[PKTSIZE]; 90204431Srajchar ackbuf[PKTSIZE]; 91204431Srajstruct sockaddr_in from; 92204431Srajint fromlen; 93204431Sraj 94204431Srajvoid tftp __P((struct tftphdr *, int)); 95204431Sraj 96204431Sraj/* 97204431Sraj * Null-terminated directory prefix list for absolute pathname requests and 98204431Sraj * search list for relative pathname requests. 99204431Sraj * 100204431Sraj * MAXDIRS should be at least as large as the number of arguments that 101204431Sraj * inetd allows (currently 20). 102204431Sraj */ 103204431Sraj#define MAXDIRS 20 104204431Srajstatic struct dirlist { 105204431Sraj char *name; 106204431Sraj int len; 107204431Sraj} dirs[MAXDIRS+1]; 108204431Srajstatic int suppress_naks; 109204431Srajstatic int logging; 110204431Srajstatic int ipchroot; 111204431Sraj 112204433Srajstatic char *errtomsg __P((int)); 113204433Srajstatic void nak __P((int)); 114204433Srajstatic void oack __P(()); 115204433Sraj 116204433Srajint 117204433Srajmain(argc, argv) 118204433Sraj int argc; 119204433Sraj char *argv[]; 120204431Sraj{ 121204431Sraj register struct tftphdr *tp; 122261215Simp register int n; 123204431Sraj int ch, on; 124204431Sraj struct sockaddr_in sin; 125204431Sraj char *chroot_dir = NULL; 126204431Sraj struct passwd *nobody; 127204431Sraj char *chuser = "nobody"; 128204431Sraj 129204431Sraj openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 130204431Sraj while ((ch = getopt(argc, argv, "cClns:u:")) != -1) { 131204431Sraj switch (ch) { 132204431Sraj case 'c': 133204431Sraj ipchroot = 1; 134204431Sraj break; 135204431Sraj case 'C': 136204431Sraj ipchroot = 2; 137204431Sraj break; 138204431Sraj case 'l': 139204431Sraj logging = 1; 140204431Sraj break; 141204431Sraj case 'n': 142204431Sraj suppress_naks = 1; 143204431Sraj break; 144204431Sraj case 's': 145204431Sraj chroot_dir = optarg; 146204431Sraj break; 147204431Sraj case 'u': 148204431Sraj chuser = optarg; 149204431Sraj break; 150204431Sraj default: 151204431Sraj syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 152204431Sraj } 153204431Sraj } 154204431Sraj if (optind < argc) { 155204431Sraj struct dirlist *dirp; 156204431Sraj 157204431Sraj /* Get list of directory prefixes. Skip relative pathnames. */ 158204431Sraj for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 159204431Sraj optind++) { 160204431Sraj if (argv[optind][0] == '/') { 161204431Sraj dirp->name = argv[optind]; 162204431Sraj dirp->len = strlen(dirp->name); 163204431Sraj dirp++; 164204431Sraj } 165204431Sraj } 166204431Sraj } 167204431Sraj else if (chroot_dir) { 168204431Sraj dirs->name = "/"; 169204431Sraj dirs->len = 1; 170204431Sraj } 171204431Sraj if (ipchroot && chroot_dir == NULL) { 172204431Sraj syslog(LOG_ERR, "-c requires -s"); 173204431Sraj exit(1); 174204431Sraj } 175204431Sraj 176204431Sraj on = 1; 177204431Sraj if (ioctl(0, FIONBIO, &on) < 0) { 178204431Sraj syslog(LOG_ERR, "ioctl(FIONBIO): %m"); 179204431Sraj exit(1); 180204431Sraj } 181204431Sraj fromlen = sizeof (from); 182204431Sraj n = recvfrom(0, buf, sizeof (buf), 0, 183204431Sraj (struct sockaddr *)&from, &fromlen); 184204431Sraj if (n < 0) { 185204431Sraj syslog(LOG_ERR, "recvfrom: %m"); 186204431Sraj exit(1); 187204431Sraj } 188204431Sraj /* 189204431Sraj * Now that we have read the message out of the UDP 190204431Sraj * socket, we fork and exit. Thus, inetd will go back 191204431Sraj * to listening to the tftp port, and the next request 192204431Sraj * to come in will start up a new instance of tftpd. 193204431Sraj * 194204431Sraj * We do this so that inetd can run tftpd in "wait" mode. 195204431Sraj * The problem with tftpd running in "nowait" mode is that 196204431Sraj * inetd may get one or more successful "selects" on the 197204431Sraj * tftp port before we do our receive, so more than one 198204431Sraj * instance of tftpd may be started up. Worse, if tftpd 199204431Sraj * break before doing the above "recvfrom", inetd would 200204431Sraj * spawn endless instances, clogging the system. 201204431Sraj */ 202204431Sraj { 203204431Sraj int pid; 204204431Sraj int i, j; 205204431Sraj 206204431Sraj for (i = 1; i < 20; i++) { 207204431Sraj pid = fork(); 208204431Sraj if (pid < 0) { 209204431Sraj sleep(i); 210204431Sraj /* 211204431Sraj * flush out to most recently sent request. 212204431Sraj * 213204431Sraj * This may drop some request, but those 214204431Sraj * will be resent by the clients when 215204431Sraj * they timeout. The positive effect of 216204431Sraj * this flush is to (try to) prevent more 217204431Sraj * than one tftpd being started up to service 218204431Sraj * a single request from a single client. 219204431Sraj */ 220204431Sraj j = sizeof from; 221204431Sraj i = recvfrom(0, buf, sizeof (buf), 0, 222204431Sraj (struct sockaddr *)&from, &j); 223204431Sraj if (i > 0) { 224204431Sraj n = i; 225204431Sraj fromlen = j; 226204431Sraj } 227204431Sraj } else { 228204431Sraj break; 229204431Sraj } 230204431Sraj } 231204431Sraj if (pid < 0) { 232204431Sraj syslog(LOG_ERR, "fork: %m"); 233204431Sraj exit(1); 234204431Sraj } else if (pid != 0) { 235204431Sraj exit(0); 236204431Sraj } 237204431Sraj } 238204431Sraj 239204431Sraj /* 240238742Simp * Since we exit here, we should do that only after the above 241204431Sraj * recvfrom to keep inetd from constantly forking should there 242204431Sraj * be a problem. See the above comment about system clogging. 243238742Simp */ 244238742Simp if (chroot_dir) { 245204431Sraj if (ipchroot) { 246204431Sraj char *tempchroot; 247204431Sraj struct stat sb; 248204431Sraj int statret; 249204431Sraj 250204431Sraj tempchroot = inet_ntoa(from.sin_addr); 251204431Sraj asprintf(&tempchroot, "%s/%s", chroot_dir, tempchroot); 252238742Simp statret = stat(tempchroot, &sb); 253238742Simp if ((sb.st_mode & S_IFDIR) && 254204431Sraj (statret == 0 || (statret == -1 && ipchroot == 1))) 255204431Sraj chroot_dir = tempchroot; 256204431Sraj } 257204431Sraj /* Must get this before chroot because /etc might go away */ 258204431Sraj if ((nobody = getpwnam(chuser)) == NULL) { 259204431Sraj syslog(LOG_ERR, "%s: no such user", chuser); 260204431Sraj exit(1); 261204431Sraj } 262204431Sraj if (chroot(chroot_dir)) { 263204431Sraj syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); 264204431Sraj exit(1); 265204431Sraj } 266204431Sraj chdir( "/" ); 267204431Sraj setuid(nobody->pw_uid); 268204431Sraj setgroups(1, &nobody->pw_gid); 269204431Sraj } 270204431Sraj 271204431Sraj from.sin_family = AF_INET; 272204431Sraj alarm(0); 273238742Simp close(0); 274238742Simp close(1); 275238742Simp peer = socket(AF_INET, SOCK_DGRAM, 0); 276238742Simp if (peer < 0) { 277204431Sraj syslog(LOG_ERR, "socket: %m"); 278204431Sraj exit(1); 279204431Sraj } 280204431Sraj memset(&sin, 0, sizeof(sin)); 281204431Sraj sin.sin_family = AF_INET; 282204431Sraj if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 283204431Sraj syslog(LOG_ERR, "bind: %m"); 284204431Sraj exit(1); 285 } 286 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 287 syslog(LOG_ERR, "connect: %m"); 288 exit(1); 289 } 290 tp = (struct tftphdr *)buf; 291 tp->th_opcode = ntohs(tp->th_opcode); 292 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 293 tftp(tp, n); 294 exit(1); 295} 296 297struct formats; 298int validate_access __P((char **, int)); 299void xmitfile __P((struct formats *)); 300void recvfile __P((struct formats *)); 301 302struct formats { 303 char *f_mode; 304 int (*f_validate) __P((char **, int)); 305 void (*f_send) __P((struct formats *)); 306 void (*f_recv) __P((struct formats *)); 307 int f_convert; 308} formats[] = { 309 { "netascii", validate_access, xmitfile, recvfile, 1 }, 310 { "octet", validate_access, xmitfile, recvfile, 0 }, 311#ifdef notdef 312 { "mail", validate_user, sendmail, recvmail, 1 }, 313#endif 314 { 0 } 315}; 316 317struct options { 318 char *o_type; 319 char *o_request; 320 int o_reply; /* turn into union if need be */ 321} options[] = { 322 { "tsize" }, /* OPT_TSIZE */ 323 { "timeout" }, /* OPT_TIMEOUT */ 324 { NULL } 325}; 326 327enum opt_enum { 328 OPT_TSIZE = 0, 329 OPT_TIMEOUT, 330}; 331 332/* 333 * Handle initial connection protocol. 334 */ 335void 336tftp(tp, size) 337 struct tftphdr *tp; 338 int size; 339{ 340 register char *cp; 341 int i, first = 1, has_options = 0, ecode; 342 register struct formats *pf; 343 char *filename, *mode, *option, *ccp; 344 345 filename = cp = tp->th_stuff; 346again: 347 while (cp < buf + size) { 348 if (*cp == '\0') 349 break; 350 cp++; 351 } 352 if (*cp != '\0') { 353 nak(EBADOP); 354 exit(1); 355 } 356 if (first) { 357 mode = ++cp; 358 first = 0; 359 goto again; 360 } 361 for (cp = mode; *cp; cp++) 362 if (isupper(*cp)) 363 *cp = tolower(*cp); 364 for (pf = formats; pf->f_mode; pf++) 365 if (strcmp(pf->f_mode, mode) == 0) 366 break; 367 if (pf->f_mode == 0) { 368 nak(EBADOP); 369 exit(1); 370 } 371 while (++cp < buf + size) { 372 for (i = 2, ccp = cp; i > 0; ccp++) { 373 if (ccp >= buf + size) { 374 /* 375 * Don't reject the request, just stop trying 376 * to parse the option and get on with it. 377 * Some Apple OpenFirmware versions have 378 * trailing garbage on the end of otherwise 379 * valid requests. 380 */ 381 goto option_fail; 382 } else if (*ccp == '\0') 383 i--; 384 } 385 for (option = cp; *cp; cp++) 386 if (isupper(*cp)) 387 *cp = tolower(*cp); 388 for (i = 0; options[i].o_type != NULL; i++) 389 if (strcmp(option, options[i].o_type) == 0) { 390 options[i].o_request = ++cp; 391 has_options = 1; 392 } 393 cp = ccp-1; 394 } 395 396option_fail: 397 if (options[OPT_TIMEOUT].o_request) { 398 int to = atoi(options[OPT_TIMEOUT].o_request); 399 if (to < 1 || to > 255) { 400 nak(EBADOP); 401 exit(1); 402 } 403 else if (to <= max_rexmtval) 404 options[OPT_TIMEOUT].o_reply = rexmtval = to; 405 else 406 options[OPT_TIMEOUT].o_request = NULL; 407 } 408 409 ecode = (*pf->f_validate)(&filename, tp->th_opcode); 410 if (has_options) 411 oack(); 412 if (logging) { 413 char host[MAXHOSTNAMELEN]; 414 415 realhostname(host, sizeof(host) - 1, &from.sin_addr); 416 host[sizeof(host) - 1] = '\0'; 417 syslog(LOG_INFO, "%s: %s request for %s: %s", host, 418 tp->th_opcode == WRQ ? "write" : "read", 419 filename, errtomsg(ecode)); 420 } 421 if (ecode) { 422 /* 423 * Avoid storms of naks to a RRQ broadcast for a relative 424 * bootfile pathname from a diskless Sun. 425 */ 426 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 427 exit(0); 428 nak(ecode); 429 exit(1); 430 } 431 if (tp->th_opcode == WRQ) 432 (*pf->f_recv)(pf); 433 else 434 (*pf->f_send)(pf); 435 exit(0); 436} 437 438 439FILE *file; 440 441/* 442 * Validate file access. Since we 443 * have no uid or gid, for now require 444 * file to exist and be publicly 445 * readable/writable. 446 * If we were invoked with arguments 447 * from inetd then the file must also be 448 * in one of the given directory prefixes. 449 * Note also, full path name must be 450 * given as we have no login directory. 451 */ 452int 453validate_access(filep, mode) 454 char **filep; 455 int mode; 456{ 457 struct stat stbuf; 458 int fd; 459 struct dirlist *dirp; 460 static char pathname[MAXPATHLEN]; 461 char *filename = *filep; 462 463 /* 464 * Prevent tricksters from getting around the directory restrictions 465 */ 466 if (strstr(filename, "/../")) 467 return (EACCESS); 468 469 if (*filename == '/') { 470 /* 471 * Allow the request if it's in one of the approved locations. 472 * Special case: check the null prefix ("/") by looking 473 * for length = 1 and relying on the arg. processing that 474 * it's a /. 475 */ 476 for (dirp = dirs; dirp->name != NULL; dirp++) { 477 if (dirp->len == 1 || 478 (!strncmp(filename, dirp->name, dirp->len) && 479 filename[dirp->len] == '/')) 480 break; 481 } 482 /* If directory list is empty, allow access to any file */ 483 if (dirp->name == NULL && dirp != dirs) 484 return (EACCESS); 485 if (stat(filename, &stbuf) < 0) 486 return (errno == ENOENT ? ENOTFOUND : EACCESS); 487 if ((stbuf.st_mode & S_IFMT) != S_IFREG) 488 return (ENOTFOUND); 489 if (mode == RRQ) { 490 if ((stbuf.st_mode & S_IROTH) == 0) 491 return (EACCESS); 492 } else { 493 if ((stbuf.st_mode & S_IWOTH) == 0) 494 return (EACCESS); 495 } 496 } else { 497 int err; 498 499 /* 500 * Relative file name: search the approved locations for it. 501 * Don't allow write requests that avoid directory 502 * restrictions. 503 */ 504 505 if (!strncmp(filename, "../", 3)) 506 return (EACCESS); 507 508 /* 509 * If the file exists in one of the directories and isn't 510 * readable, continue looking. However, change the error code 511 * to give an indication that the file exists. 512 */ 513 err = ENOTFOUND; 514 for (dirp = dirs; dirp->name != NULL; dirp++) { 515 snprintf(pathname, sizeof(pathname), "%s/%s", 516 dirp->name, filename); 517 if (stat(pathname, &stbuf) == 0 && 518 (stbuf.st_mode & S_IFMT) == S_IFREG) { 519 if ((stbuf.st_mode & S_IROTH) != 0) { 520 break; 521 } 522 err = EACCESS; 523 } 524 } 525 if (dirp->name == NULL) 526 return (err); 527 *filep = filename = pathname; 528 } 529 if (options[OPT_TSIZE].o_request) { 530 if (mode == RRQ) 531 options[OPT_TSIZE].o_reply = stbuf.st_size; 532 else 533 /* XXX Allows writes of all sizes. */ 534 options[OPT_TSIZE].o_reply = 535 atoi(options[OPT_TSIZE].o_request); 536 } 537 fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); 538 if (fd < 0) 539 return (errno + 100); 540 file = fdopen(fd, (mode == RRQ)? "r":"w"); 541 if (file == NULL) { 542 return errno+100; 543 } 544 return (0); 545} 546 547int timeouts; 548jmp_buf timeoutbuf; 549 550void 551timer() 552{ 553 if (++timeouts > MAX_TIMEOUTS) 554 exit(1); 555 longjmp(timeoutbuf, 1); 556} 557 558/* 559 * Send the requested file. 560 */ 561void 562xmitfile(pf) 563 struct formats *pf; 564{ 565 struct tftphdr *dp, *r_init(); 566 register struct tftphdr *ap; /* ack packet */ 567 register int size, n; 568 volatile unsigned short block; 569 570 signal(SIGALRM, timer); 571 dp = r_init(); 572 ap = (struct tftphdr *)ackbuf; 573 block = 1; 574 do { 575 size = readit(file, &dp, pf->f_convert); 576 if (size < 0) { 577 nak(errno + 100); 578 goto abort; 579 } 580 dp->th_opcode = htons((u_short)DATA); 581 dp->th_block = htons((u_short)block); 582 timeouts = 0; 583 (void)setjmp(timeoutbuf); 584 585send_data: 586 if (send(peer, dp, size + 4, 0) != size + 4) { 587 syslog(LOG_ERR, "write: %m"); 588 goto abort; 589 } 590 read_ahead(file, pf->f_convert); 591 for ( ; ; ) { 592 alarm(rexmtval); /* read the ack */ 593 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 594 alarm(0); 595 if (n < 0) { 596 syslog(LOG_ERR, "read: %m"); 597 goto abort; 598 } 599 ap->th_opcode = ntohs((u_short)ap->th_opcode); 600 ap->th_block = ntohs((u_short)ap->th_block); 601 602 if (ap->th_opcode == ERROR) 603 goto abort; 604 605 if (ap->th_opcode == ACK) { 606 if (ap->th_block == block) 607 break; 608 /* Re-synchronize with the other side */ 609 (void) synchnet(peer); 610 if (ap->th_block == (block -1)) 611 goto send_data; 612 } 613 614 } 615 block++; 616 } while (size == SEGSIZE); 617abort: 618 (void) fclose(file); 619} 620 621void 622justquit() 623{ 624 exit(0); 625} 626 627 628/* 629 * Receive a file. 630 */ 631void 632recvfile(pf) 633 struct formats *pf; 634{ 635 struct tftphdr *dp, *w_init(); 636 register struct tftphdr *ap; /* ack buffer */ 637 register int n, size; 638 volatile unsigned short block; 639 640 signal(SIGALRM, timer); 641 dp = w_init(); 642 ap = (struct tftphdr *)ackbuf; 643 block = 0; 644 do { 645 timeouts = 0; 646 ap->th_opcode = htons((u_short)ACK); 647 ap->th_block = htons((u_short)block); 648 block++; 649 (void) setjmp(timeoutbuf); 650send_ack: 651 if (send(peer, ackbuf, 4, 0) != 4) { 652 syslog(LOG_ERR, "write: %m"); 653 goto abort; 654 } 655 write_behind(file, pf->f_convert); 656 for ( ; ; ) { 657 alarm(rexmtval); 658 n = recv(peer, dp, PKTSIZE, 0); 659 alarm(0); 660 if (n < 0) { /* really? */ 661 syslog(LOG_ERR, "read: %m"); 662 goto abort; 663 } 664 dp->th_opcode = ntohs((u_short)dp->th_opcode); 665 dp->th_block = ntohs((u_short)dp->th_block); 666 if (dp->th_opcode == ERROR) 667 goto abort; 668 if (dp->th_opcode == DATA) { 669 if (dp->th_block == block) { 670 break; /* normal */ 671 } 672 /* Re-synchronize with the other side */ 673 (void) synchnet(peer); 674 if (dp->th_block == (block-1)) 675 goto send_ack; /* rexmit */ 676 } 677 } 678 /* size = write(file, dp->th_data, n - 4); */ 679 size = writeit(file, &dp, n - 4, pf->f_convert); 680 if (size != (n-4)) { /* ahem */ 681 if (size < 0) nak(errno + 100); 682 else nak(ENOSPACE); 683 goto abort; 684 } 685 } while (size == SEGSIZE); 686 write_behind(file, pf->f_convert); 687 (void) fclose(file); /* close data file */ 688 689 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 690 ap->th_block = htons((u_short)(block)); 691 (void) send(peer, ackbuf, 4, 0); 692 693 signal(SIGALRM, justquit); /* just quit on timeout */ 694 alarm(rexmtval); 695 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 696 alarm(0); 697 if (n >= 4 && /* if read some data */ 698 dp->th_opcode == DATA && /* and got a data block */ 699 block == dp->th_block) { /* then my last ack was lost */ 700 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 701 } 702abort: 703 return; 704} 705 706struct errmsg { 707 int e_code; 708 char *e_msg; 709} errmsgs[] = { 710 { EUNDEF, "Undefined error code" }, 711 { ENOTFOUND, "File not found" }, 712 { EACCESS, "Access violation" }, 713 { ENOSPACE, "Disk full or allocation exceeded" }, 714 { EBADOP, "Illegal TFTP operation" }, 715 { EBADID, "Unknown transfer ID" }, 716 { EEXISTS, "File already exists" }, 717 { ENOUSER, "No such user" }, 718 { EOPTNEG, "Option negotiation" }, 719 { -1, 0 } 720}; 721 722static char * 723errtomsg(error) 724 int error; 725{ 726 static char buf[20]; 727 register struct errmsg *pe; 728 if (error == 0) 729 return "success"; 730 for (pe = errmsgs; pe->e_code >= 0; pe++) 731 if (pe->e_code == error) 732 return pe->e_msg; 733 snprintf(buf, sizeof(buf), "error %d", error); 734 return buf; 735} 736 737/* 738 * Send a nak packet (error message). 739 * Error code passed in is one of the 740 * standard TFTP codes, or a UNIX errno 741 * offset by 100. 742 */ 743static void 744nak(error) 745 int error; 746{ 747 register struct tftphdr *tp; 748 int length; 749 register struct errmsg *pe; 750 751 tp = (struct tftphdr *)buf; 752 tp->th_opcode = htons((u_short)ERROR); 753 tp->th_code = htons((u_short)error); 754 for (pe = errmsgs; pe->e_code >= 0; pe++) 755 if (pe->e_code == error) 756 break; 757 if (pe->e_code < 0) { 758 pe->e_msg = strerror(error - 100); 759 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 760 } 761 strcpy(tp->th_msg, pe->e_msg); 762 length = strlen(pe->e_msg); 763 tp->th_msg[length] = '\0'; 764 length += 5; 765 if (send(peer, buf, length, 0) != length) 766 syslog(LOG_ERR, "nak: %m"); 767} 768 769/* 770 * Send an oack packet (option acknowledgement). 771 */ 772static void 773oack() 774{ 775 struct tftphdr *tp, *ap; 776 int size, i, n; 777 char *bp; 778 779 tp = (struct tftphdr *)buf; 780 bp = buf + 2; 781 size = sizeof(buf) - 2; 782 tp->th_opcode = htons((u_short)OACK); 783 for (i = 0; options[i].o_type != NULL; i++) { 784 if (options[i].o_request) { 785 n = snprintf(bp, size, "%s%c%d", options[i].o_type, 786 0, options[i].o_reply); 787 bp += n+1; 788 size -= n+1; 789 if (size < 0) { 790 syslog(LOG_ERR, "oack: buffer overflow"); 791 exit(1); 792 } 793 } 794 } 795 size = bp - buf; 796 ap = (struct tftphdr *)ackbuf; 797 signal(SIGALRM, timer); 798 timeouts = 0; 799 800 (void)setjmp(timeoutbuf); 801 if (send(peer, buf, size, 0) != size) { 802 syslog(LOG_INFO, "oack: %m"); 803 exit(1); 804 } 805 806 for (;;) { 807 alarm(rexmtval); 808 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 809 alarm(0); 810 if (n < 0) { 811 syslog(LOG_ERR, "recv: %m"); 812 exit(1); 813 } 814 ap->th_opcode = ntohs((u_short)ap->th_opcode); 815 ap->th_block = ntohs((u_short)ap->th_block); 816 if (ap->th_opcode == ERROR) 817 exit(1); 818 if (ap->th_opcode == ACK && ap->th_block == 0) 819 break; 820 } 821} 822