tftp_server_misc.c
Go to the documentation of this file.
1 /**
2  * @file tftp_server_misc.c
3  * @brief Helper functions for TFTP server
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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL TFTP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "tftp/tftp_server.h"
37 #include "tftp/tftp_server_misc.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (TFTP_SERVER_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Handle periodic operations
46  * @param[in] context Pointer to the TFTP server context
47  **/
48 
50 {
51  uint_t i;
53  TftpClientConnection *connection;
54 
55  //Get current time
57 
58  //Handle periodic operations
59  for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
60  {
61  //Point to the structure describing the current connection
62  connection = &context->connection[i];
63 
64  //Check current state
65  if(connection->state == TFTP_STATE_READING ||
66  connection->state == TFTP_STATE_WRITING ||
67  connection->state == TFTP_STATE_READ_COMPLETE)
68  {
69  //Check current time
70  if(timeCompare(time, connection->timestamp + TFTP_SERVER_TIMEOUT) >= 0)
71  {
72  //Handle retransmissions
73  if(connection->retransmitCount < TFTP_SERVER_MAX_RETRIES)
74  {
75  //Retransmit last packet
76  tftpServerRetransmitPacket(connection);
77 
78  //Save the time at which the packet was sent
79  connection->timestamp = osGetSystemTime();
80  //Increment retransmission counter
81  connection->retransmitCount++;
82  }
83  else
84  {
85  //Close connection
86  tftpServerCloseConnection(connection);
87  }
88  }
89  }
90  else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
91  {
92  //The host sending the final ACK will wait for a while before terminating
93  //in order to retransmit the final ACK if it has been lost
94  if(timeCompare(time, connection->timestamp + TFTP_SERVER_FINAL_DELAY) >= 0)
95  {
96  //Close connection
97  tftpServerCloseConnection(connection);
98  }
99  }
100  }
101 }
102 
103 
104 /**
105  * @brief Create client connection
106  * @param[in] context Pointer to the TFTP server context
107  * @param[in] clientIpAddr IP address of the client
108  * @param[in] clientPort Port number used by the client
109  * @return Pointer to the structure describing the connection
110  **/
111 
113  const IpAddr *clientIpAddr, uint16_t clientPort)
114 {
115  error_t error;
116  uint_t i;
117  systime_t time;
118  TftpClientConnection *connection;
119  TftpClientConnection *oldestConnection;
120 
121  //Get current time
122  time = osGetSystemTime();
123 
124  //Keep track of the oldest connection that is waiting to retransmit
125  //the final ACK
126  oldestConnection = NULL;
127 
128  //Loop through the connection table
129  for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
130  {
131  //Point to the current entry
132  connection = &context->connection[i];
133 
134  //Check the state of the current connection
135  if(connection->state == TFTP_STATE_CLOSED)
136  {
137  //The current entry is available
138  break;
139  }
140  else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
141  {
142  //Keep track of the oldest connection that is waiting to retransmit
143  //the final ACK
144  if(oldestConnection == NULL)
145  {
146  oldestConnection = connection;
147  }
148  else if((time - connection->timestamp) > (time - oldestConnection->timestamp))
149  {
150  oldestConnection = connection;
151  }
152  }
153  }
154 
155  //The oldest connection that is waiting to retransmit the final ACK can be
156  //reused when the connection table runs out of space
158  {
159  //Close the oldest connection
160  tftpServerCloseConnection(oldestConnection);
161  //Reuse the connection
162  connection = oldestConnection;
163  }
164 
165  //Failed to create a new connection?
166  if(connection == NULL)
167  return NULL;
168 
169  //Clear the structure describing the connection
170  osMemset(connection, 0, sizeof(TftpClientConnection));
171 
172  //Open a UDP socket
173  connection->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
174 
175  //Failed to open socket?
176  if(connection->socket == NULL)
177  return NULL;
178 
179  //Associate the socket with the relevant interface
180  error = socketBindToInterface(connection->socket, context->settings.interface);
181 
182  //Any error to report?
183  if(error)
184  {
185  //Clean up side effects
186  socketClose(connection->socket);
187  connection->socket = NULL;
188  //Exit immediately
189  return NULL;
190  }
191 
192  //Connect the socket to the remote TFTP client
193  error = socketConnect(connection->socket, clientIpAddr, clientPort);
194 
195  //Any error to report?
196  if(error)
197  {
198  //Clean up side effects
199  socketClose(connection->socket);
200  connection->socket = NULL;
201  //Exit immediately
202  return NULL;
203  }
204 
205  //Reference to the TFTP server settings
206  connection->settings = &context->settings;
207  //Update connection state
208  connection->state = TFTP_STATE_OPEN;
209 
210  //Pointer to the structure describing the connection
211  return connection;
212 }
213 
214 
215 /**
216  * @brief Close client connection
217  * @param[in] connection Pointer to the client connection
218  **/
219 
221 {
222  //Valid connection?
223  if(connection != NULL)
224  {
225  //Debug message
226  TRACE_INFO("TFTP Server: Closing connection...\r\n");
227 
228  //Any active connection?
229  if(connection->socket != NULL)
230  {
231  //Close UDP socket
232  socketClose(connection->socket);
233  connection->socket = NULL;
234  }
235 
236  //Check whether a read or write operation is in progress
237  if(connection->file != NULL)
238  {
239  //Properly close the file before closing the connection
240  if(connection->settings->closeFileCallback != NULL)
241  {
242  //Invoke user callback function
243  connection->settings->closeFileCallback(connection->file);
244  }
245 
246  //Mark the file as closed
247  connection->file = NULL;
248  }
249 
250  //Mark the connection as closed
251  connection->state = TFTP_STATE_CLOSED;
252  }
253 }
254 
255 
256 /**
257  * @brief Accept connection request
258  * @param[in] context Pointer to the TFTP server context
259  **/
260 
262 {
263  error_t error;
264  size_t length;
265  uint16_t opcode;
266  IpAddr clientIpAddr;
267  uint16_t clientPort;
268 
269  //Read incoming TFTP packet
270  error = socketReceiveFrom(context->socket, &clientIpAddr, &clientPort,
271  context->packet, TFTP_SERVER_MAX_PACKET_SIZE, &length, 0);
272 
273  //Failed to read packet?
274  if(error)
275  return;
276 
277  //Debug message
278  TRACE_INFO("TFTP Server: Accepting connection from %s port %" PRIu16 "...\r\n",
279  ipAddrToString(&clientIpAddr, NULL), clientPort);
280 
281  //Sanity check
282  if(length < sizeof(uint16_t))
283  return;
284 
285  //Retrieve TFTP packet type
286  opcode = LOAD16BE(context->packet);
287 
288  //Read request received?
289  if(opcode == TFTP_OPCODE_RRQ)
290  {
291  //Process RRQ packet
292  tftpServerProcessRrqPacket(context, &clientIpAddr,
293  clientPort, (TftpRrqPacket *) context->packet, length);
294  }
295  //Write request received?
296  else if(opcode == TFTP_OPCODE_WRQ)
297  {
298  //Process WRQ packet
299  tftpServerProcessWrqPacket(context, &clientIpAddr,
300  clientPort, (TftpWrqPacket *) context->packet, length);
301  }
302  //Invalid request received?
303  else
304  {
305  //Discard incoming packet
306  }
307 }
308 
309 
310 /**
311  * @brief Process incoming packet
312  * @param[in] context Pointer to the TFTP server context
313  * @param[in] connection Pointer to the client connection
314  **/
315 
317  TftpClientConnection *connection)
318 {
319  error_t error;
320  size_t length;
321  uint16_t opcode;
322  IpAddr clientIpAddr;
323  uint16_t clientPort;
324 
325  //Read incoming TFTP packet
326  error = socketReceiveFrom(connection->socket, &clientIpAddr, &clientPort,
327  context->packet, TFTP_SERVER_MAX_PACKET_SIZE, &length, 0);
328 
329  //Failed to read packet?
330  if(error)
331  return;
332 
333  //Sanity check
334  if(length < sizeof(uint16_t))
335  return;
336 
337  //Retrieve TFTP packet type
338  opcode = LOAD16BE(context->packet);
339 
340  //Data packet received?
341  if(opcode == TFTP_OPCODE_DATA)
342  {
343  //Process DATA packet
344  tftpServerProcessDataPacket(connection,
345  (TftpDataPacket *) context->packet, length);
346  }
347  //Acknowledgment packet received?
348  else if(opcode == TFTP_OPCODE_ACK)
349  {
350  //Process ACK packet
351  tftpServerProcessAckPacket(connection,
352  (TftpAckPacket *) context->packet, length);
353  }
354  //Error packet received?
355  else if(opcode == TFTP_OPCODE_ERROR)
356  {
357  //Process ERROR packet
358  tftpServerProcessErrorPacket(connection,
359  (TftpErrorPacket *) context->packet, length);
360  }
361  //Invalid packet received?
362  else
363  {
364  //Discard incoming packet
365  }
366 }
367 
368 
369 /**
370  * @brief Process incoming RRQ packet
371  * @param[in] context Pointer to the TFTP server context
372  * @param[in] clientIpAddr IP address of the client
373  * @param[in] clientPort Port number used by the client
374  * @param[in] rrqPacket Pointer to the RRQ packet
375  * @param[in] length Length of the packet, in bytes
376  **/
377 
378 void tftpServerProcessRrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
379  uint16_t clientPort, const TftpRrqPacket *rrqPacket, size_t length)
380 {
381  const char_t *mode;
382  TftpClientConnection *connection;
383 
384  //Debug message
385  TRACE_DEBUG("TFTP Server: RRQ packet received (%" PRIuSIZE " bytes)...\r\n",
386  length);
387 
388  //Make sure the length of the RRQ packet is acceptable
389  if(length <= sizeof(TftpRrqPacket))
390  return;
391 
392  //Compute the number of bytes that follows the 2-byte opcode
393  length -= sizeof(TftpRrqPacket);
394 
395  //Point to the incoming RRQ packet
396  rrqPacket = (TftpRrqPacket *) context->packet;
397 
398  //Malformed RRQ packet?
399  if(rrqPacket->filename[length - 1] != '\0')
400  return;
401 
402  //Compute the length of the mode string
403  length -= osStrlen(rrqPacket->filename) + 1;
404 
405  //Malformed RRQ packet?
406  if(length == 0)
407  return;
408 
409  //Point to the mode string
410  mode = rrqPacket->filename + osStrlen(rrqPacket->filename) + 1;
411 
412  //Debug message
413  TRACE_DEBUG(" Opcode = %u\r\n", ntohs(rrqPacket->opcode));
414  TRACE_DEBUG(" Filename = %s\r\n", rrqPacket->filename);
415  TRACE_DEBUG(" Mode = %s\r\n", mode);
416 
417  //Create a new connection
418  connection = tftpServerOpenConnection(context, clientIpAddr, clientPort);
419 
420  //Any error to report?
421  if(connection == NULL)
422  return;
423 
424  //Open the specified file for reading
425  if(context->settings.openFileCallback != NULL)
426  {
427  //Invoke user callback function
428  connection->file = context->settings.openFileCallback(rrqPacket->filename,
429  mode, FALSE);
430  }
431  else
432  {
433  //No callback function defined
434  connection->file = NULL;
435  }
436 
437  //Check if the file was successfully opened
438  if(connection->file != NULL)
439  {
440  //The read operation is in progress
441  connection->state = TFTP_STATE_READING;
442  //Initialize block number
443  connection->block = 1;
444 
445  //Send the first DATA packet
446  tftpServerSendDataPacket(connection);
447  }
448  else
449  {
450  //If the reply is an error packet, then the request has been denied
452  "Failed to open file");
453 
454  //Close the connection
455  tftpServerCloseConnection(connection);
456  }
457 }
458 
459 
460 /**
461  * @brief Process incoming WRQ packet
462  * @param[in] context Pointer to the TFTP server context
463  * @param[in] clientIpAddr IP address of the client
464  * @param[in] clientPort Port number used by the client
465  * @param[in] wrqPacket Pointer to the WRQ packet
466  * @param[in] length Length of the packet, in bytes
467  **/
468 
469 void tftpServerProcessWrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
470  uint16_t clientPort, const TftpWrqPacket *wrqPacket, size_t length)
471 {
472  const char_t *mode;
473  TftpClientConnection *connection;
474 
475  //Debug message
476  TRACE_DEBUG("TFTP Server: WRQ packet received (%" PRIuSIZE " bytes)...\r\n",
477  length);
478 
479  //Make sure the length of the WRQ packet is acceptable
480  if(length <= sizeof(TftpWrqPacket))
481  return;
482 
483  //Compute the number of bytes that follows the 2-byte opcode
484  length -= sizeof(TftpWrqPacket);
485 
486  //Point to the incoming WRQ packet
487  wrqPacket = (TftpWrqPacket *) context->packet;
488 
489  //Malformed WRQ packet?
490  if(wrqPacket->filename[length - 1] != '\0')
491  return;
492 
493  //Compute the length of the mode string
494  length -= osStrlen(wrqPacket->filename) + 1;
495 
496  //Malformed WRQ packet?
497  if(length == 0)
498  return;
499 
500  //Point to the mode string
501  mode = wrqPacket->filename + osStrlen(wrqPacket->filename) + 1;
502 
503  //Debug message
504  TRACE_DEBUG(" Opcode = %u\r\n", ntohs(wrqPacket->opcode));
505  TRACE_DEBUG(" Filename = %s\r\n", wrqPacket->filename);
506  TRACE_DEBUG(" Mode = %s\r\n", mode);
507 
508  //Create a new connection
509  connection = tftpServerOpenConnection(context, clientIpAddr, clientPort);
510 
511  //Any error to report?
512  if(connection == NULL)
513  return;
514 
515  //Open the specified file for writing
516  if(context->settings.openFileCallback != NULL)
517  {
518  //Invoke user callback function
519  connection->file = context->settings.openFileCallback(wrqPacket->filename,
520  mode, TRUE);
521  }
522  else
523  {
524  //No callback function defined
525  connection->file = NULL;
526  }
527 
528  //Check if the file was successfully opened
529  if(connection->file != NULL)
530  {
531  //The write operation is in progress
532  connection->state = TFTP_STATE_WRITING;
533  //Initialize block number
534  connection->block = 0;
535 
536  //The positive response to a write request is an acknowledgment
537  //packet with block number zero
538  tftpServerSendAckPacket(connection);
539 
540  //Increment block number
541  connection->block++;
542  }
543  else
544  {
545  //If the reply is an error packet, then the request has been denied
547  "Failed to open file");
548 
549  //Close the connection
550  tftpServerCloseConnection(connection);
551  }
552 }
553 
554 
555 /**
556  * @brief Process incoming DATA packet
557  * @param[in] connection Pointer to the client connection
558  * @param[in] dataPacket Pointer to the DATA packet
559  * @param[in] length Length of the packet, in bytes
560  **/
561 
563  const TftpDataPacket *dataPacket, size_t length)
564 {
565  error_t error;
566  size_t offset;
567 
568  //Debug message
569  TRACE_DEBUG("TFTP Server: DATA packet received (%" PRIuSIZE " bytes)...\r\n",
570  length);
571 
572  //Make sure the length of the DATA packet is acceptable
573  if(length < sizeof(TftpDataPacket))
574  return;
575 
576  //Calculate the length of the data
577  length -= sizeof(TftpDataPacket);
578 
579  //Debug message
580  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(dataPacket->opcode));
581  TRACE_DEBUG(" Block = %" PRIu16 "\r\n", ntohs(dataPacket->block));
582 
583  //Check current state
584  if(connection->state == TFTP_STATE_WRITING)
585  {
586  //Check block number
587  if(ntohs(dataPacket->block) == connection->block)
588  {
589  //Write data to the output file
590  if(connection->settings->writeFileCallback != NULL)
591  {
592  //Calculate the offset relative to the beginning of the file
593  offset = (connection->block - 1) * TFTP_SERVER_BLOCK_SIZE;
594 
595  //Invoke user callback function
596  error = connection->settings->writeFileCallback(connection->file,
597  offset, dataPacket->data, length);
598  }
599  else
600  {
601  //No callback function defined
602  error = ERROR_WRITE_FAILED;
603  }
604 
605  //Check status code
606  if(!error)
607  {
608  //Acknowledge the DATA packet
609  tftpServerSendAckPacket(connection);
610 
611  //Increment block number
612  connection->block++;
613 
614  //A data packet of less than 512 bytes signals termination of the transfer
616  {
617  //Properly close the file
618  if(connection->settings->closeFileCallback != NULL)
619  {
620  //Invoke user callback function
621  connection->settings->closeFileCallback(connection->file);
622  }
623 
624  //Mark the file as closed
625  connection->file = NULL;
626 
627  //The host sending the final ACK will wait for a while before terminating
628  //in order to retransmit the final ACK if it has been lost
629  connection->state = TFTP_STATE_WRITE_COMPLETE;
630 
631  //Save current time
632  connection->timestamp = osGetSystemTime();
633  }
634  }
635  else
636  {
637  //An error occurs during the transfer
639  "Failed to write file");
640 
641  //A TFTP server may terminate after sending an error message
642  tftpServerCloseConnection(connection);
643  }
644  }
645  else
646  {
647  //Retransmit ACK packet
648  tftpServerRetransmitPacket(connection);
649  }
650  }
651  else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
652  {
653  //The acknowledger will know that the ACK has been lost if it
654  //receives the final DATA packet again
655  tftpServerRetransmitPacket(connection);
656  }
657 }
658 
659 
660 /**
661  * @brief Process incoming ACK packet
662  * @param[in] connection Pointer to the client connection
663  * @param[in] ackPacket Pointer to the ACK packet
664  * @param[in] length Length of the packet, in bytes
665  **/
666 
668  const TftpAckPacket *ackPacket, size_t length)
669 {
670  //Debug message
671  TRACE_DEBUG("TFTP Server: ACK packet received (%" PRIuSIZE " bytes)...\r\n",
672  length);
673 
674  //Make sure the length of the ACK packet is acceptable
675  if(length < sizeof(TftpAckPacket))
676  return;
677 
678  //Debug message
679  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(ackPacket->opcode));
680  TRACE_DEBUG(" Block = %" PRIu16 "\r\n", ntohs(ackPacket->block));
681 
682  //Check current state
683  if(connection->state == TFTP_STATE_READING)
684  {
685  //Make sure the ACK is not a duplicate
686  if(ntohs(ackPacket->block) == connection->block)
687  {
688  //The block number increases by one for each new block of data
689  connection->block++;
690 
691  //Send DATA packet
692  tftpServerSendDataPacket(connection);
693  }
694  else
695  {
696  //Implementations must never resend the current DATA packet on
697  //receipt of a duplicate ACK (refer to RFC 1123, section 4.2.3.1)
698  }
699  }
700  else if(connection->state == TFTP_STATE_READ_COMPLETE)
701  {
702  //Make sure the ACK is not a duplicate
703  if(ntohs(ackPacket->block) == connection->block)
704  {
705  //The host sending the last DATA must retransmit it until the packet is
706  //acknowledged or the sending host times out. If the response is an ACK,
707  //the transmission was completed successfully
708  tftpServerCloseConnection(connection);
709  }
710  }
711 }
712 
713 
714 /**
715  * @brief Process incoming ERROR packet
716  * @param[in] connection Pointer to the client connection
717  * @param[in] errorPacket Pointer to the ERROR packet
718  * @param[in] length Length of the packet, in bytes
719  **/
720 
722  const TftpErrorPacket *errorPacket, size_t length)
723 {
724  //Debug message
725  TRACE_DEBUG("TFTP Server: ERROR packet received (%" PRIuSIZE " bytes)...\r\n",
726  length);
727 
728  //Make sure the length of the ERROR packet is acceptable
729  if(length < sizeof(TftpErrorPacket))
730  return;
731 
732  //Debug message
733  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(errorPacket->opcode));
734  TRACE_DEBUG(" Error Code = %" PRIu16 "\r\n", ntohs(errorPacket->errorCode));
735 
736  //Compute the length of the error message
737  length -= sizeof(TftpErrorPacket);
738 
739  //Make sure the error message is terminated with a zero byte
740  if(length > 1 && errorPacket->errorMsg[length - 1] == '\0')
741  {
742  //Debug message
743  TRACE_DEBUG(" Error Msg = %s\r\n", errorPacket->errorMsg);
744  }
745 
746  //Close connection
747  tftpServerCloseConnection(connection);
748 }
749 
750 
751 /**
752  * @brief Send DATA packet
753  * @param[in] connection Pointer to the client connection
754  * @return Error code
755  **/
756 
758 {
759  error_t error;
760  size_t offset;
761  TftpDataPacket *dataPacket;
762 
763  //Point to the buffer where to format the packet
764  dataPacket = (TftpDataPacket *) connection->packet;
765 
766  //Format DATA packet
767  dataPacket->opcode = HTONS(TFTP_OPCODE_DATA);
768  dataPacket->block = htons(connection->block);
769 
770  //Read more data from the input file
771  if(connection->settings->readFileCallback != NULL)
772  {
773  //Calculate the offset relative to the beginning of the file
774  offset = (connection->block - 1) * TFTP_SERVER_BLOCK_SIZE;
775 
776  //Invoke user callback function
777  error = connection->settings->readFileCallback(connection->file, offset,
778  dataPacket->data, TFTP_SERVER_BLOCK_SIZE, &connection->packetLen);
779  }
780  else
781  {
782  //No callback function defined
783  error = ERROR_READ_FAILED;
784  }
785 
786  //End of file?
787  if(error == ERROR_END_OF_FILE || error == ERROR_END_OF_STREAM)
788  {
789  //Catch exception
790  error = NO_ERROR;
791  //This is the last block of data
792  connection->packetLen = 0;
793  }
794 
795  //Check status code
796  if(!error)
797  {
798  //A data packet of less than 512 bytes signals termination of the transfer
799  if(connection->packetLen < TFTP_SERVER_BLOCK_SIZE)
800  {
801  //Properly close the file
802  if(connection->settings->closeFileCallback != NULL)
803  {
804  //Invoke user callback function
805  connection->settings->closeFileCallback(connection->file);
806  }
807 
808  //Mark the file as closed
809  connection->file = NULL;
810 
811  //The host sending the last DATA must retransmit it until the packet
812  //is acknowledged or the sending host times out
813  connection->state = TFTP_STATE_READ_COMPLETE;
814  }
815 
816  //Length of the DATA packet
817  connection->packetLen += sizeof(TftpAckPacket);
818 
819  //Debug message
820  TRACE_DEBUG("TFTP Server: Sending DATA packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
821  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(dataPacket->opcode));
822  TRACE_DEBUG(" Block = %" PRIu16 "\r\n", ntohs(dataPacket->block));
823 
824  //Send DATA packet
825  error = socketSend(connection->socket, connection->packet,
826  connection->packetLen, NULL, 0);
827 
828  //Save the time at which the packet was sent
829  connection->timestamp = osGetSystemTime();
830  //Reset retransmission counter
831  connection->retransmitCount = 0;
832  }
833  else
834  {
835  //An error occurs during the transfer
837  "Failed to read file");
838 
839  //A TFTP server may terminate after sending an error message
840  tftpServerCloseConnection(connection);
841  }
842 
843  //Return status code
844  return error;
845 }
846 
847 
848 /**
849  * @brief Send ACK packet
850  * @param[in] connection Pointer to the client connection
851  * @return Error code
852  **/
853 
855 {
856  error_t error;
857  TftpAckPacket *ackPacket;
858 
859  //Point to the buffer where to format the packet
860  ackPacket = (TftpAckPacket *) connection->packet;
861 
862  //Format ACK packet
863  ackPacket->opcode = HTONS(TFTP_OPCODE_ACK);
864  ackPacket->block = htons(connection->block);
865 
866  //Length of the ACK packet
867  connection->packetLen = sizeof(TftpAckPacket);
868 
869  //Debug message
870  TRACE_DEBUG("TFTP Server: Sending ACK packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
871  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(ackPacket->opcode));
872  TRACE_DEBUG(" Block = %" PRIu16 "\r\n", ntohs(ackPacket->block));
873 
874  //Send ACK packet
875  error = socketSend(connection->socket, connection->packet,
876  connection->packetLen, NULL, 0);
877 
878  //Save the time at which the packet was sent
879  connection->timestamp = osGetSystemTime();
880  //Reset retransmission counter
881  connection->retransmitCount = 0;
882 
883  //Return status code
884  return error;
885 }
886 
887 
888 /**
889  * @brief Send ERROR packet
890  * @param[in] connection Pointer to the client connection
891  * @param[in] errorCode Integer indicating the nature of the error
892  * @param[in] errorMsg Error message intended for human consumption
893  * @return Error code
894  **/
895 
897  uint16_t errorCode, const char_t *errorMsg)
898 {
899  error_t error;
900  TftpErrorPacket *errorPacket;
901 
902  //Check the length of the error message
905 
906  //Point to the buffer where to format the packet
907  errorPacket = (TftpErrorPacket *) connection->packet;
908 
909  //Format ERROR packet
910  errorPacket->opcode = HTONS(TFTP_OPCODE_ERROR);
911  errorPacket->errorCode = htons(errorCode);
912 
913  //Copy error message
914  osStrcpy(errorPacket->errorMsg, errorMsg);
915 
916  //Length of the ERROR packet
917  connection->packetLen = sizeof(TftpErrorPacket) + osStrlen(errorMsg) + 1;
918 
919  //Debug message
920  TRACE_DEBUG("TFTP Server: Sending ERROR packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
921  TRACE_DEBUG(" Opcode = %" PRIu16 "\r\n", ntohs(errorPacket->opcode));
922  TRACE_DEBUG(" Error Code = %" PRIu16 "\r\n", ntohs(errorPacket->errorCode));
923  TRACE_DEBUG(" Error Msg = %s\r\n", errorPacket->errorMsg);
924 
925  //Send ERROR packet
926  error = socketSend(connection->socket, connection->packet,
927  connection->packetLen, NULL, 0);
928 
929  //Save the time at which the packet was sent
930  connection->timestamp = osGetSystemTime();
931  //Reset retransmission counter
932  connection->retransmitCount = 0;
933 
934  //Return status code
935  return error;
936 }
937 
938 
939 /**
940  * @brief Retransmit the last packet
941  * @param[in] connection Pointer to the client connection
942  * @return Error code
943  **/
944 
946 {
947  error_t error;
948 
949  //Debug message
950  TRACE_DEBUG("TFTP Server: Retransmitting packet (%" PRIuSIZE " bytes)...\r\n",
951  connection->packetLen);
952 
953  //Retransmit the last packet
954  error = socketSend(connection->socket, connection->packet,
955  connection->packetLen, NULL, 0);
956 
957  //Return status code
958  return error;
959 }
960 
961 #endif
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:1491
char_t errorMsg[]
Definition: tftp_common.h:139
#define htons(value)
Definition: cpu_endian.h:413
@ SOCKET_IP_PROTO_UDP
Definition: socket.h:108
uint8_t opcode
Definition: dns_common.h:188
void tftpServerProcessPacket(TftpServerContext *context, TftpClientConnection *connection)
Process incoming packet.
@ TFTP_ERROR_NOT_DEFINED
Definition: tftp_common.h:67
@ TFTP_STATE_WRITING
Definition: tftp_server.h:130
IP network address.
Definition: ip.h:90
#define TFTP_SERVER_MAX_RETRIES
Definition: tftp_server.h:73
#define TRUE
Definition: os_port.h:50
void tftpServerCloseConnection(TftpClientConnection *connection)
Close client connection.
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2067
TftpErrorPacket
Definition: tftp_common.h:140
#define TftpServerContext
Definition: tftp_server.h:113
@ TFTP_OPCODE_ACK
Acknowledgment.
Definition: tftp_common.h:55
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:804
@ SOCKET_TYPE_DGRAM
Definition: socket.h:93
@ TFTP_OPCODE_WRQ
Write request.
Definition: tftp_common.h:53
error_t tftpServerRetransmitPacket(TftpClientConnection *connection)
Retransmit the last packet.
#define osStrlen(s)
Definition: os_port.h:168
uint16_t errorCode
Definition: tftp_common.h:138
@ ERROR_END_OF_STREAM
Definition: error.h:211
TFTP server.
#define timeCompare(t1, t2)
Definition: os_port.h:40
error_t tftpServerSendErrorPacket(TftpClientConnection *connection, uint16_t errorCode, const char_t *errorMsg)
Send ERROR packet.
void tftpServerProcessErrorPacket(TftpClientConnection *connection, const TftpErrorPacket *errorPacket, size_t length)
Process incoming ERROR packet.
@ TFTP_OPCODE_ERROR
Error.
Definition: tftp_common.h:56
TftpRrqPacket
Definition: tftp_common.h:94
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
TftpWrqPacket
Definition: tftp_common.h:105
error_t
Error codes.
Definition: error.h:43
#define TFTP_SERVER_TIMEOUT
Definition: tftp_server.h:80
#define TFTP_SERVER_MAX_PACKET_SIZE
Definition: tftp_server.h:105
#define TFTP_SERVER_BLOCK_SIZE
Definition: tftp_server.h:94
void tftpServerProcessRrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr, uint16_t clientPort, const TftpRrqPacket *rrqPacket, size_t length)
Process incoming RRQ packet.
@ ERROR_END_OF_FILE
Definition: error.h:160
error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, void *data, size_t size, size_t *received, uint_t flags)
Receive a datagram from a connectionless socket.
Definition: socket.c:1719
@ TFTP_OPCODE_DATA
Data.
Definition: tftp_common.h:54
error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
Establish a connection to a specified socket.
Definition: socket.c:1354
@ TFTP_STATE_OPEN
Definition: tftp_server.h:128
@ TFTP_STATE_READING
Definition: tftp_server.h:129
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
#define TFTP_SERVER_MAX_CONNECTIONS
Definition: tftp_server.h:59
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
@ TFTP_STATE_WRITE_COMPLETE
Definition: tftp_server.h:132
@ TFTP_STATE_READ_COMPLETE
Definition: tftp_server.h:131
TftpAckPacket
Definition: tftp_common.h:128
void tftpServerTick(TftpServerContext *context)
Handle periodic operations.
#define socketBindToInterface
Definition: net_legacy.h:193
uint32_t systime_t
System time.
void tftpServerProcessAckPacket(TftpClientConnection *connection, const TftpAckPacket *ackPacket, size_t length)
Process incoming ACK packet.
#define ntohs(value)
Definition: cpu_endian.h:421
@ TFTP_STATE_CLOSED
Definition: tftp_server.h:127
#define TRACE_DEBUG(...)
Definition: debug.h:119
char char_t
Definition: compiler_port.h:55
error_t tftpServerSendAckPacket(TftpClientConnection *connection)
Send ACK packet.
void tftpServerAcceptRequest(TftpServerContext *context)
Accept connection request.
uint32_t time
error_t tftpServerSendDataPacket(TftpClientConnection *connection)
Send DATA packet.
#define HTONS(value)
Definition: cpu_endian.h:410
@ ERROR_READ_FAILED
Definition: error.h:224
@ ERROR_WRITE_FAILED
Definition: error.h:223
#define TftpClientConnection
Definition: tftp_server.h:109
Helper functions for TFTP server.
TftpDataPacket
Definition: tftp_common.h:117
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
void tftpServerProcessDataPacket(TftpClientConnection *connection, const TftpDataPacket *dataPacket, size_t length)
Process incoming DATA packet.
#define LOAD16BE(p)
Definition: cpu_endian.h:186
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
TftpClientConnection * tftpServerOpenConnection(TftpServerContext *context, const IpAddr *clientIpAddr, uint16_t clientPort)
Create client connection.
#define osStrcpy(s1, s2)
Definition: os_port.h:210
void tftpServerProcessWrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr, uint16_t clientPort, const TftpWrqPacket *wrqPacket, size_t length)
Process incoming WRQ packet.
#define TFTP_SERVER_FINAL_DELAY
Definition: tftp_server.h:87
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
systime_t osGetSystemTime(void)
Retrieve system time.
@ TFTP_OPCODE_RRQ
Read request.
Definition: tftp_common.h:52