113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_TFTP
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3013498266Sopenharmony_ci#include <netinet/in.h>
3113498266Sopenharmony_ci#endif
3213498266Sopenharmony_ci#ifdef HAVE_NETDB_H
3313498266Sopenharmony_ci#include <netdb.h>
3413498266Sopenharmony_ci#endif
3513498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
3613498266Sopenharmony_ci#include <arpa/inet.h>
3713498266Sopenharmony_ci#endif
3813498266Sopenharmony_ci#ifdef HAVE_NET_IF_H
3913498266Sopenharmony_ci#include <net/if.h>
4013498266Sopenharmony_ci#endif
4113498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
4213498266Sopenharmony_ci#include <sys/ioctl.h>
4313498266Sopenharmony_ci#endif
4413498266Sopenharmony_ci
4513498266Sopenharmony_ci#ifdef HAVE_SYS_PARAM_H
4613498266Sopenharmony_ci#include <sys/param.h>
4713498266Sopenharmony_ci#endif
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci#include "urldata.h"
5013498266Sopenharmony_ci#include <curl/curl.h>
5113498266Sopenharmony_ci#include "cf-socket.h"
5213498266Sopenharmony_ci#include "transfer.h"
5313498266Sopenharmony_ci#include "sendf.h"
5413498266Sopenharmony_ci#include "tftp.h"
5513498266Sopenharmony_ci#include "progress.h"
5613498266Sopenharmony_ci#include "connect.h"
5713498266Sopenharmony_ci#include "strerror.h"
5813498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */
5913498266Sopenharmony_ci#include "multiif.h"
6013498266Sopenharmony_ci#include "url.h"
6113498266Sopenharmony_ci#include "strcase.h"
6213498266Sopenharmony_ci#include "speedcheck.h"
6313498266Sopenharmony_ci#include "select.h"
6413498266Sopenharmony_ci#include "escape.h"
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci/* The last 3 #include files should be in this order */
6713498266Sopenharmony_ci#include "curl_printf.h"
6813498266Sopenharmony_ci#include "curl_memory.h"
6913498266Sopenharmony_ci#include "memdebug.h"
7013498266Sopenharmony_ci
7113498266Sopenharmony_ci/* RFC2348 allows the block size to be negotiated */
7213498266Sopenharmony_ci#define TFTP_BLKSIZE_DEFAULT 512
7313498266Sopenharmony_ci#define TFTP_OPTION_BLKSIZE "blksize"
7413498266Sopenharmony_ci
7513498266Sopenharmony_ci/* from RFC2349: */
7613498266Sopenharmony_ci#define TFTP_OPTION_TSIZE    "tsize"
7713498266Sopenharmony_ci#define TFTP_OPTION_INTERVAL "timeout"
7813498266Sopenharmony_ci
7913498266Sopenharmony_citypedef enum {
8013498266Sopenharmony_ci  TFTP_MODE_NETASCII = 0,
8113498266Sopenharmony_ci  TFTP_MODE_OCTET
8213498266Sopenharmony_ci} tftp_mode_t;
8313498266Sopenharmony_ci
8413498266Sopenharmony_citypedef enum {
8513498266Sopenharmony_ci  TFTP_STATE_START = 0,
8613498266Sopenharmony_ci  TFTP_STATE_RX,
8713498266Sopenharmony_ci  TFTP_STATE_TX,
8813498266Sopenharmony_ci  TFTP_STATE_FIN
8913498266Sopenharmony_ci} tftp_state_t;
9013498266Sopenharmony_ci
9113498266Sopenharmony_citypedef enum {
9213498266Sopenharmony_ci  TFTP_EVENT_NONE = -1,
9313498266Sopenharmony_ci  TFTP_EVENT_INIT = 0,
9413498266Sopenharmony_ci  TFTP_EVENT_RRQ = 1,
9513498266Sopenharmony_ci  TFTP_EVENT_WRQ = 2,
9613498266Sopenharmony_ci  TFTP_EVENT_DATA = 3,
9713498266Sopenharmony_ci  TFTP_EVENT_ACK = 4,
9813498266Sopenharmony_ci  TFTP_EVENT_ERROR = 5,
9913498266Sopenharmony_ci  TFTP_EVENT_OACK = 6,
10013498266Sopenharmony_ci  TFTP_EVENT_TIMEOUT
10113498266Sopenharmony_ci} tftp_event_t;
10213498266Sopenharmony_ci
10313498266Sopenharmony_citypedef enum {
10413498266Sopenharmony_ci  TFTP_ERR_UNDEF = 0,
10513498266Sopenharmony_ci  TFTP_ERR_NOTFOUND,
10613498266Sopenharmony_ci  TFTP_ERR_PERM,
10713498266Sopenharmony_ci  TFTP_ERR_DISKFULL,
10813498266Sopenharmony_ci  TFTP_ERR_ILLEGAL,
10913498266Sopenharmony_ci  TFTP_ERR_UNKNOWNID,
11013498266Sopenharmony_ci  TFTP_ERR_EXISTS,
11113498266Sopenharmony_ci  TFTP_ERR_NOSUCHUSER,  /* This will never be triggered by this code */
11213498266Sopenharmony_ci
11313498266Sopenharmony_ci  /* The remaining error codes are internal to curl */
11413498266Sopenharmony_ci  TFTP_ERR_NONE = -100,
11513498266Sopenharmony_ci  TFTP_ERR_TIMEOUT,
11613498266Sopenharmony_ci  TFTP_ERR_NORESPONSE
11713498266Sopenharmony_ci} tftp_error_t;
11813498266Sopenharmony_ci
11913498266Sopenharmony_cistruct tftp_packet {
12013498266Sopenharmony_ci  unsigned char *data;
12113498266Sopenharmony_ci};
12213498266Sopenharmony_ci
12313498266Sopenharmony_cistruct tftp_state_data {
12413498266Sopenharmony_ci  tftp_state_t    state;
12513498266Sopenharmony_ci  tftp_mode_t     mode;
12613498266Sopenharmony_ci  tftp_error_t    error;
12713498266Sopenharmony_ci  tftp_event_t    event;
12813498266Sopenharmony_ci  struct Curl_easy *data;
12913498266Sopenharmony_ci  curl_socket_t   sockfd;
13013498266Sopenharmony_ci  int             retries;
13113498266Sopenharmony_ci  int             retry_time;
13213498266Sopenharmony_ci  int             retry_max;
13313498266Sopenharmony_ci  time_t          rx_time;
13413498266Sopenharmony_ci  struct Curl_sockaddr_storage   local_addr;
13513498266Sopenharmony_ci  struct Curl_sockaddr_storage   remote_addr;
13613498266Sopenharmony_ci  curl_socklen_t  remote_addrlen;
13713498266Sopenharmony_ci  int             rbytes;
13813498266Sopenharmony_ci  int             sbytes;
13913498266Sopenharmony_ci  int             blksize;
14013498266Sopenharmony_ci  int             requested_blksize;
14113498266Sopenharmony_ci  unsigned short  block;
14213498266Sopenharmony_ci  struct tftp_packet rpacket;
14313498266Sopenharmony_ci  struct tftp_packet spacket;
14413498266Sopenharmony_ci};
14513498266Sopenharmony_ci
14613498266Sopenharmony_ci
14713498266Sopenharmony_ci/* Forward declarations */
14813498266Sopenharmony_cistatic CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event);
14913498266Sopenharmony_cistatic CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event);
15013498266Sopenharmony_cistatic CURLcode tftp_connect(struct Curl_easy *data, bool *done);
15113498266Sopenharmony_cistatic CURLcode tftp_disconnect(struct Curl_easy *data,
15213498266Sopenharmony_ci                                struct connectdata *conn,
15313498266Sopenharmony_ci                                bool dead_connection);
15413498266Sopenharmony_cistatic CURLcode tftp_do(struct Curl_easy *data, bool *done);
15513498266Sopenharmony_cistatic CURLcode tftp_done(struct Curl_easy *data,
15613498266Sopenharmony_ci                          CURLcode, bool premature);
15713498266Sopenharmony_cistatic CURLcode tftp_setup_connection(struct Curl_easy *data,
15813498266Sopenharmony_ci                                      struct connectdata *conn);
15913498266Sopenharmony_cistatic CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done);
16013498266Sopenharmony_cistatic CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done);
16113498266Sopenharmony_cistatic int tftp_getsock(struct Curl_easy *data, struct connectdata *conn,
16213498266Sopenharmony_ci                        curl_socket_t *socks);
16313498266Sopenharmony_cistatic CURLcode tftp_translate_code(tftp_error_t error);
16413498266Sopenharmony_ci
16513498266Sopenharmony_ci
16613498266Sopenharmony_ci/*
16713498266Sopenharmony_ci * TFTP protocol handler.
16813498266Sopenharmony_ci */
16913498266Sopenharmony_ci
17013498266Sopenharmony_ciconst struct Curl_handler Curl_handler_tftp = {
17113498266Sopenharmony_ci  "TFTP",                               /* scheme */
17213498266Sopenharmony_ci  tftp_setup_connection,                /* setup_connection */
17313498266Sopenharmony_ci  tftp_do,                              /* do_it */
17413498266Sopenharmony_ci  tftp_done,                            /* done */
17513498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
17613498266Sopenharmony_ci  tftp_connect,                         /* connect_it */
17713498266Sopenharmony_ci  tftp_multi_statemach,                 /* connecting */
17813498266Sopenharmony_ci  tftp_doing,                           /* doing */
17913498266Sopenharmony_ci  tftp_getsock,                         /* proto_getsock */
18013498266Sopenharmony_ci  tftp_getsock,                         /* doing_getsock */
18113498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
18213498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
18313498266Sopenharmony_ci  tftp_disconnect,                      /* disconnect */
18413498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
18513498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
18613498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
18713498266Sopenharmony_ci  PORT_TFTP,                            /* defport */
18813498266Sopenharmony_ci  CURLPROTO_TFTP,                       /* protocol */
18913498266Sopenharmony_ci  CURLPROTO_TFTP,                       /* family */
19013498266Sopenharmony_ci  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
19113498266Sopenharmony_ci};
19213498266Sopenharmony_ci
19313498266Sopenharmony_ci/**********************************************************
19413498266Sopenharmony_ci *
19513498266Sopenharmony_ci * tftp_set_timeouts -
19613498266Sopenharmony_ci *
19713498266Sopenharmony_ci * Set timeouts based on state machine state.
19813498266Sopenharmony_ci * Use user provided connect timeouts until DATA or ACK
19913498266Sopenharmony_ci * packet is received, then use user-provided transfer timeouts
20013498266Sopenharmony_ci *
20113498266Sopenharmony_ci *
20213498266Sopenharmony_ci **********************************************************/
20313498266Sopenharmony_cistatic CURLcode tftp_set_timeouts(struct tftp_state_data *state)
20413498266Sopenharmony_ci{
20513498266Sopenharmony_ci  time_t maxtime, timeout;
20613498266Sopenharmony_ci  timediff_t timeout_ms;
20713498266Sopenharmony_ci  bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE;
20813498266Sopenharmony_ci
20913498266Sopenharmony_ci  /* Compute drop-dead time */
21013498266Sopenharmony_ci  timeout_ms = Curl_timeleft(state->data, NULL, start);
21113498266Sopenharmony_ci
21213498266Sopenharmony_ci  if(timeout_ms < 0) {
21313498266Sopenharmony_ci    /* time-out, bail out, go home */
21413498266Sopenharmony_ci    failf(state->data, "Connection time-out");
21513498266Sopenharmony_ci    return CURLE_OPERATION_TIMEDOUT;
21613498266Sopenharmony_ci  }
21713498266Sopenharmony_ci
21813498266Sopenharmony_ci  if(timeout_ms > 0)
21913498266Sopenharmony_ci    maxtime = (time_t)(timeout_ms + 500) / 1000;
22013498266Sopenharmony_ci  else
22113498266Sopenharmony_ci    maxtime = 3600; /* use for calculating block timeouts */
22213498266Sopenharmony_ci
22313498266Sopenharmony_ci  /* Set per-block timeout to total */
22413498266Sopenharmony_ci  timeout = maxtime;
22513498266Sopenharmony_ci
22613498266Sopenharmony_ci  /* Average reposting an ACK after 5 seconds */
22713498266Sopenharmony_ci  state->retry_max = (int)timeout/5;
22813498266Sopenharmony_ci
22913498266Sopenharmony_ci  /* But bound the total number */
23013498266Sopenharmony_ci  if(state->retry_max<3)
23113498266Sopenharmony_ci    state->retry_max = 3;
23213498266Sopenharmony_ci
23313498266Sopenharmony_ci  if(state->retry_max>50)
23413498266Sopenharmony_ci    state->retry_max = 50;
23513498266Sopenharmony_ci
23613498266Sopenharmony_ci  /* Compute the re-ACK interval to suit the timeout */
23713498266Sopenharmony_ci  state->retry_time = (int)(timeout/state->retry_max);
23813498266Sopenharmony_ci  if(state->retry_time<1)
23913498266Sopenharmony_ci    state->retry_time = 1;
24013498266Sopenharmony_ci
24113498266Sopenharmony_ci  infof(state->data,
24213498266Sopenharmony_ci        "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T
24313498266Sopenharmony_ci        ", retry %d maxtry %d",
24413498266Sopenharmony_ci        (int)state->state, timeout_ms, state->retry_time, state->retry_max);
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci  /* init RX time */
24713498266Sopenharmony_ci  time(&state->rx_time);
24813498266Sopenharmony_ci
24913498266Sopenharmony_ci  return CURLE_OK;
25013498266Sopenharmony_ci}
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci/**********************************************************
25313498266Sopenharmony_ci *
25413498266Sopenharmony_ci * tftp_set_send_first
25513498266Sopenharmony_ci *
25613498266Sopenharmony_ci * Event handler for the START state
25713498266Sopenharmony_ci *
25813498266Sopenharmony_ci **********************************************************/
25913498266Sopenharmony_ci
26013498266Sopenharmony_cistatic void setpacketevent(struct tftp_packet *packet, unsigned short num)
26113498266Sopenharmony_ci{
26213498266Sopenharmony_ci  packet->data[0] = (unsigned char)(num >> 8);
26313498266Sopenharmony_ci  packet->data[1] = (unsigned char)(num & 0xff);
26413498266Sopenharmony_ci}
26513498266Sopenharmony_ci
26613498266Sopenharmony_ci
26713498266Sopenharmony_cistatic void setpacketblock(struct tftp_packet *packet, unsigned short num)
26813498266Sopenharmony_ci{
26913498266Sopenharmony_ci  packet->data[2] = (unsigned char)(num >> 8);
27013498266Sopenharmony_ci  packet->data[3] = (unsigned char)(num & 0xff);
27113498266Sopenharmony_ci}
27213498266Sopenharmony_ci
27313498266Sopenharmony_cistatic unsigned short getrpacketevent(const struct tftp_packet *packet)
27413498266Sopenharmony_ci{
27513498266Sopenharmony_ci  return (unsigned short)((packet->data[0] << 8) | packet->data[1]);
27613498266Sopenharmony_ci}
27713498266Sopenharmony_ci
27813498266Sopenharmony_cistatic unsigned short getrpacketblock(const struct tftp_packet *packet)
27913498266Sopenharmony_ci{
28013498266Sopenharmony_ci  return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
28113498266Sopenharmony_ci}
28213498266Sopenharmony_ci
28313498266Sopenharmony_cistatic size_t tftp_strnlen(const char *string, size_t maxlen)
28413498266Sopenharmony_ci{
28513498266Sopenharmony_ci  const char *end = memchr(string, '\0', maxlen);
28613498266Sopenharmony_ci  return end ? (size_t) (end - string) : maxlen;
28713498266Sopenharmony_ci}
28813498266Sopenharmony_ci
28913498266Sopenharmony_cistatic const char *tftp_option_get(const char *buf, size_t len,
29013498266Sopenharmony_ci                                   const char **option, const char **value)
29113498266Sopenharmony_ci{
29213498266Sopenharmony_ci  size_t loc;
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci  loc = tftp_strnlen(buf, len);
29513498266Sopenharmony_ci  loc++; /* NULL term */
29613498266Sopenharmony_ci
29713498266Sopenharmony_ci  if(loc >= len)
29813498266Sopenharmony_ci    return NULL;
29913498266Sopenharmony_ci  *option = buf;
30013498266Sopenharmony_ci
30113498266Sopenharmony_ci  loc += tftp_strnlen(buf + loc, len-loc);
30213498266Sopenharmony_ci  loc++; /* NULL term */
30313498266Sopenharmony_ci
30413498266Sopenharmony_ci  if(loc > len)
30513498266Sopenharmony_ci    return NULL;
30613498266Sopenharmony_ci  *value = &buf[strlen(*option) + 1];
30713498266Sopenharmony_ci
30813498266Sopenharmony_ci  return &buf[loc];
30913498266Sopenharmony_ci}
31013498266Sopenharmony_ci
31113498266Sopenharmony_cistatic CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
31213498266Sopenharmony_ci                                      const char *ptr, int len)
31313498266Sopenharmony_ci{
31413498266Sopenharmony_ci  const char *tmp = ptr;
31513498266Sopenharmony_ci  struct Curl_easy *data = state->data;
31613498266Sopenharmony_ci
31713498266Sopenharmony_ci  /* if OACK doesn't contain blksize option, the default (512) must be used */
31813498266Sopenharmony_ci  state->blksize = TFTP_BLKSIZE_DEFAULT;
31913498266Sopenharmony_ci
32013498266Sopenharmony_ci  while(tmp < ptr + len) {
32113498266Sopenharmony_ci    const char *option, *value;
32213498266Sopenharmony_ci
32313498266Sopenharmony_ci    tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value);
32413498266Sopenharmony_ci    if(!tmp) {
32513498266Sopenharmony_ci      failf(data, "Malformed ACK packet, rejecting");
32613498266Sopenharmony_ci      return CURLE_TFTP_ILLEGAL;
32713498266Sopenharmony_ci    }
32813498266Sopenharmony_ci
32913498266Sopenharmony_ci    infof(data, "got option=(%s) value=(%s)", option, value);
33013498266Sopenharmony_ci
33113498266Sopenharmony_ci    if(checkprefix(TFTP_OPTION_BLKSIZE, option)) {
33213498266Sopenharmony_ci      long blksize;
33313498266Sopenharmony_ci
33413498266Sopenharmony_ci      blksize = strtol(value, NULL, 10);
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci      if(!blksize) {
33713498266Sopenharmony_ci        failf(data, "invalid blocksize value in OACK packet");
33813498266Sopenharmony_ci        return CURLE_TFTP_ILLEGAL;
33913498266Sopenharmony_ci      }
34013498266Sopenharmony_ci      if(blksize > TFTP_BLKSIZE_MAX) {
34113498266Sopenharmony_ci        failf(data, "%s (%d)", "blksize is larger than max supported",
34213498266Sopenharmony_ci              TFTP_BLKSIZE_MAX);
34313498266Sopenharmony_ci        return CURLE_TFTP_ILLEGAL;
34413498266Sopenharmony_ci      }
34513498266Sopenharmony_ci      else if(blksize < TFTP_BLKSIZE_MIN) {
34613498266Sopenharmony_ci        failf(data, "%s (%d)", "blksize is smaller than min supported",
34713498266Sopenharmony_ci              TFTP_BLKSIZE_MIN);
34813498266Sopenharmony_ci        return CURLE_TFTP_ILLEGAL;
34913498266Sopenharmony_ci      }
35013498266Sopenharmony_ci      else if(blksize > state->requested_blksize) {
35113498266Sopenharmony_ci        /* could realloc pkt buffers here, but the spec doesn't call out
35213498266Sopenharmony_ci         * support for the server requesting a bigger blksize than the client
35313498266Sopenharmony_ci         * requests */
35413498266Sopenharmony_ci        failf(data, "%s (%ld)",
35513498266Sopenharmony_ci              "server requested blksize larger than allocated", blksize);
35613498266Sopenharmony_ci        return CURLE_TFTP_ILLEGAL;
35713498266Sopenharmony_ci      }
35813498266Sopenharmony_ci
35913498266Sopenharmony_ci      state->blksize = (int)blksize;
36013498266Sopenharmony_ci      infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
36113498266Sopenharmony_ci            state->blksize, "requested", state->requested_blksize);
36213498266Sopenharmony_ci    }
36313498266Sopenharmony_ci    else if(checkprefix(TFTP_OPTION_TSIZE, option)) {
36413498266Sopenharmony_ci      long tsize = 0;
36513498266Sopenharmony_ci
36613498266Sopenharmony_ci      tsize = strtol(value, NULL, 10);
36713498266Sopenharmony_ci      infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
36813498266Sopenharmony_ci
36913498266Sopenharmony_ci      /* tsize should be ignored on upload: Who cares about the size of the
37013498266Sopenharmony_ci         remote file? */
37113498266Sopenharmony_ci      if(!data->state.upload) {
37213498266Sopenharmony_ci        if(!tsize) {
37313498266Sopenharmony_ci          failf(data, "invalid tsize -:%s:- value in OACK packet", value);
37413498266Sopenharmony_ci          return CURLE_TFTP_ILLEGAL;
37513498266Sopenharmony_ci        }
37613498266Sopenharmony_ci        Curl_pgrsSetDownloadSize(data, tsize);
37713498266Sopenharmony_ci      }
37813498266Sopenharmony_ci    }
37913498266Sopenharmony_ci  }
38013498266Sopenharmony_ci
38113498266Sopenharmony_ci  return CURLE_OK;
38213498266Sopenharmony_ci}
38313498266Sopenharmony_ci
38413498266Sopenharmony_cistatic CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
38513498266Sopenharmony_ci                                char *buf, const char *option)
38613498266Sopenharmony_ci{
38713498266Sopenharmony_ci  if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
38813498266Sopenharmony_ci    return CURLE_TFTP_ILLEGAL;
38913498266Sopenharmony_ci  strcpy(buf, option);
39013498266Sopenharmony_ci  *csize += strlen(option) + 1;
39113498266Sopenharmony_ci  return CURLE_OK;
39213498266Sopenharmony_ci}
39313498266Sopenharmony_ci
39413498266Sopenharmony_cistatic CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
39513498266Sopenharmony_ci                                    tftp_event_t event)
39613498266Sopenharmony_ci{
39713498266Sopenharmony_ci  CURLcode result;
39813498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
39913498266Sopenharmony_ci  struct Curl_easy *data = state->data;
40013498266Sopenharmony_ci
40113498266Sopenharmony_ci  infof(data, "%s", "Connected for transmit");
40213498266Sopenharmony_ci#endif
40313498266Sopenharmony_ci  state->state = TFTP_STATE_TX;
40413498266Sopenharmony_ci  result = tftp_set_timeouts(state);
40513498266Sopenharmony_ci  if(result)
40613498266Sopenharmony_ci    return result;
40713498266Sopenharmony_ci  return tftp_tx(state, event);
40813498266Sopenharmony_ci}
40913498266Sopenharmony_ci
41013498266Sopenharmony_cistatic CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
41113498266Sopenharmony_ci                                    tftp_event_t event)
41213498266Sopenharmony_ci{
41313498266Sopenharmony_ci  CURLcode result;
41413498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
41513498266Sopenharmony_ci  struct Curl_easy *data = state->data;
41613498266Sopenharmony_ci
41713498266Sopenharmony_ci  infof(data, "%s", "Connected for receive");
41813498266Sopenharmony_ci#endif
41913498266Sopenharmony_ci  state->state = TFTP_STATE_RX;
42013498266Sopenharmony_ci  result = tftp_set_timeouts(state);
42113498266Sopenharmony_ci  if(result)
42213498266Sopenharmony_ci    return result;
42313498266Sopenharmony_ci  return tftp_rx(state, event);
42413498266Sopenharmony_ci}
42513498266Sopenharmony_ci
42613498266Sopenharmony_cistatic CURLcode tftp_send_first(struct tftp_state_data *state,
42713498266Sopenharmony_ci                                tftp_event_t event)
42813498266Sopenharmony_ci{
42913498266Sopenharmony_ci  size_t sbytes;
43013498266Sopenharmony_ci  ssize_t senddata;
43113498266Sopenharmony_ci  const char *mode = "octet";
43213498266Sopenharmony_ci  char *filename;
43313498266Sopenharmony_ci  struct Curl_easy *data = state->data;
43413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
43513498266Sopenharmony_ci
43613498266Sopenharmony_ci  /* Set ascii mode if -B flag was used */
43713498266Sopenharmony_ci  if(data->state.prefer_ascii)
43813498266Sopenharmony_ci    mode = "netascii";
43913498266Sopenharmony_ci
44013498266Sopenharmony_ci  switch(event) {
44113498266Sopenharmony_ci
44213498266Sopenharmony_ci  case TFTP_EVENT_INIT:    /* Send the first packet out */
44313498266Sopenharmony_ci  case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
44413498266Sopenharmony_ci    /* Increment the retry counter, quit if over the limit */
44513498266Sopenharmony_ci    state->retries++;
44613498266Sopenharmony_ci    if(state->retries>state->retry_max) {
44713498266Sopenharmony_ci      state->error = TFTP_ERR_NORESPONSE;
44813498266Sopenharmony_ci      state->state = TFTP_STATE_FIN;
44913498266Sopenharmony_ci      return result;
45013498266Sopenharmony_ci    }
45113498266Sopenharmony_ci
45213498266Sopenharmony_ci    if(data->state.upload) {
45313498266Sopenharmony_ci      /* If we are uploading, send an WRQ */
45413498266Sopenharmony_ci      setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
45513498266Sopenharmony_ci      state->data->req.upload_fromhere =
45613498266Sopenharmony_ci        (char *)state->spacket.data + 4;
45713498266Sopenharmony_ci      if(data->state.infilesize != -1)
45813498266Sopenharmony_ci        Curl_pgrsSetUploadSize(data, data->state.infilesize);
45913498266Sopenharmony_ci    }
46013498266Sopenharmony_ci    else {
46113498266Sopenharmony_ci      /* If we are downloading, send an RRQ */
46213498266Sopenharmony_ci      setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
46313498266Sopenharmony_ci    }
46413498266Sopenharmony_ci    /* As RFC3617 describes the separator slash is not actually part of the
46513498266Sopenharmony_ci       file name so we skip the always-present first letter of the path
46613498266Sopenharmony_ci       string. */
46713498266Sopenharmony_ci    result = Curl_urldecode(&state->data->state.up.path[1], 0,
46813498266Sopenharmony_ci                            &filename, NULL, REJECT_ZERO);
46913498266Sopenharmony_ci    if(result)
47013498266Sopenharmony_ci      return result;
47113498266Sopenharmony_ci
47213498266Sopenharmony_ci    if(strlen(filename) > (state->blksize - strlen(mode) - 4)) {
47313498266Sopenharmony_ci      failf(data, "TFTP file name too long");
47413498266Sopenharmony_ci      free(filename);
47513498266Sopenharmony_ci      return CURLE_TFTP_ILLEGAL; /* too long file name field */
47613498266Sopenharmony_ci    }
47713498266Sopenharmony_ci
47813498266Sopenharmony_ci    msnprintf((char *)state->spacket.data + 2,
47913498266Sopenharmony_ci              state->blksize,
48013498266Sopenharmony_ci              "%s%c%s%c", filename, '\0',  mode, '\0');
48113498266Sopenharmony_ci    sbytes = 4 + strlen(filename) + strlen(mode);
48213498266Sopenharmony_ci
48313498266Sopenharmony_ci    /* optional addition of TFTP options */
48413498266Sopenharmony_ci    if(!data->set.tftp_no_options) {
48513498266Sopenharmony_ci      char buf[64];
48613498266Sopenharmony_ci      /* add tsize option */
48713498266Sopenharmony_ci      if(data->state.upload && (data->state.infilesize != -1))
48813498266Sopenharmony_ci        msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T,
48913498266Sopenharmony_ci                  data->state.infilesize);
49013498266Sopenharmony_ci      else
49113498266Sopenharmony_ci        strcpy(buf, "0"); /* the destination is large enough */
49213498266Sopenharmony_ci
49313498266Sopenharmony_ci      result = tftp_option_add(state, &sbytes,
49413498266Sopenharmony_ci                               (char *)state->spacket.data + sbytes,
49513498266Sopenharmony_ci                               TFTP_OPTION_TSIZE);
49613498266Sopenharmony_ci      if(result == CURLE_OK)
49713498266Sopenharmony_ci        result = tftp_option_add(state, &sbytes,
49813498266Sopenharmony_ci                                 (char *)state->spacket.data + sbytes, buf);
49913498266Sopenharmony_ci
50013498266Sopenharmony_ci      /* add blksize option */
50113498266Sopenharmony_ci      msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
50213498266Sopenharmony_ci      if(result == CURLE_OK)
50313498266Sopenharmony_ci        result = tftp_option_add(state, &sbytes,
50413498266Sopenharmony_ci                                 (char *)state->spacket.data + sbytes,
50513498266Sopenharmony_ci                                 TFTP_OPTION_BLKSIZE);
50613498266Sopenharmony_ci      if(result == CURLE_OK)
50713498266Sopenharmony_ci        result = tftp_option_add(state, &sbytes,
50813498266Sopenharmony_ci                                 (char *)state->spacket.data + sbytes, buf);
50913498266Sopenharmony_ci
51013498266Sopenharmony_ci      /* add timeout option */
51113498266Sopenharmony_ci      msnprintf(buf, sizeof(buf), "%d", state->retry_time);
51213498266Sopenharmony_ci      if(result == CURLE_OK)
51313498266Sopenharmony_ci        result = tftp_option_add(state, &sbytes,
51413498266Sopenharmony_ci                                 (char *)state->spacket.data + sbytes,
51513498266Sopenharmony_ci                                 TFTP_OPTION_INTERVAL);
51613498266Sopenharmony_ci      if(result == CURLE_OK)
51713498266Sopenharmony_ci        result = tftp_option_add(state, &sbytes,
51813498266Sopenharmony_ci                                 (char *)state->spacket.data + sbytes, buf);
51913498266Sopenharmony_ci
52013498266Sopenharmony_ci      if(result != CURLE_OK) {
52113498266Sopenharmony_ci        failf(data, "TFTP buffer too small for options");
52213498266Sopenharmony_ci        free(filename);
52313498266Sopenharmony_ci        return CURLE_TFTP_ILLEGAL;
52413498266Sopenharmony_ci      }
52513498266Sopenharmony_ci    }
52613498266Sopenharmony_ci
52713498266Sopenharmony_ci    /* the typecase for the 3rd argument is mostly for systems that do
52813498266Sopenharmony_ci       not have a size_t argument, like older unixes that want an 'int' */
52913498266Sopenharmony_ci    senddata = sendto(state->sockfd, (void *)state->spacket.data,
53013498266Sopenharmony_ci                      (SEND_TYPE_ARG3)sbytes, 0,
53113498266Sopenharmony_ci                      &data->conn->remote_addr->sa_addr,
53213498266Sopenharmony_ci                      data->conn->remote_addr->addrlen);
53313498266Sopenharmony_ci    if(senddata != (ssize_t)sbytes) {
53413498266Sopenharmony_ci      char buffer[STRERROR_LEN];
53513498266Sopenharmony_ci      failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
53613498266Sopenharmony_ci    }
53713498266Sopenharmony_ci    free(filename);
53813498266Sopenharmony_ci    break;
53913498266Sopenharmony_ci
54013498266Sopenharmony_ci  case TFTP_EVENT_OACK:
54113498266Sopenharmony_ci    if(data->state.upload) {
54213498266Sopenharmony_ci      result = tftp_connect_for_tx(state, event);
54313498266Sopenharmony_ci    }
54413498266Sopenharmony_ci    else {
54513498266Sopenharmony_ci      result = tftp_connect_for_rx(state, event);
54613498266Sopenharmony_ci    }
54713498266Sopenharmony_ci    break;
54813498266Sopenharmony_ci
54913498266Sopenharmony_ci  case TFTP_EVENT_ACK: /* Connected for transmit */
55013498266Sopenharmony_ci    result = tftp_connect_for_tx(state, event);
55113498266Sopenharmony_ci    break;
55213498266Sopenharmony_ci
55313498266Sopenharmony_ci  case TFTP_EVENT_DATA: /* Connected for receive */
55413498266Sopenharmony_ci    result = tftp_connect_for_rx(state, event);
55513498266Sopenharmony_ci    break;
55613498266Sopenharmony_ci
55713498266Sopenharmony_ci  case TFTP_EVENT_ERROR:
55813498266Sopenharmony_ci    state->state = TFTP_STATE_FIN;
55913498266Sopenharmony_ci    break;
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci  default:
56213498266Sopenharmony_ci    failf(state->data, "tftp_send_first: internal error");
56313498266Sopenharmony_ci    break;
56413498266Sopenharmony_ci  }
56513498266Sopenharmony_ci
56613498266Sopenharmony_ci  return result;
56713498266Sopenharmony_ci}
56813498266Sopenharmony_ci
56913498266Sopenharmony_ci/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
57013498266Sopenharmony_ci   boundary */
57113498266Sopenharmony_ci#define NEXT_BLOCKNUM(x) (((x) + 1)&0xffff)
57213498266Sopenharmony_ci
57313498266Sopenharmony_ci/**********************************************************
57413498266Sopenharmony_ci *
57513498266Sopenharmony_ci * tftp_rx
57613498266Sopenharmony_ci *
57713498266Sopenharmony_ci * Event handler for the RX state
57813498266Sopenharmony_ci *
57913498266Sopenharmony_ci **********************************************************/
58013498266Sopenharmony_cistatic CURLcode tftp_rx(struct tftp_state_data *state,
58113498266Sopenharmony_ci                        tftp_event_t event)
58213498266Sopenharmony_ci{
58313498266Sopenharmony_ci  ssize_t sbytes;
58413498266Sopenharmony_ci  int rblock;
58513498266Sopenharmony_ci  struct Curl_easy *data = state->data;
58613498266Sopenharmony_ci  char buffer[STRERROR_LEN];
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci  switch(event) {
58913498266Sopenharmony_ci
59013498266Sopenharmony_ci  case TFTP_EVENT_DATA:
59113498266Sopenharmony_ci    /* Is this the block we expect? */
59213498266Sopenharmony_ci    rblock = getrpacketblock(&state->rpacket);
59313498266Sopenharmony_ci    if(NEXT_BLOCKNUM(state->block) == rblock) {
59413498266Sopenharmony_ci      /* This is the expected block.  Reset counters and ACK it. */
59513498266Sopenharmony_ci      state->retries = 0;
59613498266Sopenharmony_ci    }
59713498266Sopenharmony_ci    else if(state->block == rblock) {
59813498266Sopenharmony_ci      /* This is the last recently received block again. Log it and ACK it
59913498266Sopenharmony_ci         again. */
60013498266Sopenharmony_ci      infof(data, "Received last DATA packet block %d again.", rblock);
60113498266Sopenharmony_ci    }
60213498266Sopenharmony_ci    else {
60313498266Sopenharmony_ci      /* totally unexpected, just log it */
60413498266Sopenharmony_ci      infof(data,
60513498266Sopenharmony_ci            "Received unexpected DATA packet block %d, expecting block %d",
60613498266Sopenharmony_ci            rblock, NEXT_BLOCKNUM(state->block));
60713498266Sopenharmony_ci      break;
60813498266Sopenharmony_ci    }
60913498266Sopenharmony_ci
61013498266Sopenharmony_ci    /* ACK this block. */
61113498266Sopenharmony_ci    state->block = (unsigned short)rblock;
61213498266Sopenharmony_ci    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
61313498266Sopenharmony_ci    setpacketblock(&state->spacket, state->block);
61413498266Sopenharmony_ci    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
61513498266Sopenharmony_ci                    4, SEND_4TH_ARG,
61613498266Sopenharmony_ci                    (struct sockaddr *)&state->remote_addr,
61713498266Sopenharmony_ci                    state->remote_addrlen);
61813498266Sopenharmony_ci    if(sbytes < 0) {
61913498266Sopenharmony_ci      failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
62013498266Sopenharmony_ci      return CURLE_SEND_ERROR;
62113498266Sopenharmony_ci    }
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    /* Check if completed (That is, a less than full packet is received) */
62413498266Sopenharmony_ci    if(state->rbytes < (ssize_t)state->blksize + 4) {
62513498266Sopenharmony_ci      state->state = TFTP_STATE_FIN;
62613498266Sopenharmony_ci    }
62713498266Sopenharmony_ci    else {
62813498266Sopenharmony_ci      state->state = TFTP_STATE_RX;
62913498266Sopenharmony_ci    }
63013498266Sopenharmony_ci    time(&state->rx_time);
63113498266Sopenharmony_ci    break;
63213498266Sopenharmony_ci
63313498266Sopenharmony_ci  case TFTP_EVENT_OACK:
63413498266Sopenharmony_ci    /* ACK option acknowledgement so we can move on to data */
63513498266Sopenharmony_ci    state->block = 0;
63613498266Sopenharmony_ci    state->retries = 0;
63713498266Sopenharmony_ci    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
63813498266Sopenharmony_ci    setpacketblock(&state->spacket, state->block);
63913498266Sopenharmony_ci    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
64013498266Sopenharmony_ci                    4, SEND_4TH_ARG,
64113498266Sopenharmony_ci                    (struct sockaddr *)&state->remote_addr,
64213498266Sopenharmony_ci                    state->remote_addrlen);
64313498266Sopenharmony_ci    if(sbytes < 0) {
64413498266Sopenharmony_ci      failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
64513498266Sopenharmony_ci      return CURLE_SEND_ERROR;
64613498266Sopenharmony_ci    }
64713498266Sopenharmony_ci
64813498266Sopenharmony_ci    /* we're ready to RX data */
64913498266Sopenharmony_ci    state->state = TFTP_STATE_RX;
65013498266Sopenharmony_ci    time(&state->rx_time);
65113498266Sopenharmony_ci    break;
65213498266Sopenharmony_ci
65313498266Sopenharmony_ci  case TFTP_EVENT_TIMEOUT:
65413498266Sopenharmony_ci    /* Increment the retry count and fail if over the limit */
65513498266Sopenharmony_ci    state->retries++;
65613498266Sopenharmony_ci    infof(data,
65713498266Sopenharmony_ci          "Timeout waiting for block %d ACK.  Retries = %d",
65813498266Sopenharmony_ci          NEXT_BLOCKNUM(state->block), state->retries);
65913498266Sopenharmony_ci    if(state->retries > state->retry_max) {
66013498266Sopenharmony_ci      state->error = TFTP_ERR_TIMEOUT;
66113498266Sopenharmony_ci      state->state = TFTP_STATE_FIN;
66213498266Sopenharmony_ci    }
66313498266Sopenharmony_ci    else {
66413498266Sopenharmony_ci      /* Resend the previous ACK */
66513498266Sopenharmony_ci      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
66613498266Sopenharmony_ci                      4, SEND_4TH_ARG,
66713498266Sopenharmony_ci                      (struct sockaddr *)&state->remote_addr,
66813498266Sopenharmony_ci                      state->remote_addrlen);
66913498266Sopenharmony_ci      if(sbytes<0) {
67013498266Sopenharmony_ci        failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
67113498266Sopenharmony_ci        return CURLE_SEND_ERROR;
67213498266Sopenharmony_ci      }
67313498266Sopenharmony_ci    }
67413498266Sopenharmony_ci    break;
67513498266Sopenharmony_ci
67613498266Sopenharmony_ci  case TFTP_EVENT_ERROR:
67713498266Sopenharmony_ci    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
67813498266Sopenharmony_ci    setpacketblock(&state->spacket, state->block);
67913498266Sopenharmony_ci    (void)sendto(state->sockfd, (void *)state->spacket.data,
68013498266Sopenharmony_ci                 4, SEND_4TH_ARG,
68113498266Sopenharmony_ci                 (struct sockaddr *)&state->remote_addr,
68213498266Sopenharmony_ci                 state->remote_addrlen);
68313498266Sopenharmony_ci    /* don't bother with the return code, but if the socket is still up we
68413498266Sopenharmony_ci     * should be a good TFTP client and let the server know we're done */
68513498266Sopenharmony_ci    state->state = TFTP_STATE_FIN;
68613498266Sopenharmony_ci    break;
68713498266Sopenharmony_ci
68813498266Sopenharmony_ci  default:
68913498266Sopenharmony_ci    failf(data, "%s", "tftp_rx: internal error");
69013498266Sopenharmony_ci    return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
69113498266Sopenharmony_ci                                  this */
69213498266Sopenharmony_ci  }
69313498266Sopenharmony_ci  return CURLE_OK;
69413498266Sopenharmony_ci}
69513498266Sopenharmony_ci
69613498266Sopenharmony_ci/**********************************************************
69713498266Sopenharmony_ci *
69813498266Sopenharmony_ci * tftp_tx
69913498266Sopenharmony_ci *
70013498266Sopenharmony_ci * Event handler for the TX state
70113498266Sopenharmony_ci *
70213498266Sopenharmony_ci **********************************************************/
70313498266Sopenharmony_cistatic CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
70413498266Sopenharmony_ci{
70513498266Sopenharmony_ci  struct Curl_easy *data = state->data;
70613498266Sopenharmony_ci  ssize_t sbytes;
70713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
70813498266Sopenharmony_ci  struct SingleRequest *k = &data->req;
70913498266Sopenharmony_ci  size_t cb; /* Bytes currently read */
71013498266Sopenharmony_ci  char buffer[STRERROR_LEN];
71113498266Sopenharmony_ci
71213498266Sopenharmony_ci  switch(event) {
71313498266Sopenharmony_ci
71413498266Sopenharmony_ci  case TFTP_EVENT_ACK:
71513498266Sopenharmony_ci  case TFTP_EVENT_OACK:
71613498266Sopenharmony_ci    if(event == TFTP_EVENT_ACK) {
71713498266Sopenharmony_ci      /* Ack the packet */
71813498266Sopenharmony_ci      int rblock = getrpacketblock(&state->rpacket);
71913498266Sopenharmony_ci
72013498266Sopenharmony_ci      if(rblock != state->block &&
72113498266Sopenharmony_ci         /* There's a bug in tftpd-hpa that causes it to send us an ack for
72213498266Sopenharmony_ci          * 65535 when the block number wraps to 0. So when we're expecting
72313498266Sopenharmony_ci          * 0, also accept 65535. See
72413498266Sopenharmony_ci          * https://www.syslinux.org/archives/2010-September/015612.html
72513498266Sopenharmony_ci          * */
72613498266Sopenharmony_ci         !(state->block == 0 && rblock == 65535)) {
72713498266Sopenharmony_ci        /* This isn't the expected block.  Log it and up the retry counter */
72813498266Sopenharmony_ci        infof(data, "Received ACK for block %d, expecting %d",
72913498266Sopenharmony_ci              rblock, state->block);
73013498266Sopenharmony_ci        state->retries++;
73113498266Sopenharmony_ci        /* Bail out if over the maximum */
73213498266Sopenharmony_ci        if(state->retries>state->retry_max) {
73313498266Sopenharmony_ci          failf(data, "tftp_tx: giving up waiting for block %d ack",
73413498266Sopenharmony_ci                state->block);
73513498266Sopenharmony_ci          result = CURLE_SEND_ERROR;
73613498266Sopenharmony_ci        }
73713498266Sopenharmony_ci        else {
73813498266Sopenharmony_ci          /* Re-send the data packet */
73913498266Sopenharmony_ci          sbytes = sendto(state->sockfd, (void *)state->spacket.data,
74013498266Sopenharmony_ci                          4 + state->sbytes, SEND_4TH_ARG,
74113498266Sopenharmony_ci                          (struct sockaddr *)&state->remote_addr,
74213498266Sopenharmony_ci                          state->remote_addrlen);
74313498266Sopenharmony_ci          /* Check all sbytes were sent */
74413498266Sopenharmony_ci          if(sbytes<0) {
74513498266Sopenharmony_ci            failf(data, "%s", Curl_strerror(SOCKERRNO,
74613498266Sopenharmony_ci                                            buffer, sizeof(buffer)));
74713498266Sopenharmony_ci            result = CURLE_SEND_ERROR;
74813498266Sopenharmony_ci          }
74913498266Sopenharmony_ci        }
75013498266Sopenharmony_ci
75113498266Sopenharmony_ci        return result;
75213498266Sopenharmony_ci      }
75313498266Sopenharmony_ci      /* This is the expected packet.  Reset the counters and send the next
75413498266Sopenharmony_ci         block */
75513498266Sopenharmony_ci      time(&state->rx_time);
75613498266Sopenharmony_ci      state->block++;
75713498266Sopenharmony_ci    }
75813498266Sopenharmony_ci    else
75913498266Sopenharmony_ci      state->block = 1; /* first data block is 1 when using OACK */
76013498266Sopenharmony_ci
76113498266Sopenharmony_ci    state->retries = 0;
76213498266Sopenharmony_ci    setpacketevent(&state->spacket, TFTP_EVENT_DATA);
76313498266Sopenharmony_ci    setpacketblock(&state->spacket, state->block);
76413498266Sopenharmony_ci    if(state->block > 1 && state->sbytes < state->blksize) {
76513498266Sopenharmony_ci      state->state = TFTP_STATE_FIN;
76613498266Sopenharmony_ci      return CURLE_OK;
76713498266Sopenharmony_ci    }
76813498266Sopenharmony_ci
76913498266Sopenharmony_ci    /* TFTP considers data block size < 512 bytes as an end of session. So
77013498266Sopenharmony_ci     * in some cases we must wait for additional data to build full (512 bytes)
77113498266Sopenharmony_ci     * data block.
77213498266Sopenharmony_ci     * */
77313498266Sopenharmony_ci    state->sbytes = 0;
77413498266Sopenharmony_ci    state->data->req.upload_fromhere = (char *)state->spacket.data + 4;
77513498266Sopenharmony_ci    do {
77613498266Sopenharmony_ci      result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb);
77713498266Sopenharmony_ci      if(result)
77813498266Sopenharmony_ci        return result;
77913498266Sopenharmony_ci      state->sbytes += (int)cb;
78013498266Sopenharmony_ci      state->data->req.upload_fromhere += cb;
78113498266Sopenharmony_ci    } while(state->sbytes < state->blksize && cb);
78213498266Sopenharmony_ci
78313498266Sopenharmony_ci    sbytes = sendto(state->sockfd, (void *) state->spacket.data,
78413498266Sopenharmony_ci                    4 + state->sbytes, SEND_4TH_ARG,
78513498266Sopenharmony_ci                    (struct sockaddr *)&state->remote_addr,
78613498266Sopenharmony_ci                    state->remote_addrlen);
78713498266Sopenharmony_ci    /* Check all sbytes were sent */
78813498266Sopenharmony_ci    if(sbytes<0) {
78913498266Sopenharmony_ci      failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
79013498266Sopenharmony_ci      return CURLE_SEND_ERROR;
79113498266Sopenharmony_ci    }
79213498266Sopenharmony_ci    /* Update the progress meter */
79313498266Sopenharmony_ci    k->writebytecount += state->sbytes;
79413498266Sopenharmony_ci    Curl_pgrsSetUploadCounter(data, k->writebytecount);
79513498266Sopenharmony_ci    break;
79613498266Sopenharmony_ci
79713498266Sopenharmony_ci  case TFTP_EVENT_TIMEOUT:
79813498266Sopenharmony_ci    /* Increment the retry counter and log the timeout */
79913498266Sopenharmony_ci    state->retries++;
80013498266Sopenharmony_ci    infof(data, "Timeout waiting for block %d ACK. "
80113498266Sopenharmony_ci          " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
80213498266Sopenharmony_ci    /* Decide if we've had enough */
80313498266Sopenharmony_ci    if(state->retries > state->retry_max) {
80413498266Sopenharmony_ci      state->error = TFTP_ERR_TIMEOUT;
80513498266Sopenharmony_ci      state->state = TFTP_STATE_FIN;
80613498266Sopenharmony_ci    }
80713498266Sopenharmony_ci    else {
80813498266Sopenharmony_ci      /* Re-send the data packet */
80913498266Sopenharmony_ci      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
81013498266Sopenharmony_ci                      4 + state->sbytes, SEND_4TH_ARG,
81113498266Sopenharmony_ci                      (struct sockaddr *)&state->remote_addr,
81213498266Sopenharmony_ci                      state->remote_addrlen);
81313498266Sopenharmony_ci      /* Check all sbytes were sent */
81413498266Sopenharmony_ci      if(sbytes<0) {
81513498266Sopenharmony_ci        failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
81613498266Sopenharmony_ci        return CURLE_SEND_ERROR;
81713498266Sopenharmony_ci      }
81813498266Sopenharmony_ci      /* since this was a re-send, we remain at the still byte position */
81913498266Sopenharmony_ci      Curl_pgrsSetUploadCounter(data, k->writebytecount);
82013498266Sopenharmony_ci    }
82113498266Sopenharmony_ci    break;
82213498266Sopenharmony_ci
82313498266Sopenharmony_ci  case TFTP_EVENT_ERROR:
82413498266Sopenharmony_ci    state->state = TFTP_STATE_FIN;
82513498266Sopenharmony_ci    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
82613498266Sopenharmony_ci    setpacketblock(&state->spacket, state->block);
82713498266Sopenharmony_ci    (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
82813498266Sopenharmony_ci                 (struct sockaddr *)&state->remote_addr,
82913498266Sopenharmony_ci                 state->remote_addrlen);
83013498266Sopenharmony_ci    /* don't bother with the return code, but if the socket is still up we
83113498266Sopenharmony_ci     * should be a good TFTP client and let the server know we're done */
83213498266Sopenharmony_ci    state->state = TFTP_STATE_FIN;
83313498266Sopenharmony_ci    break;
83413498266Sopenharmony_ci
83513498266Sopenharmony_ci  default:
83613498266Sopenharmony_ci    failf(data, "tftp_tx: internal error, event: %i", (int)(event));
83713498266Sopenharmony_ci    break;
83813498266Sopenharmony_ci  }
83913498266Sopenharmony_ci
84013498266Sopenharmony_ci  return result;
84113498266Sopenharmony_ci}
84213498266Sopenharmony_ci
84313498266Sopenharmony_ci/**********************************************************
84413498266Sopenharmony_ci *
84513498266Sopenharmony_ci * tftp_translate_code
84613498266Sopenharmony_ci *
84713498266Sopenharmony_ci * Translate internal error codes to CURL error codes
84813498266Sopenharmony_ci *
84913498266Sopenharmony_ci **********************************************************/
85013498266Sopenharmony_cistatic CURLcode tftp_translate_code(tftp_error_t error)
85113498266Sopenharmony_ci{
85213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
85313498266Sopenharmony_ci
85413498266Sopenharmony_ci  if(error != TFTP_ERR_NONE) {
85513498266Sopenharmony_ci    switch(error) {
85613498266Sopenharmony_ci    case TFTP_ERR_NOTFOUND:
85713498266Sopenharmony_ci      result = CURLE_TFTP_NOTFOUND;
85813498266Sopenharmony_ci      break;
85913498266Sopenharmony_ci    case TFTP_ERR_PERM:
86013498266Sopenharmony_ci      result = CURLE_TFTP_PERM;
86113498266Sopenharmony_ci      break;
86213498266Sopenharmony_ci    case TFTP_ERR_DISKFULL:
86313498266Sopenharmony_ci      result = CURLE_REMOTE_DISK_FULL;
86413498266Sopenharmony_ci      break;
86513498266Sopenharmony_ci    case TFTP_ERR_UNDEF:
86613498266Sopenharmony_ci    case TFTP_ERR_ILLEGAL:
86713498266Sopenharmony_ci      result = CURLE_TFTP_ILLEGAL;
86813498266Sopenharmony_ci      break;
86913498266Sopenharmony_ci    case TFTP_ERR_UNKNOWNID:
87013498266Sopenharmony_ci      result = CURLE_TFTP_UNKNOWNID;
87113498266Sopenharmony_ci      break;
87213498266Sopenharmony_ci    case TFTP_ERR_EXISTS:
87313498266Sopenharmony_ci      result = CURLE_REMOTE_FILE_EXISTS;
87413498266Sopenharmony_ci      break;
87513498266Sopenharmony_ci    case TFTP_ERR_NOSUCHUSER:
87613498266Sopenharmony_ci      result = CURLE_TFTP_NOSUCHUSER;
87713498266Sopenharmony_ci      break;
87813498266Sopenharmony_ci    case TFTP_ERR_TIMEOUT:
87913498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
88013498266Sopenharmony_ci      break;
88113498266Sopenharmony_ci    case TFTP_ERR_NORESPONSE:
88213498266Sopenharmony_ci      result = CURLE_COULDNT_CONNECT;
88313498266Sopenharmony_ci      break;
88413498266Sopenharmony_ci    default:
88513498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
88613498266Sopenharmony_ci      break;
88713498266Sopenharmony_ci    }
88813498266Sopenharmony_ci  }
88913498266Sopenharmony_ci  else
89013498266Sopenharmony_ci    result = CURLE_OK;
89113498266Sopenharmony_ci
89213498266Sopenharmony_ci  return result;
89313498266Sopenharmony_ci}
89413498266Sopenharmony_ci
89513498266Sopenharmony_ci/**********************************************************
89613498266Sopenharmony_ci *
89713498266Sopenharmony_ci * tftp_state_machine
89813498266Sopenharmony_ci *
89913498266Sopenharmony_ci * The tftp state machine event dispatcher
90013498266Sopenharmony_ci *
90113498266Sopenharmony_ci **********************************************************/
90213498266Sopenharmony_cistatic CURLcode tftp_state_machine(struct tftp_state_data *state,
90313498266Sopenharmony_ci                                   tftp_event_t event)
90413498266Sopenharmony_ci{
90513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
90613498266Sopenharmony_ci  struct Curl_easy *data = state->data;
90713498266Sopenharmony_ci
90813498266Sopenharmony_ci  switch(state->state) {
90913498266Sopenharmony_ci  case TFTP_STATE_START:
91013498266Sopenharmony_ci    DEBUGF(infof(data, "TFTP_STATE_START"));
91113498266Sopenharmony_ci    result = tftp_send_first(state, event);
91213498266Sopenharmony_ci    break;
91313498266Sopenharmony_ci  case TFTP_STATE_RX:
91413498266Sopenharmony_ci    DEBUGF(infof(data, "TFTP_STATE_RX"));
91513498266Sopenharmony_ci    result = tftp_rx(state, event);
91613498266Sopenharmony_ci    break;
91713498266Sopenharmony_ci  case TFTP_STATE_TX:
91813498266Sopenharmony_ci    DEBUGF(infof(data, "TFTP_STATE_TX"));
91913498266Sopenharmony_ci    result = tftp_tx(state, event);
92013498266Sopenharmony_ci    break;
92113498266Sopenharmony_ci  case TFTP_STATE_FIN:
92213498266Sopenharmony_ci    infof(data, "%s", "TFTP finished");
92313498266Sopenharmony_ci    break;
92413498266Sopenharmony_ci  default:
92513498266Sopenharmony_ci    DEBUGF(infof(data, "STATE: %d", state->state));
92613498266Sopenharmony_ci    failf(data, "%s", "Internal state machine error");
92713498266Sopenharmony_ci    result = CURLE_TFTP_ILLEGAL;
92813498266Sopenharmony_ci    break;
92913498266Sopenharmony_ci  }
93013498266Sopenharmony_ci
93113498266Sopenharmony_ci  return result;
93213498266Sopenharmony_ci}
93313498266Sopenharmony_ci
93413498266Sopenharmony_ci/**********************************************************
93513498266Sopenharmony_ci *
93613498266Sopenharmony_ci * tftp_disconnect
93713498266Sopenharmony_ci *
93813498266Sopenharmony_ci * The disconnect callback
93913498266Sopenharmony_ci *
94013498266Sopenharmony_ci **********************************************************/
94113498266Sopenharmony_cistatic CURLcode tftp_disconnect(struct Curl_easy *data,
94213498266Sopenharmony_ci                                struct connectdata *conn, bool dead_connection)
94313498266Sopenharmony_ci{
94413498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
94513498266Sopenharmony_ci  (void) data;
94613498266Sopenharmony_ci  (void) dead_connection;
94713498266Sopenharmony_ci
94813498266Sopenharmony_ci  /* done, free dynamically allocated pkt buffers */
94913498266Sopenharmony_ci  if(state) {
95013498266Sopenharmony_ci    Curl_safefree(state->rpacket.data);
95113498266Sopenharmony_ci    Curl_safefree(state->spacket.data);
95213498266Sopenharmony_ci    free(state);
95313498266Sopenharmony_ci  }
95413498266Sopenharmony_ci
95513498266Sopenharmony_ci  return CURLE_OK;
95613498266Sopenharmony_ci}
95713498266Sopenharmony_ci
95813498266Sopenharmony_ci/**********************************************************
95913498266Sopenharmony_ci *
96013498266Sopenharmony_ci * tftp_connect
96113498266Sopenharmony_ci *
96213498266Sopenharmony_ci * The connect callback
96313498266Sopenharmony_ci *
96413498266Sopenharmony_ci **********************************************************/
96513498266Sopenharmony_cistatic CURLcode tftp_connect(struct Curl_easy *data, bool *done)
96613498266Sopenharmony_ci{
96713498266Sopenharmony_ci  struct tftp_state_data *state;
96813498266Sopenharmony_ci  int blksize;
96913498266Sopenharmony_ci  int need_blksize;
97013498266Sopenharmony_ci  struct connectdata *conn = data->conn;
97113498266Sopenharmony_ci
97213498266Sopenharmony_ci  blksize = TFTP_BLKSIZE_DEFAULT;
97313498266Sopenharmony_ci
97413498266Sopenharmony_ci  state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data));
97513498266Sopenharmony_ci  if(!state)
97613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
97713498266Sopenharmony_ci
97813498266Sopenharmony_ci  /* alloc pkt buffers based on specified blksize */
97913498266Sopenharmony_ci  if(data->set.tftp_blksize)
98013498266Sopenharmony_ci    /* range checked when set */
98113498266Sopenharmony_ci    blksize = (int)data->set.tftp_blksize;
98213498266Sopenharmony_ci
98313498266Sopenharmony_ci  need_blksize = blksize;
98413498266Sopenharmony_ci  /* default size is the fallback when no OACK is received */
98513498266Sopenharmony_ci  if(need_blksize < TFTP_BLKSIZE_DEFAULT)
98613498266Sopenharmony_ci    need_blksize = TFTP_BLKSIZE_DEFAULT;
98713498266Sopenharmony_ci
98813498266Sopenharmony_ci  if(!state->rpacket.data) {
98913498266Sopenharmony_ci    state->rpacket.data = calloc(1, need_blksize + 2 + 2);
99013498266Sopenharmony_ci
99113498266Sopenharmony_ci    if(!state->rpacket.data)
99213498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
99313498266Sopenharmony_ci  }
99413498266Sopenharmony_ci
99513498266Sopenharmony_ci  if(!state->spacket.data) {
99613498266Sopenharmony_ci    state->spacket.data = calloc(1, need_blksize + 2 + 2);
99713498266Sopenharmony_ci
99813498266Sopenharmony_ci    if(!state->spacket.data)
99913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
100013498266Sopenharmony_ci  }
100113498266Sopenharmony_ci
100213498266Sopenharmony_ci  /* we don't keep TFTP connections up basically because there's none or very
100313498266Sopenharmony_ci   * little gain for UDP */
100413498266Sopenharmony_ci  connclose(conn, "TFTP");
100513498266Sopenharmony_ci
100613498266Sopenharmony_ci  state->data = data;
100713498266Sopenharmony_ci  state->sockfd = conn->sock[FIRSTSOCKET];
100813498266Sopenharmony_ci  state->state = TFTP_STATE_START;
100913498266Sopenharmony_ci  state->error = TFTP_ERR_NONE;
101013498266Sopenharmony_ci  state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */
101113498266Sopenharmony_ci  state->requested_blksize = blksize;
101213498266Sopenharmony_ci
101313498266Sopenharmony_ci  ((struct sockaddr *)&state->local_addr)->sa_family =
101413498266Sopenharmony_ci    (CURL_SA_FAMILY_T)(conn->remote_addr->family);
101513498266Sopenharmony_ci
101613498266Sopenharmony_ci  tftp_set_timeouts(state);
101713498266Sopenharmony_ci
101813498266Sopenharmony_ci  if(!conn->bits.bound) {
101913498266Sopenharmony_ci    /* If not already bound, bind to any interface, random UDP port. If it is
102013498266Sopenharmony_ci     * reused or a custom local port was desired, this has already been done!
102113498266Sopenharmony_ci     *
102213498266Sopenharmony_ci     * We once used the size of the local_addr struct as the third argument
102313498266Sopenharmony_ci     * for bind() to better work with IPv6 or whatever size the struct could
102413498266Sopenharmony_ci     * have, but we learned that at least Tru64, AIX and IRIX *requires* the
102513498266Sopenharmony_ci     * size of that argument to match the exact size of a 'sockaddr_in' struct
102613498266Sopenharmony_ci     * when running IPv4-only.
102713498266Sopenharmony_ci     *
102813498266Sopenharmony_ci     * Therefore we use the size from the address we connected to, which we
102913498266Sopenharmony_ci     * assume uses the same IP version and thus hopefully this works for both
103013498266Sopenharmony_ci     * IPv4 and IPv6...
103113498266Sopenharmony_ci     */
103213498266Sopenharmony_ci    int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
103313498266Sopenharmony_ci                  conn->remote_addr->addrlen);
103413498266Sopenharmony_ci    if(rc) {
103513498266Sopenharmony_ci      char buffer[STRERROR_LEN];
103613498266Sopenharmony_ci      failf(data, "bind() failed; %s",
103713498266Sopenharmony_ci            Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
103813498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
103913498266Sopenharmony_ci    }
104013498266Sopenharmony_ci    conn->bits.bound = TRUE;
104113498266Sopenharmony_ci  }
104213498266Sopenharmony_ci
104313498266Sopenharmony_ci  Curl_pgrsStartNow(data);
104413498266Sopenharmony_ci
104513498266Sopenharmony_ci  *done = TRUE;
104613498266Sopenharmony_ci
104713498266Sopenharmony_ci  return CURLE_OK;
104813498266Sopenharmony_ci}
104913498266Sopenharmony_ci
105013498266Sopenharmony_ci/**********************************************************
105113498266Sopenharmony_ci *
105213498266Sopenharmony_ci * tftp_done
105313498266Sopenharmony_ci *
105413498266Sopenharmony_ci * The done callback
105513498266Sopenharmony_ci *
105613498266Sopenharmony_ci **********************************************************/
105713498266Sopenharmony_cistatic CURLcode tftp_done(struct Curl_easy *data, CURLcode status,
105813498266Sopenharmony_ci                          bool premature)
105913498266Sopenharmony_ci{
106013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
106113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
106213498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
106313498266Sopenharmony_ci
106413498266Sopenharmony_ci  (void)status; /* unused */
106513498266Sopenharmony_ci  (void)premature; /* not used */
106613498266Sopenharmony_ci
106713498266Sopenharmony_ci  if(Curl_pgrsDone(data))
106813498266Sopenharmony_ci    return CURLE_ABORTED_BY_CALLBACK;
106913498266Sopenharmony_ci
107013498266Sopenharmony_ci  /* If we have encountered an error */
107113498266Sopenharmony_ci  if(state)
107213498266Sopenharmony_ci    result = tftp_translate_code(state->error);
107313498266Sopenharmony_ci
107413498266Sopenharmony_ci  return result;
107513498266Sopenharmony_ci}
107613498266Sopenharmony_ci
107713498266Sopenharmony_ci/**********************************************************
107813498266Sopenharmony_ci *
107913498266Sopenharmony_ci * tftp_getsock
108013498266Sopenharmony_ci *
108113498266Sopenharmony_ci * The getsock callback
108213498266Sopenharmony_ci *
108313498266Sopenharmony_ci **********************************************************/
108413498266Sopenharmony_cistatic int tftp_getsock(struct Curl_easy *data,
108513498266Sopenharmony_ci                        struct connectdata *conn, curl_socket_t *socks)
108613498266Sopenharmony_ci{
108713498266Sopenharmony_ci  (void)data;
108813498266Sopenharmony_ci  socks[0] = conn->sock[FIRSTSOCKET];
108913498266Sopenharmony_ci  return GETSOCK_READSOCK(0);
109013498266Sopenharmony_ci}
109113498266Sopenharmony_ci
109213498266Sopenharmony_ci/**********************************************************
109313498266Sopenharmony_ci *
109413498266Sopenharmony_ci * tftp_receive_packet
109513498266Sopenharmony_ci *
109613498266Sopenharmony_ci * Called once select fires and data is ready on the socket
109713498266Sopenharmony_ci *
109813498266Sopenharmony_ci **********************************************************/
109913498266Sopenharmony_cistatic CURLcode tftp_receive_packet(struct Curl_easy *data)
110013498266Sopenharmony_ci{
110113498266Sopenharmony_ci  struct Curl_sockaddr_storage fromaddr;
110213498266Sopenharmony_ci  curl_socklen_t        fromlen;
110313498266Sopenharmony_ci  CURLcode              result = CURLE_OK;
110413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
110513498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
110613498266Sopenharmony_ci
110713498266Sopenharmony_ci  /* Receive the packet */
110813498266Sopenharmony_ci  fromlen = sizeof(fromaddr);
110913498266Sopenharmony_ci  state->rbytes = (int)recvfrom(state->sockfd,
111013498266Sopenharmony_ci                                (void *)state->rpacket.data,
111113498266Sopenharmony_ci                                state->blksize + 4,
111213498266Sopenharmony_ci                                0,
111313498266Sopenharmony_ci                                (struct sockaddr *)&fromaddr,
111413498266Sopenharmony_ci                                &fromlen);
111513498266Sopenharmony_ci  if(state->remote_addrlen == 0) {
111613498266Sopenharmony_ci    memcpy(&state->remote_addr, &fromaddr, fromlen);
111713498266Sopenharmony_ci    state->remote_addrlen = fromlen;
111813498266Sopenharmony_ci  }
111913498266Sopenharmony_ci
112013498266Sopenharmony_ci  /* Sanity check packet length */
112113498266Sopenharmony_ci  if(state->rbytes < 4) {
112213498266Sopenharmony_ci    failf(data, "Received too short packet");
112313498266Sopenharmony_ci    /* Not a timeout, but how best to handle it? */
112413498266Sopenharmony_ci    state->event = TFTP_EVENT_TIMEOUT;
112513498266Sopenharmony_ci  }
112613498266Sopenharmony_ci  else {
112713498266Sopenharmony_ci    /* The event is given by the TFTP packet time */
112813498266Sopenharmony_ci    unsigned short event = getrpacketevent(&state->rpacket);
112913498266Sopenharmony_ci    state->event = (tftp_event_t)event;
113013498266Sopenharmony_ci
113113498266Sopenharmony_ci    switch(state->event) {
113213498266Sopenharmony_ci    case TFTP_EVENT_DATA:
113313498266Sopenharmony_ci      /* Don't pass to the client empty or retransmitted packets */
113413498266Sopenharmony_ci      if(state->rbytes > 4 &&
113513498266Sopenharmony_ci         (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
113613498266Sopenharmony_ci        result = Curl_client_write(data, CLIENTWRITE_BODY,
113713498266Sopenharmony_ci                                   (char *)state->rpacket.data + 4,
113813498266Sopenharmony_ci                                   state->rbytes-4);
113913498266Sopenharmony_ci        if(result) {
114013498266Sopenharmony_ci          tftp_state_machine(state, TFTP_EVENT_ERROR);
114113498266Sopenharmony_ci          return result;
114213498266Sopenharmony_ci        }
114313498266Sopenharmony_ci      }
114413498266Sopenharmony_ci      break;
114513498266Sopenharmony_ci    case TFTP_EVENT_ERROR:
114613498266Sopenharmony_ci    {
114713498266Sopenharmony_ci      unsigned short error = getrpacketblock(&state->rpacket);
114813498266Sopenharmony_ci      char *str = (char *)state->rpacket.data + 4;
114913498266Sopenharmony_ci      size_t strn = state->rbytes - 4;
115013498266Sopenharmony_ci      state->error = (tftp_error_t)error;
115113498266Sopenharmony_ci      if(tftp_strnlen(str, strn) < strn)
115213498266Sopenharmony_ci        infof(data, "TFTP error: %s", str);
115313498266Sopenharmony_ci      break;
115413498266Sopenharmony_ci    }
115513498266Sopenharmony_ci    case TFTP_EVENT_ACK:
115613498266Sopenharmony_ci      break;
115713498266Sopenharmony_ci    case TFTP_EVENT_OACK:
115813498266Sopenharmony_ci      result = tftp_parse_option_ack(state,
115913498266Sopenharmony_ci                                     (const char *)state->rpacket.data + 2,
116013498266Sopenharmony_ci                                     state->rbytes-2);
116113498266Sopenharmony_ci      if(result)
116213498266Sopenharmony_ci        return result;
116313498266Sopenharmony_ci      break;
116413498266Sopenharmony_ci    case TFTP_EVENT_RRQ:
116513498266Sopenharmony_ci    case TFTP_EVENT_WRQ:
116613498266Sopenharmony_ci    default:
116713498266Sopenharmony_ci      failf(data, "%s", "Internal error: Unexpected packet");
116813498266Sopenharmony_ci      break;
116913498266Sopenharmony_ci    }
117013498266Sopenharmony_ci
117113498266Sopenharmony_ci    /* Update the progress meter */
117213498266Sopenharmony_ci    if(Curl_pgrsUpdate(data)) {
117313498266Sopenharmony_ci      tftp_state_machine(state, TFTP_EVENT_ERROR);
117413498266Sopenharmony_ci      return CURLE_ABORTED_BY_CALLBACK;
117513498266Sopenharmony_ci    }
117613498266Sopenharmony_ci  }
117713498266Sopenharmony_ci  return result;
117813498266Sopenharmony_ci}
117913498266Sopenharmony_ci
118013498266Sopenharmony_ci/**********************************************************
118113498266Sopenharmony_ci *
118213498266Sopenharmony_ci * tftp_state_timeout
118313498266Sopenharmony_ci *
118413498266Sopenharmony_ci * Check if timeouts have been reached
118513498266Sopenharmony_ci *
118613498266Sopenharmony_ci **********************************************************/
118713498266Sopenharmony_cistatic timediff_t tftp_state_timeout(struct Curl_easy *data,
118813498266Sopenharmony_ci                                     tftp_event_t *event)
118913498266Sopenharmony_ci{
119013498266Sopenharmony_ci  time_t current;
119113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
119213498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
119313498266Sopenharmony_ci  timediff_t timeout_ms;
119413498266Sopenharmony_ci
119513498266Sopenharmony_ci  if(event)
119613498266Sopenharmony_ci    *event = TFTP_EVENT_NONE;
119713498266Sopenharmony_ci
119813498266Sopenharmony_ci  timeout_ms = Curl_timeleft(state->data, NULL,
119913498266Sopenharmony_ci                             (state->state == TFTP_STATE_START));
120013498266Sopenharmony_ci  if(timeout_ms < 0) {
120113498266Sopenharmony_ci    state->error = TFTP_ERR_TIMEOUT;
120213498266Sopenharmony_ci    state->state = TFTP_STATE_FIN;
120313498266Sopenharmony_ci    return 0;
120413498266Sopenharmony_ci  }
120513498266Sopenharmony_ci  time(&current);
120613498266Sopenharmony_ci  if(current > state->rx_time + state->retry_time) {
120713498266Sopenharmony_ci    if(event)
120813498266Sopenharmony_ci      *event = TFTP_EVENT_TIMEOUT;
120913498266Sopenharmony_ci    time(&state->rx_time); /* update even though we received nothing */
121013498266Sopenharmony_ci  }
121113498266Sopenharmony_ci
121213498266Sopenharmony_ci  return timeout_ms;
121313498266Sopenharmony_ci}
121413498266Sopenharmony_ci
121513498266Sopenharmony_ci/**********************************************************
121613498266Sopenharmony_ci *
121713498266Sopenharmony_ci * tftp_multi_statemach
121813498266Sopenharmony_ci *
121913498266Sopenharmony_ci * Handle single RX socket event and return
122013498266Sopenharmony_ci *
122113498266Sopenharmony_ci **********************************************************/
122213498266Sopenharmony_cistatic CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
122313498266Sopenharmony_ci{
122413498266Sopenharmony_ci  tftp_event_t event;
122513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
122613498266Sopenharmony_ci  struct connectdata *conn = data->conn;
122713498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
122813498266Sopenharmony_ci  timediff_t timeout_ms = tftp_state_timeout(data, &event);
122913498266Sopenharmony_ci
123013498266Sopenharmony_ci  *done = FALSE;
123113498266Sopenharmony_ci
123213498266Sopenharmony_ci  if(timeout_ms < 0) {
123313498266Sopenharmony_ci    failf(data, "TFTP response timeout");
123413498266Sopenharmony_ci    return CURLE_OPERATION_TIMEDOUT;
123513498266Sopenharmony_ci  }
123613498266Sopenharmony_ci  if(event != TFTP_EVENT_NONE) {
123713498266Sopenharmony_ci    result = tftp_state_machine(state, event);
123813498266Sopenharmony_ci    if(result)
123913498266Sopenharmony_ci      return result;
124013498266Sopenharmony_ci    *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
124113498266Sopenharmony_ci    if(*done)
124213498266Sopenharmony_ci      /* Tell curl we're done */
124313498266Sopenharmony_ci      Curl_setup_transfer(data, -1, -1, FALSE, -1);
124413498266Sopenharmony_ci  }
124513498266Sopenharmony_ci  else {
124613498266Sopenharmony_ci    /* no timeouts to handle, check our socket */
124713498266Sopenharmony_ci    int rc = SOCKET_READABLE(state->sockfd, 0);
124813498266Sopenharmony_ci
124913498266Sopenharmony_ci    if(rc == -1) {
125013498266Sopenharmony_ci      /* bail out */
125113498266Sopenharmony_ci      int error = SOCKERRNO;
125213498266Sopenharmony_ci      char buffer[STRERROR_LEN];
125313498266Sopenharmony_ci      failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer)));
125413498266Sopenharmony_ci      state->event = TFTP_EVENT_ERROR;
125513498266Sopenharmony_ci    }
125613498266Sopenharmony_ci    else if(rc) {
125713498266Sopenharmony_ci      result = tftp_receive_packet(data);
125813498266Sopenharmony_ci      if(result)
125913498266Sopenharmony_ci        return result;
126013498266Sopenharmony_ci      result = tftp_state_machine(state, state->event);
126113498266Sopenharmony_ci      if(result)
126213498266Sopenharmony_ci        return result;
126313498266Sopenharmony_ci      *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
126413498266Sopenharmony_ci      if(*done)
126513498266Sopenharmony_ci        /* Tell curl we're done */
126613498266Sopenharmony_ci        Curl_setup_transfer(data, -1, -1, FALSE, -1);
126713498266Sopenharmony_ci    }
126813498266Sopenharmony_ci    /* if rc == 0, then select() timed out */
126913498266Sopenharmony_ci  }
127013498266Sopenharmony_ci
127113498266Sopenharmony_ci  return result;
127213498266Sopenharmony_ci}
127313498266Sopenharmony_ci
127413498266Sopenharmony_ci/**********************************************************
127513498266Sopenharmony_ci *
127613498266Sopenharmony_ci * tftp_doing
127713498266Sopenharmony_ci *
127813498266Sopenharmony_ci * Called from multi.c while DOing
127913498266Sopenharmony_ci *
128013498266Sopenharmony_ci **********************************************************/
128113498266Sopenharmony_cistatic CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
128213498266Sopenharmony_ci{
128313498266Sopenharmony_ci  CURLcode result;
128413498266Sopenharmony_ci  result = tftp_multi_statemach(data, dophase_done);
128513498266Sopenharmony_ci
128613498266Sopenharmony_ci  if(*dophase_done) {
128713498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete"));
128813498266Sopenharmony_ci  }
128913498266Sopenharmony_ci  else if(!result) {
129013498266Sopenharmony_ci    /* The multi code doesn't have this logic for the DOING state so we
129113498266Sopenharmony_ci       provide it for TFTP since it may do the entire transfer in this
129213498266Sopenharmony_ci       state. */
129313498266Sopenharmony_ci    if(Curl_pgrsUpdate(data))
129413498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
129513498266Sopenharmony_ci    else
129613498266Sopenharmony_ci      result = Curl_speedcheck(data, Curl_now());
129713498266Sopenharmony_ci  }
129813498266Sopenharmony_ci  return result;
129913498266Sopenharmony_ci}
130013498266Sopenharmony_ci
130113498266Sopenharmony_ci/**********************************************************
130213498266Sopenharmony_ci *
130313498266Sopenharmony_ci * tftp_perform
130413498266Sopenharmony_ci *
130513498266Sopenharmony_ci * Entry point for transfer from tftp_do, starts state mach
130613498266Sopenharmony_ci *
130713498266Sopenharmony_ci **********************************************************/
130813498266Sopenharmony_cistatic CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
130913498266Sopenharmony_ci{
131013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
131113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
131213498266Sopenharmony_ci  struct tftp_state_data *state = conn->proto.tftpc;
131313498266Sopenharmony_ci
131413498266Sopenharmony_ci  *dophase_done = FALSE;
131513498266Sopenharmony_ci
131613498266Sopenharmony_ci  result = tftp_state_machine(state, TFTP_EVENT_INIT);
131713498266Sopenharmony_ci
131813498266Sopenharmony_ci  if((state->state == TFTP_STATE_FIN) || result)
131913498266Sopenharmony_ci    return result;
132013498266Sopenharmony_ci
132113498266Sopenharmony_ci  tftp_multi_statemach(data, dophase_done);
132213498266Sopenharmony_ci
132313498266Sopenharmony_ci  if(*dophase_done)
132413498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete"));
132513498266Sopenharmony_ci
132613498266Sopenharmony_ci  return result;
132713498266Sopenharmony_ci}
132813498266Sopenharmony_ci
132913498266Sopenharmony_ci
133013498266Sopenharmony_ci/**********************************************************
133113498266Sopenharmony_ci *
133213498266Sopenharmony_ci * tftp_do
133313498266Sopenharmony_ci *
133413498266Sopenharmony_ci * The do callback
133513498266Sopenharmony_ci *
133613498266Sopenharmony_ci * This callback initiates the TFTP transfer
133713498266Sopenharmony_ci *
133813498266Sopenharmony_ci **********************************************************/
133913498266Sopenharmony_ci
134013498266Sopenharmony_cistatic CURLcode tftp_do(struct Curl_easy *data, bool *done)
134113498266Sopenharmony_ci{
134213498266Sopenharmony_ci  struct tftp_state_data *state;
134313498266Sopenharmony_ci  CURLcode result;
134413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
134513498266Sopenharmony_ci
134613498266Sopenharmony_ci  *done = FALSE;
134713498266Sopenharmony_ci
134813498266Sopenharmony_ci  if(!conn->proto.tftpc) {
134913498266Sopenharmony_ci    result = tftp_connect(data, done);
135013498266Sopenharmony_ci    if(result)
135113498266Sopenharmony_ci      return result;
135213498266Sopenharmony_ci  }
135313498266Sopenharmony_ci
135413498266Sopenharmony_ci  state = conn->proto.tftpc;
135513498266Sopenharmony_ci  if(!state)
135613498266Sopenharmony_ci    return CURLE_TFTP_ILLEGAL;
135713498266Sopenharmony_ci
135813498266Sopenharmony_ci  result = tftp_perform(data, done);
135913498266Sopenharmony_ci
136013498266Sopenharmony_ci  /* If tftp_perform() returned an error, use that for return code. If it
136113498266Sopenharmony_ci     was OK, see if tftp_translate_code() has an error. */
136213498266Sopenharmony_ci  if(!result)
136313498266Sopenharmony_ci    /* If we have encountered an internal tftp error, translate it. */
136413498266Sopenharmony_ci    result = tftp_translate_code(state->error);
136513498266Sopenharmony_ci
136613498266Sopenharmony_ci  return result;
136713498266Sopenharmony_ci}
136813498266Sopenharmony_ci
136913498266Sopenharmony_cistatic CURLcode tftp_setup_connection(struct Curl_easy *data,
137013498266Sopenharmony_ci                                      struct connectdata *conn)
137113498266Sopenharmony_ci{
137213498266Sopenharmony_ci  char *type;
137313498266Sopenharmony_ci
137413498266Sopenharmony_ci  conn->transport = TRNSPRT_UDP;
137513498266Sopenharmony_ci
137613498266Sopenharmony_ci  /* TFTP URLs support an extension like ";mode=<typecode>" that
137713498266Sopenharmony_ci   * we'll try to get now! */
137813498266Sopenharmony_ci  type = strstr(data->state.up.path, ";mode=");
137913498266Sopenharmony_ci
138013498266Sopenharmony_ci  if(!type)
138113498266Sopenharmony_ci    type = strstr(conn->host.rawalloc, ";mode=");
138213498266Sopenharmony_ci
138313498266Sopenharmony_ci  if(type) {
138413498266Sopenharmony_ci    char command;
138513498266Sopenharmony_ci    *type = 0;                   /* it was in the middle of the hostname */
138613498266Sopenharmony_ci    command = Curl_raw_toupper(type[6]);
138713498266Sopenharmony_ci
138813498266Sopenharmony_ci    switch(command) {
138913498266Sopenharmony_ci    case 'A': /* ASCII mode */
139013498266Sopenharmony_ci    case 'N': /* NETASCII mode */
139113498266Sopenharmony_ci      data->state.prefer_ascii = TRUE;
139213498266Sopenharmony_ci      break;
139313498266Sopenharmony_ci
139413498266Sopenharmony_ci    case 'O': /* octet mode */
139513498266Sopenharmony_ci    case 'I': /* binary mode */
139613498266Sopenharmony_ci    default:
139713498266Sopenharmony_ci      /* switch off ASCII */
139813498266Sopenharmony_ci      data->state.prefer_ascii = FALSE;
139913498266Sopenharmony_ci      break;
140013498266Sopenharmony_ci    }
140113498266Sopenharmony_ci  }
140213498266Sopenharmony_ci
140313498266Sopenharmony_ci  return CURLE_OK;
140413498266Sopenharmony_ci}
140513498266Sopenharmony_ci#endif
1406