Newer
Older
Daniel Stenberg
committed
*
* Therefore we use the size from the address we connected to, which we
* assume uses the same IP version and thus hopefully this works for both
* IPv4 and IPv6...
*/
rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
conn->ip_addr->ai_addrlen);
if(rc) {
Daniel Stenberg
committed
failf(conn->data, "bind() failed; %s",
Curl_strerror(conn, SOCKERRNO));
Daniel Stenberg
committed
return CURLE_COULDNT_CONNECT;
}
conn->bits.bound = TRUE;
Curl_pgrsStartNow(conn->data);
*done = TRUE;
code = CURLE_OK;
return(code);
}
/**********************************************************
*
* tftp_done
*
* The done callback
*
**********************************************************/
static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
{
Daniel Stenberg
committed
CURLcode code = CURLE_OK;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
(void)status; /* unused */
Daniel Stenberg
committed
(void)premature; /* not used */
if(Curl_pgrsDone(conn))
return CURLE_ABORTED_BY_CALLBACK;
Daniel Stenberg
committed
/* If we have encountered an error */
code = tftp_translate_code(state->error);
Daniel Stenberg
committed
return code;
}
/**********************************************************
*
Daniel Stenberg
committed
* tftp_getsock
*
Daniel Stenberg
committed
* The getsock callback
*
**********************************************************/
Daniel Stenberg
committed
static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
Daniel Stenberg
committed
{
if(!numsocks)
return GETSOCK_BLANK;
Daniel Stenberg
committed
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0);
}
/**********************************************************
*
* tftp_receive_packet
*
* Called once select fires and data is ready on the socket
*
**********************************************************/
static CURLcode tftp_receive_packet(struct connectdata *conn)
{
Daniel Stenberg
committed
struct Curl_sockaddr_storage fromaddr;
Daniel Stenberg
committed
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
struct SingleRequest *k = &data->req;
/* Receive the packet */
fromlen = sizeof(fromaddr);
state->rbytes = (int)recvfrom(state->sockfd,
(void *)state->rpacket.data,
state->blksize+4,
0,
(struct sockaddr *)&fromaddr,
&fromlen);
Daniel Stenberg
committed
if(state->remote_addrlen==0) {
memcpy(&state->remote_addr, &fromaddr, fromlen);
state->remote_addrlen = fromlen;
}
Daniel Stenberg
committed
/* Sanity check packet length */
if(state->rbytes < 4) {
failf(data, "Received too short packet");
/* Not a timeout, but how best to handle it? */
state->event = TFTP_EVENT_TIMEOUT;
}
else {
/* The event is given by the TFTP packet time */
state->event = (tftp_event_t)getrpacketevent(&state->rpacket);
switch(state->event) {
case TFTP_EVENT_DATA:
/* Don't pass to the client empty or retransmitted packets */
if(state->rbytes > 4 &&
(NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
Daniel Stenberg
committed
result = Curl_client_write(conn, CLIENTWRITE_BODY,
(char *)state->rpacket.data+4,
state->rbytes-4);
Daniel Stenberg
committed
if(result) {
tftp_state_machine(state, TFTP_EVENT_ERROR);
return result;
}
k->bytecount += state->rbytes-4;
Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
}
break;
case TFTP_EVENT_ERROR:
state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
infof(data, "%s\n", (const char *)state->rpacket.data+4);
break;
case TFTP_EVENT_ACK:
break;
case TFTP_EVENT_OACK:
result = tftp_parse_option_ack(state,
(const char *)state->rpacket.data+2,
state->rbytes-2);
Daniel Stenberg
committed
if(result)
return result;
break;
case TFTP_EVENT_RRQ:
case TFTP_EVENT_WRQ:
default:
failf(data, "%s", "Internal error: Unexpected packet");
break;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Update the progress meter */
if(Curl_pgrsUpdate(conn)) {
tftp_state_machine(state, TFTP_EVENT_ERROR);
return CURLE_ABORTED_BY_CALLBACK;
}
}
return result;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
/**********************************************************
*
* tftp_state_timeout
*
* Check if timeouts have been reached
*
**********************************************************/
static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
{
time_t current;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
Daniel Stenberg
committed
*event = TFTP_EVENT_NONE;
time(¤t);
if(current > state->max_time) {
DEBUGF(infof(conn->data, "timeout: %ld > %ld\n",
(long)current, (long)state->max_time));
Daniel Stenberg
committed
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
Daniel Stenberg
committed
}
else if(current > state->rx_time+state->retry_time) {
if(event)
Daniel Stenberg
committed
*event = TFTP_EVENT_TIMEOUT;
time(&state->rx_time); /* update even though we received nothing */
}
/* there's a typecast below here since 'time_t' may in fact be larger than
'long', but we estimate that a 'long' will still be able to hold number
of seconds even if "only" 32 bit */
return (long)(state->max_time - current);
Daniel Stenberg
committed
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
}
/**********************************************************
*
* tftp_multi_statemach
*
* Handle single RX socket event and return
*
**********************************************************/
static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
{
int rc;
tftp_event_t event;
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
long timeout_ms = tftp_state_timeout(conn, &event);
*done = FALSE;
if(timeout_ms <= 0) {
failf(data, "TFTP response timeout");
return CURLE_OPERATION_TIMEDOUT;
}
else if(event != TFTP_EVENT_NONE) {
Daniel Stenberg
committed
result = tftp_state_machine(state, event);
if(result != CURLE_OK)
return(result);
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
Daniel Stenberg
committed
if(*done)
/* Tell curl we're done */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Daniel Stenberg
committed
}
else {
/* no timeouts to handle, check our socket */
rc = Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD, 0);
if(rc == -1) {
/* bail out */
int error = SOCKERRNO;
failf(data, "%s", Curl_strerror(conn, error));
state->event = TFTP_EVENT_ERROR;
}
Daniel Stenberg
committed
else if(rc != 0) {
result = tftp_receive_packet(conn);
if(result != CURLE_OK)
return(result);
result = tftp_state_machine(state, state->event);
if(result != CURLE_OK)
return(result);
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
Daniel Stenberg
committed
if(*done)
/* Tell curl we're done */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Daniel Stenberg
committed
}
/* if rc == 0, then select() timed out */
}
Daniel Stenberg
committed
return result;
}
/**********************************************************
*
* tftp_doing
*
* Called from multi.c while DOing
Daniel Stenberg
committed
*
**********************************************************/
static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done)
{
CURLcode result;
result = tftp_multi_statemach(conn, dophase_done);
if(*dophase_done) {
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
else {
/* The multi code doesn't have this logic for the DOING state so we
provide it for TFTP since it may do the entire transfer in this
state. */
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(conn->data, Curl_tvnow());
}
Daniel Stenberg
committed
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
return result;
}
/**********************************************************
*
* tftp_peform
*
* Entry point for transfer from tftp_do, sarts state mach
*
**********************************************************/
static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
{
CURLcode result = CURLE_OK;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
*dophase_done = FALSE;
result = tftp_state_machine(state, TFTP_EVENT_INIT);
if(state->state == TFTP_STATE_FIN || result != CURLE_OK)
return(result);
tftp_multi_statemach(conn, dophase_done);
Daniel Stenberg
committed
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
if(*dophase_done)
DEBUGF(infof(conn->data, "DO phase is complete\n"));
return result;
}
/**********************************************************
*
* tftp_do
*
* The do callback
*
* This callback initiates the TFTP transfer
*
**********************************************************/
static CURLcode tftp_do(struct connectdata *conn, bool *done)
{
tftp_state_data_t *state;
CURLcode code;
*done = FALSE;
if(!conn->proto.tftpc) {
code = tftp_connect(conn, done);
if(code)
return code;
}
state = (tftp_state_data_t *)conn->proto.tftpc;
code = tftp_perform(conn, done);
/* If tftp_perform() returned an error, use that for return code. If it
was OK, see if tftp_translate_code() has an error. */
if(code == CURLE_OK)
/* If we have encountered an internal tftp error, translate it. */
code = tftp_translate_code(state->error);
Daniel Stenberg
committed
return code;
}
Patrick Monnerat
committed
static CURLcode tftp_setup_connection(struct connectdata * conn)
Patrick Monnerat
committed
{
struct SessionHandle *data = conn->data;
char * type;
char command;
conn->socktype = SOCK_DGRAM; /* UDP datagram based */
/* TFTP URLs support an extension like ";mode=<typecode>" that
* we'll try to get now! */
Daniel Stenberg
committed
type = strstr(data->state.path, ";mode=");
Patrick Monnerat
committed
Patrick Monnerat
committed
type = strstr(conn->host.rawalloc, ";mode=");
Patrick Monnerat
committed
*type = 0; /* it was in the middle of the hostname */
command = Curl_raw_toupper(type[6]);
Patrick Monnerat
committed
switch (command) {
case 'A': /* ASCII mode */
case 'N': /* NETASCII mode */
data->set.prefer_ascii = TRUE;
break;
case 'O': /* octet mode */
case 'I': /* binary mode */
default:
/* switch off ASCII */
data->set.prefer_ascii = FALSE;
break;
}
}
return CURLE_OK;
}