net: tftp: Add client context
Use client context to seperate buffer usage. Use new `TFTP_EVT_DATA` event to send data to application. Use new `TFTP_EVT_ERROR` event to report error to application. Update `tftp_get()` and `tftp_put()` API to use the client context. Signed-off-by: Jun Qing Zou <jun.qing.zou@nordicsemi.no>
This commit is contained in:
parent
3d6608ccb3
commit
755f0b7d27
|
@ -20,24 +20,6 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef tftp_callback_t
|
|
||||||
*
|
|
||||||
* Handler to handle data received from the TFTP server.
|
|
||||||
*
|
|
||||||
* @param data Data received.
|
|
||||||
* @param datalen Length of the data, 512 bytes or less.
|
|
||||||
*
|
|
||||||
* @note The handler must not call @ref tftp_get and @ref tftp_put.
|
|
||||||
*/
|
|
||||||
typedef void (*tftp_callback_t)(const uint8_t *data, size_t datalen);
|
|
||||||
|
|
||||||
struct tftpc {
|
|
||||||
uint8_t *user_buf;
|
|
||||||
uint32_t user_buf_size;
|
|
||||||
tftp_callback_t callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFC1350: the file is sent in fixed length blocks of 512 bytes.
|
* RFC1350: the file is sent in fixed length blocks of 512 bytes.
|
||||||
* Each data packet contains one block of data, and must be acknowledged
|
* Each data packet contains one block of data, and must be acknowledged
|
||||||
* by an acknowledgment packet before the next packet can be sent.
|
* by an acknowledgment packet before the next packet can be sent.
|
||||||
|
@ -45,6 +27,15 @@ struct tftpc {
|
||||||
*/
|
*/
|
||||||
#define TFTP_BLOCK_SIZE 512
|
#define TFTP_BLOCK_SIZE 512
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RFC1350: For non-request TFTP message, the header contains 2-byte operation
|
||||||
|
* code plus 2-byte block number or error code.
|
||||||
|
*/
|
||||||
|
#define TFTP_HEADER_SIZE 4
|
||||||
|
|
||||||
|
/* Maximum amount of data that can be sent or received */
|
||||||
|
#define TFTPC_MAX_BUF_SIZE (TFTP_BLOCK_SIZE + TFTP_HEADER_SIZE)
|
||||||
|
|
||||||
/* TFTP Client Error codes. */
|
/* TFTP Client Error codes. */
|
||||||
#define TFTPC_SUCCESS 0
|
#define TFTPC_SUCCESS 0
|
||||||
#define TFTPC_DUPLICATE_DATA -1
|
#define TFTPC_DUPLICATE_DATA -1
|
||||||
|
@ -53,41 +44,122 @@ struct tftpc {
|
||||||
#define TFTPC_REMOTE_ERROR -4
|
#define TFTPC_REMOTE_ERROR -4
|
||||||
#define TFTPC_RETRIES_EXHAUSTED -5
|
#define TFTPC_RETRIES_EXHAUSTED -5
|
||||||
|
|
||||||
/* @brief This function gets "file" from the remote server.
|
/**
|
||||||
|
* @brief TFTP Asynchronous Events notified to the application from the module
|
||||||
|
* through the callback registered by the application.
|
||||||
|
*/
|
||||||
|
enum tftp_evt_type {
|
||||||
|
/** DATA event when data is received from remote server.
|
||||||
|
*
|
||||||
|
* @note DATA event structure contains payload data and size.
|
||||||
|
*/
|
||||||
|
TFTP_EVT_DATA,
|
||||||
|
|
||||||
|
/** ERROR event when error is received from remote server.
|
||||||
|
*
|
||||||
|
* @note ERROR event structure contains error code and message.
|
||||||
|
*/
|
||||||
|
TFTP_EVT_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Parameters for data event. */
|
||||||
|
struct tftp_data_param {
|
||||||
|
uint8_t *data_ptr; /**< Pointer to binary data. */
|
||||||
|
uint32_t len; /**< Length of binary data. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Parameters for error event. */
|
||||||
|
struct tftp_error_param {
|
||||||
|
char *msg; /**< Error message. */
|
||||||
|
int code; /**< Error code. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines event parameters notified along with asynchronous events
|
||||||
|
* to the application.
|
||||||
|
*/
|
||||||
|
union tftp_evt_param {
|
||||||
|
/** Parameters accompanying TFTP_EVT_DATA event. */
|
||||||
|
struct tftp_data_param data;
|
||||||
|
|
||||||
|
/** Parameters accompanying TFTP_EVT_ERROR event. */
|
||||||
|
struct tftp_error_param error;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Defines TFTP asynchronous event notified to the application. */
|
||||||
|
struct tftp_evt {
|
||||||
|
/** Identifies the event. */
|
||||||
|
enum tftp_evt_type type;
|
||||||
|
|
||||||
|
/** Contains parameters (if any) accompanying the event. */
|
||||||
|
union tftp_evt_param param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef tftp_callback_t
|
||||||
*
|
*
|
||||||
* If the file is successfully received its size will be returned in
|
* @brief TFTP event notification callback registered by the application.
|
||||||
* `client->user_buf_size` parameter.
|
|
||||||
*
|
*
|
||||||
* @param server Control Block that represents the remote server.
|
* @param[in] evt Event description along with result and associated
|
||||||
* @param client Client Buffer Information.
|
* parameters (if any).
|
||||||
|
*/
|
||||||
|
typedef void (*tftp_callback_t)(const struct tftp_evt *evt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief TFTP client definition to maintain information relevant to the
|
||||||
|
* client.
|
||||||
|
*
|
||||||
|
* @note Application must initialize `server` and `callback` before calling
|
||||||
|
* GET or PUT API with the `tftpc` structure.
|
||||||
|
*/
|
||||||
|
struct tftpc {
|
||||||
|
/** Socket address pointing to the remote TFTP server */
|
||||||
|
struct sockaddr server;
|
||||||
|
|
||||||
|
/** Event notification callback. No notification if NULL */
|
||||||
|
tftp_callback_t callback;
|
||||||
|
|
||||||
|
/** Buffer for internal usage */
|
||||||
|
uint8_t tftp_buf[TFTPC_MAX_BUF_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* @brief This function gets data from a "file" on the remote server.
|
||||||
|
*
|
||||||
|
* @param client Client information of type @ref tftpc.
|
||||||
* @param remote_file Name of the remote file to get.
|
* @param remote_file Name of the remote file to get.
|
||||||
* @param mode TFTP Client "mode" setting.
|
* @param mode TFTP Client "mode" setting.
|
||||||
*
|
*
|
||||||
* @return TFTPC_SUCCESS if the operation completed successfully.
|
* @return The size of data being received if the operation completed successfully.
|
||||||
* TFTPC_BUFFER_OVERFLOW if the file is larger than the user buffer.
|
* TFTPC_BUFFER_OVERFLOW if the file is larger than the user buffer.
|
||||||
* TFTPC_REMOTE_ERROR if the server failed to process our request.
|
* TFTPC_REMOTE_ERROR if the server failed to process our request.
|
||||||
* TFTPC_RETRIES_EXHAUSTED if the client timed out waiting for server.
|
* TFTPC_RETRIES_EXHAUSTED if the client timed out waiting for server.
|
||||||
|
* -EINVAL if `client` is NULL.
|
||||||
|
*
|
||||||
|
* @note This function blocks until the transfer is completed or network error happens. The
|
||||||
|
* integrity of the `client` structure must be ensured until the function returns.
|
||||||
*/
|
*/
|
||||||
int tftp_get(struct sockaddr *server, struct tftpc *client,
|
int tftp_get(struct tftpc *client,
|
||||||
const char *remote_file, const char *mode);
|
const char *remote_file, const char *mode);
|
||||||
|
|
||||||
/* @brief This function puts data to "file" on the remote server.
|
/* @brief This function puts data to a "file" on the remote server.
|
||||||
*
|
*
|
||||||
* If the data is successfully sent, the size of data being sent will be returned in
|
* @param client Client information of type @ref tftpc.
|
||||||
* `client->user_buf_size` parameter.
|
|
||||||
*
|
|
||||||
* @param server Control Block that represents the remote server.
|
|
||||||
* @param client Client Buffer Information.
|
|
||||||
* @param remote_file Name of the remote file to put.
|
* @param remote_file Name of the remote file to put.
|
||||||
* @param mode TFTP Client "mode" setting.
|
* @param mode TFTP Client "mode" setting.
|
||||||
|
* @param user_buf Data buffer containing the data to put.
|
||||||
|
* @param user_buf_size Length of the data to put.
|
||||||
*
|
*
|
||||||
* @return TFTPC_SUCCESS if the operation completed successfully.
|
* @return The size of data being sent if the operation completed successfully.
|
||||||
* TFTPC_REMOTE_ERROR if the server failed to process our request.
|
* TFTPC_REMOTE_ERROR if the server failed to process our request.
|
||||||
* TFTPC_RETRIES_EXHAUSTED if the client timed out waiting for server.
|
* TFTPC_RETRIES_EXHAUSTED if the client timed out waiting for server.
|
||||||
* -EINVAL if `client->user_buf` is NULL or `client->user_buf_size` is zero.
|
* -EINVAL if `client` or `user_buf` is NULL or if `user_buf_size` is zero.
|
||||||
|
*
|
||||||
|
* @note This function blocks until the transfer is completed or network error happens. The
|
||||||
|
* integrity of the `client` structure must be ensured until the function returns.
|
||||||
*/
|
*/
|
||||||
int tftp_put(struct sockaddr *server, struct tftpc *client,
|
int tftp_put(struct tftpc *client,
|
||||||
const char *remote_file, const char *mode);
|
const char *remote_file, const char *mode,
|
||||||
|
const uint8_t *user_buf, uint32_t user_buf_size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,10 @@ LOG_MODULE_REGISTER(tftp_client, CONFIG_TFTP_LOG_LEVEL);
|
||||||
#include <zephyr/net/tftp.h>
|
#include <zephyr/net/tftp.h>
|
||||||
#include "tftp_client.h"
|
#include "tftp_client.h"
|
||||||
|
|
||||||
#define ADDRLEN(sock) \
|
#define ADDRLEN(sa) \
|
||||||
(((struct sockaddr *)sock)->sa_family == AF_INET ? \
|
(sa.sa_family == AF_INET ? \
|
||||||
sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
|
sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
|
||||||
|
|
||||||
/* TFTP Global Buffer. */
|
|
||||||
static uint8_t tftpc_buffer[TFTPC_MAX_BUF_SIZE];
|
|
||||||
static uint8_t ack_buffer[TFTPC_MAX_BUF_SIZE];
|
|
||||||
|
|
||||||
/* Global mutex to protect critical resources. */
|
|
||||||
K_MUTEX_DEFINE(tftpc_lock);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare a request as required by RFC1350. This packet can be sent
|
* Prepare a request as required by RFC1350. This packet can be sent
|
||||||
* out directly to the TFTP server.
|
* out directly to the TFTP server.
|
||||||
|
@ -57,7 +50,8 @@ static size_t make_request(uint8_t *buf, int request,
|
||||||
/*
|
/*
|
||||||
* Send Data message to the TFTP Server and receive ACK message from it.
|
* Send Data message to the TFTP Server and receive ACK message from it.
|
||||||
*/
|
*/
|
||||||
static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *data_buffer)
|
static int send_data(int sock, struct tftpc *client, uint32_t block_no, const uint8_t *data_buffer,
|
||||||
|
size_t data_size)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int send_count = 0, ack_count = 0;
|
int send_count = 0, ack_count = 0;
|
||||||
|
@ -66,17 +60,20 @@ static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *dat
|
||||||
.events = ZSOCK_POLLIN,
|
.events = ZSOCK_POLLIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
LOG_DBG("Client send data: block no %u, size %u", block_no, data_size);
|
LOG_DBG("Client send data: block no %u, size %u", block_no, data_size + TFTP_HEADER_SIZE);
|
||||||
|
|
||||||
/* Send data and poll for ACK response */
|
|
||||||
sys_put_be16(DATA_OPCODE, data_buffer);
|
|
||||||
sys_put_be16(block_no, data_buffer + 2);
|
|
||||||
do {
|
do {
|
||||||
if (send_count > TFTP_REQ_RETX) {
|
if (send_count > TFTP_REQ_RETX) {
|
||||||
LOG_ERR("No more retransmits. Exiting");
|
LOG_ERR("No more retransmits. Exiting");
|
||||||
return TFTPC_RETRIES_EXHAUSTED;
|
return TFTPC_RETRIES_EXHAUSTED;
|
||||||
}
|
}
|
||||||
ret = send(sock, data_buffer, data_size + TFTP_HEADER_SIZE, 0);
|
|
||||||
|
/* Prepare DATA packet, send it out then poll for ACK response */
|
||||||
|
sys_put_be16(DATA_OPCODE, client->tftp_buf);
|
||||||
|
sys_put_be16(block_no, client->tftp_buf + 2);
|
||||||
|
memcpy(client->tftp_buf + TFTP_HEADER_SIZE, data_buffer, data_size);
|
||||||
|
|
||||||
|
ret = send(sock, client->tftp_buf, data_size + TFTP_HEADER_SIZE, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("send() error: %d", -errno);
|
LOG_ERR("send() error: %d", -errno);
|
||||||
return -errno;
|
return -errno;
|
||||||
|
@ -96,7 +93,7 @@ static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *dat
|
||||||
break; /* no response, re-send data */
|
break; /* no response, re-send data */
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = recv(sock, ack_buffer, TFTPC_MAX_BUF_SIZE, 0);
|
ret = recv(sock, client->tftp_buf, TFTPC_MAX_BUF_SIZE, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("recv() error: %d", -errno);
|
LOG_ERR("recv() error: %d", -errno);
|
||||||
return -errno;
|
return -errno;
|
||||||
|
@ -106,8 +103,8 @@ static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *dat
|
||||||
break; /* wrong response, re-send data */
|
break; /* wrong response, re-send data */
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t opcode = sys_get_be16(ack_buffer);
|
uint16_t opcode = sys_get_be16(client->tftp_buf);
|
||||||
uint16_t blockno = sys_get_be16(ack_buffer + 2);
|
uint16_t blockno = sys_get_be16(client->tftp_buf + 2);
|
||||||
|
|
||||||
LOG_DBG("Receive: opcode %u, block no %u, size %d",
|
LOG_DBG("Receive: opcode %u, block no %u, size %d",
|
||||||
opcode, blockno, ret);
|
opcode, blockno, ret);
|
||||||
|
@ -118,6 +115,18 @@ static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *dat
|
||||||
LOG_WRN("Server responded with obsolete block number.");
|
LOG_WRN("Server responded with obsolete block number.");
|
||||||
ack_count++;
|
ack_count++;
|
||||||
continue; /* duplicated ACK */
|
continue; /* duplicated ACK */
|
||||||
|
} else if (opcode == ERROR_OPCODE) {
|
||||||
|
if (client->callback) {
|
||||||
|
struct tftp_evt evt = {
|
||||||
|
.type = TFTP_EVT_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
evt.param.error.msg = client->tftp_buf + TFTP_HEADER_SIZE;
|
||||||
|
evt.param.error.code = block_no;
|
||||||
|
client->callback(&evt);
|
||||||
|
}
|
||||||
|
LOG_WRN("Server responded with obsolete block number.");
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERR("Server responded with invalid opcode or block number.");
|
LOG_ERR("Server responded with invalid opcode or block number.");
|
||||||
break; /* wrong response, re-send data */
|
break; /* wrong response, re-send data */
|
||||||
|
@ -133,25 +142,25 @@ static int send_data(int sock, uint32_t block_no, size_t data_size, uint8_t *dat
|
||||||
/*
|
/*
|
||||||
* Send an Error Message to the TFTP Server.
|
* Send an Error Message to the TFTP Server.
|
||||||
*/
|
*/
|
||||||
static inline int send_err(int sock, int err_code, char *err_msg)
|
static inline int send_err(int sock, struct tftpc *client, int err_code, char *err_msg)
|
||||||
{
|
{
|
||||||
uint32_t req_size;
|
uint32_t req_size;
|
||||||
|
|
||||||
LOG_DBG("Client sending error code: %d", err_code);
|
LOG_DBG("Client sending error code: %d", err_code);
|
||||||
|
|
||||||
/* Fill in the "Err" Opcode and the actual error code. */
|
/* Fill in the "Err" Opcode and the actual error code. */
|
||||||
sys_put_be16(ERROR_OPCODE, tftpc_buffer);
|
sys_put_be16(ERROR_OPCODE, client->tftp_buf);
|
||||||
sys_put_be16(err_code, tftpc_buffer + 2);
|
sys_put_be16(err_code, client->tftp_buf + 2);
|
||||||
req_size = 4;
|
req_size = 4;
|
||||||
|
|
||||||
/* Copy the Error String. */
|
/* Copy the Error String. */
|
||||||
if (err_msg != NULL) {
|
if (err_msg != NULL) {
|
||||||
strcpy(tftpc_buffer + req_size, err_msg);
|
strcpy(client->tftp_buf + req_size, err_msg);
|
||||||
req_size += strlen(err_msg);
|
req_size += strlen(err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send Error to server. */
|
/* Send Error to server. */
|
||||||
return send(sock, tftpc_buffer, req_size, 0);
|
return send(sock, client->tftp_buf, req_size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -161,20 +170,18 @@ static inline int send_ack(int sock, struct tftphdr_ack *ackhdr)
|
||||||
{
|
{
|
||||||
LOG_DBG("Client acking block number: %d", ntohs(ackhdr->block));
|
LOG_DBG("Client acking block number: %d", ntohs(ackhdr->block));
|
||||||
|
|
||||||
send(sock, ackhdr, sizeof(struct tftphdr_ack), 0);
|
return send(sock, ackhdr, sizeof(struct tftphdr_ack), 0);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_request(int sock, struct sockaddr *server_addr,
|
static int send_request(int sock, struct tftpc *client,
|
||||||
int request, const char *remote_file, const char *mode)
|
int request, const char *remote_file, const char *mode)
|
||||||
{
|
{
|
||||||
int tx_count = 0;
|
int tx_count = 0;
|
||||||
size_t req_size;
|
size_t req_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Create TFTP Request. */
|
/* Create TFTP Request. */
|
||||||
req_size = make_request(tftpc_buffer, request, remote_file, mode);
|
req_size = make_request(client->tftp_buf, request, remote_file, mode);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
tx_count++;
|
tx_count++;
|
||||||
|
@ -183,8 +190,8 @@ static int send_request(int sock, struct sockaddr *server_addr,
|
||||||
remote_file);
|
remote_file);
|
||||||
|
|
||||||
/* Send the request to the server */
|
/* Send the request to the server */
|
||||||
ret = sendto(sock, tftpc_buffer, req_size, 0, server_addr,
|
ret = sendto(sock, client->tftp_buf, req_size, 0, &client->server,
|
||||||
ADDRLEN(server_addr));
|
ADDRLEN(client->server));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -206,10 +213,10 @@ static int send_request(int sock, struct sockaddr *server_addr,
|
||||||
struct sockaddr from_addr;
|
struct sockaddr from_addr;
|
||||||
socklen_t from_addr_len = sizeof(from_addr);
|
socklen_t from_addr_len = sizeof(from_addr);
|
||||||
|
|
||||||
ret = recvfrom(sock, tftpc_buffer, TFTPC_MAX_BUF_SIZE, 0,
|
ret = recvfrom(sock, client->tftp_buf, TFTPC_MAX_BUF_SIZE, 0,
|
||||||
&from_addr, &from_addr_len);
|
&from_addr, &from_addr_len);
|
||||||
if (ret < TFTP_HEADER_SIZE) {
|
if (ret < TFTP_HEADER_SIZE) {
|
||||||
req_size = make_request(tftpc_buffer, request,
|
req_size = make_request(client->tftp_buf, request,
|
||||||
remote_file, mode);
|
remote_file, mode);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -224,8 +231,7 @@ static int send_request(int sock, struct sockaddr *server_addr,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
int tftp_get(struct tftpc *client, const char *remote_file, const char *mode)
|
||||||
const char *remote_file, const char *mode)
|
|
||||||
{
|
{
|
||||||
int sock;
|
int sock;
|
||||||
uint32_t tftpc_block_no = 1;
|
uint32_t tftpc_block_no = 1;
|
||||||
|
@ -238,29 +244,41 @@ int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
int rcv_size;
|
int rcv_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sock = socket(server_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
if (client == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock = socket(client->server.sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
LOG_ERR("Failed to create UDP socket: %d", errno);
|
LOG_ERR("Failed to create UDP socket: %d", errno);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obtain Global Lock before accessing critical resources. */
|
|
||||||
k_mutex_lock(&tftpc_lock, K_FOREVER);
|
|
||||||
|
|
||||||
/* Send out the READ request to the TFTP Server. */
|
/* Send out the READ request to the TFTP Server. */
|
||||||
ret = send_request(sock, server_addr, READ_REQUEST, remote_file, mode);
|
ret = send_request(sock, client, READ_REQUEST, remote_file, mode);
|
||||||
rcv_size = ret;
|
rcv_size = ret;
|
||||||
|
|
||||||
while (rcv_size >= TFTP_HEADER_SIZE && rcv_size <= TFTPC_MAX_BUF_SIZE) {
|
while (rcv_size >= TFTP_HEADER_SIZE && rcv_size <= TFTPC_MAX_BUF_SIZE) {
|
||||||
/* Process server response. */
|
/* Process server response. */
|
||||||
|
uint16_t opcode = sys_get_be16(client->tftp_buf);
|
||||||
uint16_t opcode = sys_get_be16(tftpc_buffer);
|
uint16_t block_no = sys_get_be16(client->tftp_buf + 2);
|
||||||
uint16_t block_no = sys_get_be16(tftpc_buffer + 2);
|
|
||||||
|
|
||||||
LOG_DBG("Received data: opcode %u, block no %u, size %d",
|
LOG_DBG("Received data: opcode %u, block no %u, size %d",
|
||||||
opcode, block_no, rcv_size);
|
opcode, block_no, rcv_size);
|
||||||
|
|
||||||
if (opcode != DATA_OPCODE) {
|
if (opcode == ERROR_OPCODE) {
|
||||||
|
if (client->callback) {
|
||||||
|
struct tftp_evt evt = {
|
||||||
|
.type = TFTP_EVT_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
evt.param.error.msg = client->tftp_buf + TFTP_HEADER_SIZE;
|
||||||
|
evt.param.error.code = block_no;
|
||||||
|
client->callback(&evt);
|
||||||
|
}
|
||||||
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
|
break;
|
||||||
|
} else if (opcode != DATA_OPCODE) {
|
||||||
LOG_ERR("Server responded with invalid opcode.");
|
LOG_ERR("Server responded with invalid opcode.");
|
||||||
ret = TFTPC_REMOTE_ERROR;
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
break;
|
break;
|
||||||
|
@ -273,22 +291,22 @@ int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
ackhdr.block = htons(block_no);
|
ackhdr.block = htons(block_no);
|
||||||
tx_count = 0;
|
tx_count = 0;
|
||||||
|
|
||||||
if (client->callback != NULL) {
|
if (client->callback == NULL) {
|
||||||
/* Send received data directly to client */
|
LOG_ERR("No callback defined.");
|
||||||
client->callback(tftpc_buffer + TFTP_HEADER_SIZE, data_size);
|
send_err(sock, client, TFTP_ERROR_DISK_FULL, NULL);
|
||||||
} else {
|
ret = TFTPC_BUFFER_OVERFLOW;
|
||||||
/* Only copy block if user buffer has enough space */
|
goto get_end;
|
||||||
if (data_size > (client->user_buf_size - tftpc_index)) {
|
|
||||||
LOG_ERR("User buffer is full.");
|
|
||||||
send_err(sock, TFTP_ERROR_DISK_FULL, NULL);
|
|
||||||
ret = TFTPC_BUFFER_OVERFLOW;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform the actual copy. */
|
|
||||||
memcpy(client->user_buf + tftpc_index,
|
|
||||||
tftpc_buffer + TFTP_HEADER_SIZE, data_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send received data to client */
|
||||||
|
struct tftp_evt evt = {
|
||||||
|
.type = TFTP_EVT_DATA
|
||||||
|
};
|
||||||
|
|
||||||
|
evt.param.data.data_ptr = client->tftp_buf + TFTP_HEADER_SIZE;
|
||||||
|
evt.param.data.len = data_size;
|
||||||
|
client->callback(&evt);
|
||||||
|
|
||||||
/* Update the index. */
|
/* Update the index. */
|
||||||
tftpc_index += data_size;
|
tftpc_index += data_size;
|
||||||
|
|
||||||
|
@ -296,9 +314,12 @@ int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
* by datagram size < TFTPC_MAX_BUF_SIZE.
|
* by datagram size < TFTPC_MAX_BUF_SIZE.
|
||||||
*/
|
*/
|
||||||
if (rcv_size < TFTPC_MAX_BUF_SIZE) {
|
if (rcv_size < TFTPC_MAX_BUF_SIZE) {
|
||||||
ret = send_ack(sock, &ackhdr);
|
(void)send_ack(sock, &ackhdr);
|
||||||
client->user_buf_size = tftpc_index;
|
ret = tftpc_index;
|
||||||
LOG_DBG("%d bytes received.", tftpc_index);
|
LOG_DBG("%d bytes received.", tftpc_index);
|
||||||
|
/* RFC1350: The host acknowledging the final DATA packet may
|
||||||
|
* terminate its side of the connection on sending the final ACK.
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,17 +334,16 @@ int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
if (tx_count > TFTP_REQ_RETX) {
|
if (tx_count > TFTP_REQ_RETX) {
|
||||||
LOG_ERR("No more retransmits. Exiting");
|
LOG_ERR("No more retransmits. Exiting");
|
||||||
ret = TFTPC_RETRIES_EXHAUSTED;
|
ret = TFTPC_RETRIES_EXHAUSTED;
|
||||||
goto get_abort;
|
goto get_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send ACK to the TFTP Server */
|
/* Send ACK to the TFTP Server */
|
||||||
send_ack(sock, &ackhdr);
|
(void)send_ack(sock, &ackhdr);
|
||||||
tx_count++;
|
tx_count++;
|
||||||
|
|
||||||
} while (poll(&fds, 1, CONFIG_TFTPC_REQUEST_TIMEOUT) <= 0);
|
} while (poll(&fds, 1, CONFIG_TFTPC_REQUEST_TIMEOUT) <= 0);
|
||||||
|
|
||||||
/* Receive data from the TFTP Server. */
|
/* Receive data from the TFTP Server. */
|
||||||
ret = recv(sock, tftpc_buffer, TFTPC_MAX_BUF_SIZE, 0);
|
ret = recv(sock, client->tftp_buf, TFTPC_MAX_BUF_SIZE, 0);
|
||||||
rcv_size = ret;
|
rcv_size = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,14 +351,13 @@ int tftp_get(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
ret = TFTPC_REMOTE_ERROR;
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_abort:
|
get_end:
|
||||||
k_mutex_unlock(&tftpc_lock);
|
|
||||||
close(sock);
|
close(sock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tftp_put(struct sockaddr *server_addr, struct tftpc *client,
|
int tftp_put(struct tftpc *client, const char *remote_file, const char *mode,
|
||||||
const char *remote_file, const char *mode)
|
const uint8_t *user_buf, uint32_t user_buf_size)
|
||||||
{
|
{
|
||||||
int sock;
|
int sock;
|
||||||
uint32_t tftpc_block_no = 1;
|
uint32_t tftpc_block_no = 1;
|
||||||
|
@ -347,55 +366,60 @@ int tftp_put(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
uint8_t *send_buffer;
|
uint8_t *send_buffer;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (client->user_buf == NULL || client->user_buf_size == 0) {
|
if (client == NULL || user_buf == NULL || user_buf_size == 0) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sock = socket(server_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
sock = socket(client->server.sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
LOG_ERR("Failed to create UDP socket: %d", errno);
|
LOG_ERR("Failed to create UDP socket: %d", errno);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obtain Global Lock before accessing critical resources. */
|
|
||||||
k_mutex_lock(&tftpc_lock, K_FOREVER);
|
|
||||||
|
|
||||||
/* Send out the WRITE request to the TFTP Server. */
|
/* Send out the WRITE request to the TFTP Server. */
|
||||||
ret = send_request(sock, server_addr, WRITE_REQUEST, remote_file, mode);
|
ret = send_request(sock, client, WRITE_REQUEST, remote_file, mode);
|
||||||
|
|
||||||
/* Check connection initiation result */
|
/* Check connection initiation result */
|
||||||
if (ret >= TFTP_HEADER_SIZE) {
|
if (ret >= TFTP_HEADER_SIZE) {
|
||||||
uint16_t opcode = sys_get_be16(tftpc_buffer);
|
uint16_t opcode = sys_get_be16(client->tftp_buf);
|
||||||
uint16_t block_no = sys_get_be16(tftpc_buffer + 2);
|
uint16_t block_no = sys_get_be16(client->tftp_buf + 2);
|
||||||
|
|
||||||
LOG_DBG("Receive: opcode %u, block no %u, size %d", opcode, block_no, ret);
|
LOG_DBG("Receive: opcode %u, block no %u, size %d", opcode, block_no, ret);
|
||||||
|
|
||||||
if (opcode == ERROR_OPCODE) {
|
if (opcode == ERROR_OPCODE) {
|
||||||
|
if (client->callback) {
|
||||||
|
struct tftp_evt evt = {
|
||||||
|
.type = TFTP_EVT_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
evt.param.error.msg = client->tftp_buf + TFTP_HEADER_SIZE;
|
||||||
|
evt.param.error.code = block_no;
|
||||||
|
client->callback(&evt);
|
||||||
|
}
|
||||||
LOG_ERR("Server responded with service reject.");
|
LOG_ERR("Server responded with service reject.");
|
||||||
ret = TFTPC_REMOTE_ERROR;
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
goto put_abort;
|
goto put_end;
|
||||||
} else if (opcode != ACK_OPCODE || block_no != 0) {
|
} else if (opcode != ACK_OPCODE || block_no != 0) {
|
||||||
LOG_ERR("Server responded with invalid opcode or block number.");
|
LOG_ERR("Server responded with invalid opcode or block number.");
|
||||||
ret = TFTPC_REMOTE_ERROR;
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
goto put_abort;
|
goto put_end;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = TFTPC_REMOTE_ERROR;
|
ret = TFTPC_REMOTE_ERROR;
|
||||||
goto put_abort;
|
goto put_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send out data by chunks */
|
/* Send out data by chunks */
|
||||||
do {
|
do {
|
||||||
send_size = client->user_buf_size - tftpc_index;
|
send_size = user_buf_size - tftpc_index;
|
||||||
if (send_size > TFTP_BLOCK_SIZE) {
|
if (send_size > TFTP_BLOCK_SIZE) {
|
||||||
send_size = TFTP_BLOCK_SIZE;
|
send_size = TFTP_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
send_buffer = (uint8_t *)(client->user_buf + tftpc_index);
|
send_buffer = (uint8_t *)(user_buf + tftpc_index);
|
||||||
memcpy(tftpc_buffer + TFTP_HEADER_SIZE, send_buffer, send_size);
|
|
||||||
|
|
||||||
ret = send_data(sock, tftpc_block_no, send_size, tftpc_buffer);
|
ret = send_data(sock, client, tftpc_block_no, send_buffer, send_size);
|
||||||
if (ret != TFTPC_SUCCESS) {
|
if (ret != TFTPC_SUCCESS) {
|
||||||
goto put_abort;
|
goto put_end;
|
||||||
} else {
|
} else {
|
||||||
tftpc_index += send_size;
|
tftpc_index += send_size;
|
||||||
tftpc_block_no++;
|
tftpc_block_no++;
|
||||||
|
@ -404,15 +428,14 @@ int tftp_put(struct sockaddr *server_addr, struct tftpc *client,
|
||||||
/* Per RFC1350, the end of a transfer is marked
|
/* Per RFC1350, the end of a transfer is marked
|
||||||
* by datagram size < TFTPC_MAX_BUF_SIZE.
|
* by datagram size < TFTPC_MAX_BUF_SIZE.
|
||||||
*/
|
*/
|
||||||
if (send_size < TFTP_BLOCK_SIZE || tftpc_index == client->user_buf_size) {
|
if (send_size < TFTP_BLOCK_SIZE) {
|
||||||
LOG_DBG("%d bytes sent.", tftpc_index);
|
LOG_DBG("%d bytes sent.", tftpc_index);
|
||||||
client->user_buf_size = tftpc_index;
|
ret = tftpc_index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
put_abort:
|
put_end:
|
||||||
k_mutex_unlock(&tftpc_lock);
|
|
||||||
close(sock);
|
close(sock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,9 @@
|
||||||
#include <zephyr/net/tftp.h>
|
#include <zephyr/net/tftp.h>
|
||||||
|
|
||||||
/* Defines for creating static arrays for TFTP communication. */
|
/* Defines for creating static arrays for TFTP communication. */
|
||||||
#define TFTP_HEADER_SIZE 4
|
|
||||||
#define TFTP_MAX_MODE_SIZE 8
|
#define TFTP_MAX_MODE_SIZE 8
|
||||||
#define TFTP_REQ_RETX CONFIG_TFTPC_REQUEST_RETRANSMITS
|
#define TFTP_REQ_RETX CONFIG_TFTPC_REQUEST_RETRANSMITS
|
||||||
|
|
||||||
/* Maximum amount of data that can be received in a single go ! */
|
|
||||||
#define TFTPC_MAX_BUF_SIZE (TFTP_BLOCK_SIZE + TFTP_HEADER_SIZE)
|
|
||||||
|
|
||||||
/* Maximum filename size allowed by the TFTP Client. This is used as
|
/* Maximum filename size allowed by the TFTP Client. This is used as
|
||||||
* an upper bound in the "make_request" function to ensure that there
|
* an upper bound in the "make_request" function to ensure that there
|
||||||
* are no buffer overflows. The complete "tftpc_buffer" has the size of
|
* are no buffer overflows. The complete "tftpc_buffer" has the size of
|
||||||
|
|
Loading…
Reference in a new issue