| /** | |
| @file | |
| HTTP processing for the web 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. | |
| **/ | |
| #include <WebServer.h> | |
| /** | |
| Get a UTF-8 character from the buffer | |
| @param [in] pData The address of the buffer containing the character | |
| @param [out] ppData The address to receive the next character address | |
| @return The character value | |
| **/ | |
| INTN | |
| HttpCharGet ( | |
| IN UINT8 * pData, | |
| IN UINT8 ** ppData | |
| ) | |
| { | |
| INTN Data; | |
| INTN Character; | |
| INTN Control; | |
| INTN Mask; | |
| // | |
| // Verify that there is some data left | |
| // | |
| if ( NULL == pData ) { | |
| // | |
| // No data to return | |
| // | |
| pData = NULL; | |
| Character = 0; | |
| } | |
| else { | |
| // | |
| // Get the first portion of the character | |
| // | |
| Character = *pData++; | |
| Control = Character; | |
| Mask = 0xc0; | |
| // | |
| // Append the rest of the character | |
| // | |
| if ( 0 != ( Control & 0x80 )) { | |
| while ( 0 != ( Control & 0x40 )) { | |
| Character &= Mask; | |
| Mask <<= 5; | |
| Control <<= 1; | |
| Character <<= 6; | |
| Data = *pData++ & 0x3f; | |
| if ( 0x80 != ( Data & 0xc0 )) { | |
| // | |
| // Invalid character | |
| // | |
| pData = NULL; | |
| Character = 0; | |
| break; | |
| } | |
| Character |= Data & 0x3f; | |
| } | |
| } | |
| } | |
| // | |
| // Return the next character location and the character | |
| // | |
| *ppData = pData; | |
| return Character; | |
| } | |
| /** | |
| Transmit a portion of the HTTP response | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpFlush ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort | |
| ) | |
| { | |
| INTN LengthInBytes; | |
| UINT8 * pBuffer; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| pBuffer = &pPort->TxBuffer[0]; | |
| do { | |
| // | |
| // Attempt to send the data | |
| // | |
| LengthInBytes = send ( SocketFD, | |
| pBuffer, | |
| pPort->TxBytes, | |
| 0 ); | |
| if ( -1 != LengthInBytes ) { | |
| // | |
| // Account for the data sent | |
| // | |
| pBuffer += LengthInBytes; | |
| pPort->TxBytes -= LengthInBytes; | |
| } | |
| else { | |
| // | |
| // Transmit error | |
| // | |
| Status = EFI_DEVICE_ERROR; | |
| break; | |
| } | |
| } while ( 0 < pPort->TxBytes ); | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Convert the ANSI character to lower case | |
| @param [in] Character The character to convert to lower case. | |
| @return The lower case character | |
| **/ | |
| INTN | |
| HttpLowerCase ( | |
| IN INTN Character | |
| ) | |
| { | |
| // | |
| // Determine if the character is upper case | |
| // | |
| if (( 'A' <= Character ) && ( 'Z' >= Character )) { | |
| Character += 'a' - 'A'; | |
| } | |
| // | |
| // Return the lower case value of the character | |
| // | |
| return Character; | |
| } | |
| /** | |
| Match a Unicode string against a UTF-8 string | |
| @param [in] pString A zero terminated Unicode string | |
| @param [in] pData A zero terminated UTF-8 string | |
| @param [in] bIgnoreCase TRUE if case is to be ignored | |
| @return The difference between the last two characters tested. | |
| Returns -1 for error. | |
| **/ | |
| INTN | |
| HttpMatch ( | |
| IN UINT16 * pString, | |
| IN UINT8 * pData, | |
| IN BOOLEAN bIgnoreCase | |
| ) | |
| { | |
| INTN Character1; | |
| INTN Character2; | |
| INTN Difference; | |
| do { | |
| // | |
| // Get the character from the comparison string | |
| // | |
| Character1 = *pString++; | |
| // | |
| // Convert the character to lower case | |
| // | |
| if ( bIgnoreCase ) { | |
| Character1 = HttpLowerCase ( Character1 ); | |
| } | |
| // | |
| // Get the character from the request | |
| // | |
| Character2 = HttpCharGet ( pData, &pData ); | |
| if ( NULL == pData ) { | |
| // | |
| // Error getting character | |
| // | |
| Difference = -1; | |
| break; | |
| } | |
| // | |
| // Convert the character to lower case | |
| // | |
| if ( bIgnoreCase ) { | |
| Character2 = HttpLowerCase ( Character2 ); | |
| } | |
| // | |
| // Compare the characters | |
| // | |
| Difference = Character1 - Character2; | |
| if ( 0 != Difference ) { | |
| return Difference; | |
| } | |
| } while ( 0 != Character1 ); | |
| // | |
| // Return the difference | |
| // | |
| return Difference; | |
| } | |
| /** | |
| Buffer the HTTP page header | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] pTitle A zero terminated Unicode title string | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpPageHeader ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN CONST CHAR16 * pTitle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Build the page header | |
| // | |
| for ( ; ; ) { | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "<!DOCTYPE " | |
| "HTML " | |
| "PUBLIC " | |
| "\"-//W3C//DTD HTML 4.01 Transitional//EN\" " | |
| "\"http://www.w3.org/TR/html4/loose.dtd\">\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, "<html lang=\"en-US\">\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| if ( NULL != pTitle ) { | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " <head>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " <title>" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendUnicodeString ( SocketFD, pPort, pTitle ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, "</title>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " </head>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " <body>\r\n" ); | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Respond with an error indicating that the page was not found | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [out] pbDone Address to receive the request completion status | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpPageNotFound ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN BOOLEAN * pbDone | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Send the page not found | |
| // | |
| for ( ; ; ) { | |
| // | |
| // Send the page header | |
| // | |
| Status = HttpPageHeader ( SocketFD, pPort, L"404 Not found" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the page body | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "ERROR <b>404</b><br />" | |
| "Requested page is not available\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the page trailer | |
| // | |
| Status = HttpPageTrailer ( SocketFD, pPort, pbDone ); | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Buffer and send the HTTP page trailer | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [out] pbDone Address to receive the request completion status | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpPageTrailer ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN BOOLEAN * pbDone | |
| ) | |
| { | |
| int RetVal; | |
| EFI_STATUS Status; | |
| socklen_t LengthInBytes; | |
| struct sockaddr_in6 LocalAddress; | |
| struct sockaddr_in6 RemoteAddress; | |
| DBG_ENTER ( ); | |
| // | |
| // Build the page header | |
| // | |
| for ( ; ; ) { | |
| LengthInBytes = sizeof ( LocalAddress ); | |
| RetVal = getsockname ( SocketFD, (struct sockaddr *)&LocalAddress, &LengthInBytes ); | |
| if ( 0 == RetVal ) { | |
| LengthInBytes = sizeof ( LocalAddress ); | |
| RetVal = getpeername ( SocketFD, (struct sockaddr *)&RemoteAddress, &LengthInBytes ); | |
| if ( 0 == RetVal ) { | |
| // | |
| // Seperate the body from the trailer | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " <hr>\r\n<code>" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the system addresses and the page transfer direction | |
| // | |
| Status = HttpSendIpAddress ( SocketFD, pPort, &LocalAddress ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " --> " ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendIpAddress ( SocketFD, pPort, &RemoteAddress ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, "</code>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| } | |
| // | |
| // Terminate the page | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " </body>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendAnsiString ( SocketFD, pPort, " </html>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the page trailer | |
| // | |
| Status = HttpFlush ( SocketFD, pPort ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Mark the page as complete | |
| // | |
| *pbDone = TRUE; | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Replace a space with a zero | |
| @param [in] pData The request buffer address | |
| @param [in] pEnd End of buffer address | |
| @return The next character location | |
| **/ | |
| UINT8 * | |
| HttpReplaceSpace ( | |
| IN UINT8 * pData, | |
| IN UINT8 * pEnd | |
| ) | |
| { | |
| INTN Character; | |
| UINT8 * pSpace; | |
| pSpace = pData; | |
| while ( pEnd > pData ) { | |
| // | |
| // Get the character from the request | |
| // | |
| Character = HttpCharGet ( pData, &pData ); | |
| if ( ' ' == Character ) { | |
| break; | |
| } | |
| pSpace = pData; | |
| } | |
| // | |
| // Replace the space character with zero | |
| // | |
| ZeroMem ( pSpace, pData - pSpace ); | |
| // | |
| // Return the next character location | |
| // | |
| return pData; | |
| } | |
| /** | |
| Process an HTTP request | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [out] pbDone Address to receive the request completion status | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpRequest ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| OUT BOOLEAN * pbDone | |
| ) | |
| { | |
| UINT8 * pData; | |
| UINT8 * pEnd; | |
| CONST DT_PAGE * pPage; | |
| CONST DT_PAGE * pPageEnd; | |
| UINT8 * pVerb; | |
| UINT8 * pVersion; | |
| UINT8 * pWebPage; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Assume the request is not finished | |
| // | |
| *pbDone = FALSE; | |
| Status = EFI_SUCCESS; | |
| for ( ; ; ) { | |
| // | |
| // Attempt to parse the command | |
| // | |
| pData = &pPort->Request[0]; | |
| pEnd = &pData[ pPort->RequestLength ]; | |
| pVerb = pData; | |
| pWebPage = HttpReplaceSpace ( pVerb, pEnd ); | |
| if ( pEnd <= pWebPage ) { | |
| break; | |
| } | |
| pVersion = HttpReplaceSpace ( pWebPage, pEnd ); | |
| if ( pEnd <= pVersion ) { | |
| break; | |
| } | |
| // | |
| // Validate the request | |
| // | |
| if ( 0 != HttpMatch ( L"GET", pVerb, TRUE )) { | |
| // | |
| // Invalid request type | |
| // | |
| DEBUG (( DEBUG_REQUEST, | |
| "HTTP: Invalid verb\r\n" )); | |
| Status = EFI_NOT_FOUND; | |
| break; | |
| } | |
| // | |
| // Walk the page table | |
| // | |
| pPage = &mPageList[0]; | |
| pPageEnd = &pPage[ mPageCount ]; | |
| while ( pPageEnd > pPage ) { | |
| // | |
| // Determine if the page was located | |
| // | |
| if ( 0 == HttpMatch ( pPage->pPageName, pWebPage, FALSE )) { | |
| break; | |
| } | |
| // | |
| // Set the next page | |
| // | |
| pPage += 1; | |
| } | |
| if ( pPageEnd <= pPage ) { | |
| // | |
| // The page was not found | |
| // | |
| DEBUG (( DEBUG_REQUEST, | |
| "HTTP: Page not found in page table\r\n" )); | |
| Status = EFI_NOT_FOUND; | |
| break; | |
| } | |
| // | |
| // Respond with the page contents | |
| // | |
| Status = pPage->pfnResponse ( SocketFD, pPort, pbDone ); | |
| break; | |
| } | |
| // | |
| // Return page not found if necessary | |
| // | |
| if ( EFI_NOT_FOUND == Status ) { | |
| Status = HttpPageNotFound ( SocketFD, pPort, pbDone ); | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Buffer data for sending | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] LengthInBytes Length of valid data in the buffer | |
| @param [in] pBuffer Buffer of data to send | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSend ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN size_t LengthInBytes, | |
| IN CONST UINT8 * pBuffer | |
| ) | |
| { | |
| size_t DataBytes; | |
| size_t MaxBytes; | |
| EFI_STATUS Status; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| do { | |
| // | |
| // Determine how much data fits into the buffer | |
| // | |
| MaxBytes = sizeof ( pPort->TxBuffer ); | |
| DataBytes = MaxBytes - pPort->TxBytes; | |
| if ( DataBytes > LengthInBytes ) { | |
| DataBytes = LengthInBytes; | |
| } | |
| // | |
| // Copy the data into the buffer | |
| // | |
| CopyMem ( &pPort->TxBuffer[ pPort->TxBytes ], | |
| pBuffer, | |
| DataBytes ); | |
| // | |
| // Account for the data copied | |
| // | |
| pPort->TxBytes += DataBytes; | |
| LengthInBytes -= DataBytes; | |
| // | |
| // Transmit the buffer if it is full | |
| // | |
| if ( MaxBytes <= pPort->TxBytes ) { | |
| Status = HttpFlush ( SocketFD, pPort ); | |
| } | |
| } while (( EFI_SUCCESS == Status ) && ( 0 < LengthInBytes )); | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Send an ANSI string | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] pString A zero terminated Unicode string | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendAnsiString ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN CONST char * pString | |
| ) | |
| { | |
| CONST char * pData; | |
| EFI_STATUS Status; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Walk the characters in he string | |
| // | |
| pData = pString; | |
| while ( 0 != *pData ) { | |
| pData += 1; | |
| } | |
| // | |
| // Send the string | |
| // | |
| Status = HttpSend ( SocketFD, | |
| pPort, | |
| pData - pString, | |
| (CONST UINT8 *)pString ); | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Buffer a single byte | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Data The data byte to send | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendByte ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN UINT8 Data | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Send the data byte | |
| // | |
| Status = HttpSend ( SocketFD, | |
| pPort, | |
| 1, | |
| &Data ); | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Display a character | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Character Character to display | |
| @param [in] pReplacement Replacement character string | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendCharacter ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN CHAR8 Character, | |
| IN CHAR8 * pReplacement | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Determine if this is a printable character | |
| // | |
| if (( 0x20 <= Character ) && ( 0x7f > Character )) { | |
| if ( '<' == Character ) { | |
| // | |
| // Replace with HTML equivalent | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "<" ); | |
| } | |
| else if ( '>' == Character ) { | |
| // | |
| // Replace with HTML equivalent | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| ">" ); | |
| } | |
| else if ( '&' == Character ) { | |
| // | |
| // Replace with HTML equivalent | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "&" ); | |
| } | |
| else if ( '\"' == Character ) { | |
| // | |
| // Replace with HTML equivalent | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| """ ); | |
| } | |
| else { | |
| // | |
| // Display the character | |
| // | |
| Status = HttpSendByte ( SocketFD, | |
| pPort, | |
| Character ); | |
| } | |
| } | |
| else { | |
| // | |
| // Not a displayable character | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| pReplacement ); | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Send a buffer dump | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] ByteCount The number of bytes to display | |
| @param [in] pData Address of the byte array | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendDump ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN UINTN ByteCount, | |
| IN CONST UINT8 * pData | |
| ) | |
| { | |
| INTN BytesToDisplay; | |
| UINT8 Character; | |
| INTN Index; | |
| INTN InitialSpaces; | |
| CONST UINT8 * pDataEnd; | |
| CONST UINT8 * pEnd; | |
| CONST UINT8 * pTemp; | |
| EFI_STATUS Status; | |
| // | |
| // Use for/break instead of goto | |
| // | |
| for ( ; ; ) { | |
| // | |
| // Start the field value | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "<code>" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Walk the bytes to be displayed | |
| // | |
| pEnd = &pData[ ByteCount ]; | |
| while ( pEnd > pData ) { | |
| // | |
| // Display the address | |
| // | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| sizeof ( pData ) * 8, | |
| (UINT64)(UINTN)pData ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Separate the address and data | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, ':' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Position the starting data correctly | |
| // | |
| InitialSpaces = (UINTN)pData; | |
| InitialSpaces &= BYTES_ON_A_LINE - 1; | |
| for ( Index = SPACES_ADDRESS_TO_DATA | |
| + (( 2 + SPACES_BETWEEN_BYTES ) | |
| * InitialSpaces ); | |
| 0 < Index; Index-- ) { | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| " " ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the data | |
| // | |
| BytesToDisplay = pEnd - pData; | |
| if (( BYTES_ON_A_LINE - InitialSpaces ) < BytesToDisplay ) { | |
| BytesToDisplay = BYTES_ON_A_LINE - InitialSpaces; | |
| } | |
| pDataEnd = &pData[ BytesToDisplay ]; | |
| pTemp = pData; | |
| while ( pDataEnd > pTemp ) { | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| 8, | |
| *pTemp++ ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Separate the data bytes | |
| // | |
| for ( Index = SPACES_BETWEEN_BYTES; 0 < Index; Index-- ) { | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| " " ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Separate the data from the ASCII display | |
| // | |
| for ( Index = (( 2 + SPACES_BETWEEN_BYTES ) | |
| * ( BYTES_ON_A_LINE - BytesToDisplay - InitialSpaces )) | |
| - SPACES_BETWEEN_BYTES | |
| + SPACES_DATA_TO_ASCII | |
| + InitialSpaces; | |
| 0 < Index; Index-- ) { | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| " " ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the ASCII data | |
| // | |
| while ( pDataEnd > pData ) { | |
| Character = *pData++; | |
| Status = HttpSendCharacter ( SocketFD, | |
| pPort, | |
| Character, | |
| "." ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Terminate the line | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "<br/>\r\n" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| // | |
| // Terminate the field value and row | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "</code>\r\n" ); | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Display a row containing a GUID value | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] pGuid Address of the GUID to display | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendGuid ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN CONST EFI_GUID * pGuid | |
| ) | |
| { | |
| UINT32 Index; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Use for/break instead of goto | |
| // | |
| for ( ; ; ) { | |
| // | |
| // Display the GUID in a form found in the code | |
| // | |
| // E.g. 0xca16005f, 0x11ec, 0x4bdc, { 0x99, 0x97, 0x27, 0x2c, 0xa9, 0xba, 0x15, 0xe5 } | |
| // | |
| // | |
| // Display the first 32 bits | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| "0x" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| 32, | |
| pGuid->Data1 ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the second 16 bits | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| ", 0x" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| 16, | |
| pGuid->Data2 ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the thrid 16 bits | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| ", 0x" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| 16, | |
| pGuid->Data3 ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Place the last 64 bits in braces | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| ", { 0x" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| for ( Index = 0; 7 >= Index; Index++ ) { | |
| // | |
| // Display the next 8 bits | |
| // | |
| Status = HttpSendHexBits ( SocketFD, | |
| pPort, | |
| 8, | |
| pGuid->Data4[ Index ]); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Separate the bytes | |
| // | |
| Status = HttpSendAnsiString ( SocketFD, | |
| pPort, | |
| ( 7 != Index ) ? ", 0x" : " }" ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Output a hex value to the HTML page | |
| @param [in] SocketFD Socket file descriptor | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Bits Number of bits to display | |
| @param [in] Value Value to display | |
| @retval EFI_SUCCESS Successfully displayed the address | |
| **/ | |
| EFI_STATUS | |
| HttpSendHexBits ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN INT32 Bits, | |
| IN UINT64 Value | |
| ) | |
| { | |
| UINT32 Digit; | |
| INT32 Shift; | |
| EFI_STATUS Status; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Walk the list of divisors | |
| // | |
| Shift = (( Bits + 3 ) & ( ~3 )) - 4; | |
| while ( 0 <= Shift ) { | |
| // | |
| // Determine the next digit | |
| // | |
| Digit = (UINT32)(( Value >> Shift ) & 0xf ); | |
| if ( 10 <= Digit ) { | |
| Digit += 'a' - '0' - 10; | |
| } | |
| // | |
| // Display the digit | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit )); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Set the next shift | |
| // | |
| Shift -= 4; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Output a hex value to the HTML page | |
| @param [in] SocketFD Socket file descriptor | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Value Value to display | |
| @retval EFI_SUCCESS Successfully displayed the address | |
| **/ | |
| EFI_STATUS | |
| HttpSendHexValue ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN UINT64 Value | |
| ) | |
| { | |
| BOOLEAN bDisplayZeros; | |
| UINT32 Digit; | |
| INT32 Shift; | |
| EFI_STATUS Status; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Walk the list of divisors | |
| // | |
| bDisplayZeros = FALSE; | |
| Shift = 60; | |
| do { | |
| // | |
| // Determine the next digit | |
| // | |
| Digit = (UINT32)(( Value >> Shift ) & 0xf ); | |
| if ( 10 <= Digit ) { | |
| Digit += 'a' - '0' - 10; | |
| } | |
| // | |
| // Suppress leading zeros | |
| // | |
| if (( 0 != Digit ) || bDisplayZeros || ( 0 == Shift )) { | |
| bDisplayZeros = TRUE; | |
| // | |
| // Display the digit | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit )); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| // | |
| // Set the next shift | |
| // | |
| Shift -= 4; | |
| } while ( 0 <= Shift ); | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Output an IP6 address value to the HTML page | |
| @param [in] SocketFD Socket file descriptor | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Value Value to display | |
| @param [in] bFirstValue TRUE if first value | |
| @param [in] bLastValue TRUE if last value | |
| @param [in] bZeroSuppression TRUE while zeros are being suppressed | |
| @param [in] pbZeroSuppression Address to receive TRUE when zero suppression | |
| has started, use NULL if next colon value not | |
| needed. | |
| @retval EFI_SUCCESS Successfully displayed the address | |
| **/ | |
| EFI_STATUS | |
| HttpSendIp6Value ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN UINT16 Value, | |
| IN BOOLEAN bFirstValue, | |
| IN BOOLEAN bLastValue, | |
| IN BOOLEAN bZeroSuppression, | |
| IN BOOLEAN * pbZeroSuppression | |
| ) | |
| { | |
| BOOLEAN bZeroSuppressionStarting; | |
| UINT32 Digit; | |
| EFI_STATUS Status; | |
| // | |
| // Use break instead of goto | |
| // | |
| bZeroSuppressionStarting = FALSE; | |
| Status = EFI_SUCCESS; | |
| for ( ; ; ) { | |
| // | |
| // Display the leading colon if necessary | |
| // | |
| if ( bZeroSuppression && ( bLastValue || ( 0 != Value ))) { | |
| Status = HttpSendByte ( SocketFD, pPort, ':' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| // | |
| // Skip over a series of zero values | |
| // | |
| bZeroSuppressionStarting = (BOOLEAN)( 0 == Value ); | |
| if ( !bZeroSuppressionStarting ) { | |
| // | |
| // Display the value | |
| // | |
| Digit = ( Value >> 4 ) & 0xf; | |
| Status = HttpSendHexValue ( SocketFD, | |
| pPort, | |
| Digit ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Digit = Value & 0xf; | |
| Status = HttpSendHexValue ( SocketFD, | |
| pPort, | |
| Digit ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Digit = ( Value >> 12 ) & 0xf; | |
| Status = HttpSendHexValue ( SocketFD, | |
| pPort, | |
| Digit ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Digit = ( Value >> 8 ) & 0xf; | |
| Status = HttpSendHexValue ( SocketFD, | |
| pPort, | |
| Digit ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| // | |
| // Display the trailing colon if necessary | |
| // | |
| if (( !bLastValue ) && ( bFirstValue || ( 0 != Value ))) { | |
| Status = HttpSendByte ( SocketFD, pPort, ':' ); | |
| } | |
| break; | |
| } | |
| // | |
| // Return the next colon display | |
| if ( NULL != pbZeroSuppression ) { | |
| *pbZeroSuppression = bZeroSuppressionStarting; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Output an IP address to the HTML page | |
| @param [in] SocketFD Socket file descriptor | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] pAddress Address of the socket address | |
| @retval EFI_SUCCESS Successfully displayed the address | |
| **/ | |
| EFI_STATUS | |
| HttpSendIpAddress ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN struct sockaddr_in6 * pAddress | |
| ) | |
| { | |
| BOOLEAN bZeroSuppression; | |
| UINT32 Index; | |
| struct sockaddr_in * pIpv4; | |
| struct sockaddr_in6 * pIpv6; | |
| UINT16 PortNumber; | |
| EFI_STATUS Status; | |
| // | |
| // Use break instead of goto | |
| // | |
| for ( ; ; ) { | |
| // | |
| // Determine the type of address | |
| // | |
| if ( AF_INET6 == pAddress->sin6_family ) { | |
| pIpv6 = pAddress; | |
| // | |
| // Display the address in RFC2732 format | |
| // | |
| bZeroSuppression = FALSE; | |
| Status = HttpSendByte ( SocketFD, pPort, '[' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| for ( Index = 0; 8 > Index; Index++ ) { | |
| Status = HttpSendIp6Value ( SocketFD, | |
| pPort, | |
| pIpv6->sin6_addr.__u6_addr.__u6_addr16[ Index ], | |
| (BOOLEAN)( 0 == Index ), | |
| (BOOLEAN)( 7 == Index ), | |
| bZeroSuppression, | |
| &bZeroSuppression ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Separate the port number | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, ']' ); | |
| // | |
| // Get the port number | |
| // | |
| PortNumber = pIpv6->sin6_port; | |
| } | |
| else { | |
| // | |
| // Output the IPv4 address | |
| // | |
| pIpv4 = (struct sockaddr_in *)pAddress; | |
| Status = HttpSendValue ( SocketFD, pPort, (UINT8)pIpv4->sin_addr.s_addr ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendByte ( SocketFD, pPort, '.' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 8 )); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendByte ( SocketFD, pPort, '.' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 16 )); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendByte ( SocketFD, pPort, '.' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 24 )); | |
| // | |
| // Get the port number | |
| // | |
| PortNumber = pIpv4->sin_port; | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Display the port number | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, ':' ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| Status = HttpSendValue ( SocketFD, pPort, htons ( PortNumber )); | |
| break; | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Send a Unicode string | |
| @param [in] SocketFD The socket's file descriptor to add to the list. | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] pString A zero terminated Unicode string | |
| @retval EFI_SUCCESS The request was successfully processed | |
| **/ | |
| EFI_STATUS | |
| HttpSendUnicodeString ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN CONST UINT16 * pString | |
| ) | |
| { | |
| UINT8 Data; | |
| UINT16 Character; | |
| EFI_STATUS Status; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Walk the characters in he string | |
| // | |
| while ( 0 != ( Character = *pString++ )) { | |
| // | |
| // Convert the character to UTF-8 | |
| // | |
| if ( 0 != ( Character & 0xf800 )) { | |
| // | |
| // Send the upper 4 bits | |
| // | |
| Data = (UINT8)(( Character >> 12 ) & 0xf ); | |
| Data |= 0xe0; | |
| Status = HttpSendByte ( SocketFD, | |
| pPort, | |
| Data ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the next 6 bits | |
| // | |
| Data = (UINT8)(( Character >> 6 ) & 0x3f ); | |
| Data |= 0x80; | |
| Status = HttpSendByte ( SocketFD, | |
| pPort, | |
| Data ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the last 6 bits | |
| // | |
| Data = (UINT8)( Character & 0x3f ); | |
| Data |= 0x80; | |
| } | |
| else if ( 0 != ( Character & 0x0780 )) { | |
| // | |
| // Send the upper 5 bits | |
| // | |
| Data = (UINT8)(( Character >> 6 ) & 0x1f ); | |
| Data |= 0xc0; | |
| Status = HttpSendByte ( SocketFD, | |
| pPort, | |
| Data ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Send the last 6 bits | |
| // | |
| Data = (UINT8)( Character & 0x3f ); | |
| Data |= 0x80; | |
| } | |
| else { | |
| Data = (UINT8)( Character & 0x7f ); | |
| } | |
| // | |
| // Send the last data byte | |
| // | |
| Status = HttpSendByte ( SocketFD, | |
| pPort, | |
| Data ); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } | |
| /** | |
| Output a value to the HTML page | |
| @param [in] SocketFD Socket file descriptor | |
| @param [in] pPort The WSDT_PORT structure address | |
| @param [in] Value Value to display | |
| @retval EFI_SUCCESS Successfully displayed the address | |
| **/ | |
| EFI_STATUS | |
| HttpSendValue ( | |
| IN int SocketFD, | |
| IN WSDT_PORT * pPort, | |
| IN UINT64 Value | |
| ) | |
| { | |
| BOOLEAN bDisplayZeros; | |
| UINT64 Digit; | |
| CONST UINT64 * pEnd; | |
| CONST UINT64 * pDivisor; | |
| CONST UINT64 pDivisors[ ] = { | |
| 10000000000000000000ULL, | |
| 1000000000000000000ULL, | |
| 100000000000000000ULL, | |
| 10000000000000000ULL, | |
| 1000000000000000ULL, | |
| 100000000000000ULL, | |
| 10000000000000ULL, | |
| 1000000000000ULL, | |
| 100000000000ULL, | |
| 10000000000ULL, | |
| 1000000000ULL, | |
| 100000000ULL, | |
| 10000000ULL, | |
| 1000000ULL, | |
| 100000ULL, | |
| 10000ULL, | |
| 1000ULL, | |
| 100ULL, | |
| 10ULL | |
| }; | |
| EFI_STATUS Status; | |
| UINT64 Temp; | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Walk the list of divisors | |
| // | |
| bDisplayZeros = FALSE; | |
| pDivisor = &pDivisors[0]; | |
| pEnd = &pDivisor[ sizeof ( pDivisors ) / sizeof ( pDivisors[0])]; | |
| while ( pEnd > pDivisor ) { | |
| // | |
| // Determine the next digit | |
| // | |
| Digit = Value / *pDivisor; | |
| // | |
| // Suppress leading zeros | |
| // | |
| if (( 0 != Digit ) || bDisplayZeros ) { | |
| bDisplayZeros = TRUE; | |
| // | |
| // Display the digit | |
| // | |
| Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit )); | |
| if ( EFI_ERROR ( Status )) { | |
| break; | |
| } | |
| // | |
| // Determine the remainder | |
| // | |
| Temp = *pDivisor * Digit; | |
| Value -= Temp; | |
| } | |
| // | |
| // Set the next divisor | |
| // | |
| pDivisor += 1; | |
| } | |
| // | |
| // Display the final digit | |
| // | |
| if ( !EFI_ERROR ( Status )) { | |
| Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Value )); | |
| } | |
| // | |
| // Return the operation status | |
| // | |
| return Status; | |
| } |