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