| /** @file | |
| Set the socket options | |
| 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 <errno.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <Uefi.h> | |
| #include <unistd.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <sys/socket.h> | |
| #include <sys/time.h> | |
| typedef enum _DATA_TYPE { | |
| DATA_TYPE_UNKNOWN = 0, | |
| DATA_TYPE_INT32_DECIMAL, | |
| DATA_TYPE_SOCKET_TYPE, | |
| DATA_TYPE_TIMEVAL | |
| } DATA_TYPE; | |
| typedef struct { | |
| char * pOptionName; | |
| int OptionValue; | |
| int OptionLevel; | |
| BOOLEAN bSetAllowed; | |
| DATA_TYPE DataType; | |
| } OPTIONS; | |
| CONST OPTIONS mOptions[] = { | |
| { "SO_ACCEPTCONN", SO_ACCEPTCONN, SOL_SOCKET, FALSE, DATA_TYPE_UNKNOWN }, | |
| { "SO_BROADCAST", SO_BROADCAST, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_DEBUG", SO_DEBUG, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_DONTROUTE", SO_DONTROUTE, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_ERROR", SO_ERROR, SOL_SOCKET, FALSE, DATA_TYPE_UNKNOWN }, | |
| { "SO_KEEPALIVE", SO_KEEPALIVE, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_OOBINLINE", SO_OOBINLINE, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_OVERFLOWED", SO_OVERFLOWED, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_RCVBUF", SO_RCVBUF, SOL_SOCKET, TRUE, DATA_TYPE_INT32_DECIMAL }, | |
| { "SO_RCVLOWAT", SO_RCVLOWAT, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_RCVTIMEO", SO_RCVTIMEO, SOL_SOCKET, TRUE, DATA_TYPE_TIMEVAL }, | |
| { "SO_REUSEADDR", SO_REUSEADDR, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_REUSEPORT", SO_REUSEPORT, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_SNDBUF", SO_SNDBUF, SOL_SOCKET, TRUE, DATA_TYPE_INT32_DECIMAL }, | |
| { "SO_SNDLOWAT", SO_SNDLOWAT, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_SNDTIMEO", SO_SNDTIMEO, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_TIMESTAMP", SO_TIMESTAMP, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN }, | |
| { "SO_TYPE", SO_TYPE, SOL_SOCKET, FALSE, DATA_TYPE_SOCKET_TYPE }, | |
| { "SO_USELOOPBACK", SO_USELOOPBACK, SOL_SOCKET, TRUE, DATA_TYPE_UNKNOWN } | |
| }; | |
| UINT8 mBuffer[ 65536 ]; | |
| UINT8 mValue[ 65536 ]; | |
| char * mSocketType[] = { | |
| "SOCK_STREAM", | |
| "SOCK_DGRAM", | |
| "SOCK_RAW", | |
| "SOCK_RDM", | |
| "SOCK_SEQPACKET" | |
| }; | |
| void | |
| DisplayOption ( | |
| CONST OPTIONS * pOption, | |
| socklen_t LengthInBytes, | |
| BOOLEAN bDisplayUpdate, | |
| BOOLEAN bDisplayCrLf | |
| ) | |
| { | |
| UINT8 * pEnd; | |
| char * pString; | |
| union { | |
| UINT8 * u8; | |
| INT32 * i32; | |
| struct timeval * TimeVal; | |
| } Value; | |
| // | |
| // Display the value length | |
| // | |
| if ( !bDisplayUpdate ) { | |
| Print ( L"LengthInBytes: %d\r\n", LengthInBytes ); | |
| Print ( L"%a: ", pOption->pOptionName ); | |
| } | |
| else { | |
| Print ( L" --> " ); | |
| } | |
| // | |
| // Display the value | |
| // | |
| Value.u8 = &mBuffer[0]; | |
| switch ( pOption->DataType ) { | |
| case DATA_TYPE_UNKNOWN: | |
| Print ( L"%a:", pOption->pOptionName ); | |
| pEnd = &Value.u8[ LengthInBytes ]; | |
| while ( pEnd > Value.u8 ) { | |
| Print ( L" %02x", *Value.u8 ); | |
| Value.u8 += 1; | |
| } | |
| break; | |
| case DATA_TYPE_INT32_DECIMAL: | |
| if ( 4 == LengthInBytes ) { | |
| Print ( L"%d", *Value.i32 ); | |
| } | |
| else { | |
| errno = ( 4 > LengthInBytes ) ? EBUFSIZE : ERANGE; | |
| Print ( L"\r\nERROR - Invalid length, errno: %d\r\n", errno ); | |
| } | |
| break; | |
| case DATA_TYPE_SOCKET_TYPE: | |
| if ( 4 == LengthInBytes ) { | |
| if (( SOCK_STREAM <= *Value.i32 ) && ( SOCK_SEQPACKET >= *Value.i32 )) { | |
| pString = mSocketType[ *Value.i32 - SOCK_STREAM ]; | |
| Print ( L"%a", pString ); | |
| } | |
| else { | |
| Print ( L"%08x (unknown type)", *Value.i32 ); | |
| } | |
| } | |
| else { | |
| errno = ( 4 > LengthInBytes ) ? EBUFSIZE : ERANGE; | |
| Print ( L"\r\nERROR - Invalid length, errno: %d\r\n", errno ); | |
| } | |
| break; | |
| case DATA_TYPE_TIMEVAL: | |
| if ( sizeof ( *Value.TimeVal ) == LengthInBytes ) { | |
| if (( 0 == Value.TimeVal->tv_sec ) | |
| && ( 0 == Value.TimeVal->tv_usec )) { | |
| Print ( L"Infinite" ); | |
| } | |
| else { | |
| Print ( L"%d.%06d sec", | |
| Value.TimeVal->tv_sec, | |
| Value.TimeVal->tv_usec ); | |
| } | |
| } | |
| else { | |
| errno = ( 4 > LengthInBytes ) ? EBUFSIZE : ERANGE; | |
| Print ( L"\r\nERROR - Invalid length, errno: %d\r\n", errno ); | |
| } | |
| break; | |
| } | |
| // | |
| // Terminate the line | |
| // | |
| if ( bDisplayCrLf ) { | |
| Print ( L"\r\n" ); | |
| } | |
| } | |
| socklen_t | |
| GetOptionValue ( | |
| CONST OPTIONS * pOption, | |
| char * pValue | |
| ) | |
| { | |
| socklen_t BytesToWrite; | |
| union { | |
| UINT8 * u8; | |
| INT32 * i32; | |
| struct timeval * TimeVal; | |
| } Value; | |
| int Values; | |
| // | |
| // Assume failure | |
| // | |
| errno = EINVAL; | |
| BytesToWrite = 0; | |
| // | |
| // Determine the type of parameter | |
| // | |
| if ( pOption->bSetAllowed ) { | |
| Value.u8 = &mValue[0]; | |
| switch ( pOption->DataType ) { | |
| default: | |
| break; | |
| case DATA_TYPE_INT32_DECIMAL: | |
| Values = sscanf ( pValue, "%d", Value.i32 ); | |
| if ( 1 == Values ) { | |
| BytesToWrite = sizeof ( *Value.i32); | |
| errno = 0; | |
| } | |
| break; | |
| case DATA_TYPE_TIMEVAL: | |
| Values = sscanf ( pValue, "%d.%d", | |
| &Value.TimeVal->tv_sec, | |
| &Value.TimeVal->tv_usec ); | |
| if (( 2 == Values ) | |
| && ( 0 <= Value.TimeVal->tv_sec ) | |
| && ( 0 <= Value.TimeVal->tv_usec ) | |
| && ( 1000000 > Value.TimeVal->tv_usec )){ | |
| BytesToWrite = sizeof ( *Value.TimeVal ); | |
| errno = 0; | |
| } | |
| } | |
| } | |
| // | |
| // Display the error | |
| // | |
| if ( 0 == BytesToWrite ) { | |
| Print ( L"ERROR - Invalid value!\r\n" ); | |
| } | |
| // | |
| // Return the number of bytes to be written | |
| // | |
| return BytesToWrite; | |
| } | |
| /** | |
| Set the socket options | |
| @param [in] Argc The number of arguments | |
| @param [in] Argv The argument value array | |
| @retval 0 The application exited normally. | |
| @retval Other An error occurred. | |
| **/ | |
| int | |
| main ( | |
| IN int Argc, | |
| IN char **Argv | |
| ) | |
| { | |
| socklen_t BytesToWrite; | |
| socklen_t LengthInBytes; | |
| CONST OPTIONS * pEnd; | |
| CONST OPTIONS * pOption; | |
| int s; | |
| int Status; | |
| DEBUG (( DEBUG_INFO, | |
| "%a starting\r\n", | |
| Argv[0])); | |
| // | |
| // Parse the socket option | |
| // | |
| pOption = &mOptions[0]; | |
| pEnd = &pOption[sizeof ( mOptions ) / sizeof ( mOptions[0])]; | |
| if ( 2 <= Argc ) { | |
| while ( pEnd > pOption ) { | |
| if ( 0 == strcmp ( Argv[1], pOption->pOptionName )) { | |
| break; | |
| } | |
| pOption += 1; | |
| } | |
| if ( pEnd <= pOption ) { | |
| Print ( L"ERROR: Invalid option: %a\r\n", Argv[1]); | |
| Argc = 1; | |
| } | |
| } | |
| // | |
| // Display the help if necessary | |
| // | |
| if (( 2 > Argc ) || ( 3 < Argc )) { | |
| Print ( L"%a <option>\r\n", Argv[0]); | |
| Print ( L"\r\n" ); | |
| Print ( L"Option one of:\r\n" ); | |
| pOption = &mOptions[0]; | |
| while ( pEnd > pOption ) { | |
| Print ( L" %a: %a\r\n", | |
| pOption->pOptionName, | |
| pOption->bSetAllowed ? "get/set" : "get" ); | |
| pOption += 1; | |
| } | |
| errno = EINVAL; | |
| } | |
| else { | |
| // | |
| // Determine if the value is to be set | |
| // | |
| BytesToWrite = 0; | |
| if (( 3 > Argc ) | |
| || ( 0 < ( BytesToWrite = GetOptionValue ( pOption, Argv[2])))) { | |
| // | |
| // Get the socket | |
| // | |
| s = socket ( AF_INET, 0, 0 ); | |
| if ( -1 == s ) { | |
| Print ( L"ERROR - Unable to open the socket, errno: %d\r\n", errno ); | |
| } | |
| else { | |
| // | |
| // Display the option value | |
| // | |
| LengthInBytes = sizeof ( mBuffer ); | |
| Status = getsockopt ( s, | |
| pOption->OptionLevel, | |
| pOption->OptionValue, | |
| &mBuffer, | |
| &LengthInBytes ); | |
| if ( -1 == Status ) { | |
| Print ( L"ERROR - getsockopt failed, errno: %d\r\n", errno ); | |
| } | |
| else { | |
| DisplayOption ( pOption, | |
| LengthInBytes, | |
| FALSE, | |
| (BOOLEAN)( 0 == BytesToWrite )); | |
| // | |
| // Determine if the value is to be set | |
| // | |
| if (( 0 < BytesToWrite ) | |
| && ( BytesToWrite == LengthInBytes )) { | |
| // | |
| // Set the option value | |
| // | |
| Status = setsockopt ( s, | |
| pOption->OptionLevel, | |
| pOption->OptionValue, | |
| &mValue, | |
| BytesToWrite ); | |
| if ( -1 == Status ) { | |
| Print ( L"ERROR - setsockopt failed, errno: %d\r\n", errno ); | |
| } | |
| else { | |
| // | |
| // Display the updated option value | |
| // | |
| Status = getsockopt ( s, | |
| pOption->OptionLevel, | |
| pOption->OptionValue, | |
| &mBuffer, | |
| &LengthInBytes ); | |
| if ( -1 == Status ) { | |
| Print ( L"ERROR - getsockopt failed, errno: %d\r\n", errno ); | |
| } | |
| else { | |
| DisplayOption ( pOption, | |
| LengthInBytes, | |
| TRUE, | |
| TRUE ); | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Done with the socket | |
| // | |
| close ( s ); | |
| } | |
| } | |
| } | |
| // | |
| // All done | |
| // | |
| DEBUG (( DEBUG_INFO, | |
| "%a exiting, errno: %d\r\n", | |
| Argv[0], | |
| errno )); | |
| return errno; | |
| } |