# Net::DHCP::Session.pm # Version 0.0 # Author: F. van Dun # package Net::DHCP::Session; use strict; use Carp; use IO::Socket::INET; use Sys::Hostname; use Net::DHCP::Options; use Net::DHCP::Packet; use vars qw($VERSION); use IO::Select; $VERSION=0.1; my $ops = new Net::DHCP::Options; # Shortcut use static declarations, instead of Net::DHCP::Options. sub genMAC { my $tmp_mac = "00"; my $i = 0; while ($i++ < 5 ) { $tmp_mac.= sprintf("%x",int rand 16); $tmp_mac.= sprintf("%x",int rand 16); } return ($tmp_mac); } =pod =head1 NAME Net::DHCP::Session - Object methods to simulate a DHCP pc. =head1 SYNOPSIS =head1 DESCRIPTION Represents a DHCP packet as specified in RFC 1533, RFC 2132. =head2 CONSTRUCTORS =item new =head2 METHODS =cut =pod =item new(%ARGS) The hash %ARGS can contain any of these keys: =cut sub new { my ($class, %args) = @_; my $self = {}; bless $self, $class; $self->{chaddr} = $args{Chaddr} || Net::DHCP::Packet::randomstring(6); $self->{hostname} = $args{Hostname}; $self->{request_ip} = $args{Request_ip}; $self->{server_ip} = $args{Server_ip}; $self->{localaddr} = $args{Localaddr} || inet_ntoa ( scalar ( gethostbyname(hostname()) ) ); $self->{xid} = undef; $self->{ciaddr} = $args{Ciaddr}; $self->init(); return $self; } sub _getoptions { my ($self,$packet) = @_; $self->{server_ip} = inet_ntoa($packet->getOption($ops->SERVER_IP() ) ) if ( $packet->getOption($ops->SERVER_IP() ) ); $self->{lease_time} = unpack('N',$packet->getOption($ops->LEASE_TIME()) ) if ($packet->getOption($ops->LEASE_TIME()) ); $self->{subnet_mask} = inet_ntoa($packet->getOption($ops->SUBNET_MASK() ) ) if ($packet->getOption($ops->SUBNET_MASK() )); if ($packet->getOption($ops->GATEWAY_ADDRESS())) { my $n = length($packet->getOption($ops->GATEWAY_ADDRESS()))/4 ; # 4 bytes per router $self->{router} = join ( ':', map {inet_ntoa($_)} unpack("a4" x $n, $packet->getOption($ops->GATEWAY_ADDRESS()) ) ); } $self->{domain} = $packet->getOption($ops->DOMAIN() ); } sub dumpoptions { my ($self) = @_; my $s = ''; $s .= "DHCP server ip = $self->{server_ip} ,\n" if ($self->{server_ip}); $s .= "lease time = $self->{lease_time},\n" if ($self->{lease_time}); $s .= "mask = $self->{subnet_mask},\n" if ($self->{subnet_mask}); $s .= "router = $self->{router},\n" if ($self->{router}); $s .= "domain = $self->{domain}\n" if ($self->{domain}); return $s; } sub init { my ($self) = @_; if ($self->{server_ip} && $self->{server_ip} ne '255.255.255.255') { $self->{sock} = IO::Socket::INET->new( PeerAddr => $self->{server_ip}, PeerPort => '67', LocalPort => '68', LocalAddr => $self->{localaddr}, Proto => 'udp') || die "Socket creation error: $!\n"; } else { $self->{sock} = IO::Socket::INET->new( LocalPort => '68', LocalAddr => $self->{localaddr}, Proto => 'udp') || die "Socket broadcast creation error: $!\n"; $self->{BCAST} = 1; $self->{bcastaddr} = sockaddr_in("67",inet_aton("255.255.255.255")); } } sub discover { my ($self) = @_; my $pdiscover = Net::DHCP::Packet->discover(Chaddr => $self->{chaddr}, Hostname => $self->{hostname}); $self->{xid} = $pdiscover->xid() unless $self->{xid}; $self->{sock}->send($pdiscover->serialize()) || die "Error sending discovery:$!\n"; print "sent dhcp discover...\n"; } sub await_offer { my ($self) = @_; my $preply; do { my $buf; print "Waiting for Offer.\n"; $self->{sock}->recv($buf,3000) || die "recv offer:$!\n"; $preply = new Net::DHCP::Packet()->marshall($buf); print "Got a packet...\n"; #print $preply->toString(); } until ( $preply->xid() eq $self->{xid} ); exit(1) unless ($preply->getOption(Net::DHCP::Options::MESSAGE_TYPE()) eq Net::DHCP::Options::OFFER()); $self->{server_ip} = inet_ntoa($preply->getOption(Net::DHCP::Options::SERVER_IP() ) ); } sub request { my ($self) = @_; my $prequest = Net::DHCP::Packet->request(Chaddr => $self->{chaddr}, Hostname => $self->{hostname}, Xid => $self->{xid}, Server_ip => $self->{server_ip}); $self->{sock}->send($prequest->serialize()) || die "Error sending request:$!\n"; print "Sent request.\n"; } sub renew { my ($self) = @_; my $prequest = Net::DHCP::Packet->request(Chaddr => $self->{chaddr}, Hostname => $self->{hostname}); $self->{sock}->send($prequest->serialize()) || die "Error sending request:$!\n"; print "Sent request.\n"; } sub inform { my ($self) = @_; $self->{ciaddr} = $self->{localaddr}; my $pinform = Net::DHCP::Packet->inform(Chaddr => $self->{chaddr}, Hostname => $self->{hostname}, Ciaddr => $self->{ciaddr}); if ($self->{BCAST}) { $self->{sock}->sockopt(SO_BROADCAST,1); $self->{sock}->send($pinform->serialize(),0,$self->{bcastaddr}) || die "Error sending broadcast inform:$!\n"; } else { $self->{sock}->send($pinform->serialize()) || die "Error sending inform:$!\n"; } print "Sent inform.\n"; } sub await_ack { my ($self) = @_; my $ACK=0; my $preply2; do { my $buf; print "Waiting for ACK.\n"; $self->{sock}->recv($buf,3000) || die "recv ack:$!\n"; $preply2 = new Net::DHCP::Packet()->marshall($buf); print "Got a packet...\n"; } until ($preply2->xid() eq $self->{xid}); for ( $preply2->getOption(Net::DHCP::Options::MESSAGE_TYPE()) ) { ($_ eq Net::DHCP::Options::NAK) && do { print "DHCP request refused by ".$preply2->siaddr().".\n"; last; }; ($_ eq Net::DHCP::Options::ACK) && do { print "Got IP address " . $preply2->yiaddr()." for $self->{chaddr}.\n"; $self->{ciaddr} = $preply2->yiaddr(); $ACK=1; $self->_getoptions($preply2); last; }; } # } sub release { my ($self) = @_; my $prelease = Net::DHCP::Packet->release(Server_ip => $self->{server_ip}, Chaddr => $self->{chaddr}, Ciaddr => $self->{ciaddr}, Hostname => $self->{hostname} ); $self->{sock}->send($prelease->serialize()) || die "Error sending release:$!\n"; print "Released address.\n"; } sub await_informacks { my ($self) = @_; my $sel = new IO::Select( $self->{sock} ) || die "select failure : $!\n"; my $buff; while($sel->can_read(10)) { print "receiving...\n"; my $remote = $self->{sock}->recv($buff,3000); my $preply = new Net::DHCP::Packet()->marshall($buff); $self->_getoptions($preply); print $self->dumpoptions(); } } 1;