Newer
Older
Daniel Stenberg
committed
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,
Patrick Monnerat
committed
bool premature)
{
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 */
Curl_pgrsDone(conn);
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,
int numsocks)
{
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);
if(state->remote_addrlen==0) {
memcpy(&state->remote_addr, &fromaddr, fromlen);
state->remote_addrlen = fromlen;
}
Daniel Stenberg
committed
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
/* 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 &&
((state->block+1) == getrpacketblock(&state->rpacket))) {
result = Curl_client_write(conn, CLIENTWRITE_BODY,
(char *)state->rpacket.data+4,
state->rbytes-4);
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);
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;
if (event)
*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;
return(0);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else if (current > state->rx_time+state->retry_time) {
if (event)
*event = TFTP_EVENT_TIMEOUT;
time(&state->rx_time); /* update even though we received nothing */
return(state->max_time-current);
}
else {
return(state->max_time-current);
}
}
Daniel Stenberg
committed
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
static curl_off_t sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
int pkt_size)
{
curl_off_t min_sleep = 0;
curl_off_t rv = 0;
if (rate_bps == 0)
return 0;
if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
/* running too fast */
rate_bps -= rate_bps >> 6;
min_sleep = 1;
}
else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
/* running too slow */
rate_bps += rate_bps >> 6;
}
rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);
if (rv < min_sleep)
rv = min_sleep;
return rv;
}
Daniel Stenberg
committed
/**********************************************************
*
* tftp_easy_statemach
*
* Handle easy request until completion
*
**********************************************************/
static CURLcode tftp_easy_statemach(struct connectdata *conn)
{
int rc;
int check_time = 0;
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
Daniel Stenberg
committed
int fd_read;
curl_off_t timeout_ms;
struct SingleRequest *k = &data->req;
struct timeval transaction_start = Curl_tvnow();
k->start = transaction_start;
k->now = transaction_start;
/* Run the TFTP State Machine */
Daniel Stenberg
committed
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
for(; (state->state != TFTP_STATE_FIN) && (result == CURLE_OK); ) {
timeout_ms = state->retry_time * 1000;
if (data->set.upload) {
if (data->set.max_send_speed &&
(data->progress.ulspeed > data->set.max_send_speed)) {
fd_read = CURL_SOCKET_BAD;
timeout_ms = sleep_time(data->set.max_send_speed,
data->progress.ulspeed, state->blksize);
}
else {
fd_read = state->sockfd;
}
}
else {
if (data->set.max_recv_speed &&
(data->progress.dlspeed > data->set.max_recv_speed)) {
fd_read = CURL_SOCKET_BAD;
timeout_ms = sleep_time(data->set.max_recv_speed,
data->progress.dlspeed, state->blksize);
}
else {
fd_read = state->sockfd;
}
}
if(data->set.timeout) {
timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
if (timeout_ms > state->retry_time * 1000)
timeout_ms = state->retry_time * 1000;
else if(timeout_ms < 0)
timeout_ms = 0;
}
/* Wait until ready to read or timeout occurs */
Daniel Stenberg
committed
rc=Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
k->now = Curl_tvnow();
/* Force a progress callback if it's been too long */
if (Curl_tvdiff(k->now, k->start) >= data->set.timeout) {
if(Curl_pgrsUpdate(conn)) {
tftp_state_machine(state, TFTP_EVENT_ERROR);
return CURLE_ABORTED_BY_CALLBACK;
}
k->start = k->now;
}
if(rc == -1) {
/* bail out */
int error = SOCKERRNO;
Daniel Stenberg
committed
failf(data, "%s", Curl_strerror(conn, error));
Daniel Stenberg
committed
state->event = TFTP_EVENT_ERROR;
}
else {
Daniel Stenberg
committed
if(rc==0) {
/* A timeout occured, but our timeout is variable, so maybe
just continue? */
long rtms = state->retry_time * 1000;
if (Curl_tvdiff(k->now, transaction_start) > rtms) {
state->event = TFTP_EVENT_TIMEOUT;
/* Force a look at transfer timeouts */
check_time = 1;
}
else {
continue; /* skip state machine */
}
}
else {
Daniel Stenberg
committed
result = tftp_receive_packet(conn);
Daniel Stenberg
committed
if (result == CURLE_OK)
transaction_start = Curl_tvnow();
if(k->bytecountp)
*k->bytecountp = k->bytecount; /* read count */
if(k->writebytecountp)
*k->writebytecountp = k->writebytecount; /* write count */
}
}
Daniel Stenberg
committed
if(check_time) {
Daniel Stenberg
committed
tftp_state_timeout(conn, NULL);
Daniel Stenberg
committed
check_time = 0;
}
Daniel Stenberg
committed
if(result)
return(result);
Daniel Stenberg
committed
result = tftp_state_machine(state, state->event);
}
/* Tell curl we're done */
Daniel Stenberg
committed
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Daniel Stenberg
committed
return(result);
}
Daniel Stenberg
committed
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
/**********************************************************
*
* 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) {
result = tftp_state_machine(state, event);
if(result != CURLE_OK)
return(result);
*done = (bool)(state->state == TFTP_STATE_FIN);
if(*done)
/* Tell curl we're done */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
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 = (bool)(state->state == TFTP_STATE_FIN);
if(*done)
/* Tell curl we're done */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
/* if rc == 0, then select() timed out */
}
Daniel Stenberg
committed
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
return result;
}
/**********************************************************
*
* tftp_doing
*
* Called from multi.c while DOing
*
**********************************************************/
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"));
}
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);
if(conn->data->state.used_interface == Curl_if_multi)
tftp_multi_statemach(conn, dophase_done);
else {
result = tftp_easy_statemach(conn);
*dophase_done = TRUE; /* with the easy interface we are done here */
}
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;
/*
Since connections can be re-used between SessionHandles, this might be a
connection already existing but on a fresh SessionHandle struct so we must
make sure we have a good 'struct TFTP' to play with. For new connections,
the struct TFTP is allocated and setup in the tftp_connect() function.
*/
Curl_reset_reqproto(conn);
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 we have encountered an error */
code = tftp_translate_code(state->error);
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;
}