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.2
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  {
346  *written = totalLength;
347  }
348 
349  //Return status code
350  return error;
351 }
352 
353 
354 /**
355  * @brief Flush pending write operations
356  * @param[in] context Pointer to the TFTP client context
357  * @return Error code
358  **/
359 
361 {
362  error_t error;
363 
364  //Make sure the TFTP client context is valid
365  if(context == NULL)
367 
368  //Initialize status code
369  error = NO_ERROR;
370 
371  //Wait for the last DATA packet to be acknowledged
372  while(!error)
373  {
374  //Check current state
375  if(context->state == TFTP_CLIENT_STATE_DATA)
376  {
377  //Handle retransmissions
378  error = tftpClientProcessEvents(context);
379  }
380  else if(context->state == TFTP_CLIENT_STATE_ACK)
381  {
382  //The block number increases by one for each new block of data
383  context->block++;
384 
385  //Send DATA packet
386  tftpClientSendDataPacket(context);
387 
388  //A data packet of less than 512 bytes signals termination
389  //of the transfer
391  }
392  else if(context->state == TFTP_CLIENT_STATE_LAST_DATA)
393  {
394  //Handle retransmissions
395  error = tftpClientProcessEvents(context);
396  }
397  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
398  {
399  //Normal termination of the transfer
400  error = NO_ERROR;
401  break;
402  }
403  else
404  {
405  //Report an error
406  error = ERROR_WRITE_FAILED;
407  }
408  }
409 
410  //Return status code
411  return error;
412 }
413 
414 
415 /**
416  * @brief Read data from the file
417  * @param[in] context Pointer to the TFTP client context
418  * @param[in] data Pointer to the buffer where to copy the data
419  * @param[in] size Size of the buffer, in bytes
420  * @param[out] received Number of data bytes that have been read
421  * @param[in] flags Reserved parameter
422  * @return Error code
423  **/
424 
426  void *data, size_t size, size_t *received, uint_t flags)
427 {
428  error_t error;
429  size_t n;
430 
431  //Check parameters
432  if(context == NULL || data == NULL || received == NULL)
434 
435  //Initialize status code
436  error = NO_ERROR;
437 
438  //Total number of bytes that have been received
439  *received = 0;
440 
441  //Read as much data as possible
442  while(*received < size && !error)
443  {
444  //Check current state
445  if(context->state == TFTP_CLIENT_STATE_DATA)
446  {
447  //Any data pending in the receive buffer?
448  if(context->inDataPos < context->inDataLen)
449  {
450  //Compute the number of bytes available for reading
451  n = context->inDataLen - context->inDataPos;
452  //Limit the number of bytes to copy at a time
453  n = MIN(n, size - *received);
454 
455  //Copy data from the receive buffer
456  osMemcpy(data, context->inPacket + sizeof(TftpDataPacket) +
457  context->inDataPos, n);
458 
459  //Advance data pointer
460  data = (uint8_t *) data + n;
461  context->inDataPos += n;
462 
463  //Total number of bytes that have been received
464  *received += n;
465  }
466 
467  //Check whether the receive buffer is empty
468  if(context->inDataPos >= context->inDataLen)
469  {
470  //Acknowledge the DATA packet
471  tftpClientSendAckPacket(context);
472 
473  //Increment block number
474  context->block++;
475 
476  //Check the length of the DATA packet
477  if(context->inDataLen < TFTP_CLIENT_BLOCK_SIZE)
478  {
479  //A data packet of less than 512 bytes signals termination
480  //of the transfer
482  }
483  else
484  {
485  //Wait for the next DATA packet to be received
486  context->state = TFTP_CLIENT_STATE_ACK;
487  }
488  }
489  }
490  else if(context->state == TFTP_CLIENT_STATE_ACK)
491  {
492  //Handle retransmissions
493  error = tftpClientProcessEvents(context);
494  }
495  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
496  {
497  //The user must be satisfied with data already on hand
498  if(*received > 0)
499  {
500  //Some data are pending in the receive buffer
501  break;
502  }
503  else
504  {
505  //Normal termination of the transfer
506  error = ERROR_END_OF_STREAM;
507  }
508  }
509  else
510  {
511  //Report an error
512  error = ERROR_READ_FAILED;
513  }
514  }
515 
516  //Return status code
517  return error;
518 }
519 
520 
521 /**
522  * @brief Close the file
523  * @param[in] context Pointer to the TFTP client context
524  * @return Error code
525  **/
526 
528 {
529  //Make sure the TFTP client context is valid
530  if(context == NULL)
532 
533  //Close connection with the TFTP server
534  tftpClientCloseConnection(context);
535 
536  //Back to default state
537  context->state = TFTP_CLIENT_STATE_CLOSED;
538 
539  //Successful processing
540  return NO_ERROR;
541 }
542 
543 
544 /**
545  * @brief Release TFTP client context
546  * @param[in] context Pointer to the TFTP client context
547  **/
548 
550 {
551  //Make sure the TFTP client context is valid
552  if(context != NULL)
553  {
554  //Close connection with the TFTP server
555  tftpClientCloseConnection(context);
556 
557  //Clear TFTP client context
558  osMemset(context, 0, sizeof(TftpClientContext));
559  }
560 }
561 
562 #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:360
@ 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:224
@ 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:425
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:527
#define MIN(a, b)
Definition: os_port.h:63
void tftpClientDeinit(TftpClientContext *context)
Release TFTP client context.
Definition: tftp_client.c:549
#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