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