mirror of https://github.com/ipxe/ipxe.git
Handle strings as complete units, instead of a byte at a time.
parent
3a7d762c1c
commit
67577556a2
|
@ -478,23 +478,6 @@ enum iscsi_rx_state {
|
||||||
ISCSI_RX_DATA_PADDING,
|
ISCSI_RX_DATA_PADDING,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum iscsi_string_key_value {
|
|
||||||
STRING_KEY = 0,
|
|
||||||
STRING_VALUE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** iSCSI text string processor state */
|
|
||||||
struct iscsi_string_state {
|
|
||||||
/** Text string key */
|
|
||||||
char key[16];
|
|
||||||
/** Text string value */
|
|
||||||
char value[8];
|
|
||||||
/** Key/value flag */
|
|
||||||
enum iscsi_string_key_value key_value;
|
|
||||||
/** Index into current string */
|
|
||||||
unsigned int index;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** An iSCSI session */
|
/** An iSCSI session */
|
||||||
struct iscsi_session {
|
struct iscsi_session {
|
||||||
/** TCP connection for this session */
|
/** TCP connection for this session */
|
||||||
|
@ -596,8 +579,6 @@ struct iscsi_session {
|
||||||
size_t rx_len;
|
size_t rx_len;
|
||||||
/** Buffer for received data (not always used) */
|
/** Buffer for received data (not always used) */
|
||||||
void *rx_buffer;
|
void *rx_buffer;
|
||||||
/** State of strings received during login phase */
|
|
||||||
struct iscsi_string_state string;
|
|
||||||
|
|
||||||
/** Current SCSI command
|
/** Current SCSI command
|
||||||
*
|
*
|
||||||
|
|
|
@ -509,17 +509,13 @@ static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
|
||||||
* Handle iSCSI AuthMethod text value
|
* Handle iSCSI AuthMethod text value
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v value AuthMethod value
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
||||||
int finished ) {
|
const char *value ) {
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
|
||||||
|
|
||||||
if ( ! finished )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If server requests CHAP, send the CHAP_A string */
|
/* If server requests CHAP, send the CHAP_A string */
|
||||||
if ( strcmp ( string->value, "CHAP" ) == 0 ) {
|
if ( strcmp ( value, "CHAP" ) == 0 ) {
|
||||||
DBG ( "iSCSI %p initiating CHAP authentication\n", iscsi );
|
DBG ( "iSCSI %p initiating CHAP authentication\n", iscsi );
|
||||||
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
|
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
|
||||||
}
|
}
|
||||||
|
@ -529,23 +525,19 @@ static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
||||||
* Handle iSCSI CHAP_A text value
|
* Handle iSCSI CHAP_A text value
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v value CHAP_A value
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
||||||
int finished ) {
|
const char *value ) {
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ( ! finished )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
|
/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
|
||||||
* the server responds with anything else it is a protocol
|
* the server responds with anything else it is a protocol
|
||||||
* violation.
|
* violation.
|
||||||
*/
|
*/
|
||||||
if ( strcmp ( string->value, "5" ) != 0 ) {
|
if ( strcmp ( value, "5" ) != 0 ) {
|
||||||
DBG ( "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
|
DBG ( "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
|
||||||
iscsi, string->value );
|
iscsi, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare for CHAP with MD5 */
|
/* Prepare for CHAP with MD5 */
|
||||||
|
@ -559,22 +551,18 @@ static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
||||||
* Handle iSCSI CHAP_I text value
|
* Handle iSCSI CHAP_I text value
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v value CHAP_I value
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
||||||
int finished ) {
|
const char *value ) {
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
|
||||||
unsigned int identifier;
|
unsigned int identifier;
|
||||||
char *endp;
|
char *endp;
|
||||||
|
|
||||||
if ( ! finished )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* The CHAP identifier is an integer value */
|
/* The CHAP identifier is an integer value */
|
||||||
identifier = strtoul ( string->value, &endp, 0 );
|
identifier = strtoul ( value, &endp, 0 );
|
||||||
if ( *endp != '\0' ) {
|
if ( *endp != '\0' ) {
|
||||||
DBG ( "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
|
DBG ( "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
|
||||||
iscsi, string->value );
|
iscsi, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Identifier and secret are the first two components of the
|
/* Identifier and secret are the first two components of the
|
||||||
|
@ -589,159 +577,108 @@ static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
||||||
* Handle iSCSI CHAP_C text value
|
* Handle iSCSI CHAP_C text value
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v value CHAP_C value
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
|
static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
|
||||||
int finished ) {
|
const char *value ) {
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
char buf[3];
|
||||||
uint8_t byte;
|
|
||||||
char *endp;
|
char *endp;
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
/* Once the whole challenge is received, calculate the response */
|
/* Check and strip leading "0x" */
|
||||||
if ( finished ) {
|
if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
|
||||||
DBG ( "iSCSI %p sending CHAP response\n", iscsi );
|
DBG ( "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
|
||||||
chap_respond ( &iscsi->chap );
|
iscsi, value );
|
||||||
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
|
}
|
||||||
return;
|
value += 2;
|
||||||
|
|
||||||
|
/* Process challenge an octet at a time */
|
||||||
|
for ( ; ( value[0] && value[1] ) ; value += 2 ) {
|
||||||
|
memcpy ( buf, value, 2 );
|
||||||
|
buf[3] = 0;
|
||||||
|
byte = strtoul ( buf, &endp, 16 );
|
||||||
|
if ( *endp != '\0' ) {
|
||||||
|
DBG ( "iSCSI %p saw invalid CHAP challenge byte "
|
||||||
|
"\"%s\"\n", iscsi, buf );
|
||||||
|
}
|
||||||
|
chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait until a complete octet ("0x??") is received */
|
/* Build CHAP response */
|
||||||
if ( string->index != 4 )
|
DBG ( "iSCSI %p sending CHAP response\n", iscsi );
|
||||||
return;
|
chap_respond ( &iscsi->chap );
|
||||||
|
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
|
||||||
/* Add octet to challenge */
|
|
||||||
byte = strtoul ( string->value, &endp, 0 );
|
|
||||||
if ( *endp != '\0' ) {
|
|
||||||
DBG ( "iSCSI %p saw invalid CHAP challenge portion \"%s\"\n",
|
|
||||||
iscsi, string->value );
|
|
||||||
}
|
|
||||||
chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
|
|
||||||
|
|
||||||
/* Reset value back to "0x" */
|
|
||||||
string->index = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An iSCSI text string that we want to handle */
|
/** An iSCSI text string that we want to handle */
|
||||||
struct iscsi_string_type {
|
struct iscsi_string_type {
|
||||||
/** String name
|
/** String key
|
||||||
*
|
*
|
||||||
* This is the portion before the "=" sign,
|
* This is the portion up to and including the "=" sign,
|
||||||
* e.g. InitiatorName, CHAP_A, etc.
|
* e.g. "InitiatorName=", "CHAP_A=", etc.
|
||||||
*/
|
*/
|
||||||
const char *name;
|
const char *key;
|
||||||
/** Handle iSCSI string value
|
/** Handle iSCSI string value
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v value iSCSI string value
|
||||||
*
|
|
||||||
* Process the string in @c iscsi->string. This method will
|
|
||||||
* be called once for each character in the string, and once
|
|
||||||
* again at the end of the string.
|
|
||||||
*/
|
*/
|
||||||
void ( * handle_value ) ( struct iscsi_session *iscsi, int finished );
|
void ( * handle_value ) ( struct iscsi_session *iscsi,
|
||||||
|
const char *value );
|
||||||
};
|
};
|
||||||
|
|
||||||
/** iSCSI text strings that we want to handle */
|
/** iSCSI text strings that we want to handle */
|
||||||
struct iscsi_string_type iscsi_string_types[] = {
|
struct iscsi_string_type iscsi_string_types[] = {
|
||||||
{ "AuthMethod", iscsi_handle_authmethod_value },
|
{ "AuthMethod=", iscsi_handle_authmethod_value },
|
||||||
{ "CHAP_A", iscsi_handle_chap_a_value },
|
{ "CHAP_A=", iscsi_handle_chap_a_value },
|
||||||
{ "CHAP_I", iscsi_handle_chap_i_value },
|
{ "CHAP_I=", iscsi_handle_chap_i_value },
|
||||||
{ "CHAP_C", iscsi_handle_chap_c_value },
|
{ "CHAP_C=", iscsi_handle_chap_c_value },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle iSCSI string value
|
* Handle iSCSI string
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v finished Value is complete
|
* @v string iSCSI string (in "key=value" format)
|
||||||
*
|
|
||||||
* Process the string in @c iscsi->string. This function will be
|
|
||||||
* called once for each character in the string, and once again at the
|
|
||||||
* end of the string.
|
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_string_value ( struct iscsi_session *iscsi,
|
static void iscsi_handle_string ( struct iscsi_session *iscsi,
|
||||||
int finished ) {
|
const char *string ) {
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
struct iscsi_string_type *type;
|
||||||
struct iscsi_string_type *type = iscsi_string_types;
|
size_t key_len;
|
||||||
|
|
||||||
assert ( string->key_value == STRING_VALUE );
|
for ( type = iscsi_string_types ; type->key ; type++ ) {
|
||||||
|
key_len = strlen ( type->key );
|
||||||
for ( type = iscsi_string_types ; type->name ; type++ ) {
|
if ( strncmp ( string, type->key, key_len ) == 0 ) {
|
||||||
if ( strcmp ( type->name, string->key ) == 0 ) {
|
DBG ( "iSCSI %p handling %s\n", iscsi, string );
|
||||||
if ( string->index <= 1 ) {
|
type->handle_value ( iscsi, ( string + key_len ) );
|
||||||
DBG ( "iSCSI %p handling key \"%s\"\n",
|
|
||||||
iscsi, string->key );
|
|
||||||
}
|
|
||||||
type->handle_value ( iscsi, finished );
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( string->index <= 1 )
|
DBG ( "iSCSI %p ignoring %s\n", iscsi, string );
|
||||||
DBG ( "iSCSI %p ignoring key \"%s\"\n", iscsi, string->key );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle byte of an iSCSI string
|
* Handle iSCSI strings
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v byte Byte of string
|
* @v string iSCSI string buffer
|
||||||
*
|
* @v len Length of string buffer
|
||||||
* Strings are handled a byte at a time in order to simplify the
|
|
||||||
* logic, and to ensure that we can provably cope with the TCP packet
|
|
||||||
* boundaries coming at inconvenient points, such as halfway through a
|
|
||||||
* string.
|
|
||||||
*/
|
*/
|
||||||
static void iscsi_handle_string_byte ( struct iscsi_session *iscsi,
|
|
||||||
uint8_t byte ) {
|
|
||||||
struct iscsi_string_state *string = &iscsi->string;
|
|
||||||
|
|
||||||
if ( string->key_value == STRING_KEY ) {
|
|
||||||
switch ( byte ) {
|
|
||||||
case '\0':
|
|
||||||
/* Premature termination */
|
|
||||||
DBG ( "iSCSI %p premature key termination on \"%s\"\n",
|
|
||||||
iscsi, string->key );
|
|
||||||
string->index = 0;
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
/* End of key */
|
|
||||||
string->key_value = STRING_VALUE;
|
|
||||||
string->index = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Part of key */
|
|
||||||
if ( string->index < ( sizeof ( string->key ) - 1 ) ) {
|
|
||||||
string->key[string->index++] = byte;
|
|
||||||
string->key[string->index] = '\0';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch ( byte ) {
|
|
||||||
case '\0':
|
|
||||||
/* End of string */
|
|
||||||
iscsi_handle_string_value ( iscsi, 1 );
|
|
||||||
string->key_value = STRING_KEY;
|
|
||||||
string->index = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Part of value */
|
|
||||||
if ( string->index < ( sizeof ( string->value ) - 1 )){
|
|
||||||
string->value[string->index++] = byte;
|
|
||||||
string->value[string->index] = '\0';
|
|
||||||
iscsi_handle_string_value ( iscsi, 0 );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iscsi_handle_strings ( struct iscsi_session *iscsi,
|
static void iscsi_handle_strings ( struct iscsi_session *iscsi,
|
||||||
const char *strings, size_t len ) {
|
const char *strings, size_t len ) {
|
||||||
for ( ; len-- ; strings++ ) {
|
size_t string_len;
|
||||||
iscsi_handle_string_byte ( iscsi,
|
|
||||||
* ( ( uint8_t * ) strings ) );
|
/* Handle each string in turn, taking care not to overrun the
|
||||||
|
* data buffer in case of badly-terminated data.
|
||||||
|
*/
|
||||||
|
while ( 1 ) {
|
||||||
|
string_len = ( strnlen ( strings, len ) + 1 );
|
||||||
|
if ( string_len > len )
|
||||||
|
break;
|
||||||
|
iscsi_handle_string ( iscsi, strings );
|
||||||
|
strings += string_len;
|
||||||
|
len -= string_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue