Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/tftp.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 9 * | |
| 10 * This software is licensed as described in the file COPYING, which | |
| 11 * you should have received as part of this distribution. The terms | |
| 12 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 13 * | |
| 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 15 * copies of the Software, and permit persons to whom the Software is | |
| 16 * furnished to do so, under the terms of the COPYING file. | |
| 17 * | |
| 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 19 * KIND, either express or implied. | |
| 20 * | |
| 21 ***************************************************************************/ | |
| 22 | |
| 23 #include "curl_setup.h" | |
| 24 | |
| 25 #ifndef CURL_DISABLE_TFTP | |
| 26 | |
| 27 #ifdef HAVE_NETINET_IN_H | |
| 28 #include <netinet/in.h> | |
| 29 #endif | |
| 30 #ifdef HAVE_NETDB_H | |
| 31 #include <netdb.h> | |
| 32 #endif | |
| 33 #ifdef HAVE_ARPA_INET_H | |
| 34 #include <arpa/inet.h> | |
| 35 #endif | |
| 36 #ifdef HAVE_NET_IF_H | |
| 37 #include <net/if.h> | |
| 38 #endif | |
| 39 #ifdef HAVE_SYS_IOCTL_H | |
| 40 #include <sys/ioctl.h> | |
| 41 #endif | |
| 42 | |
| 43 #ifdef HAVE_SYS_PARAM_H | |
| 44 #include <sys/param.h> | |
| 45 #endif | |
| 46 | |
| 47 #include "urldata.h" | |
| 48 #include <curl/curl.h> | |
| 49 #include "transfer.h" | |
| 50 #include "sendf.h" | |
| 51 #include "tftp.h" | |
| 52 #include "progress.h" | |
| 53 #include "connect.h" | |
| 54 #include "strerror.h" | |
| 55 #include "sockaddr.h" /* required for Curl_sockaddr_storage */ | |
| 56 #include "multiif.h" | |
| 57 #include "url.h" | |
| 58 #include "strcase.h" | |
| 59 #include "speedcheck.h" | |
| 60 #include "select.h" | |
| 61 #include "escape.h" | |
| 62 | |
| 63 /* The last 3 #include files should be in this order */ | |
| 64 #include "curl_printf.h" | |
| 65 #include "curl_memory.h" | |
| 66 #include "memdebug.h" | |
| 67 | |
| 68 /* RFC2348 allows the block size to be negotiated */ | |
| 69 #define TFTP_BLKSIZE_DEFAULT 512 | |
| 70 #define TFTP_BLKSIZE_MIN 8 | |
| 71 #define TFTP_BLKSIZE_MAX 65464 | |
| 72 #define TFTP_OPTION_BLKSIZE "blksize" | |
| 73 | |
| 74 /* from RFC2349: */ | |
| 75 #define TFTP_OPTION_TSIZE "tsize" | |
| 76 #define TFTP_OPTION_INTERVAL "timeout" | |
| 77 | |
| 78 typedef enum { | |
| 79 TFTP_MODE_NETASCII = 0, | |
| 80 TFTP_MODE_OCTET | |
| 81 } tftp_mode_t; | |
| 82 | |
| 83 typedef enum { | |
| 84 TFTP_STATE_START = 0, | |
| 85 TFTP_STATE_RX, | |
| 86 TFTP_STATE_TX, | |
| 87 TFTP_STATE_FIN | |
| 88 } tftp_state_t; | |
| 89 | |
| 90 typedef enum { | |
| 91 TFTP_EVENT_NONE = -1, | |
| 92 TFTP_EVENT_INIT = 0, | |
| 93 TFTP_EVENT_RRQ = 1, | |
| 94 TFTP_EVENT_WRQ = 2, | |
| 95 TFTP_EVENT_DATA = 3, | |
| 96 TFTP_EVENT_ACK = 4, | |
| 97 TFTP_EVENT_ERROR = 5, | |
| 98 TFTP_EVENT_OACK = 6, | |
| 99 TFTP_EVENT_TIMEOUT | |
| 100 } tftp_event_t; | |
| 101 | |
| 102 typedef enum { | |
| 103 TFTP_ERR_UNDEF = 0, | |
| 104 TFTP_ERR_NOTFOUND, | |
| 105 TFTP_ERR_PERM, | |
| 106 TFTP_ERR_DISKFULL, | |
| 107 TFTP_ERR_ILLEGAL, | |
| 108 TFTP_ERR_UNKNOWNID, | |
| 109 TFTP_ERR_EXISTS, | |
| 110 TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */ | |
| 111 | |
| 112 /* The remaining error codes are internal to curl */ | |
| 113 TFTP_ERR_NONE = -100, | |
| 114 TFTP_ERR_TIMEOUT, | |
| 115 TFTP_ERR_NORESPONSE | |
| 116 } tftp_error_t; | |
| 117 | |
| 118 typedef struct tftp_packet { | |
| 119 unsigned char *data; | |
| 120 } tftp_packet_t; | |
| 121 | |
| 122 typedef struct tftp_state_data { | |
| 123 tftp_state_t state; | |
| 124 tftp_mode_t mode; | |
| 125 tftp_error_t error; | |
| 126 tftp_event_t event; | |
| 127 struct connectdata *conn; | |
| 128 curl_socket_t sockfd; | |
| 129 int retries; | |
| 130 int retry_time; | |
| 131 int retry_max; | |
| 132 time_t start_time; | |
| 133 time_t max_time; | |
| 134 time_t rx_time; | |
| 135 unsigned short block; | |
| 136 struct Curl_sockaddr_storage local_addr; | |
| 137 struct Curl_sockaddr_storage remote_addr; | |
| 138 curl_socklen_t remote_addrlen; | |
| 139 int rbytes; | |
| 140 int sbytes; | |
| 141 int blksize; | |
| 142 int requested_blksize; | |
| 143 tftp_packet_t rpacket; | |
| 144 tftp_packet_t spacket; | |
| 145 } tftp_state_data_t; | |
| 146 | |
| 147 | |
| 148 /* Forward declarations */ | |
| 149 static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event); | |
| 150 static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event); | |
| 151 static CURLcode tftp_connect(struct connectdata *conn, bool *done); | |
| 152 static CURLcode tftp_disconnect(struct connectdata *conn, | |
| 153 bool dead_connection); | |
| 154 static CURLcode tftp_do(struct connectdata *conn, bool *done); | |
| 155 static CURLcode tftp_done(struct connectdata *conn, | |
| 156 CURLcode, bool premature); | |
| 157 static CURLcode tftp_setup_connection(struct connectdata * conn); | |
| 158 static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done); | |
| 159 static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done); | |
| 160 static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks); | |
| 161 static CURLcode tftp_translate_code(tftp_error_t error); | |
| 162 | |
| 163 | |
| 164 /* | |
| 165 * TFTP protocol handler. | |
| 166 */ | |
| 167 | |
| 168 const struct Curl_handler Curl_handler_tftp = { | |
| 169 "TFTP", /* scheme */ | |
| 170 tftp_setup_connection, /* setup_connection */ | |
| 171 tftp_do, /* do_it */ | |
| 172 tftp_done, /* done */ | |
| 173 ZERO_NULL, /* do_more */ | |
| 174 tftp_connect, /* connect_it */ | |
| 175 tftp_multi_statemach, /* connecting */ | |
| 176 tftp_doing, /* doing */ | |
| 177 tftp_getsock, /* proto_getsock */ | |
| 178 tftp_getsock, /* doing_getsock */ | |
| 179 ZERO_NULL, /* domore_getsock */ | |
| 180 ZERO_NULL, /* perform_getsock */ | |
| 181 tftp_disconnect, /* disconnect */ | |
| 182 ZERO_NULL, /* readwrite */ | |
| 183 ZERO_NULL, /* connection_check */ | |
| 184 PORT_TFTP, /* defport */ | |
| 185 CURLPROTO_TFTP, /* protocol */ | |
| 186 PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ | |
| 187 }; | |
| 188 | |
| 189 /********************************************************** | |
| 190 * | |
| 191 * tftp_set_timeouts - | |
| 192 * | |
| 193 * Set timeouts based on state machine state. | |
| 194 * Use user provided connect timeouts until DATA or ACK | |
| 195 * packet is received, then use user-provided transfer timeouts | |
| 196 * | |
| 197 * | |
| 198 **********************************************************/ | |
| 199 static CURLcode tftp_set_timeouts(tftp_state_data_t *state) | |
| 200 { | |
| 201 time_t maxtime, timeout; | |
| 202 timediff_t timeout_ms; | |
| 203 bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; | |
| 204 | |
| 205 time(&state->start_time); | |
| 206 | |
| 207 /* Compute drop-dead time */ | |
| 208 timeout_ms = Curl_timeleft(state->conn->data, NULL, start); | |
| 209 | |
| 210 if(timeout_ms < 0) { | |
| 211 /* time-out, bail out, go home */ | |
| 212 failf(state->conn->data, "Connection time-out"); | |
| 213 return CURLE_OPERATION_TIMEDOUT; | |
| 214 } | |
| 215 | |
| 216 if(start) { | |
| 217 | |
| 218 maxtime = (time_t)(timeout_ms + 500) / 1000; | |
| 219 state->max_time = state->start_time + maxtime; | |
| 220 | |
| 221 /* Set per-block timeout to total */ | |
| 222 timeout = maxtime; | |
| 223 | |
| 224 /* Average restart after 5 seconds */ | |
| 225 state->retry_max = (int)timeout/5; | |
| 226 | |
| 227 if(state->retry_max < 1) | |
| 228 /* avoid division by zero below */ | |
| 229 state->retry_max = 1; | |
| 230 | |
| 231 /* Compute the re-start interval to suit the timeout */ | |
| 232 state->retry_time = (int)timeout/state->retry_max; | |
| 233 if(state->retry_time<1) | |
| 234 state->retry_time = 1; | |
| 235 | |
| 236 } | |
| 237 else { | |
| 238 if(timeout_ms > 0) | |
| 239 maxtime = (time_t)(timeout_ms + 500) / 1000; | |
| 240 else | |
| 241 maxtime = 3600; | |
| 242 | |
| 243 state->max_time = state->start_time + maxtime; | |
| 244 | |
| 245 /* Set per-block timeout to total */ | |
| 246 timeout = maxtime; | |
| 247 | |
| 248 /* Average reposting an ACK after 5 seconds */ | |
| 249 state->retry_max = (int)timeout/5; | |
| 250 } | |
| 251 /* But bound the total number */ | |
| 252 if(state->retry_max<3) | |
| 253 state->retry_max = 3; | |
| 254 | |
| 255 if(state->retry_max>50) | |
| 256 state->retry_max = 50; | |
| 257 | |
| 258 /* Compute the re-ACK interval to suit the timeout */ | |
| 259 state->retry_time = (int)(timeout/state->retry_max); | |
| 260 if(state->retry_time<1) | |
| 261 state->retry_time = 1; | |
| 262 | |
| 263 infof(state->conn->data, | |
| 264 "set timeouts for state %d; Total %ld, retry %d maxtry %d\n", | |
| 265 (int)state->state, (long)(state->max_time-state->start_time), | |
| 266 state->retry_time, state->retry_max); | |
| 267 | |
| 268 /* init RX time */ | |
| 269 time(&state->rx_time); | |
| 270 | |
| 271 return CURLE_OK; | |
| 272 } | |
| 273 | |
| 274 /********************************************************** | |
| 275 * | |
| 276 * tftp_set_send_first | |
| 277 * | |
| 278 * Event handler for the START state | |
| 279 * | |
| 280 **********************************************************/ | |
| 281 | |
| 282 static void setpacketevent(tftp_packet_t *packet, unsigned short num) | |
| 283 { | |
| 284 packet->data[0] = (unsigned char)(num >> 8); | |
| 285 packet->data[1] = (unsigned char)(num & 0xff); | |
| 286 } | |
| 287 | |
| 288 | |
| 289 static void setpacketblock(tftp_packet_t *packet, unsigned short num) | |
| 290 { | |
| 291 packet->data[2] = (unsigned char)(num >> 8); | |
| 292 packet->data[3] = (unsigned char)(num & 0xff); | |
| 293 } | |
| 294 | |
| 295 static unsigned short getrpacketevent(const tftp_packet_t *packet) | |
| 296 { | |
| 297 return (unsigned short)((packet->data[0] << 8) | packet->data[1]); | |
| 298 } | |
| 299 | |
| 300 static unsigned short getrpacketblock(const tftp_packet_t *packet) | |
| 301 { | |
| 302 return (unsigned short)((packet->data[2] << 8) | packet->data[3]); | |
| 303 } | |
| 304 | |
| 305 static size_t Curl_strnlen(const char *string, size_t maxlen) | |
| 306 { | |
| 307 const char *end = memchr(string, '\0', maxlen); | |
| 308 return end ? (size_t) (end - string) : maxlen; | |
| 309 } | |
| 310 | |
| 311 static const char *tftp_option_get(const char *buf, size_t len, | |
| 312 const char **option, const char **value) | |
| 313 { | |
| 314 size_t loc; | |
| 315 | |
| 316 loc = Curl_strnlen(buf, len); | |
| 317 loc++; /* NULL term */ | |
| 318 | |
| 319 if(loc >= len) | |
| 320 return NULL; | |
| 321 *option = buf; | |
| 322 | |
| 323 loc += Curl_strnlen(buf + loc, len-loc); | |
| 324 loc++; /* NULL term */ | |
| 325 | |
| 326 if(loc > len) | |
| 327 return NULL; | |
| 328 *value = &buf[strlen(*option) + 1]; | |
| 329 | |
| 330 return &buf[loc]; | |
| 331 } | |
| 332 | |
| 333 static CURLcode tftp_parse_option_ack(tftp_state_data_t *state, | |
| 334 const char *ptr, int len) | |
| 335 { | |
| 336 const char *tmp = ptr; | |
| 337 struct Curl_easy *data = state->conn->data; | |
| 338 | |
| 339 /* if OACK doesn't contain blksize option, the default (512) must be used */ | |
| 340 state->blksize = TFTP_BLKSIZE_DEFAULT; | |
| 341 | |
| 342 while(tmp < ptr + len) { | |
| 343 const char *option, *value; | |
| 344 | |
| 345 tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); | |
| 346 if(tmp == NULL) { | |
| 347 failf(data, "Malformed ACK packet, rejecting"); | |
| 348 return CURLE_TFTP_ILLEGAL; | |
| 349 } | |
| 350 | |
| 351 infof(data, "got option=(%s) value=(%s)\n", option, value); | |
| 352 | |
| 353 if(checkprefix(option, TFTP_OPTION_BLKSIZE)) { | |
| 354 long blksize; | |
| 355 | |
| 356 blksize = strtol(value, NULL, 10); | |
| 357 | |
| 358 if(!blksize) { | |
| 359 failf(data, "invalid blocksize value in OACK packet"); | |
| 360 return CURLE_TFTP_ILLEGAL; | |
| 361 } | |
| 362 if(blksize > TFTP_BLKSIZE_MAX) { | |
| 363 failf(data, "%s (%d)", "blksize is larger than max supported", | |
| 364 TFTP_BLKSIZE_MAX); | |
| 365 return CURLE_TFTP_ILLEGAL; | |
| 366 } | |
| 367 else if(blksize < TFTP_BLKSIZE_MIN) { | |
| 368 failf(data, "%s (%d)", "blksize is smaller than min supported", | |
| 369 TFTP_BLKSIZE_MIN); | |
| 370 return CURLE_TFTP_ILLEGAL; | |
| 371 } | |
| 372 else if(blksize > state->requested_blksize) { | |
| 373 /* could realloc pkt buffers here, but the spec doesn't call out | |
| 374 * support for the server requesting a bigger blksize than the client | |
| 375 * requests */ | |
| 376 failf(data, "%s (%ld)", | |
| 377 "server requested blksize larger than allocated", blksize); | |
| 378 return CURLE_TFTP_ILLEGAL; | |
| 379 } | |
| 380 | |
| 381 state->blksize = (int)blksize; | |
| 382 infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK", | |
| 383 state->blksize, "requested", state->requested_blksize); | |
| 384 } | |
| 385 else if(checkprefix(option, TFTP_OPTION_TSIZE)) { | |
| 386 long tsize = 0; | |
| 387 | |
| 388 tsize = strtol(value, NULL, 10); | |
| 389 infof(data, "%s (%ld)\n", "tsize parsed from OACK", tsize); | |
| 390 | |
| 391 /* tsize should be ignored on upload: Who cares about the size of the | |
| 392 remote file? */ | |
| 393 if(!data->set.upload) { | |
| 394 if(!tsize) { | |
| 395 failf(data, "invalid tsize -:%s:- value in OACK packet", value); | |
| 396 return CURLE_TFTP_ILLEGAL; | |
| 397 } | |
| 398 Curl_pgrsSetDownloadSize(data, tsize); | |
| 399 } | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 return CURLE_OK; | |
| 404 } | |
| 405 | |
| 406 static CURLcode tftp_option_add(tftp_state_data_t *state, size_t *csize, | |
| 407 char *buf, const char *option) | |
| 408 { | |
| 409 if(( strlen(option) + *csize + 1) > (size_t)state->blksize) | |
| 410 return CURLE_TFTP_ILLEGAL; | |
| 411 strcpy(buf, option); | |
| 412 *csize += strlen(option) + 1; | |
| 413 return CURLE_OK; | |
| 414 } | |
| 415 | |
| 416 static CURLcode tftp_connect_for_tx(tftp_state_data_t *state, | |
| 417 tftp_event_t event) | |
| 418 { | |
| 419 CURLcode result; | |
| 420 #ifndef CURL_DISABLE_VERBOSE_STRINGS | |
| 421 struct Curl_easy *data = state->conn->data; | |
| 422 | |
| 423 infof(data, "%s\n", "Connected for transmit"); | |
| 424 #endif | |
| 425 state->state = TFTP_STATE_TX; | |
| 426 result = tftp_set_timeouts(state); | |
| 427 if(result) | |
| 428 return result; | |
| 429 return tftp_tx(state, event); | |
| 430 } | |
| 431 | |
| 432 static CURLcode tftp_connect_for_rx(tftp_state_data_t *state, | |
| 433 tftp_event_t event) | |
| 434 { | |
| 435 CURLcode result; | |
| 436 #ifndef CURL_DISABLE_VERBOSE_STRINGS | |
| 437 struct Curl_easy *data = state->conn->data; | |
| 438 | |
| 439 infof(data, "%s\n", "Connected for receive"); | |
| 440 #endif | |
| 441 state->state = TFTP_STATE_RX; | |
| 442 result = tftp_set_timeouts(state); | |
| 443 if(result) | |
| 444 return result; | |
| 445 return tftp_rx(state, event); | |
| 446 } | |
| 447 | |
| 448 static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) | |
| 449 { | |
| 450 size_t sbytes; | |
| 451 ssize_t senddata; | |
| 452 const char *mode = "octet"; | |
| 453 char *filename; | |
| 454 struct Curl_easy *data = state->conn->data; | |
| 455 CURLcode result = CURLE_OK; | |
| 456 | |
| 457 /* Set ascii mode if -B flag was used */ | |
| 458 if(data->set.prefer_ascii) | |
| 459 mode = "netascii"; | |
| 460 | |
| 461 switch(event) { | |
| 462 | |
| 463 case TFTP_EVENT_INIT: /* Send the first packet out */ | |
| 464 case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ | |
| 465 /* Increment the retry counter, quit if over the limit */ | |
| 466 state->retries++; | |
| 467 if(state->retries>state->retry_max) { | |
| 468 state->error = TFTP_ERR_NORESPONSE; | |
| 469 state->state = TFTP_STATE_FIN; | |
| 470 return result; | |
| 471 } | |
| 472 | |
| 473 if(data->set.upload) { | |
| 474 /* If we are uploading, send an WRQ */ | |
| 475 setpacketevent(&state->spacket, TFTP_EVENT_WRQ); | |
| 476 state->conn->data->req.upload_fromhere = | |
| 477 (char *)state->spacket.data + 4; | |
| 478 if(data->state.infilesize != -1) | |
| 479 Curl_pgrsSetUploadSize(data, data->state.infilesize); | |
| 480 } | |
| 481 else { | |
| 482 /* If we are downloading, send an RRQ */ | |
| 483 setpacketevent(&state->spacket, TFTP_EVENT_RRQ); | |
| 484 } | |
| 485 /* As RFC3617 describes the separator slash is not actually part of the | |
| 486 file name so we skip the always-present first letter of the path | |
| 487 string. */ | |
| 488 result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0, | |
| 489 &filename, NULL, FALSE); | |
| 490 if(result) | |
| 491 return result; | |
| 492 | |
| 493 if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { | |
| 494 failf(data, "TFTP file name too long\n"); | |
| 495 free(filename); | |
| 496 return CURLE_TFTP_ILLEGAL; /* too long file name field */ | |
| 497 } | |
| 498 | |
| 499 msnprintf((char *)state->spacket.data + 2, | |
| 500 state->blksize, | |
| 501 "%s%c%s%c", filename, '\0', mode, '\0'); | |
| 502 sbytes = 4 + strlen(filename) + strlen(mode); | |
| 503 | |
| 504 /* optional addition of TFTP options */ | |
| 505 if(!data->set.tftp_no_options) { | |
| 506 char buf[64]; | |
| 507 /* add tsize option */ | |
| 508 if(data->set.upload && (data->state.infilesize != -1)) | |
| 509 msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T, | |
| 510 data->state.infilesize); | |
| 511 else | |
| 512 strcpy(buf, "0"); /* the destination is large enough */ | |
| 513 | |
| 514 result = tftp_option_add(state, &sbytes, | |
| 515 (char *)state->spacket.data + sbytes, | |
| 516 TFTP_OPTION_TSIZE); | |
| 517 if(result == CURLE_OK) | |
| 518 result = tftp_option_add(state, &sbytes, | |
| 519 (char *)state->spacket.data + sbytes, buf); | |
| 520 | |
| 521 /* add blksize option */ | |
| 522 msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); | |
| 523 if(result == CURLE_OK) | |
| 524 result = tftp_option_add(state, &sbytes, | |
| 525 (char *)state->spacket.data + sbytes, | |
| 526 TFTP_OPTION_BLKSIZE); | |
| 527 if(result == CURLE_OK) | |
| 528 result = tftp_option_add(state, &sbytes, | |
| 529 (char *)state->spacket.data + sbytes, buf); | |
| 530 | |
| 531 /* add timeout option */ | |
| 532 msnprintf(buf, sizeof(buf), "%d", state->retry_time); | |
| 533 if(result == CURLE_OK) | |
| 534 result = tftp_option_add(state, &sbytes, | |
| 535 (char *)state->spacket.data + sbytes, | |
| 536 TFTP_OPTION_INTERVAL); | |
| 537 if(result == CURLE_OK) | |
| 538 result = tftp_option_add(state, &sbytes, | |
| 539 (char *)state->spacket.data + sbytes, buf); | |
| 540 | |
| 541 if(result != CURLE_OK) { | |
| 542 failf(data, "TFTP buffer too small for options"); | |
| 543 free(filename); | |
| 544 return CURLE_TFTP_ILLEGAL; | |
| 545 } | |
| 546 } | |
| 547 | |
| 548 /* the typecase for the 3rd argument is mostly for systems that do | |
| 549 not have a size_t argument, like older unixes that want an 'int' */ | |
| 550 senddata = sendto(state->sockfd, (void *)state->spacket.data, | |
| 551 (SEND_TYPE_ARG3)sbytes, 0, | |
| 552 state->conn->ip_addr->ai_addr, | |
| 553 state->conn->ip_addr->ai_addrlen); | |
| 554 if(senddata != (ssize_t)sbytes) { | |
| 555 char buffer[STRERROR_LEN]; | |
| 556 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 557 } | |
| 558 free(filename); | |
| 559 break; | |
| 560 | |
| 561 case TFTP_EVENT_OACK: | |
| 562 if(data->set.upload) { | |
| 563 result = tftp_connect_for_tx(state, event); | |
| 564 } | |
| 565 else { | |
| 566 result = tftp_connect_for_rx(state, event); | |
| 567 } | |
| 568 break; | |
| 569 | |
| 570 case TFTP_EVENT_ACK: /* Connected for transmit */ | |
| 571 result = tftp_connect_for_tx(state, event); | |
| 572 break; | |
| 573 | |
| 574 case TFTP_EVENT_DATA: /* Connected for receive */ | |
| 575 result = tftp_connect_for_rx(state, event); | |
| 576 break; | |
| 577 | |
| 578 case TFTP_EVENT_ERROR: | |
| 579 state->state = TFTP_STATE_FIN; | |
| 580 break; | |
| 581 | |
| 582 default: | |
| 583 failf(state->conn->data, "tftp_send_first: internal error"); | |
| 584 break; | |
| 585 } | |
| 586 | |
| 587 return result; | |
| 588 } | |
| 589 | |
| 590 /* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit | |
| 591 boundary */ | |
| 592 #define NEXT_BLOCKNUM(x) (((x) + 1)&0xffff) | |
| 593 | |
| 594 /********************************************************** | |
| 595 * | |
| 596 * tftp_rx | |
| 597 * | |
| 598 * Event handler for the RX state | |
| 599 * | |
| 600 **********************************************************/ | |
| 601 static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) | |
| 602 { | |
| 603 ssize_t sbytes; | |
| 604 int rblock; | |
| 605 struct Curl_easy *data = state->conn->data; | |
| 606 char buffer[STRERROR_LEN]; | |
| 607 | |
| 608 switch(event) { | |
| 609 | |
| 610 case TFTP_EVENT_DATA: | |
| 611 /* Is this the block we expect? */ | |
| 612 rblock = getrpacketblock(&state->rpacket); | |
| 613 if(NEXT_BLOCKNUM(state->block) == rblock) { | |
| 614 /* This is the expected block. Reset counters and ACK it. */ | |
| 615 state->retries = 0; | |
| 616 } | |
| 617 else if(state->block == rblock) { | |
| 618 /* This is the last recently received block again. Log it and ACK it | |
| 619 again. */ | |
| 620 infof(data, "Received last DATA packet block %d again.\n", rblock); | |
| 621 } | |
| 622 else { | |
| 623 /* totally unexpected, just log it */ | |
| 624 infof(data, | |
| 625 "Received unexpected DATA packet block %d, expecting block %d\n", | |
| 626 rblock, NEXT_BLOCKNUM(state->block)); | |
| 627 break; | |
| 628 } | |
| 629 | |
| 630 /* ACK this block. */ | |
| 631 state->block = (unsigned short)rblock; | |
| 632 setpacketevent(&state->spacket, TFTP_EVENT_ACK); | |
| 633 setpacketblock(&state->spacket, state->block); | |
| 634 sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| 635 4, SEND_4TH_ARG, | |
| 636 (struct sockaddr *)&state->remote_addr, | |
| 637 state->remote_addrlen); | |
| 638 if(sbytes < 0) { | |
| 639 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 640 return CURLE_SEND_ERROR; | |
| 641 } | |
| 642 | |
| 643 /* Check if completed (That is, a less than full packet is received) */ | |
| 644 if(state->rbytes < (ssize_t)state->blksize + 4) { | |
| 645 state->state = TFTP_STATE_FIN; | |
| 646 } | |
| 647 else { | |
| 648 state->state = TFTP_STATE_RX; | |
| 649 } | |
| 650 time(&state->rx_time); | |
| 651 break; | |
| 652 | |
| 653 case TFTP_EVENT_OACK: | |
| 654 /* ACK option acknowledgement so we can move on to data */ | |
| 655 state->block = 0; | |
| 656 state->retries = 0; | |
| 657 setpacketevent(&state->spacket, TFTP_EVENT_ACK); | |
| 658 setpacketblock(&state->spacket, state->block); | |
| 659 sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| 660 4, SEND_4TH_ARG, | |
| 661 (struct sockaddr *)&state->remote_addr, | |
| 662 state->remote_addrlen); | |
| 663 if(sbytes < 0) { | |
| 664 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 665 return CURLE_SEND_ERROR; | |
| 666 } | |
| 667 | |
| 668 /* we're ready to RX data */ | |
| 669 state->state = TFTP_STATE_RX; | |
| 670 time(&state->rx_time); | |
| 671 break; | |
| 672 | |
| 673 case TFTP_EVENT_TIMEOUT: | |
| 674 /* Increment the retry count and fail if over the limit */ | |
| 675 state->retries++; | |
| 676 infof(data, | |
| 677 "Timeout waiting for block %d ACK. Retries = %d\n", | |
| 678 NEXT_BLOCKNUM(state->block), state->retries); | |
| 679 if(state->retries > state->retry_max) { | |
| 680 state->error = TFTP_ERR_TIMEOUT; | |
| 681 state->state = TFTP_STATE_FIN; | |
| 682 } | |
| 683 else { | |
| 684 /* Resend the previous ACK */ | |
| 685 sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| 686 4, SEND_4TH_ARG, | |
| 687 (struct sockaddr *)&state->remote_addr, | |
| 688 state->remote_addrlen); | |
| 689 if(sbytes<0) { | |
| 690 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 691 return CURLE_SEND_ERROR; | |
| 692 } | |
| 693 } | |
| 694 break; | |
| 695 | |
| 696 case TFTP_EVENT_ERROR: | |
| 697 setpacketevent(&state->spacket, TFTP_EVENT_ERROR); | |
| 698 setpacketblock(&state->spacket, state->block); | |
| 699 (void)sendto(state->sockfd, (void *)state->spacket.data, | |
| 700 4, SEND_4TH_ARG, | |
| 701 (struct sockaddr *)&state->remote_addr, | |
| 702 state->remote_addrlen); | |
| 703 /* don't bother with the return code, but if the socket is still up we | |
| 704 * should be a good TFTP client and let the server know we're done */ | |
| 705 state->state = TFTP_STATE_FIN; | |
| 706 break; | |
| 707 | |
| 708 default: | |
| 709 failf(data, "%s", "tftp_rx: internal error"); | |
| 710 return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for | |
| 711 this */ | |
| 712 } | |
| 713 return CURLE_OK; | |
| 714 } | |
| 715 | |
| 716 /********************************************************** | |
| 717 * | |
| 718 * tftp_tx | |
| 719 * | |
| 720 * Event handler for the TX state | |
| 721 * | |
| 722 **********************************************************/ | |
| 723 static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) | |
| 724 { | |
| 725 struct Curl_easy *data = state->conn->data; | |
| 726 ssize_t sbytes; | |
| 727 CURLcode result = CURLE_OK; | |
| 728 struct SingleRequest *k = &data->req; | |
| 729 size_t cb; /* Bytes currently read */ | |
| 730 char buffer[STRERROR_LEN]; | |
| 731 | |
| 732 switch(event) { | |
| 733 | |
| 734 case TFTP_EVENT_ACK: | |
| 735 case TFTP_EVENT_OACK: | |
| 736 if(event == TFTP_EVENT_ACK) { | |
| 737 /* Ack the packet */ | |
| 738 int rblock = getrpacketblock(&state->rpacket); | |
| 739 | |
| 740 if(rblock != state->block && | |
| 741 /* There's a bug in tftpd-hpa that causes it to send us an ack for | |
| 742 * 65535 when the block number wraps to 0. So when we're expecting | |
| 743 * 0, also accept 65535. See | |
| 744 * http://syslinux.zytor.com/archives/2010-September/015253.html | |
| 745 * */ | |
| 746 !(state->block == 0 && rblock == 65535)) { | |
| 747 /* This isn't the expected block. Log it and up the retry counter */ | |
| 748 infof(data, "Received ACK for block %d, expecting %d\n", | |
| 749 rblock, state->block); | |
| 750 state->retries++; | |
| 751 /* Bail out if over the maximum */ | |
| 752 if(state->retries>state->retry_max) { | |
| 753 failf(data, "tftp_tx: giving up waiting for block %d ack", | |
| 754 state->block); | |
| 755 result = CURLE_SEND_ERROR; | |
| 756 } | |
| 757 else { | |
| 758 /* Re-send the data packet */ | |
| 759 sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| 760 4 + state->sbytes, SEND_4TH_ARG, | |
| 761 (struct sockaddr *)&state->remote_addr, | |
| 762 state->remote_addrlen); | |
| 763 /* Check all sbytes were sent */ | |
| 764 if(sbytes<0) { | |
| 765 failf(data, "%s", Curl_strerror(SOCKERRNO, | |
| 766 buffer, sizeof(buffer))); | |
| 767 result = CURLE_SEND_ERROR; | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 return result; | |
| 772 } | |
| 773 /* This is the expected packet. Reset the counters and send the next | |
| 774 block */ | |
| 775 time(&state->rx_time); | |
| 776 state->block++; | |
| 777 } | |
| 778 else | |
| 779 state->block = 1; /* first data block is 1 when using OACK */ | |
| 780 | |
| 781 state->retries = 0; | |
| 782 setpacketevent(&state->spacket, TFTP_EVENT_DATA); | |
| 783 setpacketblock(&state->spacket, state->block); | |
| 784 if(state->block > 1 && state->sbytes < state->blksize) { | |
| 785 state->state = TFTP_STATE_FIN; | |
| 786 return CURLE_OK; | |
| 787 } | |
| 788 | |
| 789 /* TFTP considers data block size < 512 bytes as an end of session. So | |
| 790 * in some cases we must wait for additional data to build full (512 bytes) | |
| 791 * data block. | |
| 792 * */ | |
| 793 state->sbytes = 0; | |
| 794 state->conn->data->req.upload_fromhere = (char *)state->spacket.data + 4; | |
| 795 do { | |
| 796 result = Curl_fillreadbuffer(state->conn, state->blksize - state->sbytes, | |
| 797 &cb); | |
| 798 if(result) | |
| 799 return result; | |
| 800 state->sbytes += (int)cb; | |
| 801 state->conn->data->req.upload_fromhere += cb; | |
| 802 } while(state->sbytes < state->blksize && cb != 0); | |
| 803 | |
| 804 sbytes = sendto(state->sockfd, (void *) state->spacket.data, | |
| 805 4 + state->sbytes, SEND_4TH_ARG, | |
| 806 (struct sockaddr *)&state->remote_addr, | |
| 807 state->remote_addrlen); | |
| 808 /* Check all sbytes were sent */ | |
| 809 if(sbytes<0) { | |
| 810 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 811 return CURLE_SEND_ERROR; | |
| 812 } | |
| 813 /* Update the progress meter */ | |
| 814 k->writebytecount += state->sbytes; | |
| 815 Curl_pgrsSetUploadCounter(data, k->writebytecount); | |
| 816 break; | |
| 817 | |
| 818 case TFTP_EVENT_TIMEOUT: | |
| 819 /* Increment the retry counter and log the timeout */ | |
| 820 state->retries++; | |
| 821 infof(data, "Timeout waiting for block %d ACK. " | |
| 822 " Retries = %d\n", NEXT_BLOCKNUM(state->block), state->retries); | |
| 823 /* Decide if we've had enough */ | |
| 824 if(state->retries > state->retry_max) { | |
| 825 state->error = TFTP_ERR_TIMEOUT; | |
| 826 state->state = TFTP_STATE_FIN; | |
| 827 } | |
| 828 else { | |
| 829 /* Re-send the data packet */ | |
| 830 sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| 831 4 + state->sbytes, SEND_4TH_ARG, | |
| 832 (struct sockaddr *)&state->remote_addr, | |
| 833 state->remote_addrlen); | |
| 834 /* Check all sbytes were sent */ | |
| 835 if(sbytes<0) { | |
| 836 failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 837 return CURLE_SEND_ERROR; | |
| 838 } | |
| 839 /* since this was a re-send, we remain at the still byte position */ | |
| 840 Curl_pgrsSetUploadCounter(data, k->writebytecount); | |
| 841 } | |
| 842 break; | |
| 843 | |
| 844 case TFTP_EVENT_ERROR: | |
| 845 state->state = TFTP_STATE_FIN; | |
| 846 setpacketevent(&state->spacket, TFTP_EVENT_ERROR); | |
| 847 setpacketblock(&state->spacket, state->block); | |
| 848 (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, | |
| 849 (struct sockaddr *)&state->remote_addr, | |
| 850 state->remote_addrlen); | |
| 851 /* don't bother with the return code, but if the socket is still up we | |
| 852 * should be a good TFTP client and let the server know we're done */ | |
| 853 state->state = TFTP_STATE_FIN; | |
| 854 break; | |
| 855 | |
| 856 default: | |
| 857 failf(data, "tftp_tx: internal error, event: %i", (int)(event)); | |
| 858 break; | |
| 859 } | |
| 860 | |
| 861 return result; | |
| 862 } | |
| 863 | |
| 864 /********************************************************** | |
| 865 * | |
| 866 * tftp_translate_code | |
| 867 * | |
| 868 * Translate internal error codes to CURL error codes | |
| 869 * | |
| 870 **********************************************************/ | |
| 871 static CURLcode tftp_translate_code(tftp_error_t error) | |
| 872 { | |
| 873 CURLcode result = CURLE_OK; | |
| 874 | |
| 875 if(error != TFTP_ERR_NONE) { | |
| 876 switch(error) { | |
| 877 case TFTP_ERR_NOTFOUND: | |
| 878 result = CURLE_TFTP_NOTFOUND; | |
| 879 break; | |
| 880 case TFTP_ERR_PERM: | |
| 881 result = CURLE_TFTP_PERM; | |
| 882 break; | |
| 883 case TFTP_ERR_DISKFULL: | |
| 884 result = CURLE_REMOTE_DISK_FULL; | |
| 885 break; | |
| 886 case TFTP_ERR_UNDEF: | |
| 887 case TFTP_ERR_ILLEGAL: | |
| 888 result = CURLE_TFTP_ILLEGAL; | |
| 889 break; | |
| 890 case TFTP_ERR_UNKNOWNID: | |
| 891 result = CURLE_TFTP_UNKNOWNID; | |
| 892 break; | |
| 893 case TFTP_ERR_EXISTS: | |
| 894 result = CURLE_REMOTE_FILE_EXISTS; | |
| 895 break; | |
| 896 case TFTP_ERR_NOSUCHUSER: | |
| 897 result = CURLE_TFTP_NOSUCHUSER; | |
| 898 break; | |
| 899 case TFTP_ERR_TIMEOUT: | |
| 900 result = CURLE_OPERATION_TIMEDOUT; | |
| 901 break; | |
| 902 case TFTP_ERR_NORESPONSE: | |
| 903 result = CURLE_COULDNT_CONNECT; | |
| 904 break; | |
| 905 default: | |
| 906 result = CURLE_ABORTED_BY_CALLBACK; | |
| 907 break; | |
| 908 } | |
| 909 } | |
| 910 else | |
| 911 result = CURLE_OK; | |
| 912 | |
| 913 return result; | |
| 914 } | |
| 915 | |
| 916 /********************************************************** | |
| 917 * | |
| 918 * tftp_state_machine | |
| 919 * | |
| 920 * The tftp state machine event dispatcher | |
| 921 * | |
| 922 **********************************************************/ | |
| 923 static CURLcode tftp_state_machine(tftp_state_data_t *state, | |
| 924 tftp_event_t event) | |
| 925 { | |
| 926 CURLcode result = CURLE_OK; | |
| 927 struct Curl_easy *data = state->conn->data; | |
| 928 | |
| 929 switch(state->state) { | |
| 930 case TFTP_STATE_START: | |
| 931 DEBUGF(infof(data, "TFTP_STATE_START\n")); | |
| 932 result = tftp_send_first(state, event); | |
| 933 break; | |
| 934 case TFTP_STATE_RX: | |
| 935 DEBUGF(infof(data, "TFTP_STATE_RX\n")); | |
| 936 result = tftp_rx(state, event); | |
| 937 break; | |
| 938 case TFTP_STATE_TX: | |
| 939 DEBUGF(infof(data, "TFTP_STATE_TX\n")); | |
| 940 result = tftp_tx(state, event); | |
| 941 break; | |
| 942 case TFTP_STATE_FIN: | |
| 943 infof(data, "%s\n", "TFTP finished"); | |
| 944 break; | |
| 945 default: | |
| 946 DEBUGF(infof(data, "STATE: %d\n", state->state)); | |
| 947 failf(data, "%s", "Internal state machine error"); | |
| 948 result = CURLE_TFTP_ILLEGAL; | |
| 949 break; | |
| 950 } | |
| 951 | |
| 952 return result; | |
| 953 } | |
| 954 | |
| 955 /********************************************************** | |
| 956 * | |
| 957 * tftp_disconnect | |
| 958 * | |
| 959 * The disconnect callback | |
| 960 * | |
| 961 **********************************************************/ | |
| 962 static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection) | |
| 963 { | |
| 964 tftp_state_data_t *state = conn->proto.tftpc; | |
| 965 (void) dead_connection; | |
| 966 | |
| 967 /* done, free dynamically allocated pkt buffers */ | |
| 968 if(state) { | |
| 969 Curl_safefree(state->rpacket.data); | |
| 970 Curl_safefree(state->spacket.data); | |
| 971 free(state); | |
| 972 } | |
| 973 | |
| 974 return CURLE_OK; | |
| 975 } | |
| 976 | |
| 977 /********************************************************** | |
| 978 * | |
| 979 * tftp_connect | |
| 980 * | |
| 981 * The connect callback | |
| 982 * | |
| 983 **********************************************************/ | |
| 984 static CURLcode tftp_connect(struct connectdata *conn, bool *done) | |
| 985 { | |
| 986 tftp_state_data_t *state; | |
| 987 int blksize; | |
| 988 int need_blksize; | |
| 989 | |
| 990 blksize = TFTP_BLKSIZE_DEFAULT; | |
| 991 | |
| 992 state = conn->proto.tftpc = calloc(1, sizeof(tftp_state_data_t)); | |
| 993 if(!state) | |
| 994 return CURLE_OUT_OF_MEMORY; | |
| 995 | |
| 996 /* alloc pkt buffers based on specified blksize */ | |
| 997 if(conn->data->set.tftp_blksize) { | |
| 998 blksize = (int)conn->data->set.tftp_blksize; | |
| 999 if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN) | |
| 1000 return CURLE_TFTP_ILLEGAL; | |
| 1001 } | |
| 1002 | |
| 1003 need_blksize = blksize; | |
| 1004 /* default size is the fallback when no OACK is received */ | |
| 1005 if(need_blksize < TFTP_BLKSIZE_DEFAULT) | |
| 1006 need_blksize = TFTP_BLKSIZE_DEFAULT; | |
| 1007 | |
| 1008 if(!state->rpacket.data) { | |
| 1009 state->rpacket.data = calloc(1, need_blksize + 2 + 2); | |
| 1010 | |
| 1011 if(!state->rpacket.data) | |
| 1012 return CURLE_OUT_OF_MEMORY; | |
| 1013 } | |
| 1014 | |
| 1015 if(!state->spacket.data) { | |
| 1016 state->spacket.data = calloc(1, need_blksize + 2 + 2); | |
| 1017 | |
| 1018 if(!state->spacket.data) | |
| 1019 return CURLE_OUT_OF_MEMORY; | |
| 1020 } | |
| 1021 | |
| 1022 /* we don't keep TFTP connections up basically because there's none or very | |
| 1023 * little gain for UDP */ | |
| 1024 connclose(conn, "TFTP"); | |
| 1025 | |
| 1026 state->conn = conn; | |
| 1027 state->sockfd = state->conn->sock[FIRSTSOCKET]; | |
| 1028 state->state = TFTP_STATE_START; | |
| 1029 state->error = TFTP_ERR_NONE; | |
| 1030 state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */ | |
| 1031 state->requested_blksize = blksize; | |
| 1032 | |
| 1033 ((struct sockaddr *)&state->local_addr)->sa_family = | |
| 1034 (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family); | |
| 1035 | |
| 1036 tftp_set_timeouts(state); | |
| 1037 | |
| 1038 if(!conn->bits.bound) { | |
| 1039 /* If not already bound, bind to any interface, random UDP port. If it is | |
| 1040 * reused or a custom local port was desired, this has already been done! | |
| 1041 * | |
| 1042 * We once used the size of the local_addr struct as the third argument | |
| 1043 * for bind() to better work with IPv6 or whatever size the struct could | |
| 1044 * have, but we learned that at least Tru64, AIX and IRIX *requires* the | |
| 1045 * size of that argument to match the exact size of a 'sockaddr_in' struct | |
| 1046 * when running IPv4-only. | |
| 1047 * | |
| 1048 * Therefore we use the size from the address we connected to, which we | |
| 1049 * assume uses the same IP version and thus hopefully this works for both | |
| 1050 * IPv4 and IPv6... | |
| 1051 */ | |
| 1052 int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, | |
| 1053 conn->ip_addr->ai_addrlen); | |
| 1054 if(rc) { | |
| 1055 char buffer[STRERROR_LEN]; | |
| 1056 failf(conn->data, "bind() failed; %s", | |
| 1057 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| 1058 return CURLE_COULDNT_CONNECT; | |
| 1059 } | |
| 1060 conn->bits.bound = TRUE; | |
| 1061 } | |
| 1062 | |
| 1063 Curl_pgrsStartNow(conn->data); | |
| 1064 | |
| 1065 *done = TRUE; | |
| 1066 | |
| 1067 return CURLE_OK; | |
| 1068 } | |
| 1069 | |
| 1070 /********************************************************** | |
| 1071 * | |
| 1072 * tftp_done | |
| 1073 * | |
| 1074 * The done callback | |
| 1075 * | |
| 1076 **********************************************************/ | |
| 1077 static CURLcode tftp_done(struct connectdata *conn, CURLcode status, | |
| 1078 bool premature) | |
| 1079 { | |
| 1080 CURLcode result = CURLE_OK; | |
| 1081 tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1082 | |
| 1083 (void)status; /* unused */ | |
| 1084 (void)premature; /* not used */ | |
| 1085 | |
| 1086 if(Curl_pgrsDone(conn)) | |
| 1087 return CURLE_ABORTED_BY_CALLBACK; | |
| 1088 | |
| 1089 /* If we have encountered an error */ | |
| 1090 if(state) | |
| 1091 result = tftp_translate_code(state->error); | |
| 1092 | |
| 1093 return result; | |
| 1094 } | |
| 1095 | |
| 1096 /********************************************************** | |
| 1097 * | |
| 1098 * tftp_getsock | |
| 1099 * | |
| 1100 * The getsock callback | |
| 1101 * | |
| 1102 **********************************************************/ | |
| 1103 static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks) | |
| 1104 { | |
| 1105 socks[0] = conn->sock[FIRSTSOCKET]; | |
| 1106 return GETSOCK_READSOCK(0); | |
| 1107 } | |
| 1108 | |
| 1109 /********************************************************** | |
| 1110 * | |
| 1111 * tftp_receive_packet | |
| 1112 * | |
| 1113 * Called once select fires and data is ready on the socket | |
| 1114 * | |
| 1115 **********************************************************/ | |
| 1116 static CURLcode tftp_receive_packet(struct connectdata *conn) | |
| 1117 { | |
| 1118 struct Curl_sockaddr_storage fromaddr; | |
| 1119 curl_socklen_t fromlen; | |
| 1120 CURLcode result = CURLE_OK; | |
| 1121 struct Curl_easy *data = conn->data; | |
| 1122 tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1123 struct SingleRequest *k = &data->req; | |
| 1124 | |
| 1125 /* Receive the packet */ | |
| 1126 fromlen = sizeof(fromaddr); | |
| 1127 state->rbytes = (int)recvfrom(state->sockfd, | |
| 1128 (void *)state->rpacket.data, | |
| 1129 state->blksize + 4, | |
| 1130 0, | |
| 1131 (struct sockaddr *)&fromaddr, | |
| 1132 &fromlen); | |
| 1133 if(state->remote_addrlen == 0) { | |
| 1134 memcpy(&state->remote_addr, &fromaddr, fromlen); | |
| 1135 state->remote_addrlen = fromlen; | |
| 1136 } | |
| 1137 | |
| 1138 /* Sanity check packet length */ | |
| 1139 if(state->rbytes < 4) { | |
| 1140 failf(data, "Received too short packet"); | |
| 1141 /* Not a timeout, but how best to handle it? */ | |
| 1142 state->event = TFTP_EVENT_TIMEOUT; | |
| 1143 } | |
| 1144 else { | |
| 1145 /* The event is given by the TFTP packet time */ | |
| 1146 unsigned short event = getrpacketevent(&state->rpacket); | |
| 1147 state->event = (tftp_event_t)event; | |
| 1148 | |
| 1149 switch(state->event) { | |
| 1150 case TFTP_EVENT_DATA: | |
| 1151 /* Don't pass to the client empty or retransmitted packets */ | |
| 1152 if(state->rbytes > 4 && | |
| 1153 (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { | |
| 1154 result = Curl_client_write(conn, CLIENTWRITE_BODY, | |
| 1155 (char *)state->rpacket.data + 4, | |
| 1156 state->rbytes-4); | |
| 1157 if(result) { | |
| 1158 tftp_state_machine(state, TFTP_EVENT_ERROR); | |
| 1159 return result; | |
| 1160 } | |
| 1161 k->bytecount += state->rbytes-4; | |
| 1162 Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount); | |
| 1163 } | |
| 1164 break; | |
| 1165 case TFTP_EVENT_ERROR: | |
| 1166 { | |
| 1167 unsigned short error = getrpacketblock(&state->rpacket); | |
| 1168 char *str = (char *)state->rpacket.data + 4; | |
| 1169 size_t strn = state->rbytes - 4; | |
| 1170 state->error = (tftp_error_t)error; | |
| 1171 if(Curl_strnlen(str, strn) < strn) | |
| 1172 infof(data, "TFTP error: %s\n", str); | |
| 1173 break; | |
| 1174 } | |
| 1175 case TFTP_EVENT_ACK: | |
| 1176 break; | |
| 1177 case TFTP_EVENT_OACK: | |
| 1178 result = tftp_parse_option_ack(state, | |
| 1179 (const char *)state->rpacket.data + 2, | |
| 1180 state->rbytes-2); | |
| 1181 if(result) | |
| 1182 return result; | |
| 1183 break; | |
| 1184 case TFTP_EVENT_RRQ: | |
| 1185 case TFTP_EVENT_WRQ: | |
| 1186 default: | |
| 1187 failf(data, "%s", "Internal error: Unexpected packet"); | |
| 1188 break; | |
| 1189 } | |
| 1190 | |
| 1191 /* Update the progress meter */ | |
| 1192 if(Curl_pgrsUpdate(conn)) { | |
| 1193 tftp_state_machine(state, TFTP_EVENT_ERROR); | |
| 1194 return CURLE_ABORTED_BY_CALLBACK; | |
| 1195 } | |
| 1196 } | |
| 1197 return result; | |
| 1198 } | |
| 1199 | |
| 1200 /********************************************************** | |
| 1201 * | |
| 1202 * tftp_state_timeout | |
| 1203 * | |
| 1204 * Check if timeouts have been reached | |
| 1205 * | |
| 1206 **********************************************************/ | |
| 1207 static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event) | |
| 1208 { | |
| 1209 time_t current; | |
| 1210 tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1211 | |
| 1212 if(event) | |
| 1213 *event = TFTP_EVENT_NONE; | |
| 1214 | |
| 1215 time(¤t); | |
| 1216 if(current > state->max_time) { | |
| 1217 DEBUGF(infof(conn->data, "timeout: %ld > %ld\n", | |
| 1218 (long)current, (long)state->max_time)); | |
| 1219 state->error = TFTP_ERR_TIMEOUT; | |
| 1220 state->state = TFTP_STATE_FIN; | |
| 1221 return 0; | |
| 1222 } | |
| 1223 if(current > state->rx_time + state->retry_time) { | |
| 1224 if(event) | |
| 1225 *event = TFTP_EVENT_TIMEOUT; | |
| 1226 time(&state->rx_time); /* update even though we received nothing */ | |
| 1227 } | |
| 1228 | |
| 1229 /* there's a typecast below here since 'time_t' may in fact be larger than | |
| 1230 'long', but we estimate that a 'long' will still be able to hold number | |
| 1231 of seconds even if "only" 32 bit */ | |
| 1232 return (long)(state->max_time - current); | |
| 1233 } | |
| 1234 | |
| 1235 /********************************************************** | |
| 1236 * | |
| 1237 * tftp_multi_statemach | |
| 1238 * | |
| 1239 * Handle single RX socket event and return | |
| 1240 * | |
| 1241 **********************************************************/ | |
| 1242 static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done) | |
| 1243 { | |
| 1244 tftp_event_t event; | |
| 1245 CURLcode result = CURLE_OK; | |
| 1246 struct Curl_easy *data = conn->data; | |
| 1247 tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1248 long timeout_ms = tftp_state_timeout(conn, &event); | |
| 1249 | |
| 1250 *done = FALSE; | |
| 1251 | |
| 1252 if(timeout_ms <= 0) { | |
| 1253 failf(data, "TFTP response timeout"); | |
| 1254 return CURLE_OPERATION_TIMEDOUT; | |
| 1255 } | |
| 1256 if(event != TFTP_EVENT_NONE) { | |
| 1257 result = tftp_state_machine(state, event); | |
| 1258 if(result) | |
| 1259 return result; | |
| 1260 *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; | |
| 1261 if(*done) | |
| 1262 /* Tell curl we're done */ | |
| 1263 Curl_setup_transfer(data, -1, -1, FALSE, -1); | |
| 1264 } | |
| 1265 else { | |
| 1266 /* no timeouts to handle, check our socket */ | |
| 1267 int rc = SOCKET_READABLE(state->sockfd, 0); | |
| 1268 | |
| 1269 if(rc == -1) { | |
| 1270 /* bail out */ | |
| 1271 int error = SOCKERRNO; | |
| 1272 char buffer[STRERROR_LEN]; | |
| 1273 failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer))); | |
| 1274 state->event = TFTP_EVENT_ERROR; | |
| 1275 } | |
| 1276 else if(rc != 0) { | |
| 1277 result = tftp_receive_packet(conn); | |
| 1278 if(result) | |
| 1279 return result; | |
| 1280 result = tftp_state_machine(state, state->event); | |
| 1281 if(result) | |
| 1282 return result; | |
| 1283 *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; | |
| 1284 if(*done) | |
| 1285 /* Tell curl we're done */ | |
| 1286 Curl_setup_transfer(data, -1, -1, FALSE, -1); | |
| 1287 } | |
| 1288 /* if rc == 0, then select() timed out */ | |
| 1289 } | |
| 1290 | |
| 1291 return result; | |
| 1292 } | |
| 1293 | |
| 1294 /********************************************************** | |
| 1295 * | |
| 1296 * tftp_doing | |
| 1297 * | |
| 1298 * Called from multi.c while DOing | |
| 1299 * | |
| 1300 **********************************************************/ | |
| 1301 static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done) | |
| 1302 { | |
| 1303 CURLcode result; | |
| 1304 result = tftp_multi_statemach(conn, dophase_done); | |
| 1305 | |
| 1306 if(*dophase_done) { | |
| 1307 DEBUGF(infof(conn->data, "DO phase is complete\n")); | |
| 1308 } | |
| 1309 else if(!result) { | |
| 1310 /* The multi code doesn't have this logic for the DOING state so we | |
| 1311 provide it for TFTP since it may do the entire transfer in this | |
| 1312 state. */ | |
| 1313 if(Curl_pgrsUpdate(conn)) | |
| 1314 result = CURLE_ABORTED_BY_CALLBACK; | |
| 1315 else | |
| 1316 result = Curl_speedcheck(conn->data, Curl_now()); | |
| 1317 } | |
| 1318 return result; | |
| 1319 } | |
| 1320 | |
| 1321 /********************************************************** | |
| 1322 * | |
| 1323 * tftp_peform | |
| 1324 * | |
| 1325 * Entry point for transfer from tftp_do, sarts state mach | |
| 1326 * | |
| 1327 **********************************************************/ | |
| 1328 static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done) | |
| 1329 { | |
| 1330 CURLcode result = CURLE_OK; | |
| 1331 tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1332 | |
| 1333 *dophase_done = FALSE; | |
| 1334 | |
| 1335 result = tftp_state_machine(state, TFTP_EVENT_INIT); | |
| 1336 | |
| 1337 if((state->state == TFTP_STATE_FIN) || result) | |
| 1338 return result; | |
| 1339 | |
| 1340 tftp_multi_statemach(conn, dophase_done); | |
| 1341 | |
| 1342 if(*dophase_done) | |
| 1343 DEBUGF(infof(conn->data, "DO phase is complete\n")); | |
| 1344 | |
| 1345 return result; | |
| 1346 } | |
| 1347 | |
| 1348 | |
| 1349 /********************************************************** | |
| 1350 * | |
| 1351 * tftp_do | |
| 1352 * | |
| 1353 * The do callback | |
| 1354 * | |
| 1355 * This callback initiates the TFTP transfer | |
| 1356 * | |
| 1357 **********************************************************/ | |
| 1358 | |
| 1359 static CURLcode tftp_do(struct connectdata *conn, bool *done) | |
| 1360 { | |
| 1361 tftp_state_data_t *state; | |
| 1362 CURLcode result; | |
| 1363 | |
| 1364 *done = FALSE; | |
| 1365 | |
| 1366 if(!conn->proto.tftpc) { | |
| 1367 result = tftp_connect(conn, done); | |
| 1368 if(result) | |
| 1369 return result; | |
| 1370 } | |
| 1371 | |
| 1372 state = (tftp_state_data_t *)conn->proto.tftpc; | |
| 1373 if(!state) | |
| 1374 return CURLE_TFTP_ILLEGAL; | |
| 1375 | |
| 1376 result = tftp_perform(conn, done); | |
| 1377 | |
| 1378 /* If tftp_perform() returned an error, use that for return code. If it | |
| 1379 was OK, see if tftp_translate_code() has an error. */ | |
| 1380 if(!result) | |
| 1381 /* If we have encountered an internal tftp error, translate it. */ | |
| 1382 result = tftp_translate_code(state->error); | |
| 1383 | |
| 1384 return result; | |
| 1385 } | |
| 1386 | |
| 1387 static CURLcode tftp_setup_connection(struct connectdata * conn) | |
| 1388 { | |
| 1389 struct Curl_easy *data = conn->data; | |
| 1390 char *type; | |
| 1391 | |
| 1392 conn->transport = TRNSPRT_UDP; | |
| 1393 | |
| 1394 /* TFTP URLs support an extension like ";mode=<typecode>" that | |
| 1395 * we'll try to get now! */ | |
| 1396 type = strstr(data->state.up.path, ";mode="); | |
| 1397 | |
| 1398 if(!type) | |
| 1399 type = strstr(conn->host.rawalloc, ";mode="); | |
| 1400 | |
| 1401 if(type) { | |
| 1402 char command; | |
| 1403 *type = 0; /* it was in the middle of the hostname */ | |
| 1404 command = Curl_raw_toupper(type[6]); | |
| 1405 | |
| 1406 switch(command) { | |
| 1407 case 'A': /* ASCII mode */ | |
| 1408 case 'N': /* NETASCII mode */ | |
| 1409 data->set.prefer_ascii = TRUE; | |
| 1410 break; | |
| 1411 | |
| 1412 case 'O': /* octet mode */ | |
| 1413 case 'I': /* binary mode */ | |
| 1414 default: | |
| 1415 /* switch off ASCII */ | |
| 1416 data->set.prefer_ascii = FALSE; | |
| 1417 break; | |
| 1418 } | |
| 1419 } | |
| 1420 | |
| 1421 return CURLE_OK; | |
| 1422 } | |
| 1423 #endif |
