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.