use strict; use warnings; package NetPacket::IPX; use parent qw(NetPacket); use Carp; sub new { my ($class, %packet) = @_; foreach my $key(qw(tc type dest_network dest_node dest_socket src_network src_node src_socket data)) { croak("Missing $key argument") unless(defined($packet{$key})); } croak("Invalid tc argument") unless($packet{tc} =~ m/^\d+$/ && $packet{tc} <= 255); croak("Invalid type argument") unless($packet{type} =~ m/^\d+$/ && $packet{type} <= 255); _check_address("destination", $packet{dest_network}, $packet{dest_node}, $packet{dest_socket}); _check_address("source", $packet{src_network}, $packet{src_node}, $packet{src_socket}); return bless(\%packet, $class); } sub _check_address { my ($direction, $network, $node, $socket) = @_; my $OCTET = qr/[0-9A-F][0-9A-F]?/i; croak("Invalid $direction network") unless($network =~ m/^$OCTET(:$OCTET){3}$/); croak("Invalid $direction node") unless($node =~ m/^$OCTET(:$OCTET){5}$/); croak("Invalid $direction socket") unless($socket =~ m/^\d+$/ && $socket <= 65535); } # # Decode the packet # sub decode { my ($class, $pkt, $parent) = @_; my $self = bless({ _parent => $parent, _frame => $pkt, }, $class); if(defined($pkt)) { if(length($pkt) < 30) { carp("Truncated packet (incomplete header)"); return $self; } # Use array slices to capture the appropriate number of bytes # from each address field. my ( $checksum, $length, $tc, $type, @dst_network, @dst_node, $dst_socket, @src_network, @src_node, $src_socket, ); ( $checksum, $length, $tc, $type, @dst_network[0..3], @dst_node[0..5], $dst_socket, @src_network[0..3], @src_node[0..5], $src_socket, ) = unpack("nnCC C4C6n C4C6n", $pkt); $self->{tc} = $tc; $self->{type} = $type; $self->{dest_network} = _addr_to_string(@dst_network); $self->{dest_node} = _addr_to_string(@dst_node); $self->{dest_socket} = $dst_socket; $self->{src_network} = _addr_to_string(@src_network); $self->{src_node} = _addr_to_string(@src_node); $self->{src_socket} = $src_socket; if($length < 30) { carp("Invalid packet (length < 30)"); return $self; } if(length($pkt) < $length) { carp("Truncated packet (data truncated)"); $self->{data} = substr($pkt, 30); } else{ $self->{data} = substr($pkt, 30, ($length - 30)); } } return $self; } # # Strip header from packet and return the data contained in it # sub strip { my ($pkt) = @_; return NetPacket::IPX->decode($pkt)->{data}; } # # Encode a packet # sub encode { my ($self) = @_; return pack("nnCC", 0xFFFF, 30 + length($self->{data}), $self->{tc}, $self->{type}) ._addr_from_string($self->{dest_network}) ._addr_from_string($self->{dest_node}) .pack("n", $self->{dest_socket}) ._addr_from_string($self->{src_network}) ._addr_from_string($self->{src_node}) .pack("n", $self->{src_socket}) .$self->{data}; } sub _addr_to_string { my (@bytes) = @_; return join(":", map { sprintf("%02X", $_) } @bytes); } sub _addr_from_string { my ($string) = @_; return join("", map { pack("C", hex($_)) } split(m/:/, $string)); } 1; __END__ =pod =head1 NAME NetPacket::IPX - Assemble and disassemble IPX packets. =head1 SYNOPSIS use NetPacket::IPX; my $ipx = NetPacket::IPX->decode($raw_pkt); my $raw_pkt = $ipx->encode(); my $ipx = NetPacket::IPX->new( tc => 0, type => 1, dest_network => "00:00:00:01", dest_node => "FF:FF:FF:FF:FF:FF", dest_socket => 1234, src_network => "00:00:00:01", src_node => "12:34:56:78:90:AB", src_socket => 5678, data => "...", ); =head1 DESCRIPTION C is a C class for encoding and decoding IPX packets. =head1 METHODS =head2 decode($raw_pkt) Decode a packet and return a C instance. =head2 encode() Return the encoded form of a C instance. =head2 new(%options) Construct a C instance with arbitrary contents. All arguments listed in the SYNOPSIS are mandatory. Throws an exception on missing/invalid arguments. =head1 INSTANCE DATA The following fields are available in a C instance: =over =item tc Traffic Control field, the number of routers an IPX packet has passed through. =item type Type field. =item dest_network Destination network number, in the format C. =item dest_node Destination node number, in the format C. =item dest_socket Destination socket number. =item src_network Source network number, in the format C. =item dest_node Source node number, in the format C. =item dest_socket Source socket number. =item data Packet payload. =back =head1 COPYRIGHT Copyright (C) 2014 Daniel Collins This module is free software. You can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Daniel Collins Esolemnwarning@solemnwarning.netE =cut