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