1 /* $OpenBSD: tftp.c,v 1.20 2006/07/26 09:10:03 mglocker Exp $ */ 2 /* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 36 #endif 37 static const char rcsid[] = 38 "$OpenBSD: tftp.c,v 1.20 2006/07/26 09:10:03 mglocker Exp $"; 39 #endif /* not lint */ 40 41 /* 42 * TFTP User Program -- Protocol Machines 43 * 44 * This version includes many modifications by Jim Guyton <guyton@rand-unix> 45 */ 46 47 #include <sys/types.h> 48 #include <sys/socket.h> 49 #include <sys/time.h> 50 #include <sys/stat.h> 51 52 #include <netinet/in.h> 53 #include <arpa/tftp.h> 54 55 #include <err.h> 56 #include <errno.h> 57 #include <poll.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stddef.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 65 #include "extern.h" 66 #include "tftpsubs.h" 67 68 static int makerequest(int, const char *, struct tftphdr *, const char *); 69 static void nak(int); 70 static void tpacket(const char *, struct tftphdr *, int); 71 static void startclock(void); 72 static void stopclock(void); 73 static void printstats(const char *, unsigned long); 74 static void printtimeout(void); 75 static void oack(struct tftphdr *, int, int); 76 static int oack_set(const char *, const char *); 77 78 extern struct sockaddr_in peeraddr; /* filled in by main */ 79 extern int f; /* the opened socket */ 80 extern int trace; 81 extern int verbose; 82 extern int rexmtval; 83 extern int maxtimeout; 84 extern FILE *file; 85 extern volatile sig_atomic_t intrflag; 86 extern char *ackbuf; 87 extern int has_options; 88 extern int opt_tsize; 89 extern int opt_tout; 90 extern int opt_blksize; 91 92 struct timeval tstart; 93 struct timeval tstop; 94 unsigned int segment_size = SEGSIZE; 95 unsigned int packet_size = SEGSIZE + 4; 96 97 struct errmsg { 98 int e_code; 99 char *e_msg; 100 } errmsgs[] = { 101 { EUNDEF, "Undefined error code" }, 102 { ENOTFOUND, "File not found" }, 103 { EACCESS, "Access violation" }, 104 { ENOSPACE, "Disk full or allocation exceeded" }, 105 { EBADOP, "Illegal TFTP operation" }, 106 { EBADID, "Unknown transfer ID" }, 107 { EEXISTS, "File already exists" }, 108 { ENOUSER, "No such user" }, 109 { EOPTNEG, "Option negotiation failed" }, 110 { -1, NULL } 111 }; 112 113 struct options { 114 const char *o_type; 115 } options[] = { 116 { "tsize" }, 117 { "timeout" }, 118 { "blksize" }, 119 { NULL } 120 }; 121 122 enum opt_enum { 123 OPT_TSIZE = 0, 124 OPT_TIMEOUT, 125 OPT_BLKSIZE 126 }; 127 128 /* 129 * Send the requested file. 130 */ 131 void 132 sendfile(int fd, char *name, char *mode) 133 { 134 struct tftphdr *dp, *ap; /* data and ack packets */ 135 struct sockaddr_in from; 136 struct pollfd pfd[1]; 137 unsigned long amount; 138 int convert; /* true if converting crlf -> lf */ 139 int n, nfds, error, fromlen, timeouts, block, size; 140 141 startclock(); /* start stat's clock */ 142 dp = r_init(); /* reset fillbuf/read-ahead code */ 143 ap = (struct tftphdr *)ackbuf; 144 file = fdopen(fd, "r"); 145 convert = !strcmp(mode, "netascii"); 146 block = 0; 147 amount = 0; 148 149 do { 150 /* read data from file */ 151 if (!block) 152 size = makerequest(WRQ, name, dp, mode) - 4; 153 else { 154 size = readit(file, &dp, convert, segment_size); 155 if (size < 0) { 156 nak(errno + 100); 157 break; 158 } 159 dp->th_opcode = htons((u_short)DATA); 160 dp->th_block = htons((u_short)block); 161 } 162 163 /* send data to server and wait for server ACK */ 164 for (timeouts = 0, error = 0; !intrflag;) { 165 if (timeouts >= maxtimeout) { 166 printtimeout(); 167 goto abort; 168 } 169 170 if (!error) { 171 if (trace) 172 tpacket("sent", dp, size + 4); 173 if (sendto(f, dp, size + 4, 0, 174 (struct sockaddr *)&peeraddr, 175 sizeof(peeraddr)) != size + 4) { 176 warn("sendto"); 177 goto abort; 178 } 179 if (block > 0) 180 read_ahead(file, convert, segment_size); 181 } 182 error = 0; 183 184 pfd[0].fd = f; 185 pfd[0].events = POLLIN; 186 nfds = poll(pfd, 1, rexmtval * 1000); 187 if (nfds == 0) { 188 timeouts += rexmtval; 189 continue; 190 } 191 if (nfds == -1) { 192 error = 1; 193 if (errno == EINTR) 194 continue; 195 warn("poll"); 196 goto abort; 197 } 198 fromlen = sizeof(from); 199 n = recvfrom(f, ackbuf, packet_size, 0, 200 (struct sockaddr *)&from, &fromlen); 201 if (n == 0) { 202 warn("recvfrom"); 203 goto abort; 204 } 205 if (n == -1) { 206 error = 1; 207 if (errno == EINTR) 208 continue; 209 warn("recvfrom"); 210 goto abort; 211 } 212 peeraddr.sin_port = from.sin_port; /* added */ 213 if (trace) 214 tpacket("received", ap, n); 215 216 ap->th_opcode = ntohs(ap->th_opcode); 217 218 if (ap->th_opcode == OACK) { 219 oack(ap, n, 0); 220 break; 221 } 222 223 ap->th_block = ntohs(ap->th_block); 224 225 if (ap->th_opcode == ERROR) { 226 printf("Error code %d: %s\n", 227 ap->th_code, ap->th_msg); 228 goto abort; 229 } 230 if (ap->th_opcode == ACK) { 231 int j; 232 if (ap->th_block == block) 233 break; 234 /* re-synchronize with other side */ 235 j = synchnet(f); 236 if (j && trace) 237 printf("discarded %d packets\n", j); 238 if (ap->th_block == (block - 1)) 239 continue; 240 } 241 error = 1; /* received packet does not match */ 242 } 243 244 if (block > 0) 245 amount += size; 246 block++; 247 } while ((size == segment_size || block == 1) && !intrflag); 248 249 abort: 250 fclose(file); 251 stopclock(); 252 if (amount > 0) { 253 if (intrflag) 254 putchar('\n'); 255 printstats("Sent", amount); 256 } 257 } 258 259 /* 260 * Receive a file. 261 */ 262 void 263 recvfile(int fd, char *name, char *mode) 264 { 265 struct tftphdr *dp, *ap; /* data and ack packets */ 266 struct sockaddr_in from; 267 struct pollfd pfd[1]; 268 unsigned long amount; 269 int convert; /* true if converting crlf -> lf */ 270 int n, nfds, error, fromlen, timeouts, block, size; 271 int firsttrip; 272 273 startclock(); /* start stat's clock */ 274 dp = w_init(); /* reset fillbuf/read-ahead code */ 275 ap = (struct tftphdr *)ackbuf; 276 file = fdopen(fd, "w"); 277 convert = !strcmp(mode, "netascii"); 278 n = 0; 279 block = 1; 280 amount = 0; 281 firsttrip = 1; 282 283 options: 284 do { 285 /* create new ACK packet */ 286 if (firsttrip) { 287 size = makerequest(RRQ, name, ap, mode); 288 firsttrip = 0; 289 } else { 290 ap->th_opcode = htons((u_short)ACK); 291 ap->th_block = htons((u_short)(block)); 292 size = 4; 293 block++; 294 } 295 296 /* send ACK to server and wait for server data */ 297 for (timeouts = 0, error = 0; !intrflag;) { 298 if (timeouts >= maxtimeout) { 299 printtimeout(); 300 goto abort; 301 } 302 303 if (!error) { 304 if (trace) 305 tpacket("sent", ap, size); 306 if (sendto(f, ackbuf, size, 0, 307 (struct sockaddr *)&peeraddr, 308 sizeof(peeraddr)) != size) { 309 warn("sendto"); 310 goto abort; 311 } 312 write_behind(file, convert); 313 } 314 error = 0; 315 316 pfd[0].fd = f; 317 pfd[0].events = POLLIN; 318 nfds = poll(pfd, 1, rexmtval * 1000); 319 if (nfds == 0) { 320 timeouts += rexmtval; 321 continue; 322 } 323 if (nfds == -1) { 324 error = 1; 325 if (errno == EINTR) 326 continue; 327 warn("poll"); 328 goto abort; 329 } 330 fromlen = sizeof(from); 331 n = recvfrom(f, dp, packet_size, 0, 332 (struct sockaddr *)&from, &fromlen); 333 if (n == 0) { 334 warn("recvfrom"); 335 goto abort; 336 } 337 if (n == -1) { 338 error = 1; 339 if (errno == EINTR) 340 continue; 341 warn("recvfrom"); 342 goto abort; 343 } 344 peeraddr.sin_port = from.sin_port; /* added */ 345 if (trace) 346 tpacket("received", dp, n); 347 348 dp->th_opcode = ntohs(dp->th_opcode); 349 350 if (dp->th_opcode == OACK) { 351 oack(dp, n, 0); 352 block = 0; 353 goto options; 354 } 355 356 dp->th_block = ntohs(dp->th_block); 357 358 if (dp->th_opcode == ERROR) { 359 printf("Error code %d: %s\n", 360 dp->th_code, dp->th_msg); 361 goto abort; 362 } 363 if (dp->th_opcode == DATA) { 364 int j; 365 if (dp->th_block == block) 366 break; 367 /* re-synchronize with other side */ 368 j = synchnet(f); 369 if (j && trace) 370 printf("discarded %d packets\n", j); 371 if (dp->th_block == (block - 1)) 372 continue; 373 } 374 error = 1; /* received packet does not match */ 375 } 376 377 /* write data to file */ 378 size = writeit(file, &dp, n - 4, convert); 379 if (size < 0) { 380 nak(errno + 100); 381 break; 382 } 383 amount += size; 384 } while (size == segment_size && !intrflag); 385 386 abort: 387 /* ok to ack, since user has seen err msg */ 388 ap->th_opcode = htons((u_short)ACK); 389 ap->th_block = htons((u_short)block); 390 (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 391 sizeof(peeraddr)); 392 write_behind(file, convert); /* flush last buffer */ 393 394 fclose(file); 395 stopclock(); 396 if (amount > 0) { 397 if (intrflag) 398 putchar('\n'); 399 printstats("Received", amount); 400 } 401 } 402 403 static int 404 makerequest(int request, const char *name, struct tftphdr *tp, 405 const char *mode) 406 { 407 char *cp; 408 int len, pktlen; 409 off_t fsize = 0; 410 struct stat st; 411 412 tp->th_opcode = htons((u_short)request); 413 cp = tp->th_stuff; 414 pktlen = packet_size - offsetof(struct tftphdr, th_stuff); 415 len = strlen(name) + 1; 416 strlcpy(cp, name, pktlen); 417 strlcpy(cp + len, mode, pktlen - len); 418 len += strlen(mode) + 1; 419 420 if (opt_tsize) { 421 if (request == WRQ) { 422 stat(name, &st); 423 fsize = st.st_size; 424 } 425 len += snprintf(cp + len, pktlen - len, "%s%c%lld%c", 426 options[OPT_TSIZE].o_type, 0, fsize, 0); 427 } 428 if (opt_tout) 429 len += snprintf(cp + len, pktlen - len, "%s%c%d%c", 430 options[OPT_TIMEOUT].o_type, 0, rexmtval, 0); 431 if (opt_blksize) 432 len += snprintf(cp + len, pktlen - len, "%s%c%d%c", 433 options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0); 434 435 return (cp + len - (char *)tp); 436 } 437 438 /* 439 * Send a nak packet (error message). 440 * Error code passed in is one of the 441 * standard TFTP codes, or a UNIX errno 442 * offset by 100. 443 */ 444 static void 445 nak(int error) 446 { 447 struct errmsg *pe; 448 struct tftphdr *tp; 449 int length; 450 451 tp = (struct tftphdr *)ackbuf; 452 tp->th_opcode = htons((u_short)ERROR); 453 tp->th_code = htons((u_short)error); 454 for (pe = errmsgs; pe->e_code >= 0; pe++) 455 if (pe->e_code == error) 456 break; 457 if (pe->e_code < 0) { 458 pe->e_msg = strerror(error - 100); 459 tp->th_code = EUNDEF; 460 } 461 length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5; 462 if (length > packet_size) 463 length = packet_size; 464 if (trace) 465 tpacket("sent", tp, length); 466 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 467 sizeof(peeraddr)) != length) 468 warn("nak"); 469 } 470 471 static void 472 tpacket(const char *s, struct tftphdr *tp, int n) 473 { 474 char *cp, *file; 475 static char *opcodes[] = 476 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; 477 478 u_short op = ntohs(tp->th_opcode); 479 480 if (op < RRQ || op > OACK) 481 printf("%s opcode=%x ", s, op); 482 else 483 printf("%s %s ", s, opcodes[op]); 484 485 switch (op) { 486 case RRQ: 487 case WRQ: 488 n -= 2; 489 file = cp = tp->th_stuff; 490 cp = strchr(cp, '\0'); 491 printf("<file=%s, mode=%s", file, cp + 1); 492 if (has_options) 493 oack(tp, n, 1); 494 printf(">\n"); 495 break; 496 case DATA: 497 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 498 break; 499 case ACK: 500 printf("<block=%d>\n", ntohs(tp->th_block)); 501 break; 502 case ERROR: 503 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 504 break; 505 case OACK: 506 printf("<"); 507 oack(tp, n, 1); 508 printf(">\n"); 509 break; 510 } 511 } 512 513 static void 514 startclock(void) 515 { 516 (void)gettimeofday(&tstart, NULL); 517 } 518 519 static void 520 stopclock(void) 521 { 522 (void)gettimeofday(&tstop, NULL); 523 } 524 525 static void 526 printstats(const char *direction, unsigned long amount) 527 { 528 double delta; 529 530 /* compute delta in 1/10's second units */ 531 delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) - 532 ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000)); 533 delta = delta / 10.; /* back to seconds */ 534 printf("%s %lu bytes in %.1f seconds", direction, amount, delta); 535 if (verbose) 536 printf(" [%.0f bits/sec]", (amount * 8.) / delta); 537 putchar('\n'); 538 } 539 540 static void 541 printtimeout(void) 542 { 543 printf("Transfer timed out.\n"); 544 } 545 546 static void 547 oack(struct tftphdr *tp, int size, int trace) 548 { 549 int i, len, off; 550 char *opt, *val; 551 552 u_short op = ntohs(tp->th_opcode); 553 554 opt = tp->th_u.tu_stuff; 555 val = tp->th_u.tu_stuff; 556 557 if (op == RRQ || op == WRQ) { 558 len = strlen(opt) + 1; 559 opt = strchr(opt, '\0'); 560 opt++; 561 len += strlen(opt) + 1; 562 opt = strchr(opt, '\0'); 563 opt++; 564 val = opt; 565 off = len; 566 if (trace) 567 printf(", "); 568 } else 569 off = 2; 570 571 for (i = off, len = 0; i < size - 1; i++) { 572 if (*val != '\0') { 573 val++; 574 continue; 575 } 576 /* got option and value */ 577 val++; 578 if (trace) 579 printf("%s=%s", opt, val); 580 else 581 if (oack_set(opt, val) == -1) 582 break; 583 len = strlen(val) + 1; 584 val += len; 585 opt = val; 586 i += len; 587 if (trace && i < size - 1) 588 printf(", "); 589 } 590 } 591 592 int 593 oack_set(const char *option, const char *value) 594 { 595 int i, n; 596 const char *errstr; 597 598 for (i = 0; options[i].o_type != NULL; i++) { 599 if (!strcasecmp(options[i].o_type, option)) { 600 if (i == OPT_TSIZE) { 601 /* XXX verify OACK response */ 602 } 603 if (i == OPT_TIMEOUT) { 604 /* verify OACK response */ 605 n = strtonum(value, TIMEOUT_MIN, TIMEOUT_MAX, 606 &errstr); 607 if (errstr || rexmtval != n || 608 opt_tout == 0) { 609 nak(EOPTNEG); 610 intrflag = 1; 611 return (-1); 612 } 613 /* OK */ 614 } 615 if (i == OPT_BLKSIZE) { 616 /* verify OACK response */ 617 n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX, 618 &errstr); 619 if (errstr || opt_blksize != n || 620 opt_blksize == 0) { 621 nak(EOPTNEG); 622 intrflag = 1; 623 return (-1); 624 } 625 /* OK, set option */ 626 segment_size = n; 627 packet_size = segment_size + 4; 628 } 629 } 630 } 631 632 return (1); 633 } 634