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