/** @file
  Definitions for the TFTP server.

  Copyright (c) 2011, 2012, Intel Corporation
  All rights reserved. This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#ifndef _TFTP_SERVER_H_
#define _TFTP_SERVER_H_

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Uefi.h>

#include <Guid/EventGroup.h>

#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/TimerLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/BlockIo.h>

#include <netinet/in.h>
#include <netinet6/in6.h>

#include <sys/EfiSysCall.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>

//------------------------------------------------------------------------------
//  Macros
//------------------------------------------------------------------------------

#if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
#define DBG_ENTER()             DEBUG (( DEBUG_ENTER_EXIT, "Entering " __FUNCTION__ "\n" )) ///<  Display routine entry
#define DBG_EXIT()              DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ "\n" ))  ///<  Display routine exit
#define DBG_EXIT_DEC(Status)    DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: %d\n", Status ))      ///<  Display routine exit with decimal value
#define DBG_EXIT_HEX(Status)    DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status ))  ///<  Display routine exit with hex value
#define DBG_EXIT_STATUS(Status) DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: %r\n", Status ))      ///<  Display routine exit with status value
#define DBG_EXIT_TF(Status)     DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" ))  ///<  Display routine with TRUE/FALSE value
#else   //  _MSC_VER
#define DBG_ENTER()
#define DBG_EXIT()
#define DBG_EXIT_DEC(Status)
#define DBG_EXIT_HEX(Status)
#define DBG_EXIT_STATUS(Status)
#define DBG_EXIT_TF(Status)
#endif  //  _MSC_VER

#define DIM(x)    ( sizeof ( x ) / sizeof ( x[0] ))   ///<  Compute the number of entries in an array

//------------------------------------------------------------------------------
//  Constants
//------------------------------------------------------------------------------

#define ACK_SHIFT               4           ///<  Number of samples in ACK average

#define DEBUG_WINDOW            0x00000001  ///<  Display the window messages
#define DEBUG_TX_PACKET         0x00000002  ///<  Display the transmit packet messages
#define DEBUG_FILE_BUFFER       0x00000004  ///<  Display the file buffer messages
#define DEBUG_SERVER_TIMER      0x00000008  ///<  Display the socket poll messages
#define DEBUG_TFTP_REQUEST      0x00000010  ///<  Display the TFTP request messages
#define DEBUG_PORT_WORK         0x00000020  ///<  Display the port work messages
#define DEBUG_SOCKET_POLL       0x00000040  ///<  Display the socket poll messages
#define DEBUG_TFTP_PORT         0x00000080  ///<  Display the TFTP port messages
#define DEBUG_TX                0x00000100  ///<  Display transmit messages
#define DEBUG_RX                0x00000200  ///<  Display receive messages
#define DEBUG_TFTP_ACK          0x00000400  ///<  Display the TFTP ACK messages
#define DEBUG_ENTER_EXIT        0x00000800  ///<  Display entry and exit messages

#define MAX_PACKETS             8           ///<  Maximum number of packets in the window

#define TFTP_PORT_POLL_DELAY  ( 2 * 1000 )  ///<  Delay in milliseconds for attempts to open the TFTP port
#define CLIENT_POLL_DELAY     50            ///<  Delay in milliseconds between client polls

#define TPL_TFTP_SERVER        TPL_CALLBACK ///<  TPL for routine synchronization

/**
  Verify new TPL value

  This macro which is enabled when debug is enabled verifies that
  the new TPL value is >= the current TPL value.
**/
#ifdef VERIFY_TPL
#undef VERIFY_TPL
#endif  //  VERIFY_TPL

#if !defined(MDEPKG_NDEBUG)

#define VERIFY_TPL(tpl)                           \
{                                                 \
  EFI_TPL PreviousTpl;                            \
                                                  \
  PreviousTpl = gBS->RaiseTPL ( TPL_HIGH_LEVEL ); \
  gBS->RestoreTPL ( PreviousTpl );                \
  if ( PreviousTpl > tpl ) {                      \
    DEBUG (( DEBUG_ERROR, "Current TPL: %d, New TPL: %d\r\n", PreviousTpl, tpl ));  \
    ASSERT ( PreviousTpl <= tpl );                \
  }                                               \
}

#else   //  MDEPKG_NDEBUG

#define VERIFY_TPL(tpl)

#endif  //  MDEPKG_NDEBUG

#define TFTP_SERVER_SIGNATURE       SIGNATURE_32('T','F','T','P') ///<  TSDT_TFTP_SERVER memory signature

//
//  See: http://www.rfc-editor.org/rfc/pdfrfc/rfc1350.txt.pdf
//
//  TFTP Operations
//

