tftpd.c revision 85299
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 85299 2001-10-22 01:55:40Z obrien $"; 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 __P((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 __P((int)); 113static void nak __P((int)); 114static void oack __P(()); 115 116int 117main(argc, argv) 118 int argc; 119 char *argv[]; 120{ 121 register struct tftphdr *tp; 122 register int n; 123 int ch, on; 124 struct sockaddr_in sin; 125 char *chroot_dir = NULL; 126 struct passwd *nobody; 127 char *chuser = "nobody"; 128 129 openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 130 while ((ch = getopt(argc, argv, "cClns:u:")) != -1) { 131 switch (ch) { 132 case 'c': 133 ipchroot = 1; 134 break; 135 case 'C': 136 ipchroot = 2; 137 break; 138 case 'l': 139 logging = 1; 140 break; 141 case 'n': 142 suppress_naks = 1; 143 break; 144 case 's': 145 chroot_dir = optarg; 146 break; 147 case 'u': 148 chuser = optarg; 149 break; 150 default: 151 syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 152 } 153 } 154 if (optind < argc) { 155 struct dirlist *dirp; 156 157 /* Get list of directory prefixes. Skip relative pathnames. */ 158 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 159 optind++) { 160 if (argv[optind][0] == '/') { 161 dirp->name = argv[optind]; 162 dirp->len = strlen(dirp->name); 163 dirp++; 164 } 165 } 166 } 167 else if (chroot_dir) { 168 dirs->name = "/"; 169 dirs->len = 1; 170 } 171 if (ipchroot && chroot_dir == NULL) { 172 syslog(LOG_ERR, "-c requires -s"); 173 exit(1); 174 } 175 176 on = 1; 177 if (ioctl(0, FIONBIO, &on) < 0) { 178 syslog(LOG_ERR, "ioctl(FIONBIO): %m"); 179 exit(1); 180 } 181 fromlen = sizeof (from); 182 n = recvfrom(0, buf, sizeof (buf), 0, 183 (struct sockaddr *)&from, &fromlen); 184 if (n < 0) { 185 syslog(LOG_ERR, "recvfrom: %m"); 186 exit(1); 187 } 188 /* 189 * Now that we have read the message out of the UDP 190 * socket, we fork and exit. Thus, inetd will go back 191 * to listening to the tftp port, and the next request 192 * to come in will start up a new instance of tftpd. 193 * 194 * We do this so that inetd can run tftpd in "wait" mode. 195 * The problem with tftpd running in "nowait" mode is that 196 * inetd may get one or more successful "selects" on the 197 * tftp port before we do our receive, so more than one 198 * instance of tftpd may be started up. Worse, if tftpd 199 * break before doing the above "recvfrom", inetd would 200 * spawn endless instances, clogging the system. 201 */ 202 { 203 int pid; 204 int i, j; 205 206 for (i = 1; i < 20; i++) { 207 pid = fork(); 208 if (pid < 0) { 209 sleep(i); 210 /* 211 * flush out to most recently sent request. 212 * 213 * This may drop some request, but those 214 * will be resent by the clients when 215 * they timeout. The positive effect of 216 * this flush is to (try to) prevent more 217 * than one tftpd being started up to service 218 * a single request from a single client. 219 */ 220 j = sizeof from; 221 i = recvfrom(0, buf, sizeof (buf), 0, 222 (struct sockaddr *)&from, &j); 223 if (i > 0) { 224 n = i; 225 fromlen = j; 226 } 227 } else { 228 break; 229 } 230 } 231 if (pid < 0) { 232 syslog(LOG_ERR, "fork: %m"); 233 exit(1); 234 } else if (pid != 0) { 235 exit(0); 236 } 237 } 238 239 /* 240 * Since we exit here, we should do that only after the above 241 * recvfrom to keep inetd from constantly forking should there 242 * be a problem. See the above comment about system clogging. 243 */ 244 if (chroot_dir) { 245 if (ipchroot) { 246 char *tempchroot; 247 struct stat sb; 248 int statret; 249 250 tempchroot = inet_ntoa(from.sin_addr); 251 asprintf(&tempchroot, "%s/%s", chroot_dir, tempchroot); 252 statret = stat(tempchroot, &sb); 253 if ((sb.st_mode & S_IFDIR) && 254 (statret == 0 || (statret == -1 && ipchroot == 1))) 255 chroot_dir = tempchroot; 256 } 257 /* Must get this before chroot because /etc might go away */ 258 if ((nobody = getpwnam(chuser)) == NULL) { 259 syslog(LOG_ERR, "%s: no such user", chuser); 260 exit(1); 261 } 262 if (chroot(chroot_dir)) { 263 syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); 264 exit(1); 265 } 266 chdir( "/" ); 267 setuid(nobody->pw_uid); 268 setgroups(1, &nobody->pw_gid); 269 } 270 271 from.sin_family = AF_INET; 272 alarm(0); 273 close(0); 274 close(1); 275 peer = socket(AF_INET, SOCK_DGRAM, 0); 276 if (peer < 0) { 277 syslog(LOG_ERR, "socket: %m"); 278 exit(1); 279 } 280 memset(&sin, 0, sizeof(sin)); 281 sin.sin_family = AF_INET; 282 if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 283 syslog(LOG_ERR, "bind: %m"); 284 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 nak(EBADOP); 375 exit(1); 376 } else if (*ccp == '\0') 377 i--; 378 } 379 for (option = cp; *cp; cp++) 380 if (isupper(*cp)) 381 *cp = tolower(*cp); 382 for (i = 0; options[i].o_type != NULL; i++) 383 if (strcmp(option, options[i].o_type) == 0) { 384 options[i].o_request = ++cp; 385 has_options = 1; 386 } 387 cp = ccp-1; 388 } 389 390 if (options[OPT_TIMEOUT].o_request) { 391 int to = atoi(options[OPT_TIMEOUT].o_request); 392 if (to < 1 || to > 255) { 393 nak(EBADOP); 394 exit(1); 395 } 396 else if (to <= max_rexmtval) 397 options[OPT_TIMEOUT].o_reply = rexmtval = to; 398 else 399 options[OPT_TIMEOUT].o_request = NULL; 400 } 401 402 ecode = (*pf->f_validate)(&filename, tp->th_opcode); 403 if (has_options) 404 oack(); 405 if (logging) { 406 char host[MAXHOSTNAMELEN]; 407 408 realhostname(host, sizeof(host) - 1, &from.sin_addr); 409 host[sizeof(host) - 1] = '\0'; 410 syslog(LOG_INFO, "%s: %s request for %s: %s", host, 411 tp->th_opcode == WRQ ? "write" : "read", 412 filename, errtomsg(ecode)); 413 } 414 if (ecode) { 415 /* 416 * Avoid storms of naks to a RRQ broadcast for a relative 417 * bootfile pathname from a diskless Sun. 418 */ 419 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 420 exit(0); 421 nak(ecode); 422 exit(1); 423 } 424 if (tp->th_opcode == WRQ) 425 (*pf->f_recv)(pf); 426 else 427 (*pf->f_send)(pf); 428 exit(0); 429} 430 431 432FILE *file; 433 434/* 435 * Validate file access. Since we 436 * have no uid or gid, for now require 437 * file to exist and be publicly 438 * readable/writable. 439 * If we were invoked with arguments 440 * from inetd then the file must also be 441 * in one of the given directory prefixes. 442 * Note also, full path name must be 443 * given as we have no login directory. 444 */ 445int 446validate_access(filep, mode) 447 char **filep; 448 int mode; 449{ 450 struct stat stbuf; 451 int fd; 452 struct dirlist *dirp; 453 static char pathname[MAXPATHLEN]; 454 char *filename = *filep; 455 456 /* 457 * Prevent tricksters from getting around the directory restrictions 458 */ 459 if (strstr(filename, "/../")) 460 return (EACCESS); 461 462 if (*filename == '/') { 463 /* 464 * Allow the request if it's in one of the approved locations. 465 * Special case: check the null prefix ("/") by looking 466 * for length = 1 and relying on the arg. processing that 467 * it's a /. 468 */ 469 for (dirp = dirs; dirp->name != NULL; dirp++) { 470 if (dirp->len == 1 || 471 (!strncmp(filename, dirp->name, dirp->len) && 472 filename[dirp->len] == '/')) 473 break; 474 } 475 /* If directory list is empty, allow access to any file */ 476 if (dirp->name == NULL && dirp != dirs) 477 return (EACCESS); 478 if (stat(filename, &stbuf) < 0) 479 return (errno == ENOENT ? ENOTFOUND : EACCESS); 480 if ((stbuf.st_mode & S_IFMT) != S_IFREG) 481 return (ENOTFOUND); 482 if (mode == RRQ) { 483 if ((stbuf.st_mode & S_IROTH) == 0) 484 return (EACCESS); 485 } else { 486 if ((stbuf.st_mode & S_IWOTH) == 0) 487 return (EACCESS); 488 } 489 } else { 490 int err; 491 492 /* 493 * Relative file name: search the approved locations for it. 494 * Don't allow write requests that avoid directory 495 * restrictions. 496 */ 497 498 if (!strncmp(filename, "../", 3)) 499 return (EACCESS); 500 501 /* 502 * If the file exists in one of the directories and isn't 503 * readable, continue looking. However, change the error code 504 * to give an indication that the file exists. 505 */ 506 err = ENOTFOUND; 507 for (dirp = dirs; dirp->name != NULL; dirp++) { 508 snprintf(pathname, sizeof(pathname), "%s/%s", 509 dirp->name, filename); 510 if (stat(pathname, &stbuf) == 0 && 511 (stbuf.st_mode & S_IFMT) == S_IFREG) { 512 if ((stbuf.st_mode & S_IROTH) != 0) { 513 break; 514 } 515 err = EACCESS; 516 } 517 } 518 if (dirp->name == NULL) 519 return (err); 520 *filep = filename = pathname; 521 } 522 if (options[OPT_TSIZE].o_request) { 523 if (mode == RRQ) 524 options[OPT_TSIZE].o_reply = stbuf.st_size; 525 else 526 /* XXX Allows writes of all sizes. */ 527 options[OPT_TSIZE].o_reply = 528 atoi(options[OPT_TSIZE].o_request); 529 } 530 fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); 531 if (fd < 0) 532 return (errno + 100); 533 file = fdopen(fd, (mode == RRQ)? "r":"w"); 534 if (file == NULL) { 535 return errno+100; 536 } 537 return (0); 538} 539 540int timeouts; 541jmp_buf timeoutbuf; 542 543void 544timer() 545{ 546 if (++timeouts > MAX_TIMEOUTS) 547 exit(1); 548 longjmp(timeoutbuf, 1); 549} 550 551/* 552 * Send the requested file. 553 */ 554void 555xmitfile(pf) 556 struct formats *pf; 557{ 558 struct tftphdr *dp, *r_init(); 559 register struct tftphdr *ap; /* ack packet */ 560 register 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 if (send(peer, dp, size + 4, 0) != size + 4) { 580 syslog(LOG_ERR, "write: %m"); 581 goto abort; 582 } 583 read_ahead(file, pf->f_convert); 584 for ( ; ; ) { 585 alarm(rexmtval); /* read the ack */ 586 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 587 alarm(0); 588 if (n < 0) { 589 syslog(LOG_ERR, "read: %m"); 590 goto abort; 591 } 592 ap->th_opcode = ntohs((u_short)ap->th_opcode); 593 ap->th_block = ntohs((u_short)ap->th_block); 594 595 if (ap->th_opcode == ERROR) 596 goto abort; 597 598 if (ap->th_opcode == ACK) { 599 if (ap->th_block == block) 600 break; 601 /* Re-synchronize with the other side */ 602 (void) synchnet(peer); 603 if (ap->th_block == (block -1)) 604 goto send_data; 605 } 606 607 } 608 block++; 609 } while (size == SEGSIZE); 610abort: 611 (void) fclose(file); 612} 613 614void 615justquit() 616{ 617 exit(0); 618} 619 620 621/* 622 * Receive a file. 623 */ 624void 625recvfile(pf) 626 struct formats *pf; 627{ 628 struct tftphdr *dp, *w_init(); 629 register struct tftphdr *ap; /* ack buffer */ 630 register int n, size; 631 volatile unsigned short block; 632 633 signal(SIGALRM, timer); 634 dp = w_init(); 635 ap = (struct tftphdr *)ackbuf; 636 block = 0; 637 do { 638 timeouts = 0; 639 ap->th_opcode = htons((u_short)ACK); 640 ap->th_block = htons((u_short)block); 641 block++; 642 (void) setjmp(timeoutbuf); 643send_ack: 644 if (send(peer, ackbuf, 4, 0) != 4) { 645 syslog(LOG_ERR, "write: %m"); 646 goto abort; 647 } 648 write_behind(file, pf->f_convert); 649 for ( ; ; ) { 650 alarm(rexmtval); 651 n = recv(peer, dp, PKTSIZE, 0); 652 alarm(0); 653 if (n < 0) { /* really? */ 654 syslog(LOG_ERR, "read: %m"); 655 goto abort; 656 } 657 dp->th_opcode = ntohs((u_short)dp->th_opcode); 658 dp->th_block = ntohs((u_short)dp->th_block); 659 if (dp->th_opcode == ERROR) 660 goto abort; 661 if (dp->th_opcode == DATA) { 662 if (dp->th_block == block) { 663 break; /* normal */ 664 } 665 /* Re-synchronize with the other side */ 666 (void) synchnet(peer); 667 if (dp->th_block == (block-1)) 668 goto send_ack; /* rexmit */ 669 } 670 } 671 /* size = write(file, dp->th_data, n - 4); */ 672 size = writeit(file, &dp, n - 4, pf->f_convert); 673 if (size != (n-4)) { /* ahem */ 674 if (size < 0) nak(errno + 100); 675 else nak(ENOSPACE); 676 goto abort; 677 } 678 } while (size == SEGSIZE); 679 write_behind(file, pf->f_convert); 680 (void) fclose(file); /* close data file */ 681 682 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 683 ap->th_block = htons((u_short)(block)); 684 (void) send(peer, ackbuf, 4, 0); 685 686 signal(SIGALRM, justquit); /* just quit on timeout */ 687 alarm(rexmtval); 688 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 689 alarm(0); 690 if (n >= 4 && /* if read some data */ 691 dp->th_opcode == DATA && /* and got a data block */ 692 block == dp->th_block) { /* then my last ack was lost */ 693 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 694 } 695abort: 696 return; 697} 698 699struct errmsg { 700 int e_code; 701 char *e_msg; 702} errmsgs[] = { 703 { EUNDEF, "Undefined error code" }, 704 { ENOTFOUND, "File not found" }, 705 { EACCESS, "Access violation" }, 706 { ENOSPACE, "Disk full or allocation exceeded" }, 707 { EBADOP, "Illegal TFTP operation" }, 708 { EBADID, "Unknown transfer ID" }, 709 { EEXISTS, "File already exists" }, 710 { ENOUSER, "No such user" }, 711 { EOPTNEG, "Option negotiation" }, 712 { -1, 0 } 713}; 714 715static char * 716errtomsg(error) 717 int error; 718{ 719 static char buf[20]; 720 register struct errmsg *pe; 721 if (error == 0) 722 return "success"; 723 for (pe = errmsgs; pe->e_code >= 0; pe++) 724 if (pe->e_code == error) 725 return pe->e_msg; 726 snprintf(buf, sizeof(buf), "error %d", error); 727 return buf; 728} 729 730/* 731 * Send a nak packet (error message). 732 * Error code passed in is one of the 733 * standard TFTP codes, or a UNIX errno 734 * offset by 100. 735 */ 736static void 737nak(error) 738 int error; 739{ 740 register struct tftphdr *tp; 741 int length; 742 register struct errmsg *pe; 743 744 tp = (struct tftphdr *)buf; 745 tp->th_opcode = htons((u_short)ERROR); 746 tp->th_code = htons((u_short)error); 747 for (pe = errmsgs; pe->e_code >= 0; pe++) 748 if (pe->e_code == error) 749 break; 750 if (pe->e_code < 0) { 751 pe->e_msg = strerror(error - 100); 752 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 753 } 754 strcpy(tp->th_msg, pe->e_msg); 755 length = strlen(pe->e_msg); 756 tp->th_msg[length] = '\0'; 757 length += 5; 758 if (send(peer, buf, length, 0) != length) 759 syslog(LOG_ERR, "nak: %m"); 760} 761 762/* 763 * Send an oack packet (option acknowledgement). 764 */ 765static void 766oack() 767{ 768 struct tftphdr *tp, *ap; 769 int size, i, n; 770 char *bp; 771 772 tp = (struct tftphdr *)buf; 773 bp = buf + 2; 774 size = sizeof(buf) - 2; 775 tp->th_opcode = htons((u_short)OACK); 776 for (i = 0; options[i].o_type != NULL; i++) { 777 if (options[i].o_request) { 778 n = snprintf(bp, size, "%s%c%d", options[i].o_type, 779 0, options[i].o_reply); 780 bp += n+1; 781 size -= n+1; 782 if (size < 0) { 783 syslog(LOG_ERR, "oack: buffer overflow"); 784 exit(1); 785 } 786 } 787 } 788 size = bp - buf; 789 ap = (struct tftphdr *)ackbuf; 790 signal(SIGALRM, timer); 791 timeouts = 0; 792 793 (void)setjmp(timeoutbuf); 794 if (send(peer, buf, size, 0) != size) { 795 syslog(LOG_INFO, "oack: %m"); 796 exit(1); 797 } 798 799 for (;;) { 800 alarm(rexmtval); 801 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 802 alarm(0); 803 if (n < 0) { 804 syslog(LOG_ERR, "recv: %m"); 805 exit(1); 806 } 807 ap->th_opcode = ntohs((u_short)ap->th_opcode); 808 ap->th_block = ntohs((u_short)ap->th_block); 809 if (ap->th_opcode == ERROR) 810 exit(1); 811 if (ap->th_opcode == ACK && ap->th_block == 0) 812 break; 813 } 814} 815