blob: 525e5cdd257bb71870f8165b990d06728d21e3b2 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "tftp/tftp.h"
#define OPCODE_RRQ 1
#define OPCODE_WRQ 2
#define OPCODE_DATA 3
#define OPCODE_ACK 4
#define OPCODE_ERROR 5
#define OPCODE_OACK 6
#ifdef __cplusplus
extern "C" {
#endif
typedef struct tftp_msg_t {
uint16_t opcode;
char data[0];
} tftp_msg;
typedef struct tftp_err_msg_t {
uint16_t opcode;
uint16_t err_code;
char msg[0];
} tftp_err_msg;
typedef struct tftp_data_msg_t {
uint16_t opcode;
uint16_t block;
uint8_t data[0];
} tftp_data_msg;
#define BLOCKSIZE_OPTION 0x01 // RFC 2348
#define TIMEOUT_OPTION 0x02 // RFC 2349
#define WINDOWSIZE_OPTION 0x04 // RFC 7440
#define DEFAULT_BLOCKSIZE 512
#define DEFAULT_TIMEOUT 1
#define DEFAULT_FILESIZE 0
#define DEFAULT_WINDOWSIZE 1
#define DEFAULT_MODE MODE_OCTET
#define DEFAULT_MAX_TIMEOUTS 5
#define DEFAULT_USE_OPCODE_PREFIX true
typedef struct tftp_options_t {
// A bitmask of the options that have been set
uint8_t mask;
uint16_t block_size;
uint8_t timeout;
uint16_t window_size;
} tftp_options;
/**
State transitions
***** READ FILE *****
client server
~~~~~~ ~~~~~~
NONE NONE
generate_request (rrq)
REQ_SENT
---- RRQ ----->
handle_rrq
REQ_RECEIVED
<---- OACK ----
handle_oack
FIRST_DATA
+------+ +-----+
| | | |
| V V |
| ---- ACK -----> |
| handle_ack |
| SENDING_DATA |
| <---- DATA ---- | |
| handle_data | |
| RECEIVING_DATA | |
| ... | |
| <---- DATA ---- | |
| handle_data | |
| | | |
+------+ +-----+
COMPLETED COMPLETED
****** WRITE FILE *****
client server
~~~~~~ ~~~~~~
NONE NONE
generate_request (wrq)
REQ_SENT
---- WRQ ----->
handle_wrq
REQ_RECEIVED
<---- OACK ----
handle_oack
FIRST_DATA
+------+ +-----+
| | | |
| V V |
| ---- DATA ----> |
| handle_data |
| RECEIVING_DATA |
| <----- ACK ---- | |
| handle_ack | |
| SENDING_DATA | |
| | | |
+------+ +-----+
COMPLETED COMPLETED
**/
typedef enum {
NONE = 0,
REQ_SENT,
REQ_RECEIVED,
FIRST_DATA,
SENDING_DATA,
RECEIVING_DATA,
ERROR,
COMPLETED,
} tftp_state;
typedef enum { SEND_FILE, RECV_FILE } tftp_file_direction;
struct tftp_session_t {
// For a client, the options we will use on a new connection. For a server, the options we
// will override, if possible, when we receive a write request.
tftp_options options;
// Tracks the options we used on the last request, so we can compare them to the options
// we get back.
tftp_options client_sent_opts;
// Maximum filename really is 505 including \0
// max request size (512) - opcode (2) - shortest mode (4) - null (1)
char filename[512];
tftp_mode mode;
// General state values
tftp_file_direction direction; // Not valid when state is NONE, ERROR, or COMPLETED.
tftp_state state;
size_t offset;
uint32_t consecutive_timeouts;
uint8_t opcode_prefix;
uint64_t block_number;
uint32_t window_index;
// Maximum number of times we will retransmit a single msg before aborting
uint16_t max_timeouts;
// Add an 8-bit prefix to the opcode so that retransmissions differ from the
// original transmission. This fixes problems with checksums on asix 88179 USB
// adapters (they send 0 checksums when they should send 0xffff, which is a
// no-no in IPv6). This modification is not RFC-compatible.
bool use_opcode_prefix;
// "Negotiated" values
size_t file_size;
uint16_t window_size;
uint16_t block_size;
uint8_t timeout;
// Callbacks
tftp_file_interface file_interface;
tftp_transport_interface transport_interface;
// Metrics
struct {
uint32_t inorder_blocks;
uint32_t outoforder_blocks;
uint32_t acks_sent;
uint32_t nacks_sent;
uint32_t sas_events; // Sorcerer's Apprentice Syndrome
uint32_t timeouts;
uint64_t inorder_bytes;
} metrics;
};
// Generates a read or write request to send to a tftp server. |filename| is
// the name sent to the server. |datalen| is the size of the data (should be
// zero for read requests). If |block_size|, |timeout|, or |window_size| are
// set, those will be passed to the server in such a way that they cannot be
// negotiated (normal, negotiable settings can be set using
// tftp_set_options()). |outgoing| must point to a scratch buffer the library
// can use to assemble the request. |outlen| is the size of the outgoing
// scratch buffer, and will be set to the size of the request. |timeout_ms| is
// set to the next timeout value the user of the library should use when
// waiting for a response.
tftp_status tftp_generate_request(tftp_session* session, tftp_file_direction direction,
const char* local_filename, const char* remote_filename,
tftp_mode mode, size_t datalen, const uint16_t* block_size,
const uint8_t* timeout, const uint16_t* window_size,
void* outgoing, size_t* outlen, uint32_t* timeout_ms);
// Handle an incoming tftp packet. |incoming| must point to the packet of size
// |inlen|. |outgoing| must point to a scratch buffer the library can use to
// assemble the next packet to send. |outlen| is the size of the outgoing
// scratch buffer. |timeout_ms| is set to the next timeout value the user of the
// library should use when waiting for a response. |cookie| will be passed to
// the tftp callback functions.
tftp_status tftp_process_msg(tftp_session* session, void* incoming, size_t inlen, void* outgoing,
size_t* outlen, uint32_t* timeout_ms, void* cookie);
// Internal handlers
tftp_status tx_data(tftp_session* session, tftp_data_msg* resp, size_t* outlen, void* cookie);
tftp_status tftp_handle_rrq(tftp_session* session, tftp_msg* rrq, size_t rrq_len, tftp_msg* resp,
size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_wrq(tftp_session* session, tftp_msg* wrq, size_t wrq_len, tftp_msg* resp,
size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_data(tftp_session* session, tftp_msg* msg, size_t msg_len, tftp_msg* resp,
size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_ack(tftp_session* session, tftp_msg* ack, size_t ack_len, tftp_msg* resp,
size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_error(tftp_session* session, tftp_err_msg* err, size_t err_len,
tftp_msg* resp, size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_oack(tftp_session* session, tftp_msg* oack, size_t oack_len, tftp_msg* resp,
size_t* resp_len, uint32_t* timeout_ms, void* cookie);
tftp_status tftp_handle_oerror(tftp_session* session, tftp_msg* oerr, size_t oerr_len,
tftp_msg* resp, size_t* resp_len, uint32_t* timeout_ms,
void* cookie);
void print_hex(uint8_t* buf, size_t len);
#ifdef __cplusplus
} // extern "C"
#endif