blob: e914724e87736550af5906853e4c6389b14a7751 [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 <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
/**
* This is a library that implements TFTP (RFC 1350) with support for the
* option extension (RFC 2347) the block size (RFC 2348) timeout interval,
* transfer size (RFC 2349) and the window size (RFC 7440) options.
*
* It also supports block count rollover, which allows us to transfer files
* larger than 65536 * block size bytes. This is purported to be a common
* extension of the TFTP protocol.
*
* This library does not deal with the transport of the protocol itself and
* should be able to be plugged into an existing client or server program.
*
* Memory management is the responsibility of the client of the library,
* allowing its use in more restricted environments like bootloaders.
*
* To use this library, one should initialize a TFTP Session and generate
* a request if the transfer needs to be triggered by the consumer of this
* library.
*
* Once a transfer has been successfully started, repeated calls to the receive
* method should be made with the incoming data. Outgoing packets will be
* generated in the outgoing buffer parameters to each method call.
*
* In the case of the passive side of the connection, the receive method should
* be called repeatedly as well. Upon reception of the first packet the
* |tftp_file_open_cb| callback will be called to prepare for receiving the
* file.
*
* A timeout value is returned when calling |tftp_generate_request| and
* |tftp_process_msg| and should be used to notify the library that the
* expected packet was not receive within the value returned.
**/
enum {
TFTP_NO_ERROR = 0,
TFTP_TRANSFER_COMPLETED = 1,
TFTP_ERR_INTERNAL = -1,
TFTP_ERR_NOT_SUPPORTED = -2,
TFTP_ERR_NOT_FOUND = -3,
TFTP_ERR_INVALID_ARGS = -10,
TFTP_ERR_BUFFER_TOO_SMALL = -15,
TFTP_ERR_BAD_STATE = -20,
TFTP_ERR_TIMED_OUT = -21,
TFTP_ERR_SHOULD_WAIT = -22,
TFTP_ERR_IO = -40,
};
enum {
TFTP_ERR_CODE_UNDEF = 0,
TFTP_ERR_CODE_FILE_NOT_FOUND = 1,
TFTP_ERR_CODE_ACCESS_VIOLATION = 2,
TFTP_ERR_CODE_DISK_FULL = 3,
TFTP_ERR_CODE_ILLEGAL_OP = 4,
TFTP_ERR_CODE_UNKNOWN_ID = 5,
TFTP_ERR_CODE_FILE_EXISTS = 6,
TFTP_ERR_CODE_NO_USER = 7,
TFTP_ERR_CODE_BAD_OPTIONS = 8,
// Fuchsia-specific error code
// BUSY is sent by a server as a response to a RRQ or WRQ, and indicates
// that the server is unavailable to process the request at the moment
// (but expects to be able to handle it at some time in the future).
// A server will send an ERR_CODE_BUSY response if its open callback
// (open_read or open_write) returns TFTP_ERR_SHOULD_WAIT.
TFTP_ERR_CODE_BUSY = 0x143, /* 'B' + 'U' + 'S' + 'Y' */
};
#ifdef __cplusplus
extern "C" {
#endif
// Opaque structure
typedef struct tftp_session_t tftp_session;
typedef int32_t tftp_status;
typedef enum {
MODE_NETASCII,
MODE_OCTET,
MODE_MAIL,
} tftp_mode;
// These are the default values used when sending a tftp request
#define TFTP_DEFAULT_CLIENT_MODE MODE_OCTET
typedef struct {
char* inbuf; // required - buffer for assembling incoming msgs
size_t inbuf_sz; // required
char* outbuf; // required - buffer for assembling outgoing msgs
size_t outbuf_sz; // required
tftp_mode* mode;
uint16_t* block_size;
uint16_t* window_size;
uint8_t* timeout;
char* err_msg;
size_t err_msg_sz;
} tftp_request_opts;
typedef struct {
char* inbuf; // required - buffer for assembling incoming msgs
size_t inbuf_sz; // required
char* outbuf; // required - buffer for assembling outgoing msgs
size_t* outbuf_sz; // required
char* err_msg;
size_t err_msg_sz;
} tftp_handler_opts;
// tftp_file_open_read_cb is called by the library to prepare for reading.
// |file_cookie| will be passed to this function from the argument to
// tftp_process_msg.
//
// This function should return the size of the file on success, or a TFTP_ERR_*
// error code on failure.
typedef ssize_t (*tftp_file_open_read_cb)(const char* filename,
void* file_cookie);
// tftp_file_open_write_cb is called by the library to prepare a file for
// writing. |file_cookie| will be passed to this function from the argument to
// tftp_process_msg. |size| indicates the size of the file that will be
// created (it may be ignored if this information is not needed on opening).
typedef tftp_status (*tftp_file_open_write_cb)(const char* filename,
size_t size,
void* file_cookie);
// tftp_file_read_cb is called by the library to read |length| bytes, starting
// at |offset|, into |data|. |file_cookie| will be passed to this function from
// the argument to tftp_process_msg.
//
// |length| is both an input and output parameter, and may be set as a value
// less than or equal to the original value to indicate a partial read.
// |length| is only used as an output parameter if the returned status is
// TFTP_NO_ERROR.
typedef tftp_status (*tftp_file_read_cb)(void* data,
size_t* length,
off_t offset,
void* file_cookie);
// tftp_file_write_cb is called by the library to write |length| bytes,
// starting at |offset|, into the destination. |file_cookie| will be passed to
// this function from the argument to tftp_process_msg.
//
// |length| is both an input and output parameter, and may be set as a value
// less than or equal to the original value to indicate a partial write.
// |length| is only used as an output parameter if the returned status is
// TFTP_NO_ERROR.
typedef tftp_status (*tftp_file_write_cb)(const void* data,
size_t* length,
off_t offset,
void* file_cookie);
// tftp_file_close_cb is called by the library to finish a file read or write
// operation. |file_cookie| will be passed to this function from the argument to
// tftp_process_msg.
typedef void (*tftp_file_close_cb)(void* file_cookie);
typedef struct {
tftp_file_open_read_cb open_read;
tftp_file_open_write_cb open_write;
tftp_file_read_cb read;
tftp_file_write_cb write;
tftp_file_close_cb close;
} tftp_file_interface;
// tftp_transport_send_cb is called by the library to send |len| bytes from
// |data| over a previously-established connection.
typedef tftp_status (*tftp_transport_send_cb)(void* data,
size_t len,
void* transport_cookie);
// tftp_transport_recv_cb is called by the library to read from the transport
// interface. It will read values into |data|, up to |len| bytes. If |block| is
// set, the operation will block until data is received or a timeout happens.
// (For starting communication, the timeout should be set by the user if
// desired. Once communication has been established, the timeout is set by the
// tftp library using the timeout_set callback).
// On success, the function should return the number of bytes received. On
// failure it should return a tftp_status error code.
typedef int (*tftp_transport_recv_cb)(void* data,
size_t len,
bool block,
void* transport_cookie);
// tftp_transport_timeout_set_cb is called by the library to set the timeout
// length of the transport interface. This function should return 0 on success
// or -1 on failure.
typedef int (*tftp_transport_timeout_set_cb)(uint32_t timeout_ms,
void* transport_cookie);
typedef struct {
tftp_transport_send_cb send;
tftp_transport_recv_cb recv;
tftp_transport_timeout_set_cb timeout_set;
} tftp_transport_interface;
// Returns the number of bytes needed to hold a tftp_session.
size_t tftp_sizeof_session(void);
// Initialize the tftp_session pointer using the memory in |buffer| of size
// |size|. Returns TFTP_ERR_BUFFER_TOO_SMALL if |size| is too small.
tftp_status tftp_init(tftp_session** session, void* buffer, size_t size);
// Specifies the callback functions to use when reading or writing files.
tftp_status tftp_session_set_file_interface(tftp_session* session,
tftp_file_interface* callbacks);
// Specifies the callback functions to use for the network interface. Note that
// setting up the transport must be performed outside of the purview of the
// library, since the initial configuration options are highly interface-
// dependent.
tftp_status tftp_session_set_transport_interface(tftp_session* session,
tftp_transport_interface* callbacks);
// Specifies how many consecutive timeouts we will endure before terminating
// a session.
void tftp_session_set_max_timeouts(tftp_session* session,
uint16_t max_timeouts);
// Specify whether to use the upper 8 bits of the opcode field as a pseudo
// retransmission count. When enabled, this tweaks the contents of a
// retransmitted packet just enough that it will have a different checksum,
// avoiding the problem we have on ASIX 88179 adapters that (reliably)
// generate incorrect checksums for certain packets.
void tftp_session_set_opcode_prefix_use(tftp_session* session,
bool enable);
// When acting as a server, the options that will be overridden when a
// value is requested by the client. Note that if the client does not
// specify a setting, the default will be used regardless of server
// setting.
// When acting as a client, the default options that will be used when
// initiating a transfer. If any of the values are not set, it will not be
// specified in the request packet, and so the tftp defaults will be used.
tftp_status tftp_set_options(tftp_session* session,
const uint16_t* block_size,
const uint8_t* timeout,
const uint16_t* window_size);
// tftp_session_has_pending returns true if the tftp_session has more data to
// send before waiting for an ack. It is recommended that the caller do a
// non-blocking read to see if an out-of-order ACK was sent by the remote host
// before sending additional data packets.
bool tftp_session_has_pending(tftp_session* session);
// Prepare a DATA packet to send to the remote host. This is only required when
// tftp_session_has_pending(session) returns true, as tftp_process_msg() will
// prepare the first DATA message in each window.
tftp_status tftp_prepare_data(tftp_session* session,
void* outgoing,
size_t* outlen,
uint32_t* timeout_ms,
void* cookie);
// If no response from the peer is received before the most recent timeout_ms
// value, this function should be called to take the next appropriate action
// (e.g., retransmit or cancel). |msg_buf| must point to the last message sent,
// which is |msg_len| bytes long. |buf_sz| represents the total size of
// |msg_buf|, which may be used to assemble the next packet to send.
// |timeout_ms| is set to the next timeout value the user of the library should
// use when waiting for a response. |file_cookie| will be passed to the tftp
// callback functions. On return, TFTP_ERR_TIMED out is returned if the maximum
// number of timeouts has been exceeded. If a message should be sent out,
// |msg_len| will be set to the size of the message.
tftp_status tftp_timeout(tftp_session* session,
void* msg_buf,
size_t* msg_len,
size_t buf_sz,
uint32_t* timeout_ms,
void* file_cookie);
// Request to send the file |local_filename| across an existing session to
// |remote_filename| on the target. |options| are required for the input and
// output buffers, but other options can be left as NULL to use the session
// defaults. Before calling this function, the client transport interface
// should be configured as needed.
tftp_status tftp_push_file(tftp_session* session,
void* transport_cookie,
void* file_cookie,
const char* local_filename,
const char* remote_filename,
tftp_request_opts* options);
// Request to retrieve the target file |remote_filename| across an existing
// session to |local_filename| on the host. |options| are required for the
// input and output buffers, but other options can be left as NULL to use the
// session defaults. Before calling this function, the client transport
// interface should be configured as needed.
tftp_status tftp_pull_file(tftp_session* session,
void* transport_cookie,
void* file_cookie,
const char* remote_filename,
const char* local_filename,
tftp_request_opts* options);
// Wait for a client to request an operation, then service that request.
// Returns (with TFTP_TRANSFER_COMPLETED) after each successful operation, or
// on error. This function will call the transport send, recv, and timeout_set
// operations as needed to facilitate communication with the requestor.
tftp_status tftp_service_request(tftp_session* session,
void* transport_cookie,
void* file_cookie,
tftp_handler_opts* opts);
// Processes a single message from the requestor, which is passed in as the
// inbuf component of |opts|. Responds to the request and updates the
// connection timeout using the appropriate transport send and timeout_set
// functions. Also, sets the value of outbuf_sz in |opts| to the size of
// the message sent (or 0 if no message was sent).
tftp_status tftp_handle_msg(tftp_session* session,
void* transport_cookie,
void* file_cookie,
tftp_handler_opts* opts);
// Get the current metrics.
// Fills the buffer with a JSON formatted text string containing metrics.
// Returns TFTP_ERR_BUFFER_TOO_SMALL if buffer is too small.
tftp_status tftp_get_metrics(tftp_session* session,
char* buf,
size_t buf_sz);
// Clear metric counters.
void tftp_clear_metrics(tftp_session* session);
// TODO: tftp_error() for client errors that need to be sent to the remote host
#ifdef __cplusplus
} // extern "C"
#endif