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-2026 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.6.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  //Attach TCP/IP stack context
70  context->netContext = netGetDefaultContext();
71 
72  //Initialize TFTP client state
74 
75  //Successful initialization
76  return NO_ERROR;
77 }
78 
79 
80 /**
81  * @brief Bind the TFTP client to a particular network interface
82  * @param[in] context Pointer to the TFTP client context
83  * @param[in] interface Network interface to be used
84  * @return Error code
85  **/
86 
88  NetInterface *interface)
89 {
90  //Make sure the TFTP client context is valid
91  if(context == NULL)
93 
94  //Explicitly associate the TFTP client with the specified interface
95  context->interface = interface;
96 
97  //Successful processing
98  return NO_ERROR;
99 }
100 
101 
102 /**
103  * @brief Specify the address of the TFTP server
104  * @param[in] context Pointer to the TFTP client context
105  * @param[in] serverIpAddr IP address of the TFTP server to connect to
106  * @param[in] serverPort UDP port number
107  * @return Error code
108  **/
109 
111  const IpAddr *serverIpAddr, uint16_t serverPort)
112 {
113  //Check parameters
114  if(context == NULL || serverIpAddr == NULL)
116 
117  //Check current state
118  if(context->state != TFTP_CLIENT_STATE_CLOSED)
119  return ERROR_WRONG_STATE;
120 
121  //Save the IP address of the remote TFTP server
122  context->serverIpAddr = *serverIpAddr;
123  //Save the UDP port number to be used
124  context->serverPort = serverPort;
125 
126  //Successful processing
127  return NO_ERROR;
128 }
129 
130 
131 /**
132  * @brief Open a file for reading or writing
133  * @param[in] context Pointer to the TFTP client context
134  * @param[in] filename NULL-terminated string specifying the filename
135  * @param[in] mode File access mode
136  * @return Error code
137  **/
138 
140  const char_t *filename, uint_t mode)
141 {
142  error_t error;
143 
144  //Check parameters
145  if(context == NULL || filename == NULL)
147 
148  //Initialize status code
149  error = NO_ERROR;
150 
151  //Read or write access?
152  if((mode & TFTP_FILE_MODE_WRITE) != 0)
153  {
154  //Wait for the WRQ request to be accepted
155  while(!error)
156  {
157  //Check current state
158  if(context->state == TFTP_CLIENT_STATE_CLOSED)
159  {
160  //Open connection with the remote TFTP server
161  error = tftpClientOpenConnection(context);
162 
163  //Check status code
164  if(!error)
165  {
166  //Send WRQ packet
167  if((mode & TFTP_FILE_MODE_NETASCII) != 0)
168  {
169  tftpClientSendWrqPacket(context, filename, "netascii");
170  }
171  else
172  {
173  tftpClientSendWrqPacket(context, filename, "octet");
174  }
175 
176  //Initialize block number
177  context->block = 0;
178  //The write request has been sent out
179  context->state = TFTP_CLIENT_STATE_WRQ;
180  }
181  }
182  else if(context->state == TFTP_CLIENT_STATE_WRQ)
183  {
184  //Wait for an ACK packet to be received
185  error = tftpClientProcessEvents(context);
186  }
187  else if(context->state == TFTP_CLIENT_STATE_ACK)
188  {
189  //The write request has been accepted
190  error = NO_ERROR;
191  //We are done
192  break;
193  }
194  else
195  {
196  //Close connection
197  tftpClientCloseConnection(context);
198  //Back to default state
199  context->state = TFTP_CLIENT_STATE_CLOSED;
200  //The write request has been rejected
201  error = ERROR_OPEN_FAILED;
202  }
203  }
204  }
205  else
206  {
207  //Wait for the RRQ request to be accepted
208  while(!error)
209  {
210  //Check current state
211  if(context->state == TFTP_CLIENT_STATE_CLOSED)
212  {
213  //Open connection with the remote TFTP server
214  error = tftpClientOpenConnection(context);
215 
216  //Check status code
217  if(!error)
218  {
219  //Send RRQ packet
220  if((mode & TFTP_FILE_MODE_NETASCII) != 0)
221  {
222  tftpClientSendRrqPacket(context, filename, "netascii");
223  }
224  else
225  {
226  tftpClientSendRrqPacket(context, filename, "octet");
227  }
228 
229  //Initialize block number
230  context->block = 1;
231  //The read request has been sent out
232  context->state = TFTP_CLIENT_STATE_RRQ;
233  }
234  }
235  else if(context->state == TFTP_CLIENT_STATE_RRQ)
236  {
237  //Wait for a DATA packet to be received
238  error = tftpClientProcessEvents(context);
239  }
240  else if(context->state == TFTP_CLIENT_STATE_DATA)
241  {
242  //The read request has been accepted
243  error = NO_ERROR;
244  //We are done
245  break;
246  }
247  else
248  {
249  //Close connection
250  tftpClientCloseConnection(context);
251  //Back to default state
252  context->state = TFTP_CLIENT_STATE_CLOSED;
253  //The read request has been rejected
254  error = ERROR_OPEN_FAILED;
255  }
256  }
257  }
258 
259  //Return status code
260  return error;
261 }
262 
263 
264 /**
265  * @brief Write data to the file
266  * @param[in] context Pointer to the TFTP client context
267  * @param[in] data Pointer to a buffer containing the data to be written
268  * @param[in] length Number of data bytes to write
269  * @param[in] written Number of bytes that have been written
270  * @param[in] flags Reserved parameter
271  * @return Error code
272  **/
273 
275  const void *data, size_t length, size_t *written, uint_t flags)
276 {
277  error_t error;
278  size_t n;
279  size_t totalLength;
280 
281  //Make sure the TFTP client context is valid
282  if(context == NULL)
284 
285  //Check parameters
286  if(data == NULL && length != 0)
288 
289  //Initialize status code
290  error = NO_ERROR;
291 
292  //Actual number of bytes written
293  totalLength = 0;
294 
295  //Write as much data as possible
296  while(totalLength < length && !error)
297  {
298  //Check current state
299  if(context->state == TFTP_CLIENT_STATE_DATA)
300  {
301  //Handle retransmissions
302  error = tftpClientProcessEvents(context);
303  }
304  else if(context->state == TFTP_CLIENT_STATE_ACK)
305  {
306  //Send buffer available for writing?
307  if(context->outDataLen < TFTP_CLIENT_BLOCK_SIZE)
308  {
309  //Compute the number of bytes available
310  n = TFTP_CLIENT_BLOCK_SIZE - context->outDataLen;
311  //Limit the number of bytes to copy at a time
312  n = MIN(n, length - totalLength);
313 
314  //Copy data to the send buffer
315  osMemcpy(context->outPacket + sizeof(TftpDataPacket) +
316  context->outDataLen, data, n);
317 
318  //Advance data pointer
319  data = (uint8_t *) data + n;
320  context->outDataLen += n;
321 
322  //Total number of bytes that have been written
323  totalLength += n;
324  }
325 
326  //Check whether the send buffer is full
327  if(context->outDataLen >= TFTP_CLIENT_BLOCK_SIZE)
328  {
329  //The block number increases by one for each new block of data
330  context->block++;
331 
332  //Send DATA packet
333  tftpClientSendDataPacket(context);
334 
335  //Wait for the DATA packet to be acknowledged
336  context->state = TFTP_CLIENT_STATE_DATA;
337  }
338  }
339  else
340  {
341  //Report an error
342  error = ERROR_WRITE_FAILED;
343  }
344  }
345 
346  //Total number of bytes successfully written
347  if(written != NULL)
348  {
349  *written = totalLength;
350  }
351 
352  //Return status code
353  return error;
354 }
355 
356 
357 /**
358  * @brief Flush pending write operations
359  * @param[in] context Pointer to the TFTP client context
360  * @return Error code
361  **/
362 
364 {
365  error_t error;
366 
367  //Make sure the TFTP client context is valid
368  if(context == NULL)
370 
371  //Initialize status code
372  error = NO_ERROR;
373 
374  //Wait for the last DATA packet to be acknowledged
375  while(!error)
376  {
377  //Check current state
378  if(context->state == TFTP_CLIENT_STATE_DATA)
379  {
380  //Handle retransmissions
381  error = tftpClientProcessEvents(context);
382  }
383  else if(context->state == TFTP_CLIENT_STATE_ACK)
384  {
385  //The block number increases by one for each new block of data
386  context->block++;
387 
388  //Send DATA packet
389  tftpClientSendDataPacket(context);
390 
391  //A data packet of less than 512 bytes signals termination
392  //of the transfer
394  }
395  else if(context->state == TFTP_CLIENT_STATE_LAST_DATA)
396  {
397  //Handle retransmissions
398  error = tftpClientProcessEvents(context);
399  }
400  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
401  {
402  //Normal termination of the transfer
403  error = NO_ERROR;
404  break;
405  }
406  else
407  {
408  //Report an error
409  error = ERROR_WRITE_FAILED;
410  }
411  }
412 
413  //Return status code
414  return error;
415 }
416 
417 
418 /**
419  * @brief Read data from the file
420  * @param[in] context Pointer to the TFTP client context
421  * @param[in] data Pointer to the buffer where to copy the data
422  * @param[in] size Size of the buffer, in bytes
423  * @param[out] received Number of data bytes that have been read
424  * @param[in] flags Reserved parameter
425  * @return Error code
426  **/
427 
429  void *data, size_t size, size_t *received, uint_t flags)
430 {
431  error_t error;
432  size_t n;
433 
434  //Check parameters
435  if(context == NULL || data == NULL || received == NULL)
437 
438  //Initialize status code
439  error = NO_ERROR;
440 
441  //Total number of bytes that have been received
442  *received = 0;
443 
444  //Read as much data as possible
445  while(*received < size && !error)
446  {
447  //Check current state
448  if(context->state == TFTP_CLIENT_STATE_DATA)
449  {
450  //Any data pending in the receive buffer?
451  if(context->inDataPos < context->inDataLen)
452  {
453  //Compute the number of bytes available for reading
454  n = context->inDataLen - context->inDataPos;
455  //Limit the number of bytes to copy at a time
456  n = MIN(n, size - *received);
457 
458  //Copy data from the receive buffer
459  osMemcpy(data, context->inPacket + sizeof(TftpDataPacket) +
460  context->inDataPos, n);
461 
462  //Advance data pointer
463  data = (uint8_t *) data + n;
464  context->inDataPos += n;
465 
466  //Total number of bytes that have been received
467  *received += n;
468  }
469 
470  //Check whether the receive buffer is empty
471  if(context->inDataPos >= context->inDataLen)
472  {
473  //Acknowledge the DATA packet
474  tftpClientSendAckPacket(context);
475 
476  //Increment block number
477  context->block++;
478 
479  //Check the length of the DATA packet
480  if(context->inDataLen < TFTP_CLIENT_BLOCK_SIZE)
481  {
482  //A data packet of less than 512 bytes signals termination
483  //of the transfer
485  }
486  else
487  {
488  //Wait for the next DATA packet to be received
489  context->state = TFTP_CLIENT_STATE_ACK;
490  }
491  }
492  }
493  else if(context->state == TFTP_CLIENT_STATE_ACK)
494  {
495  //Handle retransmissions
496  error = tftpClientProcessEvents(context);
497  }
498  else if(context->state == TFTP_CLIENT_STATE_COMPLETE)
499  {
500  //The user must be satisfied with data already on hand
501  if(*received > 0)
502  {
503  //Some data are pending in the receive buffer
504  break;
505  }
506  else
507  {
508  //Normal termination of the transfer
509  error = ERROR_END_OF_STREAM;
510  }
511  }
512  else
513  {
514  //Report an error
515  error = ERROR_READ_FAILED;
516  }
517  }
518 
519  //Return status code
520  return error;
521 }
522 
523 
524 /**
525  * @brief Close the file
526  * @param[in] context Pointer to the TFTP client context
527  * @return Error code
528  **/
529 
531 {
532  //Make sure the TFTP client context is valid
533  if(context == NULL)
535 
536  //Close connection with the TFTP server
537  tftpClientCloseConnection(context);
538 
539  //Back to default state
540  context->state = TFTP_CLIENT_STATE_CLOSED;
541 
542  //Successful processing
543  return NO_ERROR;
544 }
545 
546 
547 /**
548  * @brief Release TFTP client context
549  * @param[in] context Pointer to the TFTP client context
550  **/
551 
553 {
554  //Make sure the TFTP client context is valid
555  if(context != NULL)
556  {
557  //Close connection with the TFTP server
558  tftpClientCloseConnection(context);
559 
560  //Clear TFTP client context
561  osMemset(context, 0, sizeof(TftpClientContext));
562  }
563 }
564 
565 #endif
error_t tftpClientOpenFile(TftpClientContext *context, const char_t *filename, uint_t mode)
Open a file for reading or writing.
Definition: tftp_client.c:139
error_t tftpClientFlushFile(TftpClientContext *context)
Flush pending write operations.
Definition: tftp_client.c:363
NetContext * netContext
TCP/IP stack context.
Definition: tftp_client.h:130
@ TFTP_CLIENT_STATE_CLOSED
Definition: tftp_client.h:113
uint8_t outPacket[TFTP_CLIENT_MAX_PACKET_SIZE]
Outgoing TFTP packet.
Definition: tftp_client.h:144
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:87
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:347
@ 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:428
error_t tftpClientConnect(TftpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Specify the address of the TFTP server.
Definition: tftp_client.c:110
#define NetInterface
Definition: net.h:40
@ TFTP_CLIENT_STATE_WRQ
Definition: tftp_client.h:115
NetContext * netGetDefaultContext(void)
Get default TCP/IP stack context.
Definition: net.c:527
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:530
#define MIN(a, b)
Definition: os_port.h:63
void tftpClientDeinit(TftpClientContext *context)
Release TFTP client context.
Definition: tftp_client.c:552
#define TFTP_CLIENT_BLOCK_SIZE
Definition: tftp_client.h:75
Helper functions for TFTP client.
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:136
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:131
error_t tftpClientSendAckPacket(TftpClientContext *context)
Send ACK packet.
uint16_t block
Block number.
Definition: tftp_client.h:137
uint8_t inPacket[TFTP_CLIENT_MAX_PACKET_SIZE]
Incoming TFTP packet.
Definition: tftp_client.h:140
TftpDataPacket
Definition: tftp_common.h:117
uint8_t flags
Definition: tcp.h:358
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:133
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:274