|
|
1.1 ! root 1: /****************************************************************************** ! 2: * Copyright (c) 2004, 2008 IBM Corporation ! 3: * All rights reserved. ! 4: * This program and the accompanying materials ! 5: * are made available under the terms of the BSD License ! 6: * which accompanies this distribution, and is available at ! 7: * http://www.opensource.org/licenses/bsd-license.php ! 8: * ! 9: * Contributors: ! 10: * IBM Corporation - initial implementation ! 11: *****************************************************************************/ ! 12: ! 13: #include <tftp.h> ! 14: #include <stdio.h> ! 15: #include <stdlib.h> ! 16: #include <string.h> ! 17: #include <time.h> ! 18: #include <sys/socket.h> ! 19: ! 20: #include <ethernet.h> ! 21: #include <ipv4.h> ! 22: //#include <ipv6.h> ! 23: #include <udp.h> ! 24: ! 25: //#define __DEBUG__ ! 26: ! 27: #define MAX_BLOCKSIZE 1428 ! 28: #define BUFFER_LEN 2048 ! 29: #define ACK_BUFFER_LEN 256 ! 30: #define READ_BUFFER_LEN 256 ! 31: ! 32: #define ENOTFOUND 1 ! 33: #define EACCESS 2 ! 34: #define EBADOP 4 ! 35: #define EBADID 5 ! 36: #define ENOUSER 7 ! 37: //#define EUNDEF 0 ! 38: //#define ENOSPACE 3 ! 39: //#define EEXISTS 6 ! 40: ! 41: #define RRQ 1 ! 42: #define WRQ 2 ! 43: #define DATA 3 ! 44: #define ACK 4 ! 45: #define ERROR 5 ! 46: #define OACK 6 ! 47: ! 48: /* Local variables */ ! 49: static unsigned char *buffer = NULL; ! 50: static unsigned short block = 0; ! 51: static unsigned short blocksize; ! 52: static char blocksize_str[6]; /* Blocksize string for read request */ ! 53: static int received_len = 0; ! 54: static int retries = 0; ! 55: static int huge_load; ! 56: static int len; ! 57: static int tftp_finished = 0; ! 58: static int lost_packets = 0; ! 59: static int tftp_errno = 0; ! 60: static int ip_version = 0; ! 61: static short port_number = -1; ! 62: static tftp_err_t *tftp_err; ! 63: static filename_ip_t *fn_ip; ! 64: ! 65: /** ! 66: * dump_package - Prints a package. ! 67: * ! 68: * @package: package which is to print ! 69: * @len: length of the package ! 70: */ ! 71: #ifdef __DEBUG__ ! 72: ! 73: static void ! 74: dump_package(unsigned char *buffer, unsigned int len) ! 75: { ! 76: int i; ! 77: ! 78: for (i = 1; i <= len; i++) { ! 79: printf("%02x%02x ", buffer[i - 1], buffer[i]); ! 80: i++; ! 81: if ((i % 16) == 0) ! 82: printf("\n"); ! 83: } ! 84: printf("\n"); ! 85: } ! 86: #endif ! 87: ! 88: /** ! 89: * send_rrq - Sends a read request package. ! 90: */ ! 91: static void ! 92: send_rrq(void) ! 93: { ! 94: int ip_len = 0; ! 95: //int ip6_payload_len = 0; ! 96: unsigned short udp_len = 0; ! 97: unsigned char mode[] = "octet"; ! 98: unsigned char packet[READ_BUFFER_LEN]; ! 99: char *ptr = NULL; ! 100: struct iphdr *ip = NULL; ! 101: //struct ip6hdr *ip6 = NULL; ! 102: struct udphdr *udph = NULL; ! 103: struct tftphdr *tftp = NULL; ! 104: ! 105: memset(packet, 0, READ_BUFFER_LEN); ! 106: ! 107: if (4 == ip_version) { ! 108: ip = (struct iphdr *) packet; ! 109: udph = (struct udphdr *) (ip + 1); ! 110: ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) ! 111: + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ! 112: + strlen("blksize") + strlen(blocksize_str) + 2; ! 113: fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, ! 114: fn_ip->server_ip); ! 115: } ! 116: /* ! 117: else if (6 == ip_version) { ! 118: ip6 = (struct ip6hdr *) packet; ! 119: udph = (struct udphdr *) (ip6 + 1); ! 120: ip6_payload_len = sizeof(struct udphdr) ! 121: + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ! 122: + strlen("blksize") + strlen(blocksize_str) + 2; ! 123: ip_len = sizeof(struct ip6hdr) + ip6_payload_len; ! 124: fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), ! 125: &(fn_ip->server_ip6)); ! 126: ! 127: } ! 128: */ ! 129: udp_len = htons(sizeof(struct udphdr) ! 130: + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ! 131: + strlen("blksize") + strlen(blocksize_str) + 2); ! 132: fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69)); ! 133: ! 134: tftp = (struct tftphdr *) (udph + 1); ! 135: tftp->th_opcode = htons(RRQ); ! 136: ! 137: ptr = (char *) &tftp->th_data; ! 138: memcpy(ptr, fn_ip->filename, strlen((char *) fn_ip->filename) + 1); ! 139: ! 140: ptr += strlen((char *) fn_ip->filename) + 1; ! 141: memcpy(ptr, mode, strlen((char *) mode) + 1); ! 142: ! 143: ptr += strlen((char *) mode) + 1; ! 144: memcpy(ptr, "blksize", strlen("blksize") + 1); ! 145: ! 146: ptr += strlen("blksize") + 1; ! 147: memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1); ! 148: ! 149: send_ip (packet, ip_len); ! 150: ! 151: #ifdef __DEBUG__ ! 152: printf("tftp RRQ with %d bytes transmitted.\n", ip_len); ! 153: #endif ! 154: return; ! 155: } ! 156: ! 157: /** ! 158: * send_ack - Sends a acknowlege package. ! 159: * ! 160: * @blckno: block number ! 161: * @dport: UDP destination port ! 162: */ ! 163: static void ! 164: send_ack(int blckno, unsigned short dport) ! 165: { ! 166: int ip_len = 0; ! 167: //int ip6_payload_len = 0; ! 168: unsigned short udp_len = 0; ! 169: unsigned char packet[ACK_BUFFER_LEN]; ! 170: struct iphdr *ip = NULL; ! 171: //struct ip6hdr *ip6 = NULL; ! 172: struct udphdr *udph = NULL; ! 173: struct tftphdr *tftp = NULL; ! 174: ! 175: memset(packet, 0, ACK_BUFFER_LEN); ! 176: ! 177: if (4 == ip_version) { ! 178: ip = (struct iphdr *) packet; ! 179: udph = (struct udphdr *) (ip + 1); ! 180: ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4; ! 181: fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, ! 182: fn_ip->server_ip); ! 183: } ! 184: /* ! 185: else if (6 == ip_version) { ! 186: ip6 = (struct ip6hdr *) packet; ! 187: udph = (struct udphdr *) (ip6 + 1); ! 188: ip6_payload_len = sizeof(struct udphdr) + 4; ! 189: ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + ! 190: ip6_payload_len; ! 191: fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), ! 192: &(fn_ip->server_ip6)); ! 193: } ! 194: */ ! 195: udp_len = htons(sizeof(struct udphdr) + 4); ! 196: fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); ! 197: ! 198: tftp = (struct tftphdr *) (udph + 1); ! 199: tftp->th_opcode = htons(ACK); ! 200: tftp->th_data = htons(blckno); ! 201: ! 202: send_ip(packet, ip_len); ! 203: ! 204: #ifdef __DEBUG__ ! 205: printf("tftp ACK %d bytes transmitted.\n", ip_len); ! 206: #endif ! 207: ! 208: return; ! 209: } ! 210: ! 211: /** ! 212: * send_error - Sends an error package. ! 213: * ! 214: * @error_code: Used sub code for error packet ! 215: * @dport: UDP destination port ! 216: */ ! 217: static void ! 218: send_error(int error_code, unsigned short dport) ! 219: { ! 220: int ip_len = 0; ! 221: //int ip6_payload_len = 0; ! 222: unsigned short udp_len = 0; ! 223: unsigned char packet[256]; ! 224: //struct ip6hdr *ip6 = NULL; ! 225: struct iphdr *ip = NULL; ! 226: struct udphdr *udph = NULL; ! 227: struct tftphdr *tftp = NULL; ! 228: ! 229: memset(packet, 0, 256); ! 230: ! 231: if (4 == ip_version) { ! 232: ip = (struct iphdr *) packet; ! 233: udph = (struct udphdr *) (ip + 1); ! 234: ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5; ! 235: fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, ! 236: fn_ip->server_ip); ! 237: } ! 238: /* ! 239: else if (6 == ip_version) { ! 240: ip6 = (struct ip6hdr *) packet; ! 241: udph = (struct udphdr *) (ip6 + 1); ! 242: ip6_payload_len = sizeof(struct udphdr) + 5; ! 243: ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + ! 244: ip6_payload_len; ! 245: fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), ! 246: &(fn_ip->server_ip6)); ! 247: } ! 248: */ ! 249: udp_len = htons(sizeof(struct udphdr) + 5); ! 250: fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); ! 251: ! 252: tftp = (struct tftphdr *) (udph + 1); ! 253: tftp->th_opcode = htons(ERROR); ! 254: tftp->th_data = htons(error_code); ! 255: ((char *) &tftp->th_data)[2] = 0; ! 256: ! 257: send_ip(packet, ip_len); ! 258: ! 259: #ifdef __DEBUG__ ! 260: printf("tftp ERROR %d bytes transmitted.\n", ip_len); ! 261: #endif ! 262: ! 263: return; ! 264: } ! 265: ! 266: static void ! 267: print_progress(int urgent, int received_bytes) ! 268: { ! 269: static unsigned int i = 1; ! 270: static int first = -1; ! 271: static int last_bytes = 0; ! 272: char buffer[100]; ! 273: char *ptr; ! 274: ! 275: // 1MB steps or 0x400 times or urgent ! 276: if(((received_bytes - last_bytes) >> 20) > 0 ! 277: || (i & 0x3FF) == 0 || urgent) { ! 278: if(!first) { ! 279: sprintf(buffer, "%d KBytes", (last_bytes >> 10)); ! 280: for(ptr = buffer; *ptr != 0; ++ptr) ! 281: *ptr = '\b'; ! 282: printf(buffer); ! 283: } ! 284: printf("%d KBytes", (received_bytes >> 10)); ! 285: i = 1; ! 286: first = 0; ! 287: last_bytes = received_bytes; ! 288: } ! 289: ++i; ! 290: } ! 291: ! 292: /** ! 293: * get_blksize tries to extract the blksize from the OACK package ! 294: * the TFTP returned. From RFC 1782 ! 295: * The OACK packet has the following format: ! 296: * ! 297: * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ ! 298: * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | ! 299: * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ ! 300: * ! 301: * @param buffer the network packet ! 302: * @param len the length of the network packet ! 303: * @return the blocksize the server supports or 0 for error ! 304: */ ! 305: static int ! 306: get_blksize(unsigned char *buffer, unsigned int len) ! 307: { ! 308: unsigned char *orig = buffer; ! 309: /* skip all headers until tftp has been reached */ ! 310: buffer += sizeof(struct udphdr); ! 311: /* skip opc */ ! 312: buffer += 2; ! 313: while (buffer < orig + len) { ! 314: if (!memcmp(buffer, "blksize", strlen("blksize") + 1)) ! 315: return (unsigned short) strtoul((char *) (buffer + ! 316: strlen("blksize") + 1), ! 317: (char **) NULL, 10); ! 318: else { ! 319: /* skip the option name */ ! 320: buffer = (unsigned char *) strchr((char *) buffer, 0); ! 321: if (!buffer) ! 322: return 0; ! 323: buffer++; ! 324: /* skip the option value */ ! 325: buffer = (unsigned char *) strchr((char *) buffer, 0); ! 326: if (!buffer) ! 327: return 0; ! 328: buffer++; ! 329: } ! 330: } ! 331: return 0; ! 332: } ! 333: ! 334: /** ! 335: * Handle incoming tftp packets after read request was sent ! 336: * ! 337: * this function also prints out some status characters ! 338: * \|-/ for each packet received ! 339: * A for an arp packet ! 340: * I for an ICMP packet ! 341: * #+* for different unexpected TFTP packets (not very good) ! 342: * ! 343: * @param packet points to the UDP header of the packet ! 344: * @param len the length of the network packet ! 345: * @return ZERO if packet was handled successfully ! 346: * ERRORCODE if error occurred ! 347: */ ! 348: int32_t ! 349: handle_tftp(uint8_t *packet, int32_t packetsize) ! 350: { ! 351: struct udphdr *udph; ! 352: struct tftphdr *tftp; ! 353: ! 354: /* buffer is only set if we are handling TFTP */ ! 355: if (buffer == NULL ) ! 356: return 0; ! 357: ! 358: #ifndef __DEBUG__ ! 359: print_progress(0, received_len); ! 360: #endif ! 361: udph = (struct udphdr *) packet; ! 362: tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr)); ! 363: set_timer(TICKS_SEC); ! 364: ! 365: #ifdef __DEBUG__ ! 366: dump_package(packet, packetsize); ! 367: #endif ! 368: ! 369: port_number = udph->uh_sport; ! 370: if (tftp->th_opcode == htons(OACK)) { ! 371: /* an OACK means that the server answers our blocksize request */ ! 372: blocksize = get_blksize(packet, packetsize); ! 373: if (!blocksize || blocksize > MAX_BLOCKSIZE) { ! 374: send_error(8, port_number); ! 375: tftp_errno = -8; ! 376: goto error; ! 377: } ! 378: send_ack(0, port_number); ! 379: } else if (tftp->th_opcode == htons(ACK)) { ! 380: /* an ACK means that the server did not answers ! 381: * our blocksize request, therefore we will set the blocksize ! 382: * to the default value of 512 */ ! 383: blocksize = 512; ! 384: send_ack(0, port_number); ! 385: } else if ((unsigned char) tftp->th_opcode == ERROR) { ! 386: #ifdef __DEBUG__ ! 387: printf("tftp->th_opcode : %x\n", tftp->th_opcode); ! 388: printf("tftp->th_data : %x\n", tftp->th_data); ! 389: #endif ! 390: switch ( (uint8_t) tftp->th_data) { ! 391: case ENOTFOUND: ! 392: tftp_errno = -3; // ERROR: file not found ! 393: break; ! 394: case EACCESS: ! 395: tftp_errno = -4; // ERROR: access violation ! 396: break; ! 397: case EBADOP: ! 398: tftp_errno = -5; // ERROR: illegal TFTP operation ! 399: break; ! 400: case EBADID: ! 401: tftp_errno = -6; // ERROR: unknown transfer ID ! 402: break; ! 403: case ENOUSER: ! 404: tftp_errno = -7; // ERROR: no such user ! 405: break; ! 406: default: ! 407: tftp_errno = -1; // ERROR: unknown error ! 408: } ! 409: goto error; ! 410: } else if (tftp->th_opcode == DATA) { ! 411: /* DATA PACKAGE */ ! 412: if (block + 1 == tftp->th_data) { ! 413: ++block; ! 414: } ! 415: else if( block == 0xffff && huge_load != 0 ! 416: && (tftp->th_data == 0 || tftp->th_data == 1) ) { ! 417: block = tftp->th_data; ! 418: } ! 419: else if (tftp->th_data == block) { ! 420: #ifdef __DEBUG__ ! 421: printf ! 422: ("\nTFTP: Received block %x, expected block was %x\n", ! 423: tftp->th_data, block + 1); ! 424: printf("\b+ "); ! 425: #endif ! 426: send_ack(tftp->th_data, port_number); ! 427: lost_packets++; ! 428: tftp_err->bad_tftp_packets++; ! 429: return 0; ! 430: } else if (tftp->th_data < block) { ! 431: #ifdef __DEBUG__ ! 432: printf ! 433: ("\nTFTP: Received block %x, expected block was %x\n", ! 434: tftp->th_data, block + 1); ! 435: printf("\b* "); ! 436: #endif ! 437: /* This means that an old data packet appears (again); ! 438: * this happens sometimes if we don't answer fast enough ! 439: * and a timeout is generated on the server side; ! 440: * as we already have this packet we just ignore it */ ! 441: tftp_err->bad_tftp_packets++; ! 442: return 0; ! 443: } else { ! 444: tftp_err->blocks_missed = block + 1; ! 445: tftp_err->blocks_received = tftp->th_data; ! 446: tftp_errno = -42; ! 447: goto error; ! 448: } ! 449: tftp_err->bad_tftp_packets = 0; ! 450: /* check if our buffer is large enough */ ! 451: if (received_len + udph->uh_ulen - 12 > len) { ! 452: tftp_errno = -2; ! 453: goto error; ! 454: } ! 455: memcpy(buffer + received_len, &tftp->th_data + 1, ! 456: udph->uh_ulen - 12); ! 457: send_ack(tftp->th_data, port_number); ! 458: received_len += udph->uh_ulen - 12; ! 459: /* Last packet reached if the payload of the UDP packet ! 460: * is smaller than blocksize + 12 ! 461: * 12 = UDP header (8) + 4 bytes TFTP payload */ ! 462: if (udph->uh_ulen < blocksize + 12) { ! 463: tftp_finished = 1; ! 464: return 0; ! 465: } ! 466: /* 0xffff is the highest block number possible ! 467: * see the TFTP RFCs */ ! 468: ! 469: if (block >= 0xffff && huge_load == 0) { ! 470: tftp_errno = -9; ! 471: goto error; ! 472: } ! 473: } else { ! 474: #ifdef __DEBUG__ ! 475: printf("Unknown packet %x\n", tftp->th_opcode); ! 476: printf("\b# "); ! 477: #endif ! 478: tftp_err->bad_tftp_packets++; ! 479: return 0; ! 480: } ! 481: ! 482: return 0; ! 483: ! 484: error: ! 485: #ifdef __DEBUG__ ! 486: printf("\nTFTP errno: %d\n", tftp_errno); ! 487: #endif ! 488: tftp_finished = 1; ! 489: return tftp_errno; ! 490: } ! 491: ! 492: /** ! 493: * TFTP: This function handles situation when "Destination unreachable" ! 494: * ICMP-error occurs during sending TFTP-packet. ! 495: * ! 496: * @param err_code Error Code (e.g. "Host unreachable") ! 497: */ ! 498: void ! 499: handle_tftp_dun(uint8_t err_code) ! 500: { ! 501: tftp_errno = - err_code - 10; ! 502: tftp_finished = 1; ! 503: } ! 504: ! 505: /** ! 506: * TFTP: Interface function to load files via TFTP. ! 507: * ! 508: * @param _fn_ip contains the following configuration information: ! 509: * client IP, TFTP-server IP, filename to be loaded ! 510: * @param _buffer destination buffer for the file ! 511: * @param _len size of destination buffer ! 512: * @param _retries max number of retries ! 513: * @param _tftp_err contains info about TFTP-errors (e.g. lost packets) ! 514: * @param _mode NON ZERO - multicast, ZERO - unicast ! 515: * @param _blocksize blocksize for DATA-packets ! 516: * @return ZERO - error condition occurs ! 517: * NON ZERO - size of received file ! 518: */ ! 519: int ! 520: tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, ! 521: unsigned int _retries, tftp_err_t * _tftp_err, ! 522: int32_t _mode, int32_t _blocksize, int _ip_version) ! 523: { ! 524: retries = _retries; ! 525: fn_ip = _fn_ip; ! 526: len = _len; ! 527: huge_load = _mode; ! 528: ip_version = _ip_version; ! 529: tftp_errno = 0; ! 530: tftp_err = _tftp_err; ! 531: tftp_err->bad_tftp_packets = 0; ! 532: tftp_err->no_packets = 0; ! 533: ! 534: /* Default blocksize must be 512 for TFTP servers ! 535: * which do not support the RRQ blocksize option */ ! 536: blocksize = 512; ! 537: ! 538: /* Prefered blocksize - used as option for the read request */ ! 539: if (_blocksize < 8) ! 540: _blocksize = 8; ! 541: else if (_blocksize > MAX_BLOCKSIZE) ! 542: _blocksize = MAX_BLOCKSIZE; ! 543: sprintf(blocksize_str, "%d", _blocksize); ! 544: ! 545: printf(" Receiving data: "); ! 546: print_progress(-1, 0); ! 547: ! 548: // Setting buffer to a non-zero address enabled handling of received TFTP packets. ! 549: buffer = _buffer; ! 550: ! 551: set_timer(TICKS_SEC); ! 552: send_rrq(); ! 553: ! 554: while (! tftp_finished) { ! 555: /* if timeout (no packet received) */ ! 556: if(get_timer() <= 0) { ! 557: /* the server doesn't seem to retry let's help out a bit */ ! 558: if (tftp_err->no_packets > 4 && port_number != -1 ! 559: && block > 1) ! 560: send_ack(block, port_number); ! 561: tftp_err->no_packets++; ! 562: set_timer(TICKS_SEC); ! 563: } ! 564: ! 565: /* handle received packets */ ! 566: receive_ether(); ! 567: ! 568: /* bad_tftp_packets are counted whenever we receive a TFTP packet ! 569: * which was not expected; if this gets larger than 'retries' ! 570: * we just exit */ ! 571: if (tftp_err->bad_tftp_packets > retries) { ! 572: tftp_errno = -40; ! 573: break; ! 574: } ! 575: ! 576: /* no_packets counts the times we have returned from receive_ether() ! 577: * without any packet received; if this gets larger than 'retries' ! 578: * we also just exit */ ! 579: if (tftp_err->no_packets > retries) { ! 580: tftp_errno = -41; ! 581: break; ! 582: } ! 583: } ! 584: ! 585: // Setting buffer to NULL disables handling of received TFTP packets. ! 586: buffer = NULL; ! 587: ! 588: if (tftp_errno) ! 589: return tftp_errno; ! 590: ! 591: print_progress(-1, received_len); ! 592: printf("\n"); ! 593: if (lost_packets) ! 594: printf("Lost ACK packets: %d\n", lost_packets); ! 595: ! 596: return received_len; ! 597: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.