| /****************************************************************************** |
| * Copyright (c) 2004, 2008 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| |
| #include <tftp.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <sys/socket.h> |
| |
| #include <ethernet.h> |
| #include <ipv4.h> |
| #include <ipv6.h> |
| #include <udp.h> |
| #include <dns.h> |
| |
| //#define __DEBUG__ |
| |
| #define MAX_BLOCKSIZE 1428 |
| #define BUFFER_LEN 256 |
| #define INVALID_BUFFER ((void *)-1L) |
| |
| #define ENOTFOUND 1 |
| #define EACCESS 2 |
| #define EBADOP 4 |
| #define EBADID 5 |
| #define ENOUSER 7 |
| //#define EUNDEF 0 |
| //#define ENOSPACE 3 |
| //#define EEXISTS 6 |
| |
| #define RRQ 1 |
| #define WRQ 2 |
| #define DATA 3 |
| #define ACK 4 |
| #define ERROR 5 |
| #define OACK 6 |
| |
| /* Local variables */ |
| static unsigned char packet[BUFFER_LEN]; |
| static unsigned char *buffer = INVALID_BUFFER; |
| static unsigned short block; |
| static unsigned short blocksize; |
| static char blocksize_str[6]; /* Blocksize string for read request */ |
| static int received_len; |
| static unsigned int retries; |
| static int huge_load; |
| static int len; |
| static int tftp_finished; |
| static int lost_packets; |
| static int tftp_errno; |
| static int ip_version; |
| static short port_number; |
| static tftp_err_t *tftp_err; |
| static filename_ip_t *fn_ip; |
| static int progress_first; |
| static int progress_last_bytes; |
| |
| /** |
| * dump_package - Prints a package. |
| * |
| * @package: package which is to print |
| * @len: length of the package |
| */ |
| #ifdef __DEBUG__ |
| |
| static void dump_package(unsigned char *buffer, unsigned int len) |
| { |
| int i; |
| |
| for (i = 1; i <= len; i++) { |
| printf("%02x%02x ", buffer[i - 1], buffer[i]); |
| i++; |
| if ((i % 16) == 0) |
| printf("\n"); |
| } |
| printf("\n"); |
| } |
| #endif |
| |
| /** |
| * send_rrq - Sends a read request package. |
| * |
| * @fd: Socket Descriptor |
| */ |
| static void send_rrq(int fd) |
| { |
| int ip_len = 0; |
| int ip6_payload_len = 0; |
| unsigned short udp_len = 0; |
| const char mode[] = "octet"; |
| char *ptr = NULL; |
| struct iphdr *ip = NULL; |
| struct ip6hdr *ip6 = NULL; |
| struct udphdr *udph = NULL; |
| struct tftphdr *tftp = NULL; |
| |
| memset(packet, 0, BUFFER_LEN); |
| |
| if (4 == ip_version) { |
| ip = (struct iphdr *) packet; |
| udph = (struct udphdr *) (ip + 1); |
| ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) |
| + strlen(fn_ip->filename) + strlen(mode) + 4 |
| + strlen("blksize") + strlen(blocksize_str) + 2; |
| fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, |
| fn_ip->server_ip); |
| } |
| else if (6 == ip_version) { |
| ip6 = (struct ip6hdr *) packet; |
| udph = (struct udphdr *) (ip6 + 1); |
| ip6_payload_len = sizeof(struct udphdr) |
| + strlen(fn_ip->filename) + strlen(mode) + 4 |
| + strlen("blksize") + strlen(blocksize_str) + 2; |
| ip_len = sizeof(struct ip6hdr) + ip6_payload_len; |
| fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), |
| &(fn_ip->server_ip6)); |
| |
| } |
| udp_len = htons(sizeof(struct udphdr) |
| + strlen(fn_ip->filename) + strlen(mode) + 4 |
| + strlen("blksize") + strlen(blocksize_str) + 2); |
| fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69)); |
| |
| tftp = (struct tftphdr *) (udph + 1); |
| tftp->th_opcode = htons(RRQ); |
| |
| ptr = (char *) &tftp->th_data; |
| memcpy(ptr, fn_ip->filename, strlen(fn_ip->filename) + 1); |
| |
| ptr += strlen(fn_ip->filename) + 1; |
| memcpy(ptr, mode, strlen(mode) + 1); |
| |
| ptr += strlen(mode) + 1; |
| memcpy(ptr, "blksize", strlen("blksize") + 1); |
| |
| ptr += strlen("blksize") + 1; |
| memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1); |
| |
| send_ip (fd, packet, ip_len); |
| |
| #ifdef __DEBUG__ |
| printf("tftp RRQ with %d bytes transmitted.\n", ip_len); |
| #endif |
| return; |
| } |
| |
| /** |
| * send_ack - Sends a acknowlege package. |
| * |
| * @blckno: block number |
| * @dport: UDP destination port |
| */ |
| static void send_ack(int fd, int blckno, unsigned short dport) |
| { |
| int ip_len = 0; |
| int ip6_payload_len = 0; |
| unsigned short udp_len = 0; |
| struct iphdr *ip = NULL; |
| struct ip6hdr *ip6 = NULL; |
| struct udphdr *udph = NULL; |
| struct tftphdr *tftp = NULL; |
| |
| memset(packet, 0, BUFFER_LEN); |
| |
| if (4 == ip_version) { |
| ip = (struct iphdr *) packet; |
| udph = (struct udphdr *) (ip + 1); |
| ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4; |
| fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, |
| fn_ip->server_ip); |
| } |
| else if (6 == ip_version) { |
| ip6 = (struct ip6hdr *) packet; |
| udph = (struct udphdr *) (ip6 + 1); |
| ip6_payload_len = sizeof(struct udphdr) + 4; |
| ip_len = sizeof(struct ip6hdr) + ip6_payload_len; |
| fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), |
| &(fn_ip->server_ip6)); |
| } |
| udp_len = htons(sizeof(struct udphdr) + 4); |
| fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); |
| |
| tftp = (struct tftphdr *) (udph + 1); |
| tftp->th_opcode = htons(ACK); |
| tftp->th_data = htons(blckno); |
| |
| send_ip(fd, packet, ip_len); |
| |
| #ifdef __DEBUG__ |
| printf("tftp ACK %d bytes transmitted.\n", ip_len); |
| #endif |
| |
| return; |
| } |
| |
| /** |
| * send_error - Sends an error package. |
| * |
| * @fd: Socket Descriptor |
| * @error_code: Used sub code for error packet |
| * @dport: UDP destination port |
| */ |
| static void send_error(int fd, int error_code, unsigned short dport) |
| { |
| int ip_len = 0; |
| int ip6_payload_len = 0; |
| unsigned short udp_len = 0; |
| struct ip6hdr *ip6 = NULL; |
| struct iphdr *ip = NULL; |
| struct udphdr *udph = NULL; |
| struct tftphdr *tftp = NULL; |
| |
| memset(packet, 0, BUFFER_LEN); |
| |
| if (4 == ip_version) { |
| ip = (struct iphdr *) packet; |
| udph = (struct udphdr *) (ip + 1); |
| ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5; |
| fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, |
| fn_ip->server_ip); |
| } |
| else if (6 == ip_version) { |
| ip6 = (struct ip6hdr *) packet; |
| udph = (struct udphdr *) (ip6 + 1); |
| ip6_payload_len = sizeof(struct udphdr) + 5; |
| ip_len = sizeof(struct ip6hdr) + ip6_payload_len; |
| fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), |
| &(fn_ip->server_ip6)); |
| } |
| udp_len = htons(sizeof(struct udphdr) + 5); |
| fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); |
| |
| tftp = (struct tftphdr *) (udph + 1); |
| tftp->th_opcode = htons(ERROR); |
| tftp->th_data = htons(error_code); |
| ((char *) &tftp->th_data)[2] = 0; |
| |
| send_ip(fd, packet, ip_len); |
| |
| #ifdef __DEBUG__ |
| printf("tftp ERROR %d bytes transmitted.\n", ip_len); |
| #endif |
| |
| return; |
| } |
| |
| static void print_progress(int urgent, int received_bytes) |
| { |
| static unsigned int i = 1; |
| char buffer[100]; |
| char *ptr; |
| |
| // 1MB steps or 0x400 times or urgent |
| if(((received_bytes - progress_last_bytes) >> 20) > 0 |
| || (i & 0x3FF) == 0 || urgent) { |
| if (!progress_first) { |
| sprintf(buffer, "%d KBytes", (progress_last_bytes >> 10)); |
| for(ptr = buffer; *ptr != 0; ++ptr) |
| *ptr = '\b'; |
| printf("%s", buffer); |
| } |
| printf("%d KBytes", (received_bytes >> 10)); |
| i = 1; |
| progress_first = 0; |
| progress_last_bytes = received_bytes; |
| } |
| ++i; |
| } |
| |
| /** |
| * get_blksize tries to extract the blksize from the OACK package |
| * the TFTP returned. From RFC 1782 |
| * The OACK packet has the following format: |
| * |
| * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ |
| * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | |
| * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ |
| * |
| * @param buffer the network packet |
| * @param len the length of the network packet |
| * @return the blocksize the server supports or 0 for error |
| */ |
| static int get_blksize(unsigned char *buffer, unsigned int len) |
| { |
| unsigned char *orig = buffer; |
| /* skip all headers until tftp has been reached */ |
| buffer += sizeof(struct udphdr); |
| /* skip opc */ |
| buffer += 2; |
| while (buffer < orig + len) { |
| if (!memcmp(buffer, "blksize", strlen("blksize") + 1)) |
| return (unsigned short) strtoul((char *) (buffer + |
| strlen("blksize") + 1), |
| (char **) NULL, 10); |
| else { |
| /* skip the option name */ |
| buffer = (unsigned char *) strchr((char *) buffer, 0); |
| if (!buffer) |
| return 0; |
| buffer++; |
| /* skip the option value */ |
| buffer = (unsigned char *) strchr((char *) buffer, 0); |
| if (!buffer) |
| return 0; |
| buffer++; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * Handle incoming tftp packets after read request was sent |
| * |
| * this function also prints out some status characters |
| * \|-/ for each packet received |
| * A for an arp packet |
| * I for an ICMP packet |
| * #+* for different unexpected TFTP packets (not very good) |
| * |
| * @param fd socket descriptor |
| * @param packet points to the UDP header of the packet |
| * @param len the length of the network packet |
| * @return ZERO if packet was handled successfully |
| * ERRORCODE if error occurred |
| */ |
| int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) |
| { |
| struct udphdr *udph; |
| struct tftphdr *tftp; |
| |
| /* buffer is only set if we are handling TFTP */ |
| if (buffer == INVALID_BUFFER) |
| return 0; |
| |
| #ifndef __DEBUG__ |
| print_progress(0, received_len); |
| #endif |
| udph = (struct udphdr *) pkt; |
| tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr)); |
| set_timer(TICKS_SEC); |
| |
| #ifdef __DEBUG__ |
| dump_package(pkt, packetsize); |
| #endif |
| |
| port_number = udph->uh_sport; |
| if (tftp->th_opcode == htons(OACK)) { |
| /* an OACK means that the server answers our blocksize request */ |
| blocksize = get_blksize(pkt, packetsize); |
| if (!blocksize || blocksize > MAX_BLOCKSIZE) { |
| send_error(fd, 8, port_number); |
| tftp_errno = -8; |
| goto error; |
| } |
| send_ack(fd, 0, port_number); |
| } else if (tftp->th_opcode == htons(ACK)) { |
| /* an ACK means that the server did not answers |
| * our blocksize request, therefore we will set the blocksize |
| * to the default value of 512 */ |
| blocksize = 512; |
| send_ack(fd, 0, port_number); |
| } else if ((unsigned char) tftp->th_opcode == ERROR) { |
| #ifdef __DEBUG__ |
| printf("tftp->th_opcode : %x\n", tftp->th_opcode); |
| printf("tftp->th_data : %x\n", tftp->th_data); |
| #endif |
| switch ( (uint8_t) tftp->th_data) { |
| case ENOTFOUND: |
| tftp_errno = -3; // ERROR: file not found |
| break; |
| case EACCESS: |
| tftp_errno = -4; // ERROR: access violation |
| break; |
| case EBADOP: |
| tftp_errno = -5; // ERROR: illegal TFTP operation |
| break; |
| case EBADID: |
| tftp_errno = -6; // ERROR: unknown transfer ID |
| break; |
| case ENOUSER: |
| tftp_errno = -7; // ERROR: no such user |
| break; |
| default: |
| tftp_errno = -1; // ERROR: unknown error |
| } |
| goto error; |
| } else if (tftp->th_opcode == DATA) { |
| /* DATA PACKAGE */ |
| if (block + 1 == tftp->th_data) { |
| ++block; |
| } |
| else if( block == 0xffff && huge_load != 0 |
| && (tftp->th_data == 0 || tftp->th_data == 1) ) { |
| block = tftp->th_data; |
| } |
| else if (tftp->th_data == block) { |
| #ifdef __DEBUG__ |
| printf |
| ("\nTFTP: Received block %x, expected block was %x\n", |
| tftp->th_data, block + 1); |
| printf("\b+ "); |
| #endif |
| send_ack(fd, tftp->th_data, port_number); |
| lost_packets++; |
| tftp_err->bad_tftp_packets++; |
| return 0; |
| } else if (tftp->th_data < block) { |
| #ifdef __DEBUG__ |
| printf |
| ("\nTFTP: Received block %x, expected block was %x\n", |
| tftp->th_data, block + 1); |
| printf("\b* "); |
| #endif |
| /* This means that an old data packet appears (again); |
| * this happens sometimes if we don't answer fast enough |
| * and a timeout is generated on the server side; |
| * as we already have this packet we just ignore it */ |
| tftp_err->bad_tftp_packets++; |
| return 0; |
| } else { |
| tftp_err->blocks_missed = block + 1; |
| tftp_err->blocks_received = tftp->th_data; |
| tftp_errno = -42; |
| goto error; |
| } |
| tftp_err->bad_tftp_packets = 0; |
| /* check if our buffer is large enough */ |
| if (received_len + udph->uh_ulen - 12 > len) { |
| tftp_errno = -2; |
| goto error; |
| } |
| memcpy(buffer + received_len, &tftp->th_data + 1, |
| udph->uh_ulen - 12); |
| send_ack(fd, tftp->th_data, port_number); |
| received_len += udph->uh_ulen - 12; |
| /* Last packet reached if the payload of the UDP packet |
| * is smaller than blocksize + 12 |
| * 12 = UDP header (8) + 4 bytes TFTP payload */ |
| if (udph->uh_ulen < blocksize + 12) { |
| tftp_finished = 1; |
| return 0; |
| } |
| /* 0xffff is the highest block number possible |
| * see the TFTP RFCs */ |
| |
| if (block >= 0xffff && huge_load == 0) { |
| tftp_errno = -9; |
| goto error; |
| } |
| } else { |
| #ifdef __DEBUG__ |
| printf("Unknown packet %x\n", tftp->th_opcode); |
| printf("\b# "); |
| #endif |
| tftp_err->bad_tftp_packets++; |
| return 0; |
| } |
| |
| return 0; |
| |
| error: |
| #ifdef __DEBUG__ |
| printf("\nTFTP errno: %d\n", tftp_errno); |
| #endif |
| tftp_finished = 1; |
| return tftp_errno; |
| } |
| |
| /** |
| * TFTP: This function handles situation when "Destination unreachable" |
| * ICMP-error occurs during sending TFTP-packet. |
| * |
| * @param err_code Error Code (e.g. "Host unreachable") |
| */ |
| void handle_tftp_dun(uint8_t err_code) |
| { |
| tftp_errno = - err_code - 10; |
| tftp_finished = 1; |
| } |
| |
| /** |
| * TFTP: Interface function to load files via TFTP. |
| * |
| * @param _fn_ip contains the following configuration information: |
| * client IP, TFTP-server IP, filename to be loaded |
| * @param _buffer destination buffer for the file |
| * @param _len size of destination buffer |
| * @param _retries max number of retries |
| * @param _tftp_err contains info about TFTP-errors (e.g. lost packets) |
| * @return ZERO - error condition occurs |
| * NON ZERO - size of received file |
| */ |
| int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, |
| unsigned int _retries, tftp_err_t * _tftp_err) |
| { |
| retries = _retries; |
| fn_ip = _fn_ip; |
| len = _len; |
| ip_version = _fn_ip->ip_version; |
| tftp_errno = 0; |
| tftp_err = _tftp_err; |
| tftp_err->bad_tftp_packets = 0; |
| tftp_err->no_packets = 0; |
| |
| block = 0; |
| received_len = 0; |
| tftp_finished = 0; |
| lost_packets = 0; |
| port_number = -1; |
| progress_first = -1; |
| progress_last_bytes = 0; |
| huge_load = 1; |
| |
| /* Default blocksize must be 512 for TFTP servers |
| * which do not support the RRQ blocksize option */ |
| blocksize = 512; |
| |
| /* Preferred blocksize - used as option for the read request */ |
| sprintf(blocksize_str, "%d", MAX_BLOCKSIZE); |
| |
| printf(" Receiving data: "); |
| print_progress(-1, 0); |
| |
| /* Set buffer to a valid address, enables handling of received packets */ |
| buffer = _buffer; |
| |
| set_timer(TICKS_SEC); |
| send_rrq(fn_ip->fd); |
| |
| while (! tftp_finished) { |
| /* if timeout (no packet received) */ |
| if(get_timer() <= 0) { |
| /* the server doesn't seem to retry let's help out a bit */ |
| if (tftp_err->no_packets > 4 && port_number != -1 |
| && block > 1) { |
| send_ack(fn_ip->fd, block, port_number); |
| } |
| else if (port_number == -1 && block == 0 |
| && (tftp_err->no_packets&3) == 3) { |
| printf("\nRepeating TFTP read request...\n"); |
| send_rrq(fn_ip->fd); |
| } |
| tftp_err->no_packets++; |
| set_timer(TICKS_SEC); |
| } |
| |
| /* handle received packets */ |
| receive_ether(fn_ip->fd); |
| |
| /* bad_tftp_packets are counted whenever we receive a TFTP packet |
| * which was not expected; if this gets larger than 'retries' |
| * we just exit */ |
| if (tftp_err->bad_tftp_packets > retries) { |
| tftp_errno = -40; |
| break; |
| } |
| |
| /* no_packets counts the times we have returned from receive_ether() |
| * without any packet received; if this gets larger than 'retries' |
| * we also just exit */ |
| if (tftp_err->no_packets > retries) { |
| tftp_errno = -41; |
| break; |
| } |
| } |
| |
| /* Setting buffer invalid to disable handling of received packets */ |
| buffer = INVALID_BUFFER; |
| |
| if (tftp_errno) |
| return tftp_errno; |
| |
| print_progress(-1, received_len); |
| printf("\n"); |
| if (lost_packets) |
| printf("Lost ACK packets: %d\n", lost_packets); |
| |
| return received_len; |
| } |
| |
| /** |
| * Parses a tftp arguments, extracts all |
| * parameters and fills server ip according to this |
| * |
| * Parameters: |
| * @param buffer string with arguments, |
| * @param server_ip server ip as result |
| * @param filename default filename |
| * @param fd Socket descriptor |
| * @param len len of the buffer, |
| * @return 0 on SUCCESS and -1 on failure |
| */ |
| int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, |
| int len) |
| { |
| char *raw; |
| char *tmp, *tmp1; |
| int i, j = 0; |
| char domainname[256]; |
| uint8_t server_ip6[16]; |
| |
| raw = malloc(len); |
| if (raw == NULL) { |
| printf("\n unable to allocate memory, parsing failed\n"); |
| return -1; |
| } |
| strncpy(raw, (const char *)buffer, len); |
| /* tftp url contains tftp://[fd00:4f53:4444:90:214:5eff:fed9:b200]/testfile */ |
| if (strncmp(raw, "tftp://", 7)){ |
| printf("\n tftp missing in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| tmp = strchr(raw, '['); |
| if (tmp != NULL && *tmp == '[') { |
| /* check for valid ipv6 address */ |
| tmp1 = strchr(tmp, ']'); |
| if (tmp1 == NULL) { |
| printf("\n missing ] in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| i = tmp1 - tmp; |
| /* look for file name */ |
| tmp1 = strchr(tmp, '/'); |
| if (tmp1 == NULL) { |
| printf("\n missing filename in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| tmp[i] = '\0'; |
| /* check for 16 byte ipv6 address */ |
| if (!str_to_ipv6(tmp + 1, (uint8_t *)server_ip)) { |
| printf("\n wrong format IPV6 address in %s\n", raw); |
| free(raw); |
| return -1;; |
| } |
| else { |
| /* found filename */ |
| strcpy(filename, tmp1 + 1); |
| free(raw); |
| return 0; |
| } |
| } |
| else { |
| /* here tftp://hostname/testfile from option request of dhcp */ |
| /* look for dns server name */ |
| tmp1 = strchr(raw, '.'); |
| if (tmp1 == NULL) { |
| printf("\n missing . seperator in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| /* look for domain name beyond dns server name |
| * so ignore the current . and look for one more */ |
| tmp = strchr(tmp1 + 1, '.'); |
| if (tmp == NULL) { |
| printf("\n missing domain in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| tmp1 = strchr(tmp1, '/'); |
| if (tmp1 == NULL) { |
| printf("\n missing filename in %s\n", raw); |
| free(raw); |
| return -1; |
| } |
| j = tmp1 - (raw + 7); |
| tmp = raw + 7; |
| tmp[j] = '\0'; |
| strcpy(domainname, tmp); |
| if (dns_get_ip(fd, domainname, server_ip6, 6) == 0) { |
| printf("\n DNS failed for IPV6\n"); |
| return -1; |
| } |
| ipv6_to_str(server_ip6, server_ip); |
| |
| strcpy(filename, tmp1 + 1); |
| free(raw); |
| return 0; |
| } |
| } |
| |
| int tftp_get_error_info(filename_ip_t *fnip, tftp_err_t *tftperr, int rc, |
| const char **errstr, int *ecode) |
| { |
| static char estrbuf[80]; |
| |
| if (rc == -1) { |
| *ecode = 0x3003; |
| *errstr = "unknown TFTP error"; |
| return -103; |
| } else if (rc == -2) { |
| *ecode = 0x3004; |
| snprintf(estrbuf, sizeof(estrbuf), |
| "TFTP buffer of %d bytes is too small for %s", len, |
| fnip->filename); |
| *errstr = estrbuf; |
| return -104; |
| } else if (rc == -3) { |
| *ecode = 0x3009; |
| snprintf(estrbuf, sizeof(estrbuf), "file not found: %s", |
| fnip->filename); |
| *errstr = estrbuf; |
| return -108; |
| } else if (rc == -4) { |
| *ecode = 0x3010; |
| *errstr = "TFTP access violation"; |
| return -109; |
| } else if (rc == -5) { |
| *ecode = 0x3011; |
| *errstr = "illegal TFTP operation"; |
| return -110; |
| } else if (rc == -6) { |
| *ecode = 0x3012; |
| *errstr = "unknown TFTP transfer ID"; |
| return -111; |
| } else if (rc == -7) { |
| *ecode = 0x3013; |
| *errstr = "no such TFTP user"; |
| return -112; |
| } else if (rc == -8) { |
| *ecode = 0x3017; |
| *errstr = "TFTP blocksize negotiation failed"; |
| return -116; |
| } else if (rc == -9) { |
| *ecode = 0x3018; |
| *errstr = "file exceeds maximum TFTP transfer size"; |
| return -117; |
| } else if (rc <= -10 && rc >= -15) { |
| const char *icmp_err_str; |
| switch (rc) { |
| case -ICMP_NET_UNREACHABLE - 10: |
| icmp_err_str = "net unreachable"; |
| break; |
| case -ICMP_HOST_UNREACHABLE - 10: |
| icmp_err_str = "host unreachable"; |
| break; |
| case -ICMP_PROTOCOL_UNREACHABLE - 10: |
| icmp_err_str = "protocol unreachable"; |
| break; |
| case -ICMP_PORT_UNREACHABLE - 10: |
| icmp_err_str = "port unreachable"; |
| break; |
| case -ICMP_FRAGMENTATION_NEEDED - 10: |
| icmp_err_str = "fragmentation needed and DF set"; |
| break; |
| case -ICMP_SOURCE_ROUTE_FAILED - 10: |
| icmp_err_str = "source route failed"; |
| break; |
| default: |
| icmp_err_str = "UNKNOWN"; |
| break; |
| } |
| *ecode = 0x3005; |
| sprintf(estrbuf, "ICMP ERROR \"%s\"", icmp_err_str); |
| *errstr = estrbuf; |
| return -105; |
| } else if (rc == -40) { |
| *ecode = 0x3014; |
| sprintf(estrbuf, |
| "TFTP error occurred after %d bad packets received", |
| tftperr->bad_tftp_packets); |
| *errstr = estrbuf; |
| return -113; |
| } else if (rc == -41) { |
| *ecode = 0x3015; |
| sprintf(estrbuf, |
| "TFTP error occurred after missing %d responses", |
| tftperr->no_packets); |
| *errstr = estrbuf; |
| return -114; |
| } else if (rc == -42) { |
| *ecode = 0x3016; |
| sprintf(estrbuf, |
| "TFTP error missing block %d, expected block was %d", |
| tftperr->blocks_missed, tftperr->blocks_received); |
| *errstr = estrbuf; |
| return -115; |
| } |
| |
| return rc; |
| } |