#define TFTP_OP_READ_REQUEST      1     ///<  Read request, zero terminated file name, zero terminated mode
#define TFTP_OP_WRITE_REQUEST     2     ///<  Write request, zero terminated file name, zero terminated mode
#define TFTP_OP_DATA              3     ///<  Data block, end-of-file indicated by short block
#define TFTP_OP_ACK               4     ///<  ACK block number
#define TFTP_OP_ERROR             5     ///<  Error number and explaination
#define TFTP_OP_OACK              6     ///<  ACK the options

#define TFTP_MAX_BLOCK_SIZE       4096  ///<  Maximum block size

#define TFTP_ERROR_SEE_MSG          0   ///<  See the error message
#define TFTP_ERROR_NOT_FOUND        1   ///<  File not found
#define TFTP_ERROR_ACCESS_VIOLATION 2   ///<  Access violation
#define TFTP_ERROR_DISK_FULL        3   ///<  Disk full
#define TFTP_ERROR_ILLEGAL_OP       4   ///<  Illegal operation
#define TFTP_ERROR_UNKNOWN_XFER_ID  5   ///<  Unknown transfer ID
#define TFTP_ERROR_FILE_EXISTS      6   ///<  File already exists
#define TFTP_ERROR_NO_SUCH_USER     7   ///<  No such user

//------------------------------------------------------------------------------
//  Data Types
//------------------------------------------------------------------------------

/**
  Packet structure
**/
typedef struct _TFTP_PACKET TFTP_PACKET;
typedef struct _TFTP_PACKET {
  TFTP_PACKET * pNext;          ///<  Next packet in list
  UINT64    TxTime;             ///<  Time the transmit was performed
  ssize_t   TxBytes;            ///<  Bytes in the TX buffer
  UINT32    RetryCount;         ///<  Number of transmissions
  UINT16    BlockNumber;        ///<  Block number of this packet
  UINT8     TxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ];  ///<  Transmit buffer
} GCC_TFTP_PACKET;

/**
  Port control structure
**/
typedef struct _TSDT_CONNECTION_CONTEXT TSDT_CONNECTION_CONTEXT;
typedef struct _TSDT_CONNECTION_CONTEXT {
  //
  //  Remote connection management
  //
  TSDT_CONNECTION_CONTEXT * pNext;    ///<  Next context in the connection list
  struct sockaddr_in6 RemoteAddress;  ///<  Remote address
  int SocketFd;                       ///<  Socket file descriptor

  //
  //  File management parameters
  //
  FILE * File;                  ///<  NULL while file is closed
  UINT64 LengthInBytes;         ///<  Size of the file
  UINT64 BytesRemaining;        ///<  Number of bytes remaining to be sent
  UINT64 BytesToSend;           ///<  Number of bytes to send
  UINT64 ValidBytes;            ///<  Number of valid bytes in the buffer
  BOOLEAN bEofSent;             ///<  End of file sent
  UINT8 * pFill;                ///<  Next portion of the buffer to fill
  UINT8 * pBuffer;              ///<  Pointer into the file data
  UINT8 * pEnd;                 ///<  End of the file data
  UINT8 FileData[ 2 * MAX_PACKETS * TFTP_MAX_BLOCK_SIZE ];  ///<  File data to send
  UINT64 TimeStart;             ///<  Start of file transfer

  //
  //  TFTP management parameters
  //
  UINT16 BlockNumber;           ///<  Next block to be transmitted
  UINT32 BlockSize;             ///<  Negotiated block size

  //
  //  Window management
  //
  UINT32 AckCount;              ///<  Number of ACKs to receive before increasing the window
  UINT32 PacketsInWindow;       ///<  Number of packets in the window
  UINT32 Threshold;             ///<  Size of window when ACK count becomes logrithmic
  UINT32 WindowSize;            ///<  Size of the transmit window
  UINT64 MaxTimeout;            ///<  Maximum number of seconds to wait before retransmission
  UINT64 Rtt2x;                 ///<  Twice the average round trip time in nanoseconds

  //
  //  Buffer management
  //
  TFTP_PACKET * pFreeList;      ///<  List of free packets
  TFTP_PACKET * pTxHead;        ///<  First packet in the list of packets for transmission
  TFTP_PACKET * pTxTail;        ///<  Last packet in the list of packets for transmission
  TFTP_PACKET ErrorPacket;      ///<  Error packet
  TFTP_PACKET Tx[ MAX_PACKETS ];///<  Transmit packets
}GCC_TSDT_CONNECTION_CONTEXT;

