/** @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; | |
} |