Skip to content

tftp: add support of blksize option to client #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 122 additions & 6 deletions src/apps/tftp/tftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@
#include "lwip/timeouts.h"
#include "lwip/debug.h"

#define TFTP_MAX_PAYLOAD_SIZE 512
#define TFTP_DEFAULT_BLOCK_SIZE 512
#define TFTP_HEADER_LENGTH 4

#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
#define TFTP_ACK 4
#define TFTP_ERROR 5
#define TFTP_OACK 6

enum tftp_error {
TFTP_ERROR_FILE_NOT_FOUND = 1,
Expand All @@ -88,9 +89,11 @@ struct tftp_state {
int timer;
int last_pkt;
u16_t blknum;
u16_t blksize;
u8_t retries;
u8_t mode_write;
u8_t tftp_mode;
bool wait_oack;
};

static struct tftp_state tftp_state;
Expand Down Expand Up @@ -137,18 +140,33 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
{
size_t fname_length = strlen(fname)+1;
size_t mode_length = strlen(mode)+1;
struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
size_t blksize_length = 0;
int blksize = tftp_state.blksize;
struct pbuf* p;
char* payload;
err_t ret;

if (blksize) {
/* 'blksize\0'.\0" with . = 1 digit */
blksize_length = strlen("blksize") + 1 + 1 + 1;
while (blksize >= 10) {
blksize /= 10;
blksize_length++;
}
}

p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - 2);
if (p == NULL) {
return ERR_MEM;
}

payload = (char*) p->payload;
MEMCPY(payload+2, fname, fname_length);
MEMCPY(payload+2+fname_length, mode, mode_length);
if (tftp_state.blksize)
sprintf(payload+2+fname_length+mode_length, "blksize%c%d%c", 0, tftp_state.blksize, 0);

tftp_state.wait_oack = true;
ret = udp_sendto(tftp_state.upcb, p, addr, port);
pbuf_free(p);
return ret;
Expand Down Expand Up @@ -221,14 +239,14 @@ send_data(const ip_addr_t *addr, u16_t port)
pbuf_free(tftp_state.last_data);
}

tftp_state.last_data = init_packet(TFTP_DATA, tftp_state.blknum, TFTP_MAX_PAYLOAD_SIZE);
tftp_state.last_data = init_packet(TFTP_DATA, tftp_state.blknum, TFTP_DEFAULT_BLOCK_SIZE);
if (tftp_state.last_data == NULL) {
return;
}

payload = (u16_t *) tftp_state.last_data->payload;

ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_DEFAULT_BLOCK_SIZE);
if (ret < 0) {
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Error occurred while reading the file.");
close_handle();
Expand All @@ -239,6 +257,64 @@ send_data(const ip_addr_t *addr, u16_t port)
resend_data(addr, port);
}

static u16_t payload_size(void)
{
if (tftp_state.blksize)
return tftp_state.blksize;
return TFTP_DEFAULT_BLOCK_SIZE;
}

/**
* find_option() - check if OACK message contains option
*
* @p: message buffer
* @option: option key
* Return: option value
*/
static const char *
find_option(struct pbuf *p, const char *option)
{
const char *pos = p->payload;
int rem = p->len;

/*
* According to RFC 2347 the OACK packet has the following format:
*
* +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
* | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
* +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
*/

/* Skip opc */
pos += 2;
rem -= 2;
if (rem <= 0)
return NULL;

for (;;) {
int len;
int diff;

len = strnlen(pos, rem) + 1;
if (rem < len)
break;
diff = strcmp(pos, option);
/* Skip option */
pos += len;
rem -= len;
len = strnlen(pos, rem) + 1;
if (rem < len)
break;
if (!diff)
return pos;
/* Skip value */
pos += len;
rem -= len;
}

return NULL;
}

static void
tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
Expand Down Expand Up @@ -338,6 +414,15 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
}

blknum = lwip_ntohs(sbuf[1]);
if (tftp_state.blksize && tftp_state.wait_oack) {
/*
* Data received while we are expecting an OACK for our blksize option.
* This means the server doesn't support it, let's switch back to the
* default block size.
*/
tftp_state.blksize = 0;
tftp_state.wait_oack = false;
}
if (blknum == tftp_state.blknum) {
pbuf_remove_header(p, TFTP_HEADER_LENGTH);

Expand All @@ -349,7 +434,7 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
send_ack(addr, port, blknum);
}

if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
if (p->tot_len < payload_size()) {
close_handle();
} else {
tftp_state.blknum++;
Expand Down Expand Up @@ -386,7 +471,7 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
lastpkt = 0;

if (tftp_state.last_data != NULL) {
lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
lastpkt = tftp_state.last_data->tot_len != (TFTP_DEFAULT_BLOCK_SIZE + TFTP_HEADER_LENGTH);
}

if (!lastpkt) {
Expand All @@ -405,6 +490,25 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
close_handle();
}
break;
case PP_HTONS(TFTP_OACK): {
const char *optval = find_option(p, "blksize");
u16_t srv_blksize = 0;
tftp_state.wait_oack = false;
if (optval) {
if (!tftp_state.blksize) {
/* We did not request this option */
send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "blksize unexpected");
}
srv_blksize = atoi(optval);
if (srv_blksize <= 0 || srv_blksize > tftp_state.blksize) {
send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Invalid blksize");
}
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: accepting blksize=%d\n", srv_blksize));
tftp_state.blksize = srv_blksize;
}
send_ack(addr, port, 0);
break;
}
default:
send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
break;
Expand Down Expand Up @@ -493,6 +597,18 @@ tftp_init_client(const struct tftp_context *ctx)
return tftp_init_common(LWIP_TFTP_MODE_CLIENT, ctx);
}

/** @ingroup tftp
* Set the block size to be used by the TFTP client. The server may choose to
* accept a lower value.
* @param blksize Block size in bytes
*/
void
tftp_client_set_blksize(u16_t blksize)
{
if (blksize != TFTP_DEFAULT_BLOCK_SIZE)
tftp_state.blksize = blksize;
}

/** @ingroup tftp
* Deinitialize ("turn off") TFTP client/server.
*/
Expand Down
1 change: 1 addition & 0 deletions src/include/lwip/apps/tftp_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum tftp_transfer_mode {
};

err_t tftp_init_client(const struct tftp_context* ctx);
void tftp_client_set_blksize(u16_t blksize);
err_t tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);
err_t tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);

Expand Down