/**
  TFTP server control structure
**/
typedef struct {
  UINTN Signature;              ///<  Structure identification

  //
  //  Image attributes
  //
  EFI_HANDLE ImageHandle;       ///<  Image handle

  //
  //  Performance management
  //
  UINT64 ClockFrequency;        ///<  Frequency of the clock
  UINT64 Time1;                 ///<  Clock value after rollover
  UINT64 Time2;                 ///<  Clock value before rollover
  UINT64 RxTime;                ///<  Time when the packet was recevied

  //
  //  TFTP port management
  //
  EFI_EVENT TimerEvent;         ///<  Timer to open TFTP port
  int Udpv4Index;               ///<  Entry for UDPv4
  int Udpv6Index;               ///<  Entry for UDPv6
  int Entries;                  ///<  Number of TFTP ports
  struct pollfd TftpPort [ 2 ]; ///<  Poll descriptor for the TFTP ports (UDP4, UDP6)

  //
  //  Request management
  //
  union {
    struct sockaddr_in v4;      ///<  UDP4 address
    struct sockaddr_in6 v6;     ///<  UDP6 address
  } RemoteAddress;              ///<  Remote address
  ssize_t RxBytes;              ///<  Receive data length in bytes
  UINT8 RxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ];  ///<  Receive buffer

  //
  //  Client port management
  //
  TSDT_CONNECTION_CONTEXT * pContextList; ///<  List of connection context structures
} TSDT_TFTP_SERVER;

//#define SERVER_FROM_SERVICE(a) CR(a, TSDT_TFTP_SERVER, ServiceBinding, TFTP_SERVER_SIGNATURE) ///< Locate DT_LAYER from service binding

extern TSDT_TFTP_SERVER mTftpServer;

//------------------------------------------------------------------------------
// Support routines
//------------------------------------------------------------------------------

/**
  Queue data packets for transmission

  @param [in] pContext    Connection context structure address

  @retval TRUE if a read error occurred

**/
BOOLEAN
PacketFill (
  IN TSDT_CONNECTION_CONTEXT * pContext
  );

/**
  Free the packet

  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
  @param [in] pPacket     Address of a ::TFTP_PACKET structure

**/
VOID
PacketFree(
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN TFTP_PACKET * pPacket
  );

/**
  Get a packet for transmission

  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure

  @retval Address of a ::TFTP_PACKET structure

**/
TFTP_PACKET *
PacketGet (
  IN TSDT_CONNECTION_CONTEXT * pContext
  );

/**
  Queue the packet for transmission

  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
  @param [in] pPacket     Address of a ::TFTP_PACKET structure

  @retval TRUE if a transmission error has occurred

**/
BOOLEAN
PacketQueue (
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN TFTP_PACKET * pPacket
  );

/**
  Transmit the packet

  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
  @param [in] pPacket     Address of a ::TFTP_PACKET structure

  @retval EFI_SUCCESS   Message processed successfully

**/
EFI_STATUS
PacketTx (
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN TFTP_PACKET * pPacket
  );

/**
  Build and send an error packet

  @param [in] pContext    The context structure address.
  @param [in] Error       Error number for the packet
  @param [in] pError      Zero terminated error string address

  @retval EFI_SUCCESS     Message processed successfully

**/
EFI_STATUS
SendError (
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN UINT16 Error,
  IN UINT8 * pError
  );

/**
  Process the TFTP request

  @param [in] pOption   Address of the first zero terminated option string
  @param [in] pValue    Address to receive the value

  @retval EFI_SUCCESS   Option translated into a value

**/
EFI_STATUS
TftpOptionValue (
  IN UINT8 * pOption,
  IN INT32 * pValue
  );

/**
  Process the TFTP request

  @param [in] pTftpServer The TFTP server control structure address.
  @param [in] pContext    Connection context structure address
  @param [in] SocketFd    Socket file descriptor

**/
VOID
TftpProcessRequest (
  IN TSDT_TFTP_SERVER * pTftpServer,
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN int SocketFd
  );

/**
  Process the read request

  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
  @param [in] pContext    Connection context structure address
  @param [in] SocketFd    Socket file descriptor

  @retval TRUE if the context should be closed

**/
BOOLEAN
TftpRead (
  IN TSDT_TFTP_SERVER * pTftpServer,
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN int SocketFd
  );

/**
  Update the window due to the ACK

  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
  @param [in] pPacket     Address of a ::TFTP_PACKET structure

**/
VOID
WindowAck (
  IN TSDT_TFTP_SERVER * pTftpServer,
  IN TSDT_CONNECTION_CONTEXT * pContext,
  IN TFTP_PACKET * pPacket
  );

/**
  A timeout has occurred, close the window

  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure

**/
VOID
WindowTimeout (
  IN TSDT_CONNECTION_CONTEXT * pContext
  );

//------------------------------------------------------------------------------

#endif  //  _TFTP_SERVER_H_
