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