netx/addons/tftp/nx_tftp_server.c
2021-02-02 05:29:56 +00:00

2763 lines
136 KiB
C

/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** NetX Component */
/** Trivial File Transfer Protocol (TFTP) Server */
/** */
/** */
/**************************************************************************/
/**************************************************************************/
#define NX_TFTP_SOURCE_CODE
/* Force error checking to be disabled in this module */
#ifndef NX_DISABLE_ERROR_CHECKING
#define NX_DISABLE_ERROR_CHECKING
#endif
/* Include necessary system files. */
#include "nx_api.h"
#include "nx_ip.h"
#include "nx_tftp_server.h"
/* Bring in externs for caller checking code. */
NX_CALLER_CHECKING_EXTERNS
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nxe_tftp_server_create PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function checks for errors in the TFTP server create call. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* tftp_server_name Name of TFTP server */
/* ip_ptr Pointer to IP instance */
/* media_ptr Pointer to media structure */
/* stack_ptr Server thread's stack pointer */
/* stack_size Server thread's stack size */
/* pool_ptr Pointer to packet pool */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* NX_PTR_ERROR Invalid pointer input */
/* NX_TFTP_POOL_ERROR Invalid packet size for TFTP */
/* server packet pool */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_create Actual server create call */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nxe_tftp_server_create(NX_TFTP_SERVER *tftp_server_ptr, CHAR *tftp_server_name, NX_IP *ip_ptr,
FX_MEDIA *media_ptr, VOID *stack_ptr, ULONG stack_size, NX_PACKET_POOL *pool_ptr)
{
UINT status;
NX_PACKET *packet_ptr;
/* Check for invalid input pointers. */
if ((ip_ptr == NX_NULL) || (ip_ptr -> nx_ip_id != NX_IP_ID) ||
(tftp_server_ptr == NX_NULL) || (tftp_server_ptr -> nx_tftp_server_id == NX_TFTP_SERVER_ID) ||
(stack_ptr == NX_NULL) || (pool_ptr == NX_NULL))
{
return(NX_PTR_ERROR);
}
/* Pickup a packet from the supplied packet pool. */
packet_ptr = pool_ptr -> nx_packet_pool_available_list;
/* Determine if the packet payload is large enough (must include 512 bytes plus packet headers). */
if ((packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_data_start) < NX_TFTP_PACKET_SIZE)
{
return(NX_TFTP_POOL_ERROR);
}
/* Call actual server create function. */
status = _nx_tftp_server_create(tftp_server_ptr, tftp_server_name, ip_ptr, media_ptr, stack_ptr, stack_size, pool_ptr);
/* Return completion status. */
return(status);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_create PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function creates a TFTP server on the specified IP. In doing */
/* so this function creates an UDP socket for subsequent TFTP */
/* transfers and a thread for the TFTP server. */
/* */
/* If NX_TFTP_SERVER_RETRANSMIT_ENABLE is enabled, it also creates */
/* a timer for retransmitting TFTP server packets up to a maximum */
/* number of retries before terminating the connection. */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* tftpvserver_name Name of TFTP server */
/* ip_ptr Pointer to IP instance */
/* media_ptr Pointer to media structure */
/* stack_ptr Server thread's stack pointer */
/* stack_size Server thread's stack size */
/* pool_ptr Pointer to packet pool */
/* */
/* OUTPUT */
/* */
/* status Actual cmpletion status */
/* NX_SUCCESS Successful completion */
/* */
/* CALLS */
/* */
/* nx_udp_socket_bind Bind the TFTP server socket */
/* nx_udp_socket_create Create TFTP server socket */
/* nx_udp_socket_delete Delete TFTP server socket */
/* nx_udp_socket_unbind Unbind TFTP server socket */
/* tx_thread_create Create TFTP server thread */
/* tx_timer_create Create retransmit timer */
/* nx_udp_socket_receive_notify Set a receive callback */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_create(NX_TFTP_SERVER *tftp_server_ptr, CHAR *tftp_server_name, NX_IP *ip_ptr,
FX_MEDIA *media_ptr, VOID *stack_ptr, ULONG stack_size, NX_PACKET_POOL *pool_ptr)
{
UINT status;
/* Clear the TFTP server structure. */
memset((void *) tftp_server_ptr, 0, sizeof(NX_TFTP_SERVER));
/* Create the server's UDP socket. */
status = nx_udp_socket_create(ip_ptr, &(tftp_server_ptr -> nx_tftp_server_socket), tftp_server_name,
NX_TFTP_TYPE_OF_SERVICE, NX_TFTP_FRAGMENT_OPTION, NX_TFTP_TIME_TO_LIVE, NX_TFTP_QUEUE_DEPTH);
/* Determine if an error occurred. */
if (status != NX_SUCCESS)
{
/* Yes, return error code. */
return(status);
}
/* Register the receive function. */
nx_udp_socket_receive_notify(&(tftp_server_ptr -> nx_tftp_server_socket), _nx_tftp_server_data_present);
/* Make sure the socket points to the TFTP server. */
tftp_server_ptr -> nx_tftp_server_socket.nx_udp_socket_reserved_ptr = tftp_server_ptr;
/* Now, bind the socket to a the well known TFTP UDP port number. */
status = nx_udp_socket_bind(&(tftp_server_ptr -> nx_tftp_server_socket), NX_TFTP_SERVER_PORT, NX_NO_WAIT);
/* Determine if an error occurred. */
if (status != NX_SUCCESS)
{
/* Delete the UDP socket. */
nx_udp_socket_delete(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Yes, return error code. */
return(status);
}
/* Now create the TFTP Server thread. */
status = tx_thread_create(&(tftp_server_ptr -> nx_tftp_server_thread), "TFTP Server Thread", _nx_tftp_server_thread_entry,
(ULONG) tftp_server_ptr, stack_ptr, stack_size, NX_TFTP_SERVER_PRIORITY, NX_TFTP_SERVER_PRIORITY,
NX_TFTP_SERVER_TIME_SLICE, TX_DONT_START);
/* Determine if an error occurred creating the thread. */
if (status != NX_SUCCESS)
{
/* Unbind the UDP socket. */
nx_udp_socket_unbind(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the UDP socket. */
nx_udp_socket_delete(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Yes, return error code. */
return(status);
}
/* Create the ThreadX event flags. These will be used to drive the TFTP server thread. */
status = tx_event_flags_create(&(tftp_server_ptr -> nx_tftp_server_event_flags), "TFTP Server Thread Events");
/* Determine if an error occurred creating the event flags. */
if (status != TX_SUCCESS)
{
/* Unbind the UDP socket. */
nx_udp_socket_unbind(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the UDP socket. */
nx_udp_socket_delete(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the server thread. */
tx_thread_delete(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Error creating the server event flags. */
return(status);
}
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Create the ThreadX retransmit timeout timer. This will be used to retransmit TFTP server
packets if the Client has not responded or sends a duplicate (old) ACK or data packet. */
status = tx_timer_create(&(tftp_server_ptr -> nx_tftp_server_timer), "TFTP Server Timer",
_nx_tftp_server_timer_entry, (ULONG) tftp_server_ptr,
(NX_TFTP_SERVER_TIMEOUT_PERIOD),
(NX_TFTP_SERVER_TIMEOUT_PERIOD), TX_NO_ACTIVATE);
if (status != NX_SUCCESS)
{
/* Unbind the UDP socket. */
nx_udp_socket_unbind(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the UDP socket. */
nx_udp_socket_delete(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the event flags. */
tx_event_flags_delete(&(tftp_server_ptr -> nx_tftp_server_event_flags));
/* Delete the server thread. */
tx_thread_delete(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Yes, return error code. */
return(status);
}
#endif /* NX_TFTP_SERVER_RETRANSMIT_ENABLE */
/* Save the Server name. */
tftp_server_ptr -> nx_tftp_server_name = tftp_server_name;
/* Save the IP pointer address. */
tftp_server_ptr -> nx_tftp_server_ip_ptr = ip_ptr;
/* Save the packet pool pointer. */
tftp_server_ptr -> nx_tftp_server_packet_pool_ptr = pool_ptr;
/* Save the media pointer address. */
tftp_server_ptr -> nx_tftp_server_media_ptr = media_ptr;
/* Clear the error code and error string. */
tftp_server_ptr -> nx_tftp_server_error_code = 0;
tftp_server_ptr -> nx_tftp_server_error_string[0] = NX_NULL;
/* Set the server ID to indicate the TFTP server thread is ready. */
tftp_server_ptr -> nx_tftp_server_id = NX_TFTP_SERVER_ID;
/* Return successful completion. */
return(NX_SUCCESS);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nxe_tftp_server_delete PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function checks for errors in the TFTP server delete call. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* NX_PTR_ERROR Invalid pointer input */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_delete Actual server delete call */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nxe_tftp_server_delete(NX_TFTP_SERVER *tftp_server_ptr)
{
UINT status;
/* Check for invalid input pointers. */
if ((tftp_server_ptr == NX_NULL) || (tftp_server_ptr -> nx_tftp_server_id != NX_TFTP_SERVER_ID))
return(NX_PTR_ERROR);
/* Check for appropriate caller. */
NX_THREADS_ONLY_CALLER_CHECKING
/* Call actual server delete function. */
status = _nx_tftp_server_delete(tftp_server_ptr);
/* Return completion status. */
return(status);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_delete PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function deletes a previously created TFTP server on the */
/* specified IP. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* NX_SUCCESS Successful completion */
/* status Actual completion status */
/* */
/* CALLS */
/* */
/* fx_file_close File close */
/* nx_udp_socket_delete Delete TFTP server socket */
/* nx_udp_socket_unbind Unbind TFTP server socket */
/* tx_thread_delete Delete TFTP server thread */
/* tx_thread_suspend Suspend TFTP server thread */
/* tx_thread_terminate Terminate TFTP server */
/* thread */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_delete(NX_TFTP_SERVER *tftp_server_ptr)
{
UINT i;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
/* Clear the server ID to indicate the TFTP server is no longer ready. */
tftp_server_ptr -> nx_tftp_server_id = 0;
/* Unbind the UDP socket. */
nx_udp_socket_unbind(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Delete the UDP socket. */
nx_udp_socket_delete(&(tftp_server_ptr -> nx_tftp_server_socket));
/* Suspend the TFTP server thread. */
tx_thread_suspend(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Terminate server thread. */
tx_thread_terminate(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Delete the server flag group. */
tx_event_flags_delete(&(tftp_server_ptr -> nx_tftp_server_event_flags));
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
tx_timer_delete(&(tftp_server_ptr -> nx_tftp_server_timer));
#endif
/* Delete server thread. */
tx_thread_delete(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Walk through the server structure to close any remaining open files. */
i = 0;
client_request_ptr = &(tftp_server_ptr -> nx_tftp_server_client_list[0]);
while (i < NX_TFTP_MAX_CLIENTS)
{
/* Is this entry in use? */
/* First determine which IP address type. */
if((client_request_ptr -> nx_tftp_client_request_port != 0) &&
(client_request_ptr -> nx_tftp_client_request_ip_address != 0))
{
/* No, need to close the file on this client! */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
}
/* Increment the pointer into the client request list. */
client_request_ptr++;
i++;
}
/* Return successful completion. */
return(NX_SUCCESS);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nxe_tftp_server_start PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function checks for errors in the TFTP server start call. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* NX_PTR_ERROR Invalid pointer input */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_start Actual server start call */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nxe_tftp_server_start(NX_TFTP_SERVER *tftp_server_ptr)
{
UINT status;
/* Check for invalid input pointers. */
if ((tftp_server_ptr == NX_NULL) || (tftp_server_ptr -> nx_tftp_server_id != NX_TFTP_SERVER_ID))
return(NX_PTR_ERROR);
/* Call actual server start function. */
status = _nx_tftp_server_start(tftp_server_ptr);
/* Return completion status. */
return(status);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_start PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function starts a previously created TFTP server on the */
/* specified IP. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* NX_SUCCESS Successful completion */
/* */
/* CALLS */
/* */
/* tx_thread_resume Resume TFTP server thread */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_start(NX_TFTP_SERVER *tftp_server_ptr)
{
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Activate TFTP server timer. */
tx_timer_activate(&(tftp_server_ptr -> nx_tftp_server_timer));
#endif
/* Start the TFTP server thread. */
tx_thread_resume(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Return successful completion. */
return(NX_SUCCESS);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nxe_tftp_server_stop PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function checks for errors in the TFTP server stop call. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* NX_PTR_ERROR Invalid pointer input */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_stop Actual server start call */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nxe_tftp_server_stop(NX_TFTP_SERVER *tftp_server_ptr)
{
UINT status;
/* Check for invalid input pointers. */
if ((tftp_server_ptr == NX_NULL) || (tftp_server_ptr -> nx_tftp_server_id != NX_TFTP_SERVER_ID))
return(NX_PTR_ERROR);
/* Check for appropriate caller. */
NX_THREADS_ONLY_CALLER_CHECKING
/* Call actual server delete function. */
status = _nx_tftp_server_stop(tftp_server_ptr);
/* Return completion status. */
return(status);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_stop PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function stops a previously started TFTP server on the */
/* specified IP. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* NX_SUCCESS Successful completion */
/* */
/* CALLS */
/* */
/* tx_thread_suspend Suspend TFTP server thread */
/* */
/* CALLED BY */
/* */
/* Application Code */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_stop(NX_TFTP_SERVER *tftp_server_ptr)
{
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Deactivate TFTP server timer. */
tx_timer_deactivate(&(tftp_server_ptr -> nx_tftp_server_timer));
#endif
/* Suspend the TFTP server thread. */
tx_thread_suspend(&(tftp_server_ptr -> nx_tftp_server_thread));
/* Return successful completion. */
return(NX_SUCCESS);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_data_present PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function is notified by NetX of receive events generated by */
/* TFTP Clients connecting or sending data. */
/* */
/* INPUT */
/* */
/* socket_ptr Socket receiving data */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* tx_event_flags_set Set events for server thread */
/* */
/* CALLED BY */
/* */
/* NetX NetX TCP socket callback */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_data_present(NX_UDP_SOCKET *socket_ptr)
{
NX_TFTP_SERVER *server_ptr;
/* Pickup server pointer. This is setup in the reserved field of the TCP socket. */
server_ptr = (NX_TFTP_SERVER *)(socket_ptr -> nx_udp_socket_reserved_ptr);
/* Set the data event flag. */
tx_event_flags_set(&(server_ptr -> nx_tftp_server_event_flags), NX_TFTP_SERVER_RECEIVE_EVENT, TX_OR);
}
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_timer_entry PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function captures timer events used to determine if the Client */
/* retransmit timer has expired. */
/* */
/* */
/* INPUT */
/* */
/* ULONG Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* tx_event_flags_set Set events for server thread */
/* */
/* CALLED BY */
/* */
/* NetX NetX connect callback */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_timer_entry(ULONG tftp_server_address)
{
NX_TFTP_SERVER *server_ptr;
/* Pickup server pointer. */
server_ptr = (NX_TFTP_SERVER *) tftp_server_address;
/* Set the data event flag. */
tx_event_flags_set(&(server_ptr -> nx_tftp_server_event_flags), NX_TFTP_SERVER_TIMER_EVENT, TX_OR);
return;
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_timer_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function checks all the active TFTP client connections */
/* for their retransmission timeout. If expired, the Server retransmits*/
/* the data or ACK up to a maximum number of retries */
/* (NX_TFTP_SERVER_MAX_RETRIES) and then terminates the connection and */
/* closes any open files. The client request is cleared and available */
/* for the next client request */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* fx_file_close Close session file. */
/* fx_file_delete Delete the file */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP server task function */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_timer_process(NX_TFTP_SERVER *server_ptr)
{
UINT i;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
/* Now look through all the sockets. */
for (i = 0; i < NX_TFTP_MAX_CLIENTS; i++)
{
/* Set a pointer to client request structure. */
client_request_ptr = &(server_ptr -> nx_tftp_server_client_list[i]);
/* Check if this request has a retransmit timeout pending. */
if (client_request_ptr -> nx_tftp_client_retransmit_timeout )
{
/* Update reatransmit timeout. */
if (client_request_ptr -> nx_tftp_client_retransmit_timeout >= NX_TFTP_SERVER_TIMEOUT_PERIOD)
{
client_request_ptr -> nx_tftp_client_retransmit_timeout -= NX_TFTP_SERVER_TIMEOUT_PERIOD;
}
else
{
client_request_ptr -> nx_tftp_client_retransmit_timeout = 0;
}
/* Has the retransmit timeout expired? */
if (client_request_ptr -> nx_tftp_client_retransmit_timeout == 0)
{
/* Yes, retransmit unless we have hit the max retry limit. */
if (client_request_ptr -> nx_tftp_client_retransmit_retries < NX_TFTP_SERVER_MAX_RETRIES)
{
/* Update the Client request retransmit timeout and number of retries. */
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
client_request_ptr -> nx_tftp_client_retransmit_retries++;
/* Determine which type of request this is. */
if (client_request_ptr -> nx_tftp_client_request_open_type == NX_TFTP_STATE_WRITE_OPEN)
{
/* Retransmit the ACK. */
_nx_tftp_server_send_ack(server_ptr, client_request_ptr, NX_TRUE);
}
else
{
/* Retransmit the file data. */
_nx_tftp_server_send_data(server_ptr, client_request_ptr, NX_TRUE);
}
}
else
{
/* The session has timed out. Send error and close. */
_nx_tftp_server_send_error(server_ptr, &client_request_ptr -> nx_tftp_client_request_ip_address,
client_request_ptr -> nx_tftp_client_request_port,
NX_TFTP_SESSION_TIMED_OUT, "NetX TFTP Server: Session timed out");
/* Error, close the file and delete the client request. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
}
}
}
}
}
#endif /* NX_TFTP_SERVER_RETRANSMIT_ENABLE */
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_thread_entry PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function is the entry of the TFTP server. All basic */
/* processing is initiated by this function. */
/* */
/* */
/* INPUT */
/* */
/* tftp_server Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* tx_event_flags_get Get the TFTP events */
/* _nx_tftp_server_process_received_data Process received packet */
/* _nx_tftp_server_timer_process Process TFTP timer */
/* */
/* CALLED BY */
/* */
/* ThreadX */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_thread_entry(ULONG tftp_server)
{
NX_TFTP_SERVER *server_ptr;
UINT status;
ULONG events;
/* Setup the server pointer. */
server_ptr = (NX_TFTP_SERVER *) tftp_server;
/* Loop to process TFTP Server requests. */
while(1)
{
/* Wait for TFTP events. */
status = tx_event_flags_get(&(server_ptr -> nx_tftp_server_event_flags), NX_SERVER_TFTP_ANY_EVENT,
TX_OR_CLEAR, &events, TX_WAIT_FOREVER);
/* Check the return status. */
if (status)
{
/* If an error occurs, simply continue the loop. */
continue;
}
/* Otherwise, an event is present. Process according to the event. */
/* Check for a client receive event. */
if (events & NX_TFTP_SERVER_RECEIVE_EVENT)
{
/* Call the data received handler. */
_nx_tftp_server_process_received_data(server_ptr);
}
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Check for a timer event. */
if (events & NX_TFTP_SERVER_TIMER_EVENT)
{
/* Call the timer timeout handler. */
_nx_tftp_server_timer_process(server_ptr);
}
#endif /* NX_TFTP_SERVER_RETRANSMIT_ENABLE*/
}
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_process_received_data PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function is called when the TFTP server is notified of a */
/* receive event. It parses the TFTP Client message and calls the */
/* appropriate handler for read and write requests, client errors, or */
/* ACKing data sent by the server. */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_ack_process Process ACK from previous read*/
/* _nx_tftp_server_data_process Write data packet to file */
/* _nx_tftp_server_error_process Process error packet */
/* _nx_tftp_server_open_for_read_process Open file for reading */
/* _nx_tftp_server_open_for_write_process Open for writing */
/* nx_packet_release Release packet */
/* nx_udp_socket_receive Receive next TFTP packet */
/* */
/* CALLED BY */
/* */
/* nx_tftp_server_thread_entry TFTP thread task function */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_process_received_data(NX_TFTP_SERVER *server_ptr)
{
UINT status;
NX_PACKET *packet_ptr;
UCHAR *buffer_ptr;
UCHAR request_code;
/* Wait for a request on the TFTP UDP well known port 69. */
status = nx_udp_socket_receive(&(server_ptr -> nx_tftp_server_socket), &packet_ptr, NX_NO_WAIT);
/* Check the return status. */
if (status)
{
/* If an error occurs, simply return to caller. */
return;
}
/* Check for valid packet length (The minimum TFTP header size is ACK packet, four bytes). */
if (packet_ptr -> nx_packet_length < 4)
{
/* Release the packet. */
nx_packet_release(packet_ptr);
/* Return. */
return;
}
/* Otherwise, we have received a packet successfully. */
/* Setup a pointer to packet buffer area. */
buffer_ptr = packet_ptr -> nx_packet_prepend_ptr;
/* Pickup up the request code. First one must be zero. */
request_code = *buffer_ptr++;
/* If not zero, abort. */
if (request_code)
{
nx_packet_release(packet_ptr);
return;
}
/* Now get the actual request code. */
request_code = *buffer_ptr++;
/* Process relative to the TFTP request code. */
switch (request_code)
{
case NX_TFTP_CODE_READ:
/* Process an open for read request. */
_nx_tftp_server_open_for_read_process(server_ptr, packet_ptr);
/* Increment the number of open for read requests. */
server_ptr -> nx_tftp_server_open_for_read_requests++;
break;
case NX_TFTP_CODE_WRITE:
/* Process an open for write request. */
_nx_tftp_server_open_for_write_process(server_ptr, packet_ptr);
/* Increment the number of open for write requests. */
server_ptr -> nx_tftp_server_open_for_write_requests++;
break;
case NX_TFTP_CODE_DATA:
/* Process a data request. */
_nx_tftp_server_data_process(server_ptr, packet_ptr);
/* Increment the number of data block write requests. */
server_ptr -> nx_tftp_server_data_blocks_received++;
break;
case NX_TFTP_CODE_ACK:
/* Process an ack response. */
_nx_tftp_server_ack_process(server_ptr, packet_ptr);
/* Increment the number of acks for previous data blocks sent. */
server_ptr -> nx_tftp_server_acks_received++;
break;
case NX_TFTP_CODE_ERROR:
/* Process an error request. */
_nx_tftp_server_error_process(server_ptr, packet_ptr);
/* Increment the number of errors received. */
server_ptr -> nx_tftp_server_errors_received++;
break;
default:
/* Increment the number of unknown codes received. */
server_ptr -> nx_tftp_server_unknown_commands++;
/* Just release the packet. */
nx_packet_release(packet_ptr);
}
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_open_for_read_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function opens the specified file for reading, and returns */
/* the first block of the file. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* packet_ptr Pointer to TFTP request packet*/
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* fx_directory_information_get Get information about file */
/* fx_file_close Close file on EOF or error */
/* fx_file_open Open file for reading */
/* fx_file_read Read block from file */
/* _nx_tftp_server_find_client_request Find client entry */
/* _nx_tftp_server_send_error Send error message */
/* nx_packet_allocate Allocate a new packet */
/* nx_packet_release Release packet */
/* nx_udp_socket_send Send TFTP data packet */
/* nx_udp_source_extract Extract IP and port */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP Server thread */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
void _nx_tftp_server_open_for_read_process(NX_TFTP_SERVER *server_ptr, NX_PACKET *packet_ptr)
{
ULONG ip_address;
ULONG file_size;
ULONG actual_size;
UINT port;
UCHAR *buffer_ptr;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
NX_PACKET *new_packet = NX_NULL;
UINT status;
/* Extract the source IP and port numbers. */
nx_udp_source_extract(packet_ptr, &ip_address, &port);
/* First, try to find a matching exiting entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, port, ip_address);
/* See if we need to find a new entry. */
if (client_request_ptr == NX_NULL)
{
/* Yes, find a free entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, 0, NX_NULL);
}
else
{
/* This is a dupe request. Ignore it. */
nx_packet_release(packet_ptr);
return;
}
/* Determine if there was a free entry. */
if (client_request_ptr == NX_NULL)
{
/* Increment the maximum clients errors. */
server_ptr -> nx_tftp_server_clients_exceeded_errors++;
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NOT_DEFINED, "NetX TFTP Server: Too Many Clients");
/* No more clients can be serviced, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Initialize the client request structure. */
client_request_ptr -> nx_tftp_client_request_ip_address = ip_address;
client_request_ptr -> nx_tftp_client_request_port = port;
client_request_ptr -> nx_tftp_client_request_block_number = 1;
client_request_ptr -> nx_tftp_client_request_open_type = NX_TFTP_STATE_OPEN;
client_request_ptr -> nx_tftp_client_request_exact_fit = NX_FALSE;
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Reset the retransmission timeout and retries for the client request. */
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
client_request_ptr -> nx_tftp_client_retransmit_retries = 0;
#else
/* Clear the count of ACK retransmits from the other side. */
client_request_ptr -> nx_tftp_client_request_retransmits = 0;
#endif
/* Setup a pointer to the file name. */
buffer_ptr = (UCHAR *) (packet_ptr -> nx_packet_prepend_ptr + 2);
if (packet_ptr -> nx_packet_length < 4)
{
nx_packet_release(packet_ptr);
return;
}
/* Check if the packet buffer ends with NULL. */
if (*(UCHAR *)(packet_ptr -> nx_packet_append_ptr - 1))
{
nx_packet_release(packet_ptr);
return;
}
/* Pickup the file size. */
status = fx_directory_information_get(server_ptr -> nx_tftp_server_media_ptr, (CHAR *) buffer_ptr, NX_NULL, &file_size, NX_NULL, NX_NULL, NX_NULL, NX_NULL, NX_NULL, NX_NULL);
/* Check the return status. */
if (status != FX_SUCCESS)
{
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_FILE_NOT_FOUND, "NetX TFTP Server: File Not Found");
/* Unable to find the file size, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Attempt to open the file. */
status = fx_file_open(server_ptr -> nx_tftp_server_media_ptr, &(client_request_ptr ->nx_tftp_client_request_file), (CHAR *) buffer_ptr, FX_OPEN_FOR_READ);
/* Check the return status. */
if (status != FX_SUCCESS)
{
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_FILE_NOT_FOUND, "NetX TFTP Server: File Open Failed");
/* Unable to open the file, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* The file has been opened successfully, now try to read up to 512 bytes. */
/* Allocate packet for the read packet. Determine whether we are sending IP packets. */
status = nx_packet_allocate(server_ptr -> nx_tftp_server_packet_pool_ptr, &new_packet, NX_UDP_PACKET, NX_WAIT_FOREVER);
/* Check for successful packet allocation. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Unable to allocate net packet, release the original. */
nx_packet_release(packet_ptr);
return;
}
if (4u + NX_TFTP_FILE_TRANSFER_MAX > ((ULONG)(new_packet -> nx_packet_data_end) - (ULONG)(new_packet -> nx_packet_append_ptr)))
{
/* Release the original packet. */
nx_packet_release(packet_ptr);
nx_packet_release(new_packet);
return;
}
client_request_ptr -> nx_tftp_client_file_size = file_size;
/* Read data when length of file is larger then 0. */
if(file_size)
{
/* Attempt to read the requested file. */
status = fx_file_read(&(client_request_ptr ->nx_tftp_client_request_file), new_packet -> nx_packet_prepend_ptr+4, NX_TFTP_FILE_TRANSFER_MAX, &actual_size);
/* Check for successful file read. */
if ((status != NX_SUCCESS) ||
((file_size > NX_TFTP_FILE_TRANSFER_MAX) && (actual_size != NX_TFTP_FILE_TRANSFER_MAX)) ||
((file_size < NX_TFTP_FILE_TRANSFER_MAX) && (actual_size != file_size)))
{
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NOT_DEFINED, "NetX TFTP Server: File Read Error");
/* Unable to read the file, close it and release the packet. */
fx_file_close(&(client_request_ptr ->nx_tftp_client_request_file));
nx_packet_release(packet_ptr);
return;
}
}
else
{
actual_size = 0;
}
/* Increment the number of total bytes sent. */
server_ptr -> nx_tftp_server_total_bytes_sent += actual_size;
/* Setup the client request structure. */
client_request_ptr -> nx_tftp_client_request_remaining_bytes = file_size - actual_size;
/* Determine if the file size is evenly divisible by our TFTP transfer size. */
if (file_size % NX_TFTP_FILE_TRANSFER_MAX)
{
/* Not an exact fit, ensure the exact fit flag is clear. */
client_request_ptr -> nx_tftp_client_request_exact_fit = NX_FALSE;
}
else if (file_size > 0)
{
/* Yes, the file size happens to be evenly divisible by the TFTP transfer size. In this
case, we need to send a zero-length data packet at the end of file to let the other side
know we are at the end. */
client_request_ptr -> nx_tftp_client_request_exact_fit = NX_TRUE;
}
/* Move the TFTP data code and block number into the payload before sending it to the client. */
buffer_ptr = new_packet -> nx_packet_prepend_ptr;
*buffer_ptr++ = 0;
*buffer_ptr++ = NX_TFTP_CODE_DATA;
*buffer_ptr++ = 0;
*buffer_ptr++ = 1; /* First block number of file. */
/* Setup the packet pointers appropriately. */
new_packet -> nx_packet_length = actual_size + 4;
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr + new_packet -> nx_packet_length;
/* Send the data packet out. */
status = nx_udp_socket_send(&(server_ptr -> nx_tftp_server_socket), new_packet, ip_address, port);
/* Release packet if send fails. */
if (status)
{
nx_packet_release(new_packet);
}
/* Release the original packet. */
nx_packet_release(packet_ptr);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_open_for_write_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function opens the specified file for writing, and returns */
/* an ACK for block 0 to let the client know everything is good. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* packet_ptr Pointer to TFTP request packet*/
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* fx_file_create Create file, if necessary */
/* fx_file_close Close file on EOF or error */
/* fx_file_open Open file for reading */
/* fx_file_write Write block to file */
/* _nx_tftp_server_find_client_request Find client entry */
/* _nx_tftp_server_send_error Send error message */
/* nx_packet_allocate Allocate a new packet */
/* nx_packet_release Release packet */
/* nx_udp_socket_send Send TFTP data packet */
/* nx_udp_source_extract Extract IP and port */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP Server thread task */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_open_for_write_process(NX_TFTP_SERVER *server_ptr, NX_PACKET *packet_ptr)
{
ULONG ip_address;
UINT port;
UCHAR *buffer_ptr;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
NX_PACKET *new_packet;
UINT status;
/* Extract the source IP and port numbers. */
nx_udp_source_extract(packet_ptr, &ip_address, &port);
/* First, try to find a matching exiting entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, port, ip_address);
/* See if we need to find a new entry. */
if (client_request_ptr == NX_NULL)
{
/* Find a free entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, 0, NX_NULL);
}
else
{
/* This is a dupe request. Ignore it. */
nx_packet_release(packet_ptr);
return;
}
/* Determine if there was a free entry. */
if (client_request_ptr == NX_NULL)
{
/* Increment the maximum clients errors. */
server_ptr -> nx_tftp_server_clients_exceeded_errors++;
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NOT_DEFINED, "NetX TFTP Server: Too Many Clients");
/* No more clients can be serviced, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Setup a pointer to the file name. */
buffer_ptr = (UCHAR *) (packet_ptr -> nx_packet_prepend_ptr + 2);
if (packet_ptr -> nx_packet_length < 4)
{
nx_packet_release(packet_ptr);
return;
}
/* Check if the packet buffer ends with NULL. */
if (*(UCHAR *)(packet_ptr -> nx_packet_append_ptr - 1))
{
nx_packet_release(packet_ptr);
return;
}
/* Perform a file create. This will fail if the file is already present, which we don't care about at this point. */
fx_file_delete(server_ptr -> nx_tftp_server_media_ptr, (CHAR *) buffer_ptr);
fx_file_create(server_ptr -> nx_tftp_server_media_ptr, (CHAR *) buffer_ptr);
/* Attempt to open the file. */
status = fx_file_open(server_ptr -> nx_tftp_server_media_ptr, &(client_request_ptr -> nx_tftp_client_request_file), (CHAR *) buffer_ptr, FX_OPEN_FOR_WRITE);
/* Check for file is open errors. */
if (status != NX_SUCCESS)
{
/* This is an actual file open error. Send an error to the client. */
if (status == FX_ACCESS_ERROR)
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_ACCESS_VIOLATION, "NetX TFTP Server: File Access Error");
else
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_FILE_NOT_FOUND, "NetX TFTP Server: File Open Failed");
/* Unable to open the file, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Now, attempt to build an ACK response to let the client know it can start writing. */
/* Allocate packet for the ACK packet. Determine whether we are sending IP packets. */
status = nx_packet_allocate(server_ptr -> nx_tftp_server_packet_pool_ptr, &new_packet, NX_UDP_PACKET, NX_WAIT_FOREVER);
/* Check for successful packet allocation. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Close the file. */
fx_file_close(&(client_request_ptr ->nx_tftp_client_request_file));
/* Unable to allocate net packet, release the original. */
nx_packet_release(packet_ptr);
return;
}
if (4u > ((ULONG)(new_packet -> nx_packet_data_end) - (ULONG)(new_packet -> nx_packet_append_ptr)))
{
nx_packet_release(new_packet);
/* Release the original packet. */
nx_packet_release(packet_ptr);
return;
}
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr;
/* Setup the client request structure. */
client_request_ptr -> nx_tftp_client_request_ip_address = ip_address;
client_request_ptr -> nx_tftp_client_request_port = port;
client_request_ptr -> nx_tftp_client_request_block_number = 1;
client_request_ptr -> nx_tftp_client_request_open_type = NX_TFTP_STATE_WRITE_OPEN;
client_request_ptr -> nx_tftp_client_request_remaining_bytes = 0;
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Reset retransmission timeouts and retries on current client request. */
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
client_request_ptr -> nx_tftp_client_retransmit_retries = 0;
#else
/* Clear the count of data retransmits from the other side. */
client_request_ptr -> nx_tftp_client_request_retransmits = 0;
#endif
/* Create the ACK packet. */
buffer_ptr = new_packet -> nx_packet_prepend_ptr;
*buffer_ptr++ = 0;
*buffer_ptr++ = NX_TFTP_CODE_ACK;
*buffer_ptr++ = 0;
*buffer_ptr++ = 0; /* 0, just to signal server is ready. */
/* Setup the packet pointers appropriately. */
new_packet -> nx_packet_length = 4;
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr + 4;
/* Send the data packet out. */
status = nx_udp_socket_send(&(server_ptr -> nx_tftp_server_socket), new_packet, ip_address, port);
/* Release packet if send fails. */
if (status)
{
nx_packet_release(new_packet);
}
/* Release the original packet. */
nx_packet_release(packet_ptr);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_data_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function takes the supplied data packet and writes it to the */
/* previously opened file. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* packet_ptr Pointer to TFTP request packet*/
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* fx_file_close Close file on EOF or error */
/* fx_file_write Write block to file */
/* _nx_tftp_server_find_client_request Find client entry */
/* _nx_tftp_server_send_error Send error message */
/* nx_packet_allocate Allocate a new packet */
/* nx_packet_copy Copy packet */
/* nx_packet_release Release packet */
/* nx_udp_socket_send Send TFTP ACK packet */
/* nx_udp_source_extract Extract IP and port */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP Server thread loop */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_data_process(NX_TFTP_SERVER *server_ptr, NX_PACKET *packet_ptr)
{
ULONG ip_address;
UINT port;
USHORT block_number;
UCHAR *buffer_ptr;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
UINT status;
/* Extract the source IP and port numbers. */
nx_udp_source_extract(packet_ptr, &ip_address, &port);
/* Find the matching entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, port, ip_address);
/* Determine if there was a matching entry. */
if (client_request_ptr == NX_NULL)
{
/* Increment the unknown clients errors. */
server_ptr -> nx_tftp_server_unknown_clients_errors++;
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NO_SUCH_USER, "NetX TFTP Server: Unknown connection");
/* No more clients can be serviced, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Setup a pointer to the block number. */
buffer_ptr = (UCHAR *) (packet_ptr -> nx_packet_prepend_ptr + 2);
if (packet_ptr -> nx_packet_length < 4)
{
nx_packet_release(packet_ptr);
return;
}
/* Pickup the block number. */
block_number = (USHORT)(*buffer_ptr++);
block_number = (USHORT)((block_number << 8) | (*buffer_ptr));
/* Determine if this block number matches the current client block number. */
if (client_request_ptr -> nx_tftp_client_request_block_number != block_number)
{
/* No, it does not. */
/* Check if it matches our previous ACK e.g. it could be a retransmit from the other side. */
if (client_request_ptr -> nx_tftp_client_request_block_number == (USHORT)(block_number + 1))
{
#ifndef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* It does. Update how many we have received. */
client_request_ptr -> nx_tftp_client_request_retransmits++;
/* Decide if we should close the client request. */
if (client_request_ptr -> nx_tftp_client_request_retransmits <= NX_TFTP_MAX_CLIENT_RETRANSMITS)
{
/* Not yet. Just drop the packet for now. */
nx_packet_release(packet_ptr);
return;
}
/* Else handle as an error. */
#else
nx_packet_release(packet_ptr);
/* (Let the retransmit timeout handler retransmit our ACK to the client.) */
return;
#endif
}
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_ILLEGAL_OPERATION, "NetX TFTP Server: Bad block number");
/* Error, close the file, release the packet and delete the client request. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
/* Release the packet. */
nx_packet_release(packet_ptr);
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
return;
}
/* At this point we have a valid packet. */
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* Reset the retransmit retry counter and retransmit timeout. */
client_request_ptr -> nx_tftp_client_retransmit_retries = 0;
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
#else
/* Clear the count of retransmits from the other side. */
client_request_ptr -> nx_tftp_client_request_retransmits = 0;
#endif
/* Determine if there is anything to write. */
if (packet_ptr -> nx_packet_length > 4)
{
/* At this point, we need to write the next block of the file. */
/* Determine if the current packet is chained. */
if (packet_ptr -> nx_packet_next)
{
NX_PACKET *temp_ptr;
/* Yes, the packet is chained. We have to copy the receive packet into a packet with the
a payload of at least 560 bytes so the write request can be supplied with just the
payload pointer. */
status = nx_packet_copy(packet_ptr, &temp_ptr, server_ptr -> nx_tftp_server_packet_pool_ptr, NX_WAIT_FOREVER);
/* Check for successful packet copy. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Unable to allocate net packet, release the original. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
nx_packet_release(packet_ptr);
memset(client_request_ptr,0, sizeof(NX_TFTP_CLIENT_REQUEST));
return;
}
/* Successful packet copy. Release the original packet and reassign the packet pointer variable. */
nx_packet_release(packet_ptr);
packet_ptr = temp_ptr;
}
/* Attempt to write the block to the file. */
status = fx_file_write(&(client_request_ptr -> nx_tftp_client_request_file), packet_ptr -> nx_packet_prepend_ptr+4,
packet_ptr -> nx_packet_length - 4);
/* Check for successful file write. */
if (status != NX_SUCCESS)
{
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NOT_DEFINED, "NetX TFTP Server: File Write Error");
/* Unable to write the file, close it and release the packet. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
nx_packet_release(packet_ptr);
return;
}
}
/* Check the last packet. */
if (packet_ptr -> nx_packet_length - 4 < NX_TFTP_FILE_TRANSFER_MAX)
{
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
}
status = _nx_tftp_server_send_ack(server_ptr, client_request_ptr, NX_FALSE);
if (status == NX_SUCCESS)
{
/* Increment the number of total bytes received. */
server_ptr -> nx_tftp_server_total_bytes_received += (packet_ptr -> nx_packet_length - 4);
/* Determine if this was the last write. */
if ((packet_ptr -> nx_packet_length - 4) < NX_TFTP_FILE_TRANSFER_MAX)
{
/* No, nothing left to write. Close the file, release the packet and delete
the client request. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
}
}
/* Release the original packet. */
nx_packet_release(packet_ptr);
return;
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_ack_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function processes an ACK to the previous file read */
/* operation and prepares the next data packet to send if necessary. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* packet_ptr Pointer to TFTP request packet*/
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* fx_file_close Close file on EOF or error */
/* fx_file_read Read block from file */
/* _nx_tftp_server_find_client_request Find client entry */
/* _nx_tftp_server_send_error Send error message */
/* nx_packet_allocate Allocate a new packet */
/* nx_packet_copy Copy packet */
/* nx_packet_release Release packet */
/* nx_udp_socket_send Send TFTP data packet */
/* nx_udp_source_extract Extract IP and port */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP Server thread */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_ack_process(NX_TFTP_SERVER *server_ptr, NX_PACKET *packet_ptr)
{
ULONG ip_address;
UINT port;
USHORT block_number;
UCHAR *buffer_ptr;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
/* Extract the source IP and port numbers. */
nx_udp_source_extract(packet_ptr, &ip_address, &port);
/* Find a matching entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, port, ip_address);
/* Determine if there was a matching entry. */
if (client_request_ptr == NX_NULL)
{
/* Increment the unknown clients errors. */
server_ptr -> nx_tftp_server_unknown_clients_errors++;
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_NO_SUCH_USER, "NetX TFTP Server: Unknown connection");
/* No more clients can be serviced, release the packet. */
nx_packet_release(packet_ptr);
return;
}
/* Setup a pointer to the block number. */
buffer_ptr = (UCHAR *) (packet_ptr -> nx_packet_prepend_ptr + 2);
if (packet_ptr -> nx_packet_length < 4)
{
nx_packet_release(packet_ptr);
return;
}
/* Pickup the block number. */
block_number = (USHORT)(*buffer_ptr++);
block_number = (USHORT)((block_number << 8) | (*buffer_ptr));
/* Determine if this block number matches the request. */
if (client_request_ptr -> nx_tftp_client_request_block_number != block_number)
{
/* Check if this is a retransmitted ACK e.g. our previous data packet was dropped our
delayed. */
if (client_request_ptr -> nx_tftp_client_request_block_number == (USHORT)(block_number + 1))
{
#ifndef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* It does. Update how many we have received. */
client_request_ptr -> nx_tftp_client_request_retransmits++;
/* Decide if we should close the client request. */
if (client_request_ptr -> nx_tftp_client_request_retransmits <= NX_TFTP_MAX_CLIENT_RETRANSMITS)
{
/* Not yet. Just drop the packet for now. */
nx_packet_release(packet_ptr);
return;
}
/* Else handle as an error. */
#else
nx_packet_release(packet_ptr);
/* (Let the retransmit timeout handler retransmit our data to the client.) */
return;
#endif
}
/* Send an error to the client. */
_nx_tftp_server_send_error(server_ptr, ip_address, port, NX_TFTP_ERROR_ILLEGAL_OPERATION, "NetX TFTP Server: Bad block number");
/* Error, close the file, release the packet and delete the client request. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
nx_packet_release(packet_ptr);
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
return;
}
/* The block number matches, see if there is anything left to send. */
if ((client_request_ptr -> nx_tftp_client_request_remaining_bytes == 0) &&
(client_request_ptr -> nx_tftp_client_request_exact_fit == NX_FALSE))
{
/* No, nothing left to send. Close the file, release the packet and delete
the client request. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
nx_packet_release(packet_ptr);
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
return;
}
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
/* We have a valid ACK. Reset the retransmit retry counter and retransmit timeout. */
client_request_ptr -> nx_tftp_client_retransmit_retries = 0;
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
#endif
/* At this point, we need to send the next block of the file. */
_nx_tftp_server_send_data(server_ptr, client_request_ptr, NX_FALSE);
/* Release the original packet. */
nx_packet_release(packet_ptr);
return;
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_send_data PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function creates a data packet based on the last ACK received */
/* and sends it out. This will also retransmit a data packet if */
/* specified. On error it will close the file and the client request. */
/* It does not update the Client request e.g. block number. */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* client_request_ptr Pointer to Client request */
/* retransmit Indicate if retransmiting a */
/* previously sent ACK */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_close_client_request Terminate a client request */
/* _nx_tftp_server_send_error Send error status to Client */
/* nx_packet_allocate Allocate a new packet */
/* nx_udp_socket_send Send TFTP data packet */
/* fx_file_seek Set location in file */
/* fx_file_read Read from set location in file*/
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_timer_process TFTP timeout event */
/* _nx_tftp_server_ack_process Process a received ACK */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_send_data(NX_TFTP_SERVER *server_ptr, NX_TFTP_CLIENT_REQUEST *client_request_ptr, UINT retransmit)
{
UINT status;
ULONG actual_size = 0;
NX_PACKET *new_packet;
UCHAR *buffer_ptr;
/* Allocate packet for the read packet. Determine whether we are sending IP packets. */
status = nx_packet_allocate(server_ptr -> nx_tftp_server_packet_pool_ptr, &new_packet, NX_UDP_PACKET, NX_WAIT_FOREVER);
/* Check for successful packet allocation. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Unable to allocate net packet, release the original. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
return status;
}
if (4u + NX_TFTP_FILE_TRANSFER_MAX > ((ULONG)(new_packet -> nx_packet_data_end) - (ULONG)(new_packet -> nx_packet_append_ptr)))
{
nx_packet_release(new_packet);
return(NX_SIZE_ERROR);
}
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr;
/* Determine if there are more bytes to read. */
if (client_request_ptr -> nx_tftp_client_request_remaining_bytes)
{
status = NX_SUCCESS;
/* Are we retransmitting? */
if (retransmit)
{
/* Yes, figure out where to reset the file pointer to the previous
file read so we can retrieve the previous data we sent. */
UINT index = client_request_ptr -> nx_tftp_client_file_size -
client_request_ptr -> nx_tftp_client_request_remaining_bytes -
client_request_ptr -> nx_tftp_client_previous_write_size;
status = fx_file_seek(&(client_request_ptr -> nx_tftp_client_request_file), index);
}
/* Are we still ok to do a file read? */
if (status == NX_SUCCESS)
{
/* Yes. Attempt to read the requested file. */
UINT status_read = fx_file_read(&(client_request_ptr -> nx_tftp_client_request_file), new_packet -> nx_packet_prepend_ptr+4,
NX_TFTP_FILE_TRANSFER_MAX, &actual_size);
/* Check for successful file read. */
if ((status_read != NX_SUCCESS) ||
((client_request_ptr -> nx_tftp_client_request_remaining_bytes > NX_TFTP_FILE_TRANSFER_MAX) && (actual_size < NX_TFTP_FILE_TRANSFER_MAX)) ||
((client_request_ptr -> nx_tftp_client_request_remaining_bytes < NX_TFTP_FILE_TRANSFER_MAX) && (actual_size != client_request_ptr -> nx_tftp_client_request_remaining_bytes)))
{
/* Update our 'status' variable with the result from file read. */
status = status_read;
}
}
/* Are we ok to transmit more data? */
if (status != NX_SUCCESS)
{
/* No, send an error back to the client. */
_nx_tftp_server_send_error(server_ptr, client_request_ptr -> nx_tftp_client_request_ip_address,
client_request_ptr -> nx_tftp_client_request_port,
NX_TFTP_ERROR_NOT_DEFINED, "NetX TFTP Server: File Read Error");
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
/* Unable to read the file, close it and release the packet. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
nx_packet_release(new_packet);
return status;
}
}
else
{
/* Clear the exact fit flag since the only way we can be here is if the TFTP transfer size
evenly divided into the file size and we need to send a zero length data buffer to signal
the end of the file. */
client_request_ptr -> nx_tftp_client_request_exact_fit = NX_FALSE;
/* Set the actual size to zero for exact fit case. */
actual_size = 0;
}
/* Increment the number of total bytes sent. */
server_ptr -> nx_tftp_server_total_bytes_sent += actual_size;
/* Is this new data being sent (not a retransmit)? */
if (retransmit == NX_FALSE)
{
/* Yes, so advance the block number and number of bytes of the file sent. */
client_request_ptr -> nx_tftp_client_request_block_number++;
client_request_ptr -> nx_tftp_client_request_remaining_bytes -= actual_size;
}
client_request_ptr -> nx_tftp_client_previous_write_size = actual_size;
/* Move the TFTP data code and block number into the payload before sending it to the client. */
buffer_ptr = new_packet -> nx_packet_prepend_ptr;
*buffer_ptr++ = 0;
*buffer_ptr++ = NX_TFTP_CODE_DATA;
*buffer_ptr++ = (UCHAR) (client_request_ptr -> nx_tftp_client_request_block_number >> 8);
*buffer_ptr++ = (UCHAR) (client_request_ptr -> nx_tftp_client_request_block_number & 0xFF);
/* Setup the packet pointers appropriately. */
new_packet -> nx_packet_length = actual_size + 4;
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr + new_packet -> nx_packet_length;
/* Send the data packet out. */
status = nx_udp_socket_send(&(server_ptr -> nx_tftp_server_socket), new_packet, client_request_ptr -> nx_tftp_client_request_ip_address,
client_request_ptr -> nx_tftp_client_request_port);
/* Release packet if send fails. */
if (status)
{
nx_packet_release(new_packet);
}
return NX_SUCCESS;
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_send_ack PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function creates an ACK packet based on the last block of data */
/* received, and sends it out. This will also retransmit an ACK if */
/* specified. On error it will close the file and the client request. */
/* It does not update the Client request e.g. block number. */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* client_request_ptr Pointer to Client request */
/* retransmit Indicate if retransmiting a */
/* previously sent ACK */
/* */
/* OUTPUT */
/* */
/* status Completion status */
/* */
/* CALLS */
/* */
/* _nx_tftp_server_close_client_request Terminate a client request */
/* nx_packet_allocate Allocate a new packet */
/* nx_udp_socket_send Send TFTP data packet */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_timer_process TFTP timeout event */
/* _nx_tftp_server_data_process Process a received packet */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
UINT _nx_tftp_server_send_ack(NX_TFTP_SERVER *server_ptr, NX_TFTP_CLIENT_REQUEST *client_request_ptr, UINT retransmit)
{
UINT status;
UCHAR *buffer_ptr;
NX_PACKET *new_packet;
/* Allocate packet for the ACK. Determine whether we are sending IP packets. */
status = nx_packet_allocate(server_ptr -> nx_tftp_server_packet_pool_ptr, &new_packet, NX_UDP_PACKET, NX_WAIT_FOREVER);
/* Check for successful packet allocation. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Unable to allocate net packet, release the original. */
fx_file_close(&(client_request_ptr -> nx_tftp_client_request_file));
memset(client_request_ptr, 0, sizeof(NX_TFTP_CLIENT_REQUEST));
return status;
}
if (4u > ((ULONG)(new_packet -> nx_packet_data_end) - (ULONG)(new_packet -> nx_packet_append_ptr)))
{
nx_packet_release(new_packet);
return(NX_SIZE_ERROR);
}
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr;
/* Now build the ACK message to the client. */
buffer_ptr = new_packet -> nx_packet_prepend_ptr;
*buffer_ptr++ = 0;
*buffer_ptr++ = NX_TFTP_CODE_ACK;
/* If we are retransmitting, send the block number of the previous ACK. */
if (retransmit)
{
*buffer_ptr++ = (UCHAR) ((client_request_ptr -> nx_tftp_client_request_block_number - 1) >> 8);
*buffer_ptr++ = (UCHAR) ((client_request_ptr -> nx_tftp_client_request_block_number - 1) & 0xFF);
}
else
{
*buffer_ptr++ = (UCHAR) ((client_request_ptr -> nx_tftp_client_request_block_number) >> 8);
*buffer_ptr++ = (UCHAR) (client_request_ptr -> nx_tftp_client_request_block_number & 0xFF);
}
/* Setup the packet pointers appropriately. */
new_packet -> nx_packet_length = 4;
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr + 4;
/* Send the ACK packet out. */
status = nx_udp_socket_send(&(server_ptr -> nx_tftp_server_socket), new_packet, client_request_ptr -> nx_tftp_client_request_ip_address,
client_request_ptr -> nx_tftp_client_request_port);
/* Release packet if send fails. */
if (status)
{
nx_packet_release(new_packet);
}
/* Is this ACK for new data received (e.g. not a retransmit)? */
if (retransmit == NX_FALSE)
{
/* Yes, so increase the block number. */
client_request_ptr -> nx_tftp_client_request_block_number++;
}
return NX_SUCCESS;
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_error_process PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function processes an error sent by a client. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* packet_ptr Pointer to TFTP request packet*/
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* nx_packet_release Release packet */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_thread_entry TFTP Server thread task */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_error_process(NX_TFTP_SERVER *server_ptr, NX_PACKET *packet_ptr)
{
UINT i;
UCHAR *buffer_ptr;
UINT port;
ULONG ip_address;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
/* Pickup a pointer to the error code in the buffer. */
buffer_ptr = packet_ptr -> nx_packet_prepend_ptr + 2;
if (packet_ptr -> nx_packet_length < 4)
{
nx_packet_release(packet_ptr);
return;
}
/* Set the error code in the server control block. */
server_ptr -> nx_tftp_server_error_code = (((UINT) (*buffer_ptr)) << 8);
buffer_ptr++;
server_ptr -> nx_tftp_server_error_code |= ((UINT) (*buffer_ptr) & 0xFF);
buffer_ptr++;
/* Loop to save error message. */
server_ptr -> nx_tftp_server_error_string[sizeof(server_ptr -> nx_tftp_server_error_string) - 1] = NX_NULL;
for (i = 0; i < NX_TFTP_ERROR_STRING_MAX; i++)
{
/* Store desired file name. */
server_ptr -> nx_tftp_server_error_string[i] = (CHAR) *buffer_ptr++;
/* Check for NULL character. */
if (server_ptr -> nx_tftp_server_error_string[i] == NX_NULL)
break;
/* Check for packet buffer boundary. */
if (buffer_ptr >= packet_ptr -> nx_packet_append_ptr)
break;
}
/* Extract the source IP and port numbers. */
nx_udp_source_extract(packet_ptr, &ip_address, &port);
/* First, try to find a matching existing entry in the client request structure. */
client_request_ptr = _nx_tftp_server_find_client_request(server_ptr, port, ip_address);
/* Reset the retransmit timeout on the client request. */
if (client_request_ptr)
{
#ifdef NX_TFTP_SERVER_RETRANSMIT_ENABLE
client_request_ptr -> nx_tftp_client_retransmit_timeout = NX_TFTP_SERVER_RETRANSMIT_TIMEOUT;
client_request_ptr -> nx_tftp_client_retransmit_retries = 0;
#else
client_request_ptr -> nx_tftp_client_request_retransmits = 0;
#endif /* NX_TFTP_SERVER_RETRANSMIT_ENABLE */
}
/* Release the packet. */
nx_packet_release(packet_ptr);
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_find_client_request PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function attempts to find the specified IP and port number in */
/* the client request array of this TFTP server instance. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* port Client port number */
/* ip_address Client IP address */
/* */
/* OUTPUT */
/* */
/* NX_TFTP_CLIENT_REQUEST * NULL if not found */
/* Non null if match found */
/* */
/* CALLS */
/* */
/* nx_packet_release Release packet */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_ack_process ACK processing */
/* _nx_tftp_server_data_process Data packet processing */
/* _nx_tftp_server_open_for_write_process Open for write processing */
/* _nx_tftp_server_open_for_read_process Open for read processing */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
NX_TFTP_CLIENT_REQUEST * _nx_tftp_server_find_client_request(NX_TFTP_SERVER *server_ptr, UINT port, ULONG ip_address)
{
UINT i;
NX_TFTP_CLIENT_REQUEST *client_request_ptr;
/* First, find a free entry in the client request structure. */
i = 0;
for (i = 0; i < NX_TFTP_MAX_CLIENTS; i++)
{
client_request_ptr = &(server_ptr -> nx_tftp_server_client_list[i]);
/* First check if we are adding an address. If the caller sends in a blank
address and port, we are. */
if (port == 0)
{
/* We are adding an address. Now check if this slot is empty. */
if (client_request_ptr -> nx_tftp_client_request_port == 0)
{
/* If there is no port number, this is an empty slot. But let's clear the slot anyway. */
memset(&(server_ptr -> nx_tftp_server_client_list[i]), 0, sizeof(NX_TFTP_CLIENT_REQUEST));
break;
}
}
/* This is a valid address. Does it match the current entry? */
else
{
if((client_request_ptr -> nx_tftp_client_request_port == port) &&
(client_request_ptr -> nx_tftp_client_request_ip_address == ip_address ))
{
/* Yes, they match. */
break;
}
}
}
/* Determine if a match was found. */
if (i < NX_TFTP_MAX_CLIENTS)
{
/* Return a pointer to the matching client request. */
return(&(server_ptr -> nx_tftp_server_client_list[i]));
}
else
{
/* Return a NULL pointer indicating no match found. */
return((NX_TFTP_CLIENT_REQUEST *)NX_NULL);
}
}
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_tftp_server_send_error PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* Yuxin Zhou, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function send a TFTP error message to the client specified */
/* by the port and IP address. */
/* */
/* */
/* INPUT */
/* */
/* server_ptr Pointer to TFTP server */
/* ip_address Client IP address */
/* port Client port number */
/* error TFTP error code */
/* error_message Error string */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* nx_packet_allocate Allocate error packet */
/* nx_udp_socket_send Send TFPT error packet */
/* */
/* CALLED BY */
/* */
/* _nx_tftp_server_ack_process ACK processing */
/* _nx_tftp_server_data_process Data packet processing */
/* _nx_tftp_server_open_for_write_process Open for write processing */
/* _nx_tftp_server_open_for_read_process Open for read processing */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
/* 09-30-2020 Yuxin Zhou Modified comment(s), */
/* resulting in version 6.1 */
/* */
/**************************************************************************/
VOID _nx_tftp_server_send_error(NX_TFTP_SERVER *server_ptr, ULONG ip_address, UINT port, UINT error, CHAR *error_message)
{
UINT status;
UCHAR *buffer_ptr;
NX_PACKET *new_packet;
/* Allocate packet for the read packet. Determine whether we are sending IP packets. */
status = nx_packet_allocate(server_ptr -> nx_tftp_server_packet_pool_ptr, &new_packet, NX_UDP_PACKET, NX_WAIT_FOREVER);
/* Check for successful packet allocation. */
if (status != NX_SUCCESS)
{
/* Increment the number of server allocation errors. */
server_ptr -> nx_tftp_server_allocation_errors++;
/* Unable to allocate packet for error message, just return! */
return;
}
if (6u > ((ULONG)(new_packet -> nx_packet_data_end) - (ULONG)(new_packet -> nx_packet_append_ptr)))
{
nx_packet_release(new_packet);
return;
}
new_packet -> nx_packet_append_ptr = new_packet -> nx_packet_prepend_ptr;
/* Move the TFTP error code and message into the payload before sending it to the client. */
buffer_ptr = new_packet -> nx_packet_prepend_ptr;
*buffer_ptr++ = 0;
*buffer_ptr++ = NX_TFTP_CODE_ERROR;
*buffer_ptr++ = 0;
*buffer_ptr++ = (UCHAR) (error & 0xFF);
/* Loop to copy the error message into the buffer. */
do
{
/* Copy a byte of the error message into the buffer. */
*buffer_ptr = (UCHAR) *error_message;
/* Determine if a NULL is present. */
if (*buffer_ptr == NX_NULL)
{
/* Yes, we are at the end of the string end the loop. */
break;
}
/* Move both pointers to the next character. */
buffer_ptr++;
error_message++;
} while (buffer_ptr < (new_packet -> nx_packet_data_end - 1));
/* Ensure a NULL is in the last position! */
*buffer_ptr++ = NX_NULL;
/* Setup the packet pointers appropriately. */
new_packet -> nx_packet_length = (ULONG)(buffer_ptr - new_packet -> nx_packet_prepend_ptr);
new_packet -> nx_packet_append_ptr = buffer_ptr;
/* Send the data packet out. */
status = nx_udp_socket_send(&(server_ptr -> nx_tftp_server_socket), new_packet, ip_address, port);
/* Release packet if send fails. */
if (status)
{
nx_packet_release(new_packet);
}
}