/** @file Routines to process MTFTP4 options. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Mtftp4Impl.h" CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = { "blksize", "windowsize", "timeout", "tsize", "multicast" }; /** Check whether two ascii strings are equal, ignore the case. @param Str1 The first ascii string @param Str2 The second ascii string @retval TRUE Two strings are equal when case is ignored. @retval FALSE Two strings are not equal. **/ BOOLEAN NetStringEqualNoCase ( IN UINT8 *Str1, IN UINT8 *Str2 ) { UINT8 Ch1; UINT8 Ch2; ASSERT ((Str1 != NULL) && (Str2 != NULL)); for ( ; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) { Ch1 = *Str1; Ch2 = *Str2; // // Convert them to lower case then compare two // if (('A' <= Ch1) && (Ch1 <= 'Z')) { Ch1 += 'a' - 'A'; } if (('A' <= Ch2) && (Ch2 <= 'Z')) { Ch2 += 'a' - 'A'; } if (Ch1 != Ch2) { return FALSE; } } return (BOOLEAN)(*Str1 == *Str2); } /** Convert a string to a UINT32 number. @param Str The string to convert from @return The number get from the string **/ UINT32 NetStringToU32 ( IN UINT8 *Str ) { UINT32 Num; ASSERT (Str != NULL); Num = 0; for ( ; NET_IS_DIGIT (*Str); Str++) { Num = Num * 10 + (*Str - '0'); } return Num; } /** Convert a string of the format "192.168.0.1" to an IP address. @param Str The string representation of IP @param Ip The variable to get IP. @retval EFI_INVALID_PARAMETER The IP string is invalid. @retval EFI_SUCCESS The IP is parsed into the Ip **/ EFI_STATUS NetStringToIp ( IN UINT8 *Str, OUT IP4_ADDR *Ip ) { UINT32 Byte; UINT32 Addr; UINTN Index; *Ip = 0; Addr = 0; for (Index = 0; Index < 4; Index++) { if (!NET_IS_DIGIT (*Str)) { return EFI_INVALID_PARAMETER; } Byte = NetStringToU32 (Str); if (Byte > 255) { return EFI_INVALID_PARAMETER; } Addr = (Addr << 8) | Byte; // // Skip all the digitals and check whether the separator is the dot // while (NET_IS_DIGIT (*Str)) { Str++; } if ((Index < 3) && (*Str != '.')) { return EFI_INVALID_PARAMETER; } Str++; } *Ip = Addr; return EFI_SUCCESS; } /** Go through the packet to fill the Options array with the start addresses of each MTFTP option name/value pair. @param Packet The packet to check @param PacketLen The packet's length @param Count The size of the Options on input. The actual options on output @param Options The option array to fill in @retval EFI_INVALID_PARAMETER The packet is malformatted @retval EFI_BUFFER_TOO_SMALL The Options array is too small @retval EFI_SUCCESS The packet has been parsed into the Options array. **/ EFI_STATUS Mtftp4FillOptions ( IN EFI_MTFTP4_PACKET *Packet, IN UINT32 PacketLen, IN OUT UINT32 *Count, OUT EFI_MTFTP4_OPTION *Options OPTIONAL ) { UINT8 *Cur; UINT8 *Last; UINT8 Num; UINT8 *Name; UINT8 *Value; Num = 0; Cur = (UINT8 *)Packet + MTFTP4_OPCODE_LEN; Last = (UINT8 *)Packet + PacketLen - 1; // // process option name and value pairs. The last byte is always zero // while (Cur < Last) { Name = Cur; while (*Cur != 0) { Cur++; } if (Cur == Last) { return EFI_INVALID_PARAMETER; } Value = ++Cur; while (*Cur != 0) { Cur++; } Num++; if ((Options != NULL) && (Num <= *Count)) { Options[Num - 1].OptionStr = Name; Options[Num - 1].ValueStr = Value; } Cur++; } if ((*Count < Num) || (Options == NULL)) { *Count = Num; return EFI_BUFFER_TOO_SMALL; } *Count = Num; return EFI_SUCCESS; } /** Allocate and fill in a array of Mtftp options from the Packet. It first calls Mtftp4FillOption to get the option number, then allocate the array, at last, call Mtftp4FillOption again to save the options. @param Packet The packet to parse @param PacketLen The length of the packet @param OptionCount The number of options in the packet @param OptionList The point to get the option array. @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a well-formatted OACK packet. @retval EFI_SUCCESS The option array is build @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array **/ EFI_STATUS Mtftp4ExtractOptions ( IN EFI_MTFTP4_PACKET *Packet, IN UINT32 PacketLen, OUT UINT32 *OptionCount, OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL ) { EFI_STATUS Status; *OptionCount = 0; if (OptionList != NULL) { *OptionList = NULL; } if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) { return EFI_INVALID_PARAMETER; } if (PacketLen == MTFTP4_OPCODE_LEN) { return EFI_SUCCESS; } // // The last byte must be zero to terminate the options // if (*((UINT8 *)Packet + PacketLen - 1) != 0) { return EFI_INVALID_PARAMETER; } // // Get the number of options // Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL); if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) { return Status; } // // Allocate memory for the options, then call Mtftp4FillOptions to // fill it if caller want that. // if (OptionList == NULL) { return EFI_SUCCESS; } *OptionList = AllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION)); if (*OptionList == NULL) { return EFI_OUT_OF_RESOURCES; } Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList); return EFI_SUCCESS; } /** Parse the MTFTP multicast option. @param Value The Mtftp multicast value string @param Option The option to save the info into. @retval EFI_INVALID_PARAMETER The multicast value string is invalid. @retval EFI_SUCCESS The multicast value is parsed into the Option **/ EFI_STATUS Mtftp4ExtractMcast ( IN UINT8 *Value, IN OUT MTFTP4_OPTION *Option ) { EFI_STATUS Status; UINT32 Num; // // The multicast option is formatted like "204.0.0.1,1857,1" // The server can also omit the ip and port, use ",,1" // if (*Value == ',') { Option->McastIp = 0; } else { Status = NetStringToIp (Value, &Option->McastIp); if (EFI_ERROR (Status)) { return Status; } while ((*Value != 0) && (*Value != ',')) { Value++; } } if (*Value != ',') { return EFI_INVALID_PARAMETER; } Value++; // // Convert the port setting. the server can send us a port number or // empty string. such as the port in ",,1" // if (*Value == ',') { Option->McastPort = 0; } else { Num = NetStringToU32 (Value); if (Num > 65535) { return EFI_INVALID_PARAMETER; } Option->McastPort = (UINT16)Num; while (NET_IS_DIGIT (*Value)) { Value++; } } if (*Value != ',') { return EFI_INVALID_PARAMETER; } Value++; // // Check the master/slave setting, 1 for master, 0 for slave. // Num = NetStringToU32 (Value); if ((Num != 0) && (Num != 1)) { return EFI_INVALID_PARAMETER; } Option->Master = (BOOLEAN)(Num == 1); while (NET_IS_DIGIT (*Value)) { Value++; } if (*Value != '\0') { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Parse the option in Options array to MTFTP4_OPTION which program can access directly. @param Options The option array, which contains addresses of each option's name/value string. @param Count The number of options in the Options @param Request Whether this is a request or OACK. The format of multicast is different according to this setting. @param Operation The current performed operation. @param MtftpOption The MTFTP4_OPTION for easy access. @retval EFI_INVALID_PARAMETER The option is malformatted @retval EFI_UNSUPPORTED Some option isn't supported @retval EFI_SUCCESS The option are OK and has been parsed. **/ EFI_STATUS Mtftp4ParseOption ( IN EFI_MTFTP4_OPTION *Options, IN UINT32 Count, IN BOOLEAN Request, IN UINT16 Operation, OUT MTFTP4_OPTION *MtftpOption ) { EFI_STATUS Status; UINT32 Index; UINT32 Value; EFI_MTFTP4_OPTION *This; MtftpOption->Exist = 0; for (Index = 0; Index < Count; Index++) { This = Options + Index; if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) { return EFI_INVALID_PARAMETER; } if (NetStringEqualNoCase (This->OptionStr, (UINT8 *)"blksize")) { // // block size option, valid value is between [8, 65464] // Value = NetStringToU32 (This->ValueStr); if ((Value < 8) || (Value > 65464)) { return EFI_INVALID_PARAMETER; } MtftpOption->BlkSize = (UINT16)Value; MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST; } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *)"timeout")) { // // timeout option, valid value is between [1, 255] // Value = NetStringToU32 (This->ValueStr); if ((Value < 1) || (Value > 255)) { return EFI_INVALID_PARAMETER; } MtftpOption->Timeout = (UINT8)Value; } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *)"tsize")) { // // tsize option, the biggest transfer supported is 4GB with block size option // MtftpOption->Tsize = NetStringToU32 (This->ValueStr); MtftpOption->Exist |= MTFTP4_TSIZE_EXIST; } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *)"multicast")) { // // Multicast option, if it is a request, the value must be a zero // length string, otherwise, it is formatted like "204.0.0.1,1857,1\0" // if (Request) { if (*(This->ValueStr) != '\0') { return EFI_INVALID_PARAMETER; } } else { Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption); if (EFI_ERROR (Status)) { return Status; } } MtftpOption->Exist |= MTFTP4_MCAST_EXIST; } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *)"windowsize")) { if (Operation == EFI_MTFTP4_OPCODE_WRQ) { // // Currently, windowsize is not supported in the write operation. // return EFI_UNSUPPORTED; } Value = NetStringToU32 (This->ValueStr); if (Value < 1) { return EFI_INVALID_PARAMETER; } MtftpOption->WindowSize = (UINT16)Value; MtftpOption->Exist |= MTFTP4_WINDOWSIZE_EXIST; } else if (Request) { // // Ignore the unsupported option if it is a reply, and return // EFI_UNSUPPORTED if it's a request according to the UEFI spec. // return EFI_UNSUPPORTED; } } return EFI_SUCCESS; } /** Parse the options in the OACK packet to MTFTP4_OPTION which program can access directly. @param Packet The OACK packet to parse @param PacketLen The length of the packet @param Operation The current performed operation. @param MtftpOption The MTFTP_OPTION for easy access. @retval EFI_INVALID_PARAMETER The packet option is malformatted @retval EFI_UNSUPPORTED Some option isn't supported @retval EFI_SUCCESS The option are OK and has been parsed. **/ EFI_STATUS Mtftp4ParseOptionOack ( IN EFI_MTFTP4_PACKET *Packet, IN UINT32 PacketLen, IN UINT16 Operation, OUT MTFTP4_OPTION *MtftpOption ) { EFI_MTFTP4_OPTION *OptionList; EFI_STATUS Status; UINT32 Count; MtftpOption->Exist = 0; Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList); if (EFI_ERROR (Status) || (Count == 0)) { return Status; } ASSERT (OptionList != NULL); Status = Mtftp4ParseOption (OptionList, Count, FALSE, Operation, MtftpOption); FreePool (OptionList); return Status; }