Skip Menu |

This queue is for tickets about the Net-DHCP CPAN distribution.

Report information
The Basics
Id: 23546
Status: resolved
Priority: 0/
Queue: Net-DHCP

People
Owner: Nobody in particular
Requestors: Christof [...] chen.de
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 0.66
Fixed in: (no value)



Subject: unhandled multiple options
Concerning Net::DHCP:Packet 0.66. A packet with two VENDOR_ENCAPSULATED_OPTION fields (e.g. very large PXE options) gets malformed by marshall/serialize. Packet.pm stores the option data in a hash, so only the last data is assigned to every instance of an option during serialize. (In general, the code does not provision for multiple options of the same key). The attached patch works around this problem by storing the data for an option in an array. addOption appends to this array. replaceOption replaces the array. addOptionRaw appends to this array. replaceOptionRaw replaces the array. getOptionValue returns an array of option values. Best regards, Christof Chen
Subject: net-dhcp-options.patch
Index: lib/Net/DHCP/Packet.pm =================================================================== --- lib/Net/DHCP/Packet.pm (.../original/Net-DHCP-0.66/lib/Net/DHCP/Packet.pm) (Revision 35) +++ lib/Net/DHCP/Packet.pm (.../trunk/current/lib/Net/DHCP/Packet.pm) (Arbeitskopie) @@ -1,6 +1,7 @@ # Net::DHCP::Packet.pm # Original Author: F. van Dun # Author : S. Hadinger +# modifications: Christof Chen package Net::DHCP::Packet; @@ -9,7 +10,7 @@ use strict; our (@ISA, @EXPORT, @EXPORT_OK, $VERSION); use Exporter; -$VERSION = 0.66; +$VERSION = 0.661; @ISA = qw(Exporter); @EXPORT = qw( packinet packinets unpackinet unpackinets ); @EXPORT_OK = qw( ); @@ -241,17 +242,42 @@ sub addOptionRaw { my ($self,$key,$value_bin) = @_; - $self->{options}->{$key} = $value_bin; - push @{$self->{options_order}}, ($key); + if (!exists $self->{options}->{$key}) { + push @{$self->{options_order}}, $key; + } + push @{$self->{options}->{$key}}, $value_bin; } +sub replaceOptionRaw { + my ($self,$key,$value_bin) = @_; + if (!exists $self->{options}->{$key}) { + push @{$self->{options_order}}, ($key); + } + @{$self->{options}->{$key}} = ($value_bin); +} + sub addOptionValue($$$) { my $self = shift; my $code = shift; # option code my $value = shift; + + my $value_bin = makevalbin($code, $value); + $self->addOptionRaw($code, $value_bin); +} + +sub replaceOptionValue($$$) { + my $self = shift; + my $code = shift; # option code + my $value = shift; + + my $value_bin = makevalbin($code, $value); + $self->replaceOptionRaw($code, $value_bin); +} + +sub makevalbin () { +my ($code,$value) = @_; + my $format = ''; # format for the option my $value_bin; # option value in binary format - my $format = ''; # format for the option - carp("addOptionValue: unknown format for code ($code)") unless exists($DHO_FORMATS{$code}); $format = $DHO_FORMATS{$code} if exists($DHO_FORMATS{$code}); @@ -267,9 +293,9 @@ } elsif ($format =~ /s$/) { # ends with an 's', meaning any number of parameters ; } elsif ($format =~ /2$/) { # ends with a '2', meaning couples of parameters - croak("addOptionValue: only pairs of values expected for option '$code'") if ((@values % 2) != 0); + croak("makevalbin: only pairs of values expected for option '$code'") if ((@values % 2) != 0); } else { # only one parameter - croak("addOptionValue: exactly one value expected for option '$code'") if (@values != 1); + croak("makevalbin: exactly one value expected for option '$code'") if (@values != 1); } if ($format eq 'inet') { @@ -294,10 +320,10 @@ } else { $value_bin = $values[0]; } - - $self->addOptionRaw($code, $value_bin); + return $value_bin; } + #sub getOption { # deprecated # my $self = shift; # return $self->getOptionRaw(@_); @@ -316,38 +342,27 @@ carp("getOptionValue: unknown format for code ($code)") unless exists($DHO_FORMATS{$code}); $format = $DHO_FORMATS{$code} if exists($DHO_FORMATS{$code}); - my $value_bin = $self->getOptionRaw($code); - return undef unless defined($value_bin); - my @values = (); + my $values_arr = ($self->getOptionRaw($code)); + return undef unless defined($values_arr->[0]); + my @value = (); - if ($format eq 'inet') { - $values[0] = unpackinet($value_bin); - } elsif (($format eq 'inets') || ($format eq 'inets2')) { - @values = unpackinets_array($value_bin); - } elsif ($format eq 'int') { - $values[0] = unpack('N', $value_bin); - } elsif ($format eq 'short') { - $values[0] = unpack('n', $value_bin); - } elsif ($format eq 'shorts') { - @values = unpack('n*', $value_bin); - } elsif ($format eq 'byte') { - $values[0] = unpack('C', $value_bin); - } elsif ($format eq 'bytes') { - @values = unpack('C*', $value_bin); - } elsif ($format eq 'string') { - $values[0] = $value_bin; -# } elsif ($format eq 'relays') { -# @values = $self->decodeRelayAgent($value_bin); -# # TBM, bad format -# } elsif ($format eq 'ids') { -# $values[0] = $value_bin; -# # TBM, bad format - } else { - $values[0] = $value_bin; + foreach my $value_bin (@{$values_arr}) { + my $data = join " ", $format eq 'inet' ? unpackinet($value_bin) + : $format eq 'inets' || ($format eq 'inets2') ? unpackinets_array($value_bin) + : $format eq 'int' ? unpack('N', $value_bin) + : $format eq 'short' ? unpack('n', $value_bin) + : $format eq 'shorts' ? unpack('n*', $value_bin) + : $format eq 'byte' ? unpack('C', $value_bin) + : $format eq 'bytes' ? unpack('C*', $value_bin) + : $format eq 'string' ? $value_bin + : $value_bin + ; + push @value, $data; } - return join(" ", @values); -# return wantarray ? @values : $values[0]; + + #return join(" ", @values); + return wantarray ? @value : $value[0]; } sub removeOption { @@ -394,8 +409,10 @@ if ($self->{isDhcp}) { # add MAGIC_COOKIE and options $bytes .= MAGIC_COOKIE(); foreach my $key ( @{$self->{options_order}} ) { - $bytes .= pack('C', $key); - $bytes .= pack('C/a*', $self->{options}->{$key}); + foreach my $value (@{$self->{options}->{$key}}) { + $bytes .= pack('C', $key); + $bytes .= pack('C/a*', $value); + } } $bytes .= pack('C', 255); } @@ -555,22 +572,16 @@ foreach my $key ( @{$self->{options_order}} ) { my $value; # value of option to be printed - - if ($key == DHO_DHCP_MESSAGE_TYPE()) { - $value = $self->getOptionValue($key); - $value = (exists($REV_DHCP_MESSAGE{$value}) && $REV_DHCP_MESSAGE{$value}) || $self->getOptionValue($key); - } else { - if (exists($DHO_FORMATS{$key})) { - $value = join(" ", $self->getOptionValue($key)); - } else { - $value = $self->getOptionRaw($key); - } - $value =~ s/([[:^print:]])/ sprintf q[\x%02X], ord $1 /eg; # printable text - } - $s .= sprintf(" %s(%d) = %s\n", exists $REV_DHO_CODES{$key} ? $REV_DHO_CODES{$key}: '', $key, $value); + #my @data = exists($DHO_FORMATS{$key}) ? $self->getOptionValue($key) : $self->getOptionRaw($key); + foreach $value ($self->getOptionValue($key)) { + if ($key == DHO_DHCP_MESSAGE_TYPE()) { + $value = (exists($REV_DHCP_MESSAGE{$value}) && $REV_DHCP_MESSAGE{$value}) || $value; + } + $value =~ s/([[:^print:]])/ sprintf q[\x%02X], ord $1 /eg; # printable text + $s .= sprintf(" %s(%d) = %s\n", exists $REV_DHO_CODES{$key} ? $REV_DHO_CODES{$key}: '', $key, $value); + } } $s .= sprintf("padding [%s] = %s\n", length($self->{padding}), unpack('H*', $self->{padding})); - return $s; } #======================================================================= @@ -858,7 +869,7 @@ =item addOptionValue ( CODE, VALUE ) -Adds a DHCP option field. Common code values are listed in +Adds/appends a DHCP option field. Common code values are listed in C<Net::DHCP::Constants> C<DHO_>*. Values are automatically converted according to their data types, @@ -872,6 +883,9 @@ $pac->addOption(DHO_DHCP_MESSAGE_TYPE(), DHCPINFORM()); $pac->addOption(DHO_NAME_SERVERS(), "10.0.0.1", "10.0.0.2")); +=item replaceOptionValue ( CODE, VALUE ) +Replaces a DHCP option field. + =item getOptionValue ( CODE ) Returns the value of a DHCP option.
im merging this on to 0.68, however ive converted the orgy of ternaries/ifthenelse in to dispatcher hashes. will close when ive merged it and built test cases for it.
On Wed Nov 22 07:25:06 2006, cchen wrote: Show quoted text
> Concerning Net::DHCP:Packet 0.66. > > A packet with two VENDOR_ENCAPSULATED_OPTION fields (e.g. very large PXE > options) gets malformed by marshall/serialize. Packet.pm stores the > option data in a hash, so only the last data is assigned to every > instance of an option during serialize. > > (In general, the code does not provision for multiple options of the > same key). > > The attached patch works around this problem by storing the data for an > option in an array. > > addOption appends to this array. > replaceOption replaces the array. > addOptionRaw appends to this array. > replaceOptionRaw replaces the array. > > getOptionValue returns an array of option values. > > > Best regards, > Christof Chen
Hi Christof, would you be able to provide a sample packet so i can use it in the test suite.
Can you please look at 0.67_1 and see if it closes off your bug?