1 /* $OpenBSD: tftp.c,v 1.21 2007/05/11 01:47:48 ray 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.21 2007/05/11 01:47:48 ray 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 socklen_t fromlen; 139 int convert; /* true if converting crlf -> lf */ 140 int n, nfds, error, timeouts, block, size; 141 142 startclock(); /* start stat's clock */ 143 dp = r_init(); /* reset fillbuf/read-ahead code */ 144 ap = (struct tftphdr *)ackbuf; 145 file = fdopen(fd, "r"); 146 convert = !strcmp(mode, "netascii"); 147 block = 0; 148 amount = 0; 149 150 do { 151 /* read data from file */ 152 if (!block) 153 size = makerequest(WRQ, name, dp, mode) - 4; 154 else { 155 size = readit(file, &dp, convert, segment_size); 156 if (size < 0) { 157 nak(errno + 100); 158 break; 159 } 160 dp->th_opcode = htons((u_short)DATA); 161 dp->th_block = htons((u_short)block); 162 } 163 164 /* send data to server and wait for server ACK */ 165 for (timeouts = 0, error = 0; !intrflag;) { 166 if (timeouts >= maxtimeout) { 167 printtimeout(); 168 goto abort; 169 } 170 171 if (!error) { 172 if (trace) 173 tpacket("sent", dp, size + 4); 174 if (sendto(f, dp, size + 4, 0, 175 (struct sockaddr *)&peeraddr, 176 sizeof(peeraddr)) != size + 4) { 177 warn("sendto"); 178 goto abort; 179 } 180 if (block > 0) 181 read_ahead(file, convert, segment_size); 182 } 183 error = 0; 184 185 pfd[0].fd = f; 186 pfd[0].events = POLLIN; 187 nfds = poll(pfd, 1, rexmtval * 1000); 188 if (nfds == 0) { 189 timeouts += rexmtval; 190 continue; 191 } 192 if (nfds == -1) { 193 error = 1; 194 if (errno == EINTR) 195 continue; 196 warn("poll"); 197 goto abort; 198 } 199 fromlen = sizeof(from); 200 n = recvfrom(f, ackbuf, packet_size, 0, 201 (struct sockaddr *)&from, &fromlen); 202 if (n == 0) { 203 warn("recvfrom"); 204 goto abort; 205 } 206 if (n == -1) { 207 error = 1; 208 if (errno == EINTR) 209 continue; 210 warn("recvfrom"); 211 goto abort; 212 } 213 peeraddr.sin_port = from.sin_port; /* added */ 214 if (trace) 215 tpacket("received", ap, n); 216 217 ap->th_opcode = ntohs(ap->th_opcode); 218 219 if (ap->th_opcode == OACK) { 220 oack(ap, n, 0); 221 break; 222 } 223 224 ap->th_block = ntohs(ap->th_block); 225 226 if (ap->th_opcode == ERROR) { 227 printf("Error code %d: %s\n", 228 ap->th_code, ap->th_msg); 229 goto abort; 230 } 231 if (ap->th_opcode == ACK) { 232 int j; 233 if (ap->th_block == block) 234 break; 235 /* re-synchronize with other side */ 236 j = synchnet(f); 237 if (j && trace) 238 printf("discarded %d packets\n", j); 239 if (ap->th_block == (block - 1)) 240 continue; 241 } 242 error = 1; /* received packet does not match */ 243 } 244 245 if (block > 0) 246 amount += size; 247 block++; 248 } while ((size == segment_size || block == 1) && !intrflag); 249 250 abort: 251 fclose(file); 252 stopclock(); 253 if (amount > 0) { 254 if (intrflag) 255 putchar('\n'); 256 printstats("Sent", amount); 257 } 258 } 259 260 /* 261 * Receive a file. 262 */ 263 void 264 recvfile(int fd, char *name, char *mode) 265 { 266 struct tftphdr *dp, *ap; /* data and ack packets */ 267 struct sockaddr_in from; 268 struct pollfd pfd[1]; 269 unsigned long amount; 270 socklen_t fromlen; 271 int convert; /* true if converting crlf -> lf */ 272 int n, nfds, error, timeouts, block, size; 273 int firsttrip; 274 275 startclock(); /* start stat's clock */ 276 dp = w_init(); /* reset fillbuf/read-ahead code */ 277 ap = (struct tftphdr *)ackbuf; 278 file = fdopen(fd, "w"); 279 convert = !strcmp(mode, "netascii"); 280 n = 0; 281 block = 1; 282 amount = 0; 283 firsttrip = 1; 284 285 options: 286 do { 287 /* create new ACK packet */ 288 if (firsttrip) { 289 size = makerequest(RRQ, name, ap, mode); 290 firsttrip = 0; 291 } else { 292 ap->th_opcode = htons((u_short)ACK); 293 ap->th_block = htons((u_short)(block)); 294 size = 4; 295 block++; 296 } 297 298 /* send ACK to server and wait for server data */ 299 for (timeouts = 0, error = 0; !intrflag;) { 300 if (timeouts >= maxtimeout) { 301 printtimeout(); 302 goto abort; 303 } 304 305 if (!error) { 306 if (trace) 307 tpacket("sent", ap, size); 308 if (sendto(f, ackbuf, size, 0, 309 (struct sockaddr *)&peeraddr, 310 sizeof(peeraddr)) != size) { 311 warn("sendto"); 312 goto abort; 313 } 314 write_behind(file, convert); 315 } 316 error = 0; 317 318 pfd[0].fd = f; 319 pfd[0].events = POLLIN; 320 nfds = poll(pfd, 1, rexmtval * 1000); 321 if (nfds == 0) { 322 timeouts += rexmtval; 323 continue; 324 } 325 if (nfds == -1) { 326 error = 1; 327 if (errno == EINTR) 328 continue; 329 warn("poll"); 330 goto abort; 331 } 332 fromlen = sizeof(from); 333 n = recvfrom(f, dp, packet_size, 0, 334 (struct sockaddr *)&from, &fromlen); 335 if (n == 0) { 336 warn("recvfrom"); 337 goto abort; 338 } 339 if (n == -1) { 340 error = 1; 341 if (errno == EINTR) 342 continue; 343 warn("recvfrom"); 344 goto abort; 345 } 346 peeraddr.sin_port = from.sin_port; /* added */ 347 if (trace) 348 tpacket("received", dp, n); 349 350 dp->th_opcode = ntohs(dp->th_opcode); 351 352 if (dp->th_opcode == OACK) { 353 oack(dp, n, 0); 354 block = 0; 355 goto options; 356 } 357 358 dp->th_block = ntohs(dp->th_block); 359 360 if (dp->th_opcode == ERROR) { 361 printf("Error code %d: %s\n", 362 dp->th_code, dp->th_msg); 363 goto abort; 364 } 365 if (dp->th_opcode == DATA) { 366 int j; 367 if (dp->th_block == block) 368 break; 369 /* re-synchronize with other side */ 370 j = synchnet(f); 371 if (j && trace) 372 printf("discarded %d packets\n", j); 373 if (dp->th_block == (block - 1)) 374 continue; 375 } 376 error = 1; /* received packet does not match */ 377 } 378 379 /* write data to file */ 380 size = writeit(file, &dp, n - 4, convert); 381 if (size < 0) { 382 nak(errno + 100); 383 break; 384 } 385 amount += size; 386 } while (size == segment_size && !intrflag); 387 388 abort: 389 /* ok to ack, since user has seen err msg */ 390 ap->th_opcode = htons((u_short)ACK); 391 ap->th_block = htons((u_short)block); 392 (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 393 sizeof(peeraddr)); 394 write_behind(file, convert); /* flush last buffer */ 395 396 fclose(file); 397 stopclock(); 398 if (amount > 0) { 399 if (intrflag) 400 putchar('\n'); 401 printstats("Received", amount); 402 } 403 } 404 405 static int 406 makerequest(int request, const char *name, struct tftphdr *tp, 407 const char *mode) 408 { 409 char *cp; 410 int len, pktlen; 411 off_t fsize = 0; 412 struct stat st; 413 414 tp->th_opcode = htons((u_short)request); 415 cp = tp->th_stuff; 416 pktlen = packet_size - offsetof(struct tftphdr, th_stuff); 417 len = strlen(name) + 1; 418 strlcpy(cp, name, pktlen); 419 strlcpy(cp + len, mode, pktlen - len); 420 len += strlen(mode) + 1; 421 422 if (opt_tsize) { 423 if (request == WRQ) { 424 stat(name, &st); 425 fsize = st.st_size; 426 } 427 len += snprintf(cp + len, pktlen - len, "%s%c%lld%c", 428 options[OPT_TSIZE].o_type, 0, fsize, 0); 429 } 430 if (opt_tout) 431 len += snprintf(cp + len, pktlen - len, "%s%c%d%c", 432 options[OPT_TIMEOUT].o_type, 0, rexmtval, 0); 433 if (opt_blksize) 434 len += snprintf(cp + len, pktlen - len, "%s%c%d%c", 435 options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0); 436 437 return (cp + len - (char *)tp); 438 } 439 440 /* 441 * Send a nak packet (error message). 442 * Error code passed in is one of the 443 * standard TFTP codes, or a UNIX errno 444 * offset by 100. 445 */ 446 static void 447 nak(int error) 448 { 449 struct errmsg *pe; 450 struct tftphdr *tp; 451 int length; 452 453 tp = (struct tftphdr *)ackbuf; 454 tp->th_opcode = htons((u_short)ERROR); 455 tp->th_code = htons((u_short)error); 456 for (pe = errmsgs; pe->e_code >= 0; pe++) 457 if (pe->e_code == error) 458 break; 459 if (pe->e_code < 0) { 460 pe->e_msg = strerror(error - 100); 461 tp->th_code = EUNDEF; 462 } 463 length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5; 464 if (length > packet_size) 465 length = packet_size; 466 if (trace) 467 tpacket("sent", tp, length); 468 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 469 sizeof(peeraddr)) != length) 470 warn("nak"); 471 } 472 473 static void 474 tpacket(const char *s, struct tftphdr *tp, int n) 475 { 476 char *cp, *file; 477 static char *opcodes[] = 478 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; 479 480 u_short op = ntohs(tp->th_opcode); 481 482 if (op < RRQ || op > OACK) 483 printf("%s opcode=%x ", s, op); 484 else 485 printf("%s %s ", s, opcodes[op]); 486 487 switch (op) { 488 case RRQ: 489 case WRQ: 490 n -= 2; 491 file = cp = tp->th_stuff; 492 cp = strchr(cp, '\0'); 493 printf("<file=%s, mode=%s", file, cp + 1); 494 if (has_options) 495 oack(tp, n, 1); 496 printf(">\n"); 497 break; 498 case DATA: 499 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 500 break; 501 case ACK: 502 printf("<block=%d>\n", ntohs(tp->th_block)); 503 break; 504 case ERROR: 505 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 506 break; 507 case OACK: 508 printf("<"); 509 oack(tp, n, 1); 510 printf(">\n"); 511 break; 512 } 513 } 514 515 static void 516 startclock(void) 517 { 518 (void)gettimeofday(&tstart, NULL); 519 } 520 521 static void 522 stopclock(void) 523 { 524 (void)gettimeofday(&tstop, NULL); 525 } 526 527 static void 528 printstats(const char *direction, unsigned long amount) 529 { 530 double delta; 531 532 /* compute delta in 1/10's second units */ 533 delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) - 534 ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000)); 535 delta = delta / 10.; /* back to seconds */ 536 printf("%s %lu bytes in %.1f seconds", direction, amount, delta); 537 if (verbose) 538 printf(" [%.0f bits/sec]", (amount * 8.) / delta); 539 putchar('\n'); 540 } 541 542 static void 543 printtimeout(void) 544 { 545 printf("Transfer timed out.\n"); 546 } 547 548 static void 549 oack(struct tftphdr *tp, int size, int trace) 550 { 551 int i, len, off; 552 char *opt, *val; 553 554 u_short op = ntohs(tp->th_opcode); 555 556 opt = tp->th_u.tu_stuff; 557 val = tp->th_u.tu_stuff; 558 559 if (op == RRQ || op == WRQ) { 560 len = strlen(opt) + 1; 561 opt = strchr(opt, '\0'); 562 opt++; 563 len += strlen(opt) + 1; 564 opt = strchr(opt, '\0'); 565 opt++; 566 val = opt; 567 off = len; 568 if (trace) 569 printf(", "); 570 } else 571 off = 2; 572 573 for (i = off, len = 0; i < size - 1; i++) { 574 if (*val != '\0') { 575 val++; 576 continue; 577 } 578 /* got option and value */ 579 val++; 580 if (trace) 581 printf("%s=%s", opt, val); 582 else 583 if (oack_set(opt, val) == -1) 584 break; 585 len = strlen(val) + 1; 586 val += len; 587 opt = val; 588 i += len; 589 if (trace && i < size - 1) 590 printf(", "); 591 } 592 } 593 594 int 595 oack_set(const char *option, const char *value) 596 { 597 int i, n; 598 const char *errstr; 599 600 for (i = 0; options[i].o_type != NULL; i++) { 601 if (!strcasecmp(options[i].o_type, option)) { 602 if (i == OPT_TSIZE) { 603 /* XXX verify OACK response */ 604 } 605 if (i == OPT_TIMEOUT) { 606 /* verify OACK response */ 607 n = strtonum(value, TIMEOUT_MIN, TIMEOUT_MAX, 608 &errstr); 609 if (errstr || rexmtval != n || 610 opt_tout == 0) { 611 nak(EOPTNEG); 612 intrflag = 1; 613 return (-1); 614 } 615 /* OK */ 616 } 617 if (i == OPT_BLKSIZE) { 618 /* verify OACK response */ 619 n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX, 620 &errstr); 621 if (errstr || opt_blksize != n || 622 opt_blksize == 0) { 623 nak(EOPTNEG); 624 intrflag = 1; 625 return (-1); 626 } 627 /* OK, set option */ 628 segment_size = n; 629 packet_size = segment_size + 4; 630 } 631 } 632 } 633 634 return (1); 635 } 636