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