📄 tftp.c
字号:
//////////////////////////////////////////////////////////////////////////////////// Copyright(c) 2001 Intrinsyc Software Inc. All rights reserved.//// Module name://// tftp.c//// Description://// Handles tftp downloads and MS Platform Builder tftp uploads.//// Author://// Mike Kirkland//// Created://// October 2001//////////////////////////////////////////////////////////////////////////////////#include <types.h>#include <net.h>#include <udp.h>#include <tftp.h>#include <util.h>#include <c_main.h>#include <string.h>#include <serial.h>#include <timer.h>#include <ethernet.h>//#define DEBUG_LEVEL 4#include <debug.h>//#define SHOW_TFTP_BLOCKS 1#ifdef SHOW_TFTP_BLOCKS#define BLOCK_INDICATOR(s) itc_printf(s)#else#define BLOCK_INDICATOR(s) #endif#define BLOCK_ERROR(s) itc_printf(s)// Select the next block number, properly handling 16 bit overflows#define NEXT_BLOCK(block) (((block) + 1) % 0x10000)// States in TFTP state machinetypedef enum{ TFTP_ST_INIT = 0, // Initial state (must be 0) TFTP_ST_FINISHED, // Final state TFTP_ST_RRQ_FIRST, // Awaiting first data packet as client TFTP_ST_SERVER, // Server waiting state TFTP_ST_RECEIVE // Awaiting subsequent data packets as client} tftp_states;// State data for the TFTP state machinetypedef struct{ tftp_states state; netapiconn conn; int block; int file_length; int retries; u8 *dest; char const *file_name; u32 start_time;} tftp_state_data;// Events received by TFTP state machinetypedef enum{ TFTP_EV_PACKET_RRQ = 1, // Received RRQ packet TFTP_EV_PACKET_WRQ, // Received WRQ packet TFTP_EV_PACKET_DATA, // Received DATA packet TFTP_EV_PACKET_ACK, // Received ACK packet TFTP_EV_PACKET_ERROR, // Received ERROR packet TFTP_EV_PACKET_UNKNOWN, // Received unknown packet TFTP_EV_BEGIN_RX, // Begin receiving a file as a client TFTP_EV_BEGIN_TX, // Begin server operation TFTP_EV_TIMEOUT // Timed out} tftp_events;// Data associated with each eventtypedef union{ // Data for TFTP_EV_PACKET_* events struct { u8 *data; u16 length; } ev_packet_x;} tftp_ev_data;////////////////////////////////////////////////////////////////////////////////// tftpack :// PURPOSE: Sends a tftp ack packet, which consists of a two byte op header// (TFTP_OP_ACK), and a 2 byte block number.// PARAMS: (IN) u16 block - tftp block # being acknowledged.// (IN) netapiconn conn - tftp connection used// RETURNS: Nothing.////////////////////////////////////////////////////////////////////////////////voidtftpack(u16 block, netapiconn conn){ u8 buf[MAX_PACKET_SIZE]; u8 *packet = buf; int i; //we want the packet to be u32 aligned after the headers (42 bytes) i = (u32)(packet + ETHER_HEADER_SIZE) % sizeof(u32); packet += (sizeof (u32) - i); i = 0; *(u16 *)(packet + UDPIP_HEADER_SIZE + TFTP_TYPE_OFFSET) = htons(TFTP_OP_ACK); *(u16 *)(packet + UDPIP_HEADER_SIZE + TFTP_BLOCK_OFFSET) = htons(block); udp_send(&conn, packet, 2*sizeof(u16));}////////////////////////////////////////////////////////////////////////////////// print_tftp_error// PURPOSE: Display error received in TFTP ERROR packet// PARAMS: (IN) data - data associated with the TFTP_EV_PACKET_DATA event// RETURNS: none////////////////////////////////////////////////////////////////////////////////static voidprint_tftp_error(tftp_ev_data const *data){ char err_msg[80]; itc_strlcpy(err_msg, data->ev_packet_x.data + TFTP_ERROR_MSG_OFFSET, MIN(sizeof(err_msg), data->ev_packet_x.length - TFTP_ERROR_MSG_OFFSET + 1)); itc_printf("ERROR #%i received: %s\r\n", ntohs(*(u16 *)(data->ev_packet_x.data + TFTP_ERROR_CODE_OFFSET)), err_msg);}////////////////////////////////////////////////////////////////////////////////// show_received// PURPOSE: Display the number of received bytes so far// PARAMS: (IN) state - state data// (IN) force - force display no matter which block number// RETURNS: None.////////////////////////////////////////////////////////////////////////////////static voidshow_received(tftp_state_data const * const state, int force){#ifndef SHOW_TFTP_BLOCKS // Display the current file length every 128 blocks starting with 1 if (force || (state->block % 128) == 1) { itc_printf("%x\b\b\b\b\b\b\b\b", state->file_length); }#endif}////////////////////////////////////////////////////////////////////////////////// received_block// PURPOSE: Store received data block into memory and update metadata// PARAMS: (IN/OUT) state - state data// (IN) data - data associated with the TFTP_EV_PACKET_DATA event// RETURNS: 0 normally// 1 when state machine reaches TFTP_ST_FINISHED////////////////////////////////////////////////////////////////////////////////static voidreceived_block(tftp_state_data *state, tftp_ev_data const *data){ int datalen = data->ev_packet_x.length - TFTP_HEADER_SIZE; state->file_length += datalen; state->block = NEXT_BLOCK(state->block); DEBUG_4("%i bytes in block\r\n", datalen); //copy out the data payload itc_memcpy(state->dest, data->ev_packet_x.data + TFTP_HEADER_SIZE, datalen); state->dest += datalen; // ACK this block tftpack(state->block, state->conn); // Reset retries counter state->retries = 0; BLOCK_INDICATOR("*"); show_received(state, 0);}////////////////////////////////////////////////////////////////////////////////// ev_err// PURPOSE: Display error message for wrong event// PARAMS: (IN) state - state number// (IN) event - event number// RETURNS: none////////////////////////////////////////////////////////////////////////////////static voidev_err(tftp_states state, tftp_events event){ itc_printf("Illegal TFTP event %i in state %i\r\n", (int) event, (int) state);}////////////////////////////////////////////////////////////////////////////////// retry// PURPOSE: Increment the retry counter and see if we can try again// PARAMS: (IN) state - state data// RETURNS: 1 if we can try again, 0 if we must abort////////////////////////////////////////////////////////////////////////////////static inline intretry(tftp_state_data *state){ return ++state->retries < TFTP_RETRIES;}////////////////////////////////////////////////////////////////////////////////// tftp_sm// PURPOSE: TFTP state machine// PARAMS: (IN/OUT) state - state data// (IN) event - event into this state machine// (IN) data - data associated with this event// RETURNS: 0 normally// 1 when state machine reaches TFTP_ST_FINISHED////////////////////////////////////////////////////////////////////////////////static inttftp_sm(tftp_state_data *state, tftp_events event, tftp_ev_data const *data){ u8 buf[MAX_PACKET_SIZE]; //we want the packet to be u32 aligned after the headers (42 bytes) u8 * const packet = (u8 *)(buf + (sizeof(u32) - (((u32)buf +\ UDPIP_HEADER_SIZE) % sizeof(u32)))); DEBUG_4("Rec'd event %i in state %i\r\n", (int) event, (int) state->state); switch (state->state) {////////////////////////////////////////////////////////////////////////////////// Initialization state//////////////////////////////////////////////////////////////////////////////// case TFTP_ST_INIT: { switch (event) { case TFTP_EV_BEGIN_RX: { u8 *curpos = packet + UDPIP_HEADER_SIZE; u16 length = 0; size_t len; state->start_time = get_time_timer(); // Get port to receive udp_bind(&state->conn, 0); // Bind address for transmitting udp_connect(&state->conn, status.siaddr, IPPORT_TFTP); // Build read request packet *(u16 *)curpos = htons(TFTP_OP_RRQ); curpos += sizeof(u16); length += 2; // Copy file name into file request packet len = itc_strlcpy(curpos, state->file_name, TFTP_DATA_SIZE); curpos += len + 1; length += len + 1; length += itc_strlcpy(curpos, "octet", TFTP_DATA_SIZE) + 1; //send a TFTP read request to the server udp_send(&state->conn, packet, length); state->state = TFTP_ST_RRQ_FIRST; break; } case TFTP_EV_BEGIN_TX: { // Get port to receive udp_bind(&state->conn, PB_PORT); state->state = TFTP_ST_SERVER; break; } default: { // Illegal event in this state ev_err(state->state, event); break; } } break; }////////////////////////////////////////////////////////////////////////////////// Receive first block state//////////////////////////////////////////////////////////////////////////////// case TFTP_ST_RRQ_FIRST: { switch (event) { case TFTP_EV_PACKET_DATA: { int const block = ntohs(*(u16 *)(data->ev_packet_x.data + TFTP_BLOCK_OFFSET)); // Make sure this is the packet we are expecting if (NEXT_BLOCK(state->block) == block) { //and add it to the total received received_block(state, data); // Switch to regular receive state unless this is // the one and only packet state->state = (data->ev_packet_x.length < TFTP_DATA_SIZE + TFTP_HEADER_SIZE) ? TFTP_ST_FINISHED : TFTP_ST_RECEIVE; } else { BLOCK_ERROR("X"); } // Ignore any other data blocks (should never happen) break; } case TFTP_EV_TIMEOUT: { // Didn't receive an ACK for the RRQ BLOCK_ERROR("."); if (retry(state)) { // Recursively call the state machine again to // completely restart the transfer. state->state = TFTP_ST_INIT; tftp_sm(state, TFTP_EV_BEGIN_RX, NULL); } else { // Too many retries BLOCK_ERROR("!"); state->state = TFTP_ST_FINISHED; } break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -