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(¤t); 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