mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
- Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app
to set desired block size to use for TFTP transfers instead of the default 512 bytes.
This commit is contained in:
parent
bb86462ed7
commit
0516ce7786
4
CHANGES
4
CHANGES
@ -7,6 +7,10 @@
|
|||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
Daniel Stenberg (26 Jan 2009)
|
Daniel Stenberg (26 Jan 2009)
|
||||||
|
- Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app
|
||||||
|
to set desired block size to use for TFTP transfers instead of the default
|
||||||
|
512 bytes.
|
||||||
|
|
||||||
- The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
|
- The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
|
||||||
disable "rfc4507bis session ticket support". rfc4507bis was later turned
|
disable "rfc4507bis session ticket support". rfc4507bis was later turned
|
||||||
into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077
|
into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077
|
||||||
|
@ -12,6 +12,7 @@ This release includes the following changes:
|
|||||||
o Added CURLOPT_NOPROXY and the corresponding --noproxy
|
o Added CURLOPT_NOPROXY and the corresponding --noproxy
|
||||||
o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by
|
o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by
|
||||||
default in openssl 0.9.8j
|
default in openssl 0.9.8j
|
||||||
|
o Added CURLOPT_TFTP_BLKSIZE
|
||||||
|
|
||||||
This release includes the following bugfixes:
|
This release includes the following bugfixes:
|
||||||
|
|
||||||
@ -27,6 +28,6 @@ This release would not have looked like this without help, code, reports and
|
|||||||
advice from friends like these:
|
advice from friends like these:
|
||||||
|
|
||||||
Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta,
|
Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta,
|
||||||
Peter Sylvester
|
Peter Sylvester, Chad Monroe
|
||||||
|
|
||||||
Thanks! (and sorry if I forgot to mention someone)
|
Thanks! (and sorry if I forgot to mention someone)
|
||||||
|
@ -1008,6 +1008,14 @@ Pass a long to tell libcurl how to act on transfer decoding. If set to zero,
|
|||||||
transfer decoding will be disabled, if set to 1 it is enabled
|
transfer decoding will be disabled, if set to 1 it is enabled
|
||||||
(default). libcurl does chunked transfer decoding by default unless this
|
(default). libcurl does chunked transfer decoding by default unless this
|
||||||
option is set to zero. (added in 7.16.2)
|
option is set to zero. (added in 7.16.2)
|
||||||
|
.SH TFTP OPTIONS
|
||||||
|
.IP CURLOPT_TFTPBLKSIZE
|
||||||
|
Specify block size to use for TFTP data transmission. Valid range as per RFC
|
||||||
|
2348 is 8-65464 bytes. The default of 512 bytes will be used if this option is
|
||||||
|
not specified. The specified block size will only be used pending support by
|
||||||
|
the remote server. If the server does not return an option acknowledgement or
|
||||||
|
returns an option acknowledgement with no blksize, the default of 512 bytes
|
||||||
|
will be used. (added in 7.19.4)
|
||||||
.SH FTP OPTIONS
|
.SH FTP OPTIONS
|
||||||
.IP CURLOPT_FTPPORT
|
.IP CURLOPT_FTPPORT
|
||||||
Pass a pointer to a zero terminated string as parameter. It will be used to
|
Pass a pointer to a zero terminated string as parameter. It will be used to
|
||||||
|
@ -1159,6 +1159,9 @@ typedef enum {
|
|||||||
disables the use of proxy. */
|
disables the use of proxy. */
|
||||||
CINIT(NOPROXY, OBJECTPOINT, 177),
|
CINIT(NOPROXY, OBJECTPOINT, 177),
|
||||||
|
|
||||||
|
/* block size for TFTP transfers */
|
||||||
|
CINIT(TFTP_BLKSIZE, LONG, 178),
|
||||||
|
|
||||||
CURLOPT_LASTENTRY /* the last unused */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
315
lib/tftp.c
315
lib/tftp.c
@ -5,7 +5,7 @@
|
|||||||
* | (__| |_| | _ <| |___
|
* | (__| |_| | _ <| |___
|
||||||
* \___|\___/|_| \_\_____|
|
* \___|\___/|_| \_\_____|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
|
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
*
|
*
|
||||||
* This software is licensed as described in the file COPYING, which
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
@ -26,7 +26,6 @@
|
|||||||
#ifndef CURL_DISABLE_TFTP
|
#ifndef CURL_DISABLE_TFTP
|
||||||
/* -- WIN32 approved -- */
|
/* -- WIN32 approved -- */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -83,8 +82,13 @@
|
|||||||
/* The last #include file should be: */
|
/* The last #include file should be: */
|
||||||
#include "memdebug.h"
|
#include "memdebug.h"
|
||||||
|
|
||||||
/* RFC2348 allows the block size to be negotiated, but we don't support that */
|
/* RFC2348 allows the block size to be negotiated */
|
||||||
#define TFTP_BLOCKSIZE 512
|
#define TFTP_BLKSIZE_DEFAULT 512
|
||||||
|
#define TFTP_BLKSIZE_MIN 8
|
||||||
|
#define TFTP_BLKSIZE_MAX 65464
|
||||||
|
#define TFTP_OPTION_BLKSIZE "blksize"
|
||||||
|
#define TFTP_OPTION_TSIZE "tsize"
|
||||||
|
#define TFTP_OPTION_INTERVAL "interval"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TFTP_MODE_NETASCII=0,
|
TFTP_MODE_NETASCII=0,
|
||||||
@ -105,6 +109,7 @@ typedef enum {
|
|||||||
TFTP_EVENT_DATA = 3,
|
TFTP_EVENT_DATA = 3,
|
||||||
TFTP_EVENT_ACK = 4,
|
TFTP_EVENT_ACK = 4,
|
||||||
TFTP_EVENT_ERROR = 5,
|
TFTP_EVENT_ERROR = 5,
|
||||||
|
TFTP_EVENT_OACK = 6,
|
||||||
TFTP_EVENT_TIMEOUT
|
TFTP_EVENT_TIMEOUT
|
||||||
} tftp_event_t;
|
} tftp_event_t;
|
||||||
|
|
||||||
@ -125,7 +130,7 @@ typedef enum {
|
|||||||
} tftp_error_t;
|
} tftp_error_t;
|
||||||
|
|
||||||
typedef struct tftp_packet {
|
typedef struct tftp_packet {
|
||||||
unsigned char data[2 + 2 + TFTP_BLOCKSIZE];
|
unsigned char *data;
|
||||||
} tftp_packet_t;
|
} tftp_packet_t;
|
||||||
|
|
||||||
typedef struct tftp_state_data {
|
typedef struct tftp_state_data {
|
||||||
@ -144,7 +149,9 @@ typedef struct tftp_state_data {
|
|||||||
struct Curl_sockaddr_storage remote_addr;
|
struct Curl_sockaddr_storage remote_addr;
|
||||||
socklen_t remote_addrlen;
|
socklen_t remote_addrlen;
|
||||||
ssize_t rbytes;
|
ssize_t rbytes;
|
||||||
int sbytes;
|
size_t sbytes;
|
||||||
|
size_t blksize;
|
||||||
|
int requested_blksize;
|
||||||
tftp_packet_t rpacket;
|
tftp_packet_t rpacket;
|
||||||
tftp_packet_t spacket;
|
tftp_packet_t spacket;
|
||||||
} tftp_state_data_t;
|
} tftp_state_data_t;
|
||||||
@ -154,6 +161,7 @@ typedef struct tftp_state_data {
|
|||||||
static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
|
static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
|
||||||
static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
|
static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
|
||||||
static CURLcode tftp_connect(struct connectdata *conn, bool *done);
|
static CURLcode tftp_connect(struct connectdata *conn, bool *done);
|
||||||
|
static CURLcode tftp_disconnect(struct connectdata *conn);
|
||||||
static CURLcode tftp_do(struct connectdata *conn, bool *done);
|
static CURLcode tftp_do(struct connectdata *conn, bool *done);
|
||||||
static CURLcode tftp_done(struct connectdata *conn,
|
static CURLcode tftp_done(struct connectdata *conn,
|
||||||
CURLcode, bool premature);
|
CURLcode, bool premature);
|
||||||
@ -176,7 +184,7 @@ const struct Curl_handler Curl_handler_tftp = {
|
|||||||
ZERO_NULL, /* proto_getsock */
|
ZERO_NULL, /* proto_getsock */
|
||||||
ZERO_NULL, /* doing_getsock */
|
ZERO_NULL, /* doing_getsock */
|
||||||
ZERO_NULL, /* perform_getsock */
|
ZERO_NULL, /* perform_getsock */
|
||||||
ZERO_NULL, /* disconnect */
|
tftp_disconnect, /* disconnect */
|
||||||
PORT_TFTP, /* defport */
|
PORT_TFTP, /* defport */
|
||||||
PROT_TFTP /* protocol */
|
PROT_TFTP /* protocol */
|
||||||
};
|
};
|
||||||
@ -295,11 +303,143 @@ static unsigned short getrpacketblock(const tftp_packet_t *packet)
|
|||||||
return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
|
return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t Curl_strnlen(const char *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
const char *end = memchr (string, '\0', maxlen);
|
||||||
|
return end ? (size_t) (end - string) : maxlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *tftp_option_get(const char *buf, size_t len,
|
||||||
|
const char **option, const char **value)
|
||||||
|
{
|
||||||
|
size_t loc;
|
||||||
|
|
||||||
|
loc = Curl_strnlen( buf, len );
|
||||||
|
loc++; /* NULL term */
|
||||||
|
|
||||||
|
if (loc >= len)
|
||||||
|
return NULL;
|
||||||
|
*option = buf;
|
||||||
|
|
||||||
|
loc += Curl_strnlen( buf+loc, len-loc );
|
||||||
|
loc++; /* NULL term */
|
||||||
|
|
||||||
|
if (loc > len)
|
||||||
|
return NULL;
|
||||||
|
*value = &buf[strlen(*option) + 1];
|
||||||
|
|
||||||
|
return &buf[loc];
|
||||||
|
}
|
||||||
|
|
||||||
|
static CURLcode tftp_parse_option_ack(tftp_state_data_t *state,
|
||||||
|
const char *ptr, int len)
|
||||||
|
{
|
||||||
|
const char *tmp = ptr;
|
||||||
|
struct SessionHandle *data = state->conn->data;
|
||||||
|
|
||||||
|
/* if OACK doesn't contain blksize option, the default (512) must be used */
|
||||||
|
state->blksize = TFTP_BLKSIZE_DEFAULT;
|
||||||
|
|
||||||
|
while (tmp < ptr + len) {
|
||||||
|
const char *option, *value;
|
||||||
|
|
||||||
|
tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value);
|
||||||
|
if(tmp == NULL) {
|
||||||
|
failf(data, "Malformed ACK packet, rejecting");
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
infof(data, "got option=(%s) value=(%s)\n", option, value);
|
||||||
|
|
||||||
|
if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
|
||||||
|
int blksize;
|
||||||
|
|
||||||
|
blksize = strtol( value, NULL, 10 );
|
||||||
|
|
||||||
|
if(!blksize) {
|
||||||
|
failf(data, "invalid blocksize value in OACK packet");
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
else if(blksize > TFTP_BLKSIZE_MAX) {
|
||||||
|
failf(data, "%s (%d)", "blksize is larger than max supported",
|
||||||
|
TFTP_BLKSIZE_MAX);
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
else if(blksize < TFTP_BLKSIZE_MIN) {
|
||||||
|
failf(data, "%s (%d)", "blksize is smaller than min supported",
|
||||||
|
TFTP_BLKSIZE_MIN);
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
else if (blksize > state->requested_blksize) {
|
||||||
|
/* could realloc pkt buffers here, but the spec doesn't call out
|
||||||
|
* support for the server requesting a bigger blksize than the client
|
||||||
|
* requests */
|
||||||
|
failf(data, "%s (%d)",
|
||||||
|
"server requested blksize larger than allocated", blksize);
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->blksize = blksize;
|
||||||
|
infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
|
||||||
|
state->blksize, "requested", state->requested_blksize);
|
||||||
|
}
|
||||||
|
else if(checkprefix(option, TFTP_OPTION_TSIZE)) {
|
||||||
|
long tsize = 0;
|
||||||
|
|
||||||
|
tsize = strtol( value, NULL, 10 );
|
||||||
|
if(!tsize) {
|
||||||
|
failf(data, "invalid tsize value in OACK packet");
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
Curl_pgrsSetDownloadSize(data, tsize);
|
||||||
|
infof(data, "%s (%d)\n", "tsize parsed from OACK", tsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t tftp_option_add(tftp_state_data_t *state, size_t csize,
|
||||||
|
char *buf, const char *option)
|
||||||
|
{
|
||||||
|
if( ( strlen(option) + csize + 1U ) > state->blksize )
|
||||||
|
return 0;
|
||||||
|
strcpy(buf, option);
|
||||||
|
return( strlen(option) + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tftp_connect_for_tx(tftp_state_data_t *state, tftp_event_t event)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
struct SessionHandle *data = state->conn->data;
|
||||||
|
|
||||||
|
infof(data, "%s\n", "Connected for transmit");
|
||||||
|
state->state = TFTP_STATE_TX;
|
||||||
|
res = tftp_set_timeouts(state);
|
||||||
|
if(res)
|
||||||
|
return(res);
|
||||||
|
return tftp_tx(state, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tftp_connect_for_rx(tftp_state_data_t *state, tftp_event_t event)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
struct SessionHandle *data = state->conn->data;
|
||||||
|
|
||||||
|
infof(data, "%s\n", "Connected for receive");
|
||||||
|
state->state = TFTP_STATE_RX;
|
||||||
|
res = tftp_set_timeouts(state);
|
||||||
|
if(res)
|
||||||
|
return(res);
|
||||||
|
return tftp_rx(state, event);
|
||||||
|
}
|
||||||
|
|
||||||
static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
|
static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
|
||||||
{
|
{
|
||||||
int sbytes;
|
int sbytes;
|
||||||
const char *mode = "octet";
|
const char *mode = "octet";
|
||||||
char *filename;
|
char *filename;
|
||||||
|
char buf[8];
|
||||||
struct SessionHandle *data = state->conn->data;
|
struct SessionHandle *data = state->conn->data;
|
||||||
CURLcode res = CURLE_OK;
|
CURLcode res = CURLE_OK;
|
||||||
|
|
||||||
@ -323,7 +463,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
/* If we are uploading, send an WRQ */
|
/* If we are uploading, send an WRQ */
|
||||||
setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
|
setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
|
||||||
state->conn->data->req.upload_fromhere =
|
state->conn->data->req.upload_fromhere =
|
||||||
(char *)&state->spacket.data[4];
|
(char *)state->spacket.data+4;
|
||||||
if(data->set.infilesize != -1)
|
if(data->set.infilesize != -1)
|
||||||
Curl_pgrsSetUploadSize(data, data->set.infilesize);
|
Curl_pgrsSetUploadSize(data, data->set.infilesize);
|
||||||
}
|
}
|
||||||
@ -332,17 +472,40 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
|
setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
|
||||||
}
|
}
|
||||||
/* As RFC3617 describes the separator slash is not actually part of the
|
/* As RFC3617 describes the separator slash is not actually part of the
|
||||||
file name so we skip the always-present first letter of the path string. */
|
file name so we skip the always-present first letter of the path
|
||||||
|
string. */
|
||||||
filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0,
|
filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0,
|
||||||
NULL);
|
NULL);
|
||||||
if(!filename)
|
if(!filename)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
snprintf((char *)&state->spacket.data[2],
|
snprintf((char *)state->spacket.data+2,
|
||||||
TFTP_BLOCKSIZE,
|
state->blksize,
|
||||||
"%s%c%s%c", filename, '\0', mode, '\0');
|
"%s%c%s%c", filename, '\0', mode, '\0');
|
||||||
sbytes = 4 + (int)strlen(filename) + (int)strlen(mode);
|
sbytes = 4 + strlen(filename) + strlen(mode);
|
||||||
sbytes = sendto(state->sockfd, (void *)&state->spacket,
|
|
||||||
|
/* add tsize option */
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes,
|
||||||
|
TFTP_OPTION_TSIZE);
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes, "0");
|
||||||
|
/* add blksize option */
|
||||||
|
snprintf( buf, sizeof(buf), "%d", state->requested_blksize );
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes,
|
||||||
|
TFTP_OPTION_BLKSIZE);
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes, buf );
|
||||||
|
/* add timeout option */
|
||||||
|
snprintf( buf, sizeof(buf), "%d", state->retry_time );
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes,
|
||||||
|
TFTP_OPTION_INTERVAL);
|
||||||
|
sbytes += tftp_option_add(state, sbytes,
|
||||||
|
(char *)state->spacket.data+sbytes, buf );
|
||||||
|
|
||||||
|
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
||||||
sbytes, 0,
|
sbytes, 0,
|
||||||
state->conn->ip_addr->ai_addr,
|
state->conn->ip_addr->ai_addr,
|
||||||
state->conn->ip_addr->ai_addrlen);
|
state->conn->ip_addr->ai_addrlen);
|
||||||
@ -352,21 +515,22 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
Curl_safefree(filename);
|
Curl_safefree(filename);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TFTP_EVENT_ACK: /* Connected for transmit */
|
case TFTP_EVENT_OACK:
|
||||||
infof(data, "%s\n", "Connected for transmit");
|
if(data->set.upload) {
|
||||||
state->state = TFTP_STATE_TX;
|
res = tftp_connect_for_tx(state, event);
|
||||||
res = tftp_set_timeouts(state);
|
}
|
||||||
if(res)
|
else {
|
||||||
|
res = tftp_connect_for_rx(state, event);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
return tftp_tx(state, event);
|
|
||||||
|
|
||||||
case TFTP_EVENT_DATA: /* connected for receive */
|
case TFTP_EVENT_ACK: /* Connected for transmit */
|
||||||
infof(data, "%s\n", "Connected for receive");
|
res = tftp_connect_for_tx(state, event);
|
||||||
state->state = TFTP_STATE_RX;
|
break;
|
||||||
res = tftp_set_timeouts(state);
|
|
||||||
if(res)
|
case TFTP_EVENT_DATA: /* Connected for receive */
|
||||||
|
res = tftp_connect_for_rx(state, event);
|
||||||
break;
|
break;
|
||||||
return tftp_rx(state, event);
|
|
||||||
|
|
||||||
case TFTP_EVENT_ERROR:
|
case TFTP_EVENT_ERROR:
|
||||||
state->state = TFTP_STATE_FIN;
|
state->state = TFTP_STATE_FIN;
|
||||||
@ -395,7 +559,6 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
switch(event) {
|
switch(event) {
|
||||||
|
|
||||||
case TFTP_EVENT_DATA:
|
case TFTP_EVENT_DATA:
|
||||||
|
|
||||||
/* Is this the block we expect? */
|
/* Is this the block we expect? */
|
||||||
rblock = getrpacketblock(&state->rpacket);
|
rblock = getrpacketblock(&state->rpacket);
|
||||||
if((state->block+1) != rblock) {
|
if((state->block+1) != rblock) {
|
||||||
@ -424,7 +587,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check if completed (That is, a less than full packet is received) */
|
/* Check if completed (That is, a less than full packet is received) */
|
||||||
if(state->rbytes < (ssize_t)sizeof(state->spacket)){
|
if(state->rbytes < (ssize_t)state->blksize+4){
|
||||||
state->state = TFTP_STATE_FIN;
|
state->state = TFTP_STATE_FIN;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -432,6 +595,25 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TFTP_EVENT_OACK:
|
||||||
|
/* ACK option acknowledgement so we can move on to data */
|
||||||
|
state->block = 0;
|
||||||
|
state->retries = 0;
|
||||||
|
setpacketevent(&state->spacket, TFTP_EVENT_ACK);
|
||||||
|
setpacketblock(&state->spacket, state->block);
|
||||||
|
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
||||||
|
4, SEND_4TH_ARG,
|
||||||
|
(struct sockaddr *)&state->remote_addr,
|
||||||
|
state->remote_addrlen);
|
||||||
|
if(sbytes < 0) {
|
||||||
|
failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
|
||||||
|
return CURLE_SEND_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we're ready to RX data */
|
||||||
|
state->state = TFTP_STATE_RX;
|
||||||
|
break;
|
||||||
|
|
||||||
case TFTP_EVENT_TIMEOUT:
|
case TFTP_EVENT_TIMEOUT:
|
||||||
/* Increment the retry count and fail if over the limit */
|
/* Increment the retry count and fail if over the limit */
|
||||||
state->retries++;
|
state->retries++;
|
||||||
@ -443,7 +625,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Resend the previous ACK */
|
/* Resend the previous ACK */
|
||||||
sbytes = sendto(state->sockfd, (void *)&state->spacket,
|
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
||||||
4, SEND_4TH_ARG,
|
4, SEND_4TH_ARG,
|
||||||
(struct sockaddr *)&state->remote_addr,
|
(struct sockaddr *)&state->remote_addr,
|
||||||
state->remote_addrlen);
|
state->remote_addrlen);
|
||||||
@ -519,11 +701,12 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
state->retries = 0;
|
state->retries = 0;
|
||||||
setpacketevent(&state->spacket, TFTP_EVENT_DATA);
|
setpacketevent(&state->spacket, TFTP_EVENT_DATA);
|
||||||
setpacketblock(&state->spacket, state->block);
|
setpacketblock(&state->spacket, state->block);
|
||||||
if(state->block > 1 && state->sbytes < TFTP_BLOCKSIZE) {
|
if(state->block > 1 && state->sbytes < state->blksize) {
|
||||||
state->state = TFTP_STATE_FIN;
|
state->state = TFTP_STATE_FIN;
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
res = Curl_fillreadbuffer(state->conn, TFTP_BLOCKSIZE, &state->sbytes);
|
res = Curl_fillreadbuffer(state->conn, state->blksize,
|
||||||
|
(int *)&state->sbytes);
|
||||||
if(res)
|
if(res)
|
||||||
return res;
|
return res;
|
||||||
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
||||||
@ -552,7 +735,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Re-send the data packet */
|
/* Re-send the data packet */
|
||||||
sbytes = sendto(state->sockfd, (void *)&state->spacket,
|
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
|
||||||
4+state->sbytes, SEND_4TH_ARG,
|
4+state->sbytes, SEND_4TH_ARG,
|
||||||
(struct sockaddr *)&state->remote_addr,
|
(struct sockaddr *)&state->remote_addr,
|
||||||
state->remote_addrlen);
|
state->remote_addrlen);
|
||||||
@ -615,6 +798,26 @@ static CURLcode tftp_state_machine(tftp_state_data_t *state,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********************************************************
|
||||||
|
*
|
||||||
|
* tftp_disconnect
|
||||||
|
*
|
||||||
|
* The disconnect callback
|
||||||
|
*
|
||||||
|
**********************************************************/
|
||||||
|
static CURLcode tftp_disconnect(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
tftp_state_data_t *state = conn->proto.tftpc;
|
||||||
|
|
||||||
|
/* done, free dynamically allocated pkt buffers */
|
||||||
|
if(state) {
|
||||||
|
Curl_safefree(state->rpacket.data);
|
||||||
|
Curl_safefree(state->spacket.data);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
*
|
*
|
||||||
@ -627,18 +830,37 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
|
|||||||
{
|
{
|
||||||
CURLcode code;
|
CURLcode code;
|
||||||
tftp_state_data_t *state;
|
tftp_state_data_t *state;
|
||||||
int rc;
|
int blksize, rc;
|
||||||
|
|
||||||
|
blksize = TFTP_BLKSIZE_DEFAULT;
|
||||||
|
|
||||||
/* If there already is a protocol-specific struct allocated for this
|
/* If there already is a protocol-specific struct allocated for this
|
||||||
sessionhandle, deal with it */
|
sessionhandle, deal with it */
|
||||||
Curl_reset_reqproto(conn);
|
Curl_reset_reqproto(conn);
|
||||||
|
|
||||||
state = conn->data->state.proto.tftp;
|
state = conn->proto.tftpc = calloc(sizeof(tftp_state_data_t), 1);
|
||||||
if(!state) {
|
|
||||||
state = conn->data->state.proto.tftp = calloc(sizeof(tftp_state_data_t),
|
|
||||||
1);
|
|
||||||
if(!state)
|
if(!state)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
/* alloc pkt buffers based on specified blksize */
|
||||||
|
if(conn->data->set.tftp_blksize) {
|
||||||
|
blksize = conn->data->set.tftp_blksize;
|
||||||
|
if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN )
|
||||||
|
return CURLE_TFTP_ILLEGAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!state->rpacket.data) {
|
||||||
|
state->rpacket.data = calloc(1, blksize + 2 + 2);
|
||||||
|
|
||||||
|
if(!state->rpacket.data)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!state->spacket.data) {
|
||||||
|
state->spacket.data = calloc(1, blksize + 2 + 2);
|
||||||
|
|
||||||
|
if(!state->spacket.data)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->bits.close = FALSE; /* keep it open if possible */
|
conn->bits.close = FALSE; /* keep it open if possible */
|
||||||
@ -647,6 +869,8 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
|
|||||||
state->sockfd = state->conn->sock[FIRSTSOCKET];
|
state->sockfd = state->conn->sock[FIRSTSOCKET];
|
||||||
state->state = TFTP_STATE_START;
|
state->state = TFTP_STATE_START;
|
||||||
state->error = TFTP_ERR_NONE;
|
state->error = TFTP_ERR_NONE;
|
||||||
|
state->blksize = TFTP_BLKSIZE_DEFAULT;
|
||||||
|
state->requested_blksize = blksize;
|
||||||
|
|
||||||
((struct sockaddr *)&state->local_addr)->sa_family =
|
((struct sockaddr *)&state->local_addr)->sa_family =
|
||||||
(unsigned short)(conn->ip_addr->ai_family);
|
(unsigned short)(conn->ip_addr->ai_family);
|
||||||
@ -735,12 +959,12 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done)
|
|||||||
*/
|
*/
|
||||||
Curl_reset_reqproto(conn);
|
Curl_reset_reqproto(conn);
|
||||||
|
|
||||||
if(!data->state.proto.tftp) {
|
if(!conn->proto.tftpc) {
|
||||||
code = tftp_connect(conn, done);
|
code = tftp_connect(conn, done);
|
||||||
if(code)
|
if(code)
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
state = (tftp_state_data_t *)data->state.proto.tftp;
|
state = (tftp_state_data_t *)conn->proto.tftpc;
|
||||||
|
|
||||||
/* Run the TFTP State Machine */
|
/* Run the TFTP State Machine */
|
||||||
for(code=tftp_state_machine(state, TFTP_EVENT_INIT);
|
for(code=tftp_state_machine(state, TFTP_EVENT_INIT);
|
||||||
@ -770,8 +994,8 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done)
|
|||||||
/* Receive the packet */
|
/* Receive the packet */
|
||||||
fromlen = sizeof(fromaddr);
|
fromlen = sizeof(fromaddr);
|
||||||
state->rbytes = (ssize_t)recvfrom(state->sockfd,
|
state->rbytes = (ssize_t)recvfrom(state->sockfd,
|
||||||
(void *)&state->rpacket,
|
(void *)state->rpacket.data,
|
||||||
sizeof(state->rpacket),
|
state->blksize+4,
|
||||||
0,
|
0,
|
||||||
(struct sockaddr *)&fromaddr,
|
(struct sockaddr *)&fromaddr,
|
||||||
&fromlen);
|
&fromlen);
|
||||||
@ -797,7 +1021,7 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done)
|
|||||||
if(state->rbytes > 4 &&
|
if(state->rbytes > 4 &&
|
||||||
((state->block+1) == getrpacketblock(&state->rpacket))) {
|
((state->block+1) == getrpacketblock(&state->rpacket))) {
|
||||||
code = Curl_client_write(conn, CLIENTWRITE_BODY,
|
code = Curl_client_write(conn, CLIENTWRITE_BODY,
|
||||||
(char *)&state->rpacket.data[4],
|
(char *)state->rpacket.data+4,
|
||||||
state->rbytes-4);
|
state->rbytes-4);
|
||||||
if(code)
|
if(code)
|
||||||
return code;
|
return code;
|
||||||
@ -807,10 +1031,17 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done)
|
|||||||
break;
|
break;
|
||||||
case TFTP_EVENT_ERROR:
|
case TFTP_EVENT_ERROR:
|
||||||
state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
|
state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
|
||||||
infof(data, "%s\n", (const char *)&state->rpacket.data[4]);
|
infof(data, "%s\n", (const char *)state->rpacket.data+4);
|
||||||
break;
|
break;
|
||||||
case TFTP_EVENT_ACK:
|
case TFTP_EVENT_ACK:
|
||||||
break;
|
break;
|
||||||
|
case TFTP_EVENT_OACK:
|
||||||
|
code = tftp_parse_option_ack(state,
|
||||||
|
(const char *)state->rpacket.data+2,
|
||||||
|
state->rbytes-2);
|
||||||
|
if(code)
|
||||||
|
return code;
|
||||||
|
break;
|
||||||
case TFTP_EVENT_RRQ:
|
case TFTP_EVENT_RRQ:
|
||||||
case TFTP_EVENT_WRQ:
|
case TFTP_EVENT_WRQ:
|
||||||
default:
|
default:
|
||||||
|
@ -932,6 +932,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
|||||||
*/
|
*/
|
||||||
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
|
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
|
||||||
break;
|
break;
|
||||||
|
case CURLOPT_TFTP_BLKSIZE:
|
||||||
|
/*
|
||||||
|
* TFTP option that specifies the block size to use for data transmission
|
||||||
|
*/
|
||||||
|
data->set.tftp_blksize = va_arg(param, long);
|
||||||
|
break;
|
||||||
case CURLOPT_DIRLISTONLY:
|
case CURLOPT_DIRLISTONLY:
|
||||||
/*
|
/*
|
||||||
* An option that changes the command to one that asks for a list
|
* An option that changes the command to one that asks for a list
|
||||||
|
@ -1047,6 +1047,7 @@ struct connectdata {
|
|||||||
union {
|
union {
|
||||||
struct ftp_conn ftpc;
|
struct ftp_conn ftpc;
|
||||||
struct ssh_conn sshc;
|
struct ssh_conn sshc;
|
||||||
|
struct tftp_state_data *tftpc;
|
||||||
} proto;
|
} proto;
|
||||||
|
|
||||||
int cselect_bits; /* bitmask of socket events */
|
int cselect_bits; /* bitmask of socket events */
|
||||||
@ -1288,7 +1289,7 @@ struct UrlState {
|
|||||||
struct HTTP *http;
|
struct HTTP *http;
|
||||||
struct HTTP *https; /* alias, just for the sake of being more readable */
|
struct HTTP *https; /* alias, just for the sake of being more readable */
|
||||||
struct FTP *ftp;
|
struct FTP *ftp;
|
||||||
void *tftp; /* private for tftp.c-eyes only */
|
/* void *tftp; not used */
|
||||||
struct FILEPROTO *file;
|
struct FILEPROTO *file;
|
||||||
void *telnet; /* private for telnet.c-eyes only */
|
void *telnet; /* private for telnet.c-eyes only */
|
||||||
void *generic;
|
void *generic;
|
||||||
@ -1425,6 +1426,7 @@ struct UserDefined {
|
|||||||
long timeout; /* in milliseconds, 0 means no timeout */
|
long timeout; /* in milliseconds, 0 means no timeout */
|
||||||
long connecttimeout; /* in milliseconds, 0 means no timeout */
|
long connecttimeout; /* in milliseconds, 0 means no timeout */
|
||||||
long ftp_response_timeout; /* in milliseconds, 0 means no timeout */
|
long ftp_response_timeout; /* in milliseconds, 0 means no timeout */
|
||||||
|
long tftp_blksize ; /* in bytes, 0 means use default */
|
||||||
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
|
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
|
||||||
long low_speed_limit; /* bytes/second */
|
long low_speed_limit; /* bytes/second */
|
||||||
long low_speed_time; /* number of seconds */
|
long low_speed_time; /* number of seconds */
|
||||||
|
Loading…
Reference in New Issue
Block a user