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