Mercurial > nand-ecoscentric
changeset 2156:1cde7dd73fff
* src/dhcp_prot.c: Updated set_fixed_tag(), set_variable_tag(),
and unset_tag() to handle TAG_PAD bytes properly. Also updated
set_fixed_tag() and set_variable_tag() to permit setting options that
already exist, but are a different size, rather than asserting.
This corrects problems interacting with mis-behaving DHCP servers
that reply with modified versions of a DHCP option sent by the eCos
client, such as TAG_HOST_NAME.
| author | asl |
|---|---|
| date | Sun, 05 Mar 2006 12:48:37 +0000 |
| parents | df4d007af7e8 |
| children | 1674688917d0 |
| files | packages/net/common/current/ChangeLog packages/net/common/current/src/dhcp_prot.c |
| diffstat | 2 files changed, 80 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/common/current/ChangeLog +++ b/packages/net/common/current/ChangeLog @@ -1,3 +1,13 @@ +2006-02-27 Jay Foster <jay@systech.com> + + * src/dhcp_prot.c: Updated set_fixed_tag(), set_variable_tag(), + and unset_tag() to handle TAG_PAD bytes properly. Also updated + set_fixed_tag() and set_variable_tag() to permit setting options that + already exist, but are a different size, rather than asserting. + This corrects problems interacting with mis-behaving DHCP servers + that reply with modified versions of a DHCP option sent by the eCos + client, such as TAG_HOST_NAME. + 2005-10-24 Andrew Lunn <andrew.lunn@ascom.ch> * include/network.h: Include <string.h> to stop warnings.
--- a/packages/net/common/current/src/dhcp_prot.c +++ b/packages/net/common/current/src/dhcp_prot.c @@ -90,6 +90,9 @@ void dhcp_set_hostname(char *hostname) } #endif +/* Forward reference prototypes. */ +static int unset_tag( struct bootp *ppkt, unsigned char tag ); + // ------------------------------------------------------------------------ // Returns a pointer to the end of dhcp message (or NULL if invalid) // meaning the address of the byte *after* the TAG_END token in the vendor @@ -183,13 +186,41 @@ set_fixed_tag( struct bootp *ppkt, } if (*op == tag) // Found it... break; - op += *(op+1)+2; + if ( *op == TAG_PAD ) { + op++; + } else { + op += *(op+1)+2; + } } if (*op == tag) { // Found it... + /* There are three possibilities: + * 1) *(op+1) == len + * 2) *(op+1) > len + * 3) *(op+1) < len + * For 1, just overwrite the existing option data. + * For 2, overwrite the existing option data and pullup the + * remaining option data (if any). + * For 3, pullup any remaining option data to remove the option + * and then add the option to the end. + * For simplicity, for case 2 and 3, we just call unset_tag() + * and re-add the option to the end. + */ if ( *(op+1) != len ) { - CYG_FAIL( "Wrong size in set_fixed_tag" ); - return false; // wrong size + /* Remove existing option entry. */ + unset_tag(ppkt, tag); + /* Adjust the op pointer to re-add at the end. */ + op = scan_dhcp_size(ppkt); + CYG_ASSERT(op!=NULL, "Invalid options size in set_fixed_tag" ); + op--; + CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_fixed_tag"); + if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) { + CYG_FAIL( "Oversize DHCP packet in set_fixed_tag replace" ); + return false; + } + *op = tag; + *(op+1) = len; + *(op + len + 2) = TAG_END; } } else { // overwrite the end tag and install a new one @@ -210,7 +241,6 @@ set_fixed_tag( struct bootp *ppkt, return true; } -// Note that this does not permit changing the size of an extant tag. static int set_variable_tag( struct bootp *ppkt, unsigned char tag, @@ -228,13 +258,41 @@ set_variable_tag( struct bootp *ppkt, } if (*op == tag) // Found it... break; - op += *(op+1)+2; + if ( *op == TAG_PAD ) { + op++; + } else { + op += *(op+1)+2; + } } if (*op == tag) { // Found it... + /* There are three possibilities: + * 1) *(op+1) == len + * 2) *(op+1) > len + * 3) *(op+1) < len + * For 1, just overwrite the existing option data. + * For 2, overwrite the existing option data and pullup the + * remaining option data (if any). + * For 3, pullup any remaining option data to remove the option + * and then add the option to the end. + * For simplicity, for case 2 and 3, we just call unset_tag() + * and re-add the option to the end. + */ if ( *(op+1) != len ) { - CYG_FAIL( "Wrong size in set_variable_tag" ); - return false; // wrong size + /* Remove existing option entry. */ + unset_tag(ppkt, tag); + /* Adjust the op pointer to re-add at the end. */ + op = scan_dhcp_size(ppkt); + CYG_ASSERT(op!=NULL, "Invalid options size in set_variable_tag" ); + op--; + CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_variable_tag"); + if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) { + CYG_FAIL( "Oversize DHCP packet in set_variable_tag replace" ); + return false; + } + *op = tag; + *(op+1) = len; + *(op + len + 2) = TAG_END; } } else { // overwrite the end tag and install a new one @@ -272,7 +330,11 @@ unset_tag( struct bootp *ppkt, killp = op; // item to kill nextp = op + *(op+1)+2; // next item address } - op += *(op+1)+2; // scan to the end + if ( *op == TAG_PAD ) { + op++; + } else { + op += *(op+1)+2; + } } if ( !killp )
