tftp_client.c
Go to the documentation of this file.
1 /**
2  * @file tftp_client.c
3  * @brief TFTP client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * TFTP is a very simple protocol used to transfer files. Refer to the
30  * following RFCs for complete details:
31  * - RFC 1123: Requirements for Internet Hosts
32  * - RFC 1350: The TFTP Protocol (Revision 2)
33  * - RFC 1782: TFTP Option Extension
34  * - RFC 1783: TFTP Blocksize Option
35  * - RFC 1784: TFTP Timeout Interval and Transfer Size Options
36  *
37  * @author Oryx Embedded SARL (www.oryx-embedded.com)
38  * @version 2.5.0
39  **/
40 
41 //Switch to the appropriate trace level
42 #define TRACE_LEVEL TFTP_TRACE_LEVEL
43 
44 //Dependencies
45 #include "core/net.h"
46 #include "tftp/tftp_client.h"
47 #include "tftp/tftp_client_misc.h"
48 #include "debug.h"
49 
50 //Check TCP/IP stack configuration
51 #if (TFTP_CLIENT_SUPPORT == ENABLED)
52 
53 
54 /**
55  * @brief TFTP client initialization
56  * @param[in] context Pointer to the TFTP client context
57  * @return Error code
58  **/
59 
61 {
62  //Make sure the TFTP client context is valid
63  if(context == NULL)
65 
66  //Initialize context
67  osMemset(context, 0, sizeof(TftpClientContext));
68 
69  //Initialize TFTP client state
71 
72  //Successful initialization
73  return NO_ERROR;
74 }
75 
76 
77 /**
78  * @brief Bind the TFTP client to a particular network interface
79  * @param[in] context Pointer to the TFTP client context
80  * @param[in] interface Network interface to be used
81  * @return Error code
82  **/
83 
85  NetInterface *interface)
86 {
87  //Make sure the TFTP client context is valid
88  if(context == NULL)
90 
91  //Explicitly associate the TFTP client with the specified interface
92  context->interface = interface;
93 
94  //Successful processing
95  return NO_ERROR;
96 }
97 
98 
99 /**
100  * @brief Specify the address of the TFTP server
101  * @param[in] context Pointer to the TFTP client context
102  * @param[in] serverIpAddr IP address of the TFTP server to connect to
103  * @param[in] serverPort UDP port number
104  * @return Error code
105  **/
106 
108  const IpAddr *serverIpAddr, uint16_t serverPort)
109 {
110  //Check parameters
111  if(context == NULL || serverIpAddr == NULL)
113 
114  //Check current state
115  if(context->state != TFTP_CLIENT_STATE_CLOSED)
116  return ERROR_WRONG_STATE;
117 
118  //Save the IP address of the remote TFTP server
119  context->serverIpAddr = *serverIpAddr;
120  //Save the UDP port number to be used
121  context->serverPort = serverPort;
122 
123  //Successful processing
124  return NO_ERROR;
125 }
126 
127 
128 /**
129  * @brief Open a file for reading or writing
130  * @param[in] context Pointer to the TFTP client context
131  * @param[in] filename NULL-terminated string specifying the filename
132  * @param[in] mode File access mode
133  * @return Error code
134  **/
135 
137  const char_t *filename, uint_t mode)
138 {
139  error_t error;
140 
141  //Check parameters
142  if(context == NULL || filename == NULL)
144 
145  //Initialize status code
146  error = NO_ERROR;
147 
148  //Read or write access?
149  if((mode & TFTP_FILE_MODE_WRITE) != 0)
150  {
151  //Wait for the WRQ request to be accepted
152  while(!error)
153  {
154  //Check current state
155  if(context->state == TFTP_CLIENT_STATE_CLOSED)
156  {
157  //Open connection with the remote TFTP server
158  error = tftpClientOpenConnection(context);
159 
160  //Check status code
161  if(!error)
162  {
163  //Send WRQ packet
164  if((mode & TFTP_FILE_MODE_NETASCII) != 0)
165  {
166  tftpClientSendWrqPacket(context, filename, "netascii");
167  }
168  else
169  {
170  tftpClientSendWrqPacket(context, filename, "octet");
171  }
172 
173  //Initialize block number
174  context->block = 0;
175  //The write request has been sent out
176  context->state = TFTP_CLIENT_STATE_WRQ;
177  }
178  }
179  else if(context->state == TFTP_CLIENT_STATE_WRQ)
180  {
181  //Wait for an ACK packet to be received
182  error = tftpClientProcessEvents(context);
183  }
184  else if(context->state == TFTP_CLIENT_STATE_ACK)
185  {
186  //The write request has been accepted
187  error = NO_ERROR;
188  //We are done
189  break;
190  }
191  else
192  {
193  //Close connection
194  tftpClientCloseConnection(context);
195  //Back to default state
196  context->state = TFTP_CLIENT_STATE_CLOSED;
197  //The write request has been rejected
198  error = ERROR_OPEN_FAILED;
199  }
200  }
201  }
202  else
203  {
204  //Wait for the RRQ request to be accepted
205  while(!error)
206  {
207  //Check current state
208  if(context->state == TFTP_CLIENT_STATE_CLOSED)
209  {
210  //Open connection with the remote TFTP server
211  error = tftpClientOpenConnection(context);
212 
213  //Check status code
214  if(!error)
215  {
216  //Send RRQ packet
217  if((mode & TFTP_FILE_MODE_NETASCII) != 0)
218  {
219  tftpClientSendRrqPacket(context, filename, "netascii");
220  }
221  else
222  {
223  tftpClientSendRrqPacket(context, filename, "octet");
224  }
225 
226  //Initialize block number
227  context->block = 1;
228  //The read request has been sent out
229  context->state = TFTP_CLIENT_STATE_RRQ;
230  }
231  }
232  else if(context->state == TFTP_CLIENT_STATE_RRQ)
233  {
234  //Wait for a DATA packet to be received
235  error = tftpClientProcessEvents(context);
236  }
237  else if(context->state == TFTP_CLIENT_STATE_DATA)
238  {
239  //The read request has been accepted
240  error = NO_ERROR;
241  //We are done
242  break;
243  }
244  else
245  {
246  //Close connection
247  tftpClientCloseConnection(context);
248  //Back to default state
249  context->state = TFTP_CLIENT_STATE_CLOSED;
250  //The read request has been rejected
251  error = ERROR_OPEN_FAILED;
252  }
253  }
254  }
255 
256  //Return status code
257  return error;
258 }
259 
260 
261 /**
262  * @brief Write data to the file
263  * @param[in] context Pointer to the TFTP client context
264  * @param[in] data Pointer to a buffer containing the data to be written
265  * @param[in] length Number of data bytes to write
266  * @param[in] written Number of bytes that have been written
267  * @param[in] flags Reserved parameter
268  * @return Error code
269  **/
270 
272  const void *data, size_t length, size_t *written, uint_t flags)
273 {
274  error_t error;
275  size_t n;
276  size_t totalLength;
277 
278  //Make sure the TFTP client context is valid
279  if(context == NULL)
281 
282  //Check parameters
283  if(data == NULL && length != 0)
285 
286  //Initialize status code
287  error = NO_ERROR;
288 
289  //Actual number of bytes written
290  totalLength = 0;
291 
292  //Write as much data as possible
293  while(totalLength < length && !error)
294  {
295  //Check current state
296  if(context->state == TFTP_CLIENT_STATE_DATA)
297  {
298  //Handle retransmissions
299  error = tftpClientProcessEvents(context);
300  }
301  else if(context->state == TFTP_CLIENT_STATE_ACK)
302  {
303  //Send buffer available for writing?
304  if(context->outDataLen < TFTP_CLIENT_BLOCK_SIZE)
305  {
306  //Compute the number of bytes available
307  n = TFTP_CLIENT_BLOCK_SIZE - context->outDataLen;
308  //Limit the number of bytes to copy at a time
309  n = MIN(n, length - totalLength);
310 
311  //Copy data to the send buffer
312  osMemcpy(context->outPacket + sizeof(TftpDataPacket) +
313  context->outDataLen, data, n);
314 
315  //Advance data pointer
316  data = (uint8_t *) data + n;
317  context->outDataLen += n;
318 
319  //Total number of bytes that have been written
320  totalLength += n;
321  }
322 
323  //Check whether the send buffer is full
324  if(context->outDataLen >= TFTP_CLIENT_BLOCK_SIZE)
325  {
326  //The block number increases by one for each new block of data
327  context->block++;
328 
329  //Send DATA packet
330  tftpClientSendDataPacket(context);
331 
332  //Wait for the DATA packet to be acknowledged
333  context->state = TFTP_CLIENT_STATE_DATA;
334  }
335  }
336  else
337  {
338  //Report an error
339  error = ERROR_WRITE_FAILED;
340  }
341  }
342 
343  //Total number of bytes successfully written
344  if(written != NULL)
345  *written = totalLength;
346 
347  //Return status code
348  return error;
349 }
350 
351 
352 /**
353  * @brief Flush pending write operations
354  * @param[in] context Pointer to the TFTP client context
355  * @return Error code
356  **/
357 
359 {
360  error_t error;
361 
362  //Make sure the TFTP client context is valid
363  if(context == NULL)
365 
366  //Initialize status code
367  error = NO_ERROR;
368 
369  //Wait for the last DATA packet to be acknowledged
370  while(!error)
371  {
372  //Check current state
373  if(context->state == TFTP_CLIENT_STATE_DATA)
374  {
375  //Handle retransmissions
376  error = tftpClientProcessEvents(context);
377  }
378  else if(context->state == TFTP_CLIENT_STATE_ACK)
379  {
380  //The block number increases by one for each new block of data
381  context->block++;
382 
383  //Send DATA packet
384  tftpClientSendDataPacket(context);
385 
386  //A data packet of less than 512 bytes signals termination
387  //of the transfer
389  }
390  else if(context->state == TFTP_CLIENT_STATE_LAST_DATA)
391  {
392  //Handle retransmissions
393  error = tftpClientProcessEvents(context);
394  }
395  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
396  {
397  //Normal termination of the transfer
398  error = NO_ERROR;
399  break;
400  }
401  else
402  {
403  //Report an error
404  error = ERROR_WRITE_FAILED;
405  }
406  }
407 
408  //Return status code
409  return error;
410 }
411 
412 
413 /**
414  * @brief Read data from the file
415  * @param[in] context Pointer to the TFTP client context
416  * @param[in] data Pointer to the buffer where to copy the data
417  * @param[in] size Size of the buffer, in bytes
418  * @param[out] received Number of data bytes that have been read
419  * @param[in] flags Reserved parameter
420  * @return Error code
421  **/
422 
424  void *data, size_t size, size_t *received, uint_t flags)
425 {
426  error_t error;
427  size_t n;
428 
429  //Check parameters
430  if(context == NULL || data == NULL || received == NULL)
432 
433  //Initialize status code
434  error = NO_ERROR;
435 
436  //Total number of bytes that have been received
437  *received = 0;
438 
439  //Read as much data as possible
440  while(*received < size && !error)
441  {
442  //Check current state
443  if(context->state == TFTP_CLIENT_STATE_DATA)
444  {
445  //Any data pending in the receive buffer?
446  if(context->inDataPos < context->inDataLen)
447  {
448  //Compute the number of bytes available for reading
449  n = context->inDataLen - context->inDataPos;
450  //Limit the number of bytes to copy at a time
451  n = MIN(n, size - *received);
452 
453  //Copy data from the receive buffer
454  osMemcpy(data, context->inPacket + sizeof(TftpDataPacket) +
455  context->inDataPos, n);
456 
457  //Advance data pointer
458  data = (uint8_t *) data + n;
459  context->inDataPos += n;
460 
461  //Total number of bytes that have been received
462  *received += n;
463  }
464 
465  //Check whether the receive buffer is empty
466  if(context->inDataPos >= context->inDataLen)
467  {
468  //Acknowledge the DATA packet
469  tftpClientSendAckPacket(context);
470 
471  //Increment block number
472  context->block++;
473 
474  //Check the length of the DATA packet
475  if(context->inDataLen < TFTP_CLIENT_BLOCK_SIZE)
476  {
477  //A data packet of less than 512 bytes signals termination
478  //of the transfer
480  }
481  else
482  {
483  //Wait for the next DATA packet to be received
484  context->state = TFTP_CLIENT_STATE_ACK;
485  }
486  }
487  }
488  else if(context->state == TFTP_CLIENT_STATE_ACK)
489  {
490  //Handle retransmissions
491  error = tftpClientProcessEvents(context);
492  }
493  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
494  {
495  //The user must be satisfied with data already on hand
496  if(*received > 0)
497  {
498  //Some data are pending in the receive buffer
499  break;
500  }
501  else
502  {
503  //Normal termination of the transfer
504  error = ERROR_END_OF_STREAM;
505  }
506  }
507  else
508  {
509  //Report an error
510  error = ERROR_READ_FAILED;
511  }
512  }
513 
514  //Return status code
515  return error;
516 }
517 
518 
519 /**
520  * @brief Close the file
521  * @param[in] context Pointer to the TFTP client context
522  * @return Error code
523  **/
524 
526 {
527  //Make sure the TFTP client context is valid
528  if(context == NULL)
530 
531  //Close connection with the TFTP server
532  tftpClientCloseConnection(context);
533 
534  //Back to default state
535  context->state = TFTP_CLIENT_STATE_CLOSED;
536 
537  //Successful processing
538  return NO_ERROR;
539 }
540 
541 
542 /**
543  * @brief Release TFTP client context
544  * @param[in] context Pointer to the TFTP client context
545  **/
546 
548 {
549  //Make sure the TFTP client context is valid
550  if(context != NULL)
551  {
552  //Close connection with the TFTP server
553  tftpClientCloseConnection(context);
554 
555  //Clear TFTP client context
556  osMemset(context, 0, sizeof(TftpClientContext));
557  }
558 }
559 
560 #endif
error_t tftpClientOpenFile(TftpClientContext *context, const char_t *filename, uint_t mode)
Open a file for reading or writing.
Definition: tftp_client.c:136
error_t tftpClientFlushFile(TftpClientContext *context)
Flush pending write operations.
Definition: tftp_client.c:358
@ TFTP_CLIENT_STATE_CLOSED
Definition: tftp_client.h:113
uint8_t outPacket[TFTP_CLIENT_MAX_PACKET_SIZE]
Outgoing TFTP packet.
Definition: tftp_client.h:143
IP network address.
Definition: ip.h:90
error_t tftpClientSendDataPacket(TftpClientContext *context)
Send DATA packet.
error_t tftpClientBindToInterface(TftpClientContext *context, NetInterface *interface)
Bind the TFTP client to a particular network interface.
Definition: tftp_client.c:84
uint8_t data[]
Definition: ethernet.h:222
@ TFTP_CLIENT_STATE_COMPLETE
Definition: tftp_client.h:119
error_t tftpClientSendWrqPacket(TftpClientContext *context, const char_t *filename, const char_t *mode)
Send WRQ packet.
uint16_t totalLength
Definition: ipv4.h:323
@ ERROR_END_OF_STREAM
Definition: error.h:211
void tftpClientCloseConnection(TftpClientContext *context)
Close connection with the TFTP server.
TFTP client context.
Definition: tftp_client.h:129
error_t tftpClientOpenConnection(TftpClientContext *context)
Open connection with the TFTP server.
@ ERROR_WRONG_STATE
Definition: error.h:210
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
error_t tftpClientProcessEvents(TftpClientContext *context)
Process TFTP client events.
error_t
Error codes.
Definition: error.h:43
error_t tftpClientReadFile(TftpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Read data from the file.
Definition: tftp_client.c:423
error_t tftpClientConnect(TftpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Specify the address of the TFTP server.
Definition: tftp_client.c:107
#define NetInterface
Definition: net.h:36
@ TFTP_CLIENT_STATE_WRQ
Definition: tftp_client.h:115
char_t filename[]
Definition: tftp_common.h:93
@ TFTP_FILE_MODE_NETASCII
Definition: tftp_client.h:103
TFTP client.
uint8_t length
Definition: tcp.h:375
error_t tftpClientCloseFile(TftpClientContext *context)
Close the file.
Definition: tftp_client.c:525
#define MIN(a, b)
Definition: os_port.h:63
void tftpClientDeinit(TftpClientContext *context)
Release TFTP client context.
Definition: tftp_client.c:547
#define TFTP_CLIENT_BLOCK_SIZE
Definition: tftp_client.h:75
Helper functions for TFTP client.
uint8_t flags
Definition: tcp.h:358
char char_t
Definition: compiler_port.h:55
uint8_t n
@ ERROR_READ_FAILED
Definition: error.h:224
@ TFTP_CLIENT_STATE_LAST_DATA
Definition: tftp_client.h:118
@ ERROR_WRITE_FAILED
Definition: error.h:223
TftpClientState state
TFTP client state.
Definition: tftp_client.h:135
error_t tftpClientInit(TftpClientContext *context)
TFTP client initialization.
Definition: tftp_client.c:60
@ TFTP_CLIENT_STATE_ACK
Definition: tftp_client.h:117
@ TFTP_CLIENT_STATE_DATA
Definition: tftp_client.h:116
NetInterface * interface
Underlying network interface.
Definition: tftp_client.h:130
error_t tftpClientSendAckPacket(TftpClientContext *context)
Send ACK packet.
uint16_t block
Block number.
Definition: tftp_client.h:136
uint8_t inPacket[TFTP_CLIENT_MAX_PACKET_SIZE]
Incoming TFTP packet.
Definition: tftp_client.h:139
TftpDataPacket
Definition: tftp_common.h:117
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
@ TFTP_CLIENT_STATE_RRQ
Definition: tftp_client.h:114
error_t tftpClientSendRrqPacket(TftpClientContext *context, const char_t *filename, const char_t *mode)
Send RRQ packet.
@ TFTP_FILE_MODE_WRITE
Definition: tftp_client.h:101
@ NO_ERROR
Success.
Definition: error.h:44
uint16_t serverPort
Definition: tftp_client.h:132
Debugging facilities.
error_t tftpClientWriteFile(TftpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Write data to the file.
Definition: tftp_client.c:271