tftpd.c revision 94299
1/* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 43#endif 44static const char rcsid[] = 45 "$FreeBSD: head/libexec/tftpd/tftpd.c 94299 2002-04-09 19:13:43Z ambrisko $"; 46#endif /* not lint */ 47 48/* 49 * Trivial file transfer protocol server. 50 * 51 * This version includes many modifications by Jim Guyton 52 * <guyton@rand-unix>. 53 */ 54 55#include <sys/param.h> 56#include <sys/ioctl.h> 57#include <sys/stat.h> 58#include <sys/socket.h> 59#include <sys/types.h> 60 61#include <netinet/in.h> 62#include <arpa/tftp.h> 63#include <arpa/inet.h> 64 65#include <ctype.h> 66#include <errno.h> 67#include <fcntl.h> 68#include <libutil.h> 69#include <netdb.h> 70#include <pwd.h> 71#include <setjmp.h> 72#include <signal.h> 73#include <stdio.h> 74#include <stdlib.h> 75#include <string.h> 76#include <syslog.h> 77#include <unistd.h> 78 79#include "tftpsubs.h" 80 81#define TIMEOUT 5 82#define MAX_TIMEOUTS 5 83 84int peer; 85int rexmtval = TIMEOUT; 86int max_rexmtval = 2*TIMEOUT; 87 88#define PKTSIZE SEGSIZE+4 89char buf[PKTSIZE]; 90char ackbuf[PKTSIZE]; 91struct sockaddr_in from; 92int fromlen; 93 94void tftp(struct tftphdr *, int); 95 96/* 97 * Null-terminated directory prefix list for absolute pathname requests and 98 * search list for relative pathname requests. 99 * 100 * MAXDIRS should be at least as large as the number of arguments that 101 * inetd allows (currently 20). 102 */ 103#define MAXDIRS 20 104static struct dirlist { 105 char *name; 106 int len; 107} dirs[MAXDIRS+1]; 108static int suppress_naks; 109static int logging; 110static int ipchroot; 111 112static char *errtomsg(int); 113static void nak(int); 114static void oack(); 115 116int 117main(int argc, char *argv[]) 118{ 119 struct tftphdr *tp; 120 int n; 121 int ch, on; 122 struct sockaddr_in sin; 123 char *chroot_dir = NULL; 124 struct passwd *nobody; 125 char *chuser = "nobody"; 126 127 openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 128 while ((ch = getopt(argc, argv, "cClns:u:")) != -1) { 129 switch (ch) { 130 case 'c': 131 ipchroot = 1; 132 break; 133 case 'C': 134 ipchroot = 2; 135 break; 136 case 'l': 137 logging = 1; 138 break; 139 case 'n': 140 suppress_naks = 1; 141 break; 142 case 's': 143 chroot_dir = optarg; 144 break; 145 case 'u': 146 chuser = optarg; 147 break; 148 default: 149 syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 150 } 151 } 152 if (optind < argc) { 153 struct dirlist *dirp; 154 155 /* Get list of directory prefixes. Skip relative pathnames. */ 156 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 157 optind++) { 158 if (argv[optind][0] == '/') { 159 dirp->name = argv[optind]; 160 dirp->len = strlen(dirp->name); 161 dirp++; 162 } 163 } 164 } 165 else if (chroot_dir) { 166 dirs->name = "/"; 167 dirs->len = 1; 168 } 169 if (ipchroot && chroot_dir == NULL) { 170 syslog(LOG_ERR, "-c requires -s"); 171 exit(1); 172 } 173 174 on = 1; 175 if (ioctl(0, FIONBIO, &on) < 0) { 176 syslog(LOG_ERR, "ioctl(FIONBIO): %m"); 177 exit(1); 178 } 179 fromlen = sizeof (from); 180 n = recvfrom(0, buf, sizeof (buf), 0, 181 (struct sockaddr *)&from, &fromlen); 182 if (n < 0) { 183 syslog(LOG_ERR, "recvfrom: %m"); 184 exit(1); 185 } 186 /* 187 * Now that we have read the message out of the UDP 188 * socket, we fork and exit. Thus, inetd will go back 189 * to listening to the tftp port, and the next request 190 * to come in will start up a new instance of tftpd. 191 * 192 * We do this so that inetd can run tftpd in "wait" mode. 193 * The problem with tftpd running in "nowait" mode is that 194 * inetd may get one or more successful "selects" on the 195 * tftp port before we do our receive, so more than one 196 * instance of tftpd may be started up. Worse, if tftpd 197 * break before doing the above "recvfrom", inetd would 198 * spawn endless instances, clogging the system. 199 */ 200 { 201 int pid; 202 int i, j; 203 204 for (i = 1; i < 20; i++) { 205 pid = fork(); 206 if (pid < 0) { 207 sleep(i); 208 /* 209 * flush out to most recently sent request. 210 * 211 * This may drop some request, but those 212 * will be resent by the clients when 213 * they timeout. The positive effect of 214 * this flush is to (try to) prevent more 215 * than one tftpd being started up to service 216 * a single request from a single client. 217 */ 218 j = sizeof from; 219 i = recvfrom(0, buf, sizeof (buf), 0, 220 (struct sockaddr *)&from, &j); 221 if (i > 0) { 222 n = i; 223 fromlen = j; 224 } 225 } else { 226 break; 227 } 228 } 229 if (pid < 0) { 230 syslog(LOG_ERR, "fork: %m"); 231 exit(1); 232 } else if (pid != 0) { 233 exit(0); 234 } 235 } 236 237 /* 238 * Since we exit here, we should do that only after the above 239 * recvfrom to keep inetd from constantly forking should there 240 * be a problem. See the above comment about system clogging. 241 */ 242 if (chroot_dir) { 243 if (ipchroot) { 244 char *tempchroot; 245 struct stat sb; 246 int statret; 247 248 tempchroot = inet_ntoa(from.sin_addr); 249 asprintf(&tempchroot, "%s/%s", chroot_dir, tempchroot); 250 statret = stat(tempchroot, &sb); 251 if ((sb.st_mode & S_IFDIR) && 252 (statret == 0 || (statret == -1 && ipchroot == 1))) 253 chroot_dir = tempchroot; 254 } 255 /* Must get this before chroot because /etc might go away */ 256 if ((nobody = getpwnam(chuser)) == NULL) { 257 syslog(LOG_ERR, "%s: no such user", chuser); 258 exit(1); 259 } 260 if (chroot(chroot_dir)) { 261 syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); 262 exit(1); 263 } 264 chdir( "/" ); 265 setuid(nobody->pw_uid); 266 setgroups(1, &nobody->pw_gid); 267 } 268 269 from.sin_family = AF_INET; 270 alarm(0); 271 close(0); 272 close(1); 273 peer = socket(AF_INET, SOCK_DGRAM, 0); 274 if (peer < 0) { 275 syslog(LOG_ERR, "socket: %m"); 276 exit(1); 277 } 278 memset(&sin, 0, sizeof(sin)); 279 sin.sin_family = AF_INET; 280 if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 281 syslog(LOG_ERR, "bind: %m"); 282 exit(1); 283 } 284 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 285 syslog(LOG_ERR, "connect: %m"); 286 exit(1); 287 } 288 tp = (struct tftphdr *)buf; 289 tp->th_opcode = ntohs(tp->th_opcode); 290 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 291 tftp(tp, n); 292 exit(1); 293} 294 295struct formats; 296int validate_access(char **, int); 297void xmitfile(struct formats *); 298void recvfile(struct formats *); 299 300struct formats { 301 char *f_mode; 302 int (*f_validate)(char **, int); 303 void (*f_send)(struct formats *); 304 void (*f_recv)(struct formats *); 305 int f_convert; 306} formats[] = { 307 { "netascii", validate_access, xmitfile, recvfile, 1 }, 308 { "octet", validate_access, xmitfile, recvfile, 0 }, 309#ifdef notdef 310 { "mail", validate_user, sendmail, recvmail, 1 }, 311#endif 312 { 0 } 313}; 314 315struct options { 316 char *o_type; 317 char *o_request; 318 int o_reply; /* turn into union if need be */ 319} options[] = { 320 { "tsize" }, /* OPT_TSIZE */ 321 { "timeout" }, /* OPT_TIMEOUT */ 322 { NULL } 323}; 324 325enum opt_enum { 326 OPT_TSIZE = 0, 327 OPT_TIMEOUT, 328}; 329 330/* 331 * Handle initial connection protocol. 332 */ 333void 334tftp(struct tftphdr *tp, int size) 335{ 336 char *cp; 337 int i, first = 1, has_options = 0, ecode; 338 struct formats *pf; 339 char *filename, *mode, *option, *ccp; 340 341 filename = cp = tp->th_stuff; 342again: 343 while (cp < buf + size) { 344 if (*cp == '\0') 345 break; 346 cp++; 347 } 348 if (*cp != '\0') { 349 nak(EBADOP); 350 exit(1); 351 } 352 if (first) { 353 mode = ++cp; 354 first = 0; 355 goto again; 356 } 357 for (cp = mode; *cp; cp++) 358 if (isupper(*cp)) 359 *cp = tolower(*cp); 360 for (pf = formats; pf->f_mode; pf++) 361 if (strcmp(pf->f_mode, mode) == 0) 362 break; 363 if (pf->f_mode == 0) { 364 nak(EBADOP); 365 exit(1); 366 } 367 while (++cp < buf + size) { 368 for (i = 2, ccp = cp; i > 0; ccp++) { 369 if (ccp >= buf + size) { 370 /* 371 * Don't reject the request, just stop trying 372 * to parse the option and get on with it. 373 * Some Apple OpenFirmware versions have 374 * trailing garbage on the end of otherwise 375 * valid requests. 376 */ 377 goto option_fail; 378 } else if (*ccp == '\0') 379 i--; 380 } 381 for (option = cp; *cp; cp++) 382 if (isupper(*cp)) 383 *cp = tolower(*cp); 384 for (i = 0; options[i].o_type != NULL; i++) 385 if (strcmp(option, options[i].o_type) == 0) { 386 options[i].o_request = ++cp; 387 has_options = 1; 388 } 389 cp = ccp-1; 390 } 391 392option_fail: 393 if (options[OPT_TIMEOUT].o_request) { 394 int to = atoi(options[OPT_TIMEOUT].o_request); 395 if (to < 1 || to > 255) { 396 nak(EBADOP); 397 exit(1); 398 } 399 else if (to <= max_rexmtval) 400 options[OPT_TIMEOUT].o_reply = rexmtval = to; 401 else 402 options[OPT_TIMEOUT].o_request = NULL; 403 } 404 405 ecode = (*pf->f_validate)(&filename, tp->th_opcode); 406 if (has_options) 407 oack(); 408 if (logging) { 409 char host[MAXHOSTNAMELEN]; 410 411 realhostname(host, sizeof(host) - 1, &from.sin_addr); 412 host[sizeof(host) - 1] = '\0'; 413 syslog(LOG_INFO, "%s: %s request for %s: %s", host, 414 tp->th_opcode == WRQ ? "write" : "read", 415 filename, errtomsg(ecode)); 416 } 417 if (ecode) { 418 /* 419 * Avoid storms of naks to a RRQ broadcast for a relative 420 * bootfile pathname from a diskless Sun. 421 */ 422 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 423 exit(0); 424 nak(ecode); 425 exit(1); 426 } 427 if (tp->th_opcode == WRQ) 428 (*pf->f_recv)(pf); 429 else 430 (*pf->f_send)(pf); 431 exit(0); 432} 433 434 435FILE *file; 436 437/* 438 * Validate file access. Since we 439 * have no uid or gid, for now require 440 * file to exist and be publicly 441 * readable/writable. 442 * If we were invoked with arguments 443 * from inetd then the file must also be 444 * in one of the given directory prefixes. 445 * Note also, full path name must be 446 * given as we have no login directory. 447 */ 448int 449validate_access(char **filep, int mode) 450{ 451 struct stat stbuf; 452 int fd; 453 struct dirlist *dirp; 454 static char pathname[MAXPATHLEN]; 455 char *filename = *filep; 456 457 /* 458 * Prevent tricksters from getting around the directory restrictions 459 */ 460 if (strstr(filename, "/../")) 461 return (EACCESS); 462 463 if (*filename == '/') { 464 /* 465 * Allow the request if it's in one of the approved locations. 466 * Special case: check the null prefix ("/") by looking 467 * for length = 1 and relying on the arg. processing that 468 * it's a /. 469 */ 470 for (dirp = dirs; dirp->name != NULL; dirp++) { 471 if (dirp->len == 1 || 472 (!strncmp(filename, dirp->name, dirp->len) && 473 filename[dirp->len] == '/')) 474 break; 475 } 476 /* If directory list is empty, allow access to any file */ 477 if (dirp->name == NULL && dirp != dirs) 478 return (EACCESS); 479 if (stat(filename, &stbuf) < 0) 480 return (errno == ENOENT ? ENOTFOUND : EACCESS); 481 if ((stbuf.st_mode & S_IFMT) != S_IFREG) 482 return (ENOTFOUND); 483 if (mode == RRQ) { 484 if ((stbuf.st_mode & S_IROTH) == 0) 485 return (EACCESS); 486 } else { 487 if ((stbuf.st_mode & S_IWOTH) == 0) 488 return (EACCESS); 489 } 490 } else { 491 int err; 492 493 /* 494 * Relative file name: search the approved locations for it. 495 * Don't allow write requests that avoid directory 496 * restrictions. 497 */ 498 499 if (!strncmp(filename, "../", 3)) 500 return (EACCESS); 501 502 /* 503 * If the file exists in one of the directories and isn't 504 * readable, continue looking. However, change the error code 505 * to give an indication that the file exists. 506 */ 507 err = ENOTFOUND; 508 for (dirp = dirs; dirp->name != NULL; dirp++) { 509 snprintf(pathname, sizeof(pathname), "%s/%s", 510 dirp->name, filename); 511 if (stat(pathname, &stbuf) == 0 && 512 (stbuf.st_mode & S_IFMT) == S_IFREG) { 513 if ((stbuf.st_mode & S_IROTH) != 0) { 514 break; 515 } 516 err = EACCESS; 517 } 518 } 519 if (dirp->name == NULL) 520 return (err); 521 *filep = filename = pathname; 522 } 523 if (options[OPT_TSIZE].o_request) { 524 if (mode == RRQ) 525 options[OPT_TSIZE].o_reply = stbuf.st_size; 526 else 527 /* XXX Allows writes of all sizes. */ 528 options[OPT_TSIZE].o_reply = 529 atoi(options[OPT_TSIZE].o_request); 530 } 531 fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); 532 if (fd < 0) 533 return (errno + 100); 534 file = fdopen(fd, (mode == RRQ)? "r":"w"); 535 if (file == NULL) { 536 return errno+100; 537 } 538 return (0); 539} 540 541int timeouts; 542jmp_buf timeoutbuf; 543 544void 545timer(int sig __unused) 546{ 547 if (++timeouts > MAX_TIMEOUTS) 548 exit(1); 549 longjmp(timeoutbuf, 1); 550} 551 552/* 553 * Send the requested file. 554 */ 555void 556xmitfile(struct formats *pf) 557{ 558 struct tftphdr *dp, *r_init(); 559 struct tftphdr *ap; /* ack packet */ 560 int size, n; 561 volatile unsigned short block; 562 563 signal(SIGALRM, timer); 564 dp = r_init(); 565 ap = (struct tftphdr *)ackbuf; 566 block = 1; 567 do { 568 size = readit(file, &dp, pf->f_convert); 569 if (size < 0) { 570 nak(errno + 100); 571 goto abort; 572 } 573 dp->th_opcode = htons((u_short)DATA); 574 dp->th_block = htons((u_short)block); 575 timeouts = 0; 576 (void)setjmp(timeoutbuf); 577 578send_data: 579 { 580 int i, t = 1; 581 for (i = 0; ; i++){ 582 if (send(peer, dp, size + 4, 0) != size + 4) { 583 sleep(t); 584 t = (t < 32) ? t<< 1 : t; 585 if (i >= 12) { 586 syslog(LOG_ERR, "write: %m"); 587 goto abort; 588 } 589 } 590 break; 591 } 592 } 593 read_ahead(file, pf->f_convert); 594 for ( ; ; ) { 595 alarm(rexmtval); /* read the ack */ 596 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 597 alarm(0); 598 if (n < 0) { 599 syslog(LOG_ERR, "read: %m"); 600 goto abort; 601 } 602 ap->th_opcode = ntohs((u_short)ap->th_opcode); 603 ap->th_block = ntohs((u_short)ap->th_block); 604 605 if (ap->th_opcode == ERROR) 606 goto abort; 607 608 if (ap->th_opcode == ACK) { 609 if (ap->th_block == block) 610 break; 611 /* Re-synchronize with the other side */ 612 (void) synchnet(peer); 613 if (ap->th_block == (block -1)) 614 goto send_data; 615 } 616 617 } 618 block++; 619 } while (size == SEGSIZE); 620abort: 621 (void) fclose(file); 622} 623 624void 625justquit(int sig __unused) 626{ 627 exit(0); 628} 629 630 631/* 632 * Receive a file. 633 */ 634void 635recvfile(struct formats *pf) 636{ 637 struct tftphdr *dp, *w_init(); 638 struct tftphdr *ap; /* ack buffer */ 639 int n, size; 640 volatile unsigned short block; 641 642 signal(SIGALRM, timer); 643 dp = w_init(); 644 ap = (struct tftphdr *)ackbuf; 645 block = 0; 646 do { 647 timeouts = 0; 648 ap->th_opcode = htons((u_short)ACK); 649 ap->th_block = htons((u_short)block); 650 block++; 651 (void) setjmp(timeoutbuf); 652send_ack: 653 if (send(peer, ackbuf, 4, 0) != 4) { 654 syslog(LOG_ERR, "write: %m"); 655 goto abort; 656 } 657 write_behind(file, pf->f_convert); 658 for ( ; ; ) { 659 alarm(rexmtval); 660 n = recv(peer, dp, PKTSIZE, 0); 661 alarm(0); 662 if (n < 0) { /* really? */ 663 syslog(LOG_ERR, "read: %m"); 664 goto abort; 665 } 666 dp->th_opcode = ntohs((u_short)dp->th_opcode); 667 dp->th_block = ntohs((u_short)dp->th_block); 668 if (dp->th_opcode == ERROR) 669 goto abort; 670 if (dp->th_opcode == DATA) { 671 if (dp->th_block == block) { 672 break; /* normal */ 673 } 674 /* Re-synchronize with the other side */ 675 (void) synchnet(peer); 676 if (dp->th_block == (block-1)) 677 goto send_ack; /* rexmit */ 678 } 679 } 680 /* size = write(file, dp->th_data, n - 4); */ 681 size = writeit(file, &dp, n - 4, pf->f_convert); 682 if (size != (n-4)) { /* ahem */ 683 if (size < 0) nak(errno + 100); 684 else nak(ENOSPACE); 685 goto abort; 686 } 687 } while (size == SEGSIZE); 688 write_behind(file, pf->f_convert); 689 (void) fclose(file); /* close data file */ 690 691 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 692 ap->th_block = htons((u_short)(block)); 693 (void) send(peer, ackbuf, 4, 0); 694 695 signal(SIGALRM, justquit); /* just quit on timeout */ 696 alarm(rexmtval); 697 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 698 alarm(0); 699 if (n >= 4 && /* if read some data */ 700 dp->th_opcode == DATA && /* and got a data block */ 701 block == dp->th_block) { /* then my last ack was lost */ 702 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 703 } 704abort: 705 return; 706} 707 708struct errmsg { 709 int e_code; 710 char *e_msg; 711} errmsgs[] = { 712 { EUNDEF, "Undefined error code" }, 713 { ENOTFOUND, "File not found" }, 714 { EACCESS, "Access violation" }, 715 { ENOSPACE, "Disk full or allocation exceeded" }, 716 { EBADOP, "Illegal TFTP operation" }, 717 { EBADID, "Unknown transfer ID" }, 718 { EEXISTS, "File already exists" }, 719 { ENOUSER, "No such user" }, 720 { EOPTNEG, "Option negotiation" }, 721 { -1, 0 } 722}; 723 724static char * 725errtomsg(int error) 726{ 727 static char buf[20]; 728 struct errmsg *pe; 729 if (error == 0) 730 return "success"; 731 for (pe = errmsgs; pe->e_code >= 0; pe++) 732 if (pe->e_code == error) 733 return pe->e_msg; 734 snprintf(buf, sizeof(buf), "error %d", error); 735 return buf; 736} 737 738/* 739 * Send a nak packet (error message). 740 * Error code passed in is one of the 741 * standard TFTP codes, or a UNIX errno 742 * offset by 100. 743 */ 744static void 745nak(int error) 746{ 747 struct tftphdr *tp; 748 int length; 749 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(void) 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