Annotation of embedaddon/curl/lib/tftp.c, revision 1.1
1.1 ! misho 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>