views:

318

answers:

2

I have a Java program that spits out, in space-separated hexadecimal format, 16 bytes of raw packet received over the network. Since I dont want to change that code, I am piping the result to a Perl script that, theoretically, can simply unpack this from STDIN into recognizable variables. The following is a sample of the line input to my Perl file:

FF FF 09 7D 10  01  07  01 00  02 00  1D 00  00 00  00 00  06 00  07 00 
|--garbage-----|c--|c--|int---|int---|int---|int---|int---|int---|int---|

(c is for char/byte, int for 16bit integer variable)

I initially wanted to use unpack to cleanly separate each input line into variables that I needed. However, because of the space delimit in the string, I am not sure how to handle it (I can use 'A' as a template, but then I might as well just use split!)

Is there a elegant way of using unpack()? I am not a Perl master, but the other way is to, as I suggested before, use split and then manually convert each hex to a byte, and then use bit manipulations and masks to get what I want. Any other suggestions (if unpack doesnt save the day)?

+3  A: 

If you want to use unpack on unpacked data, you'll need to pack it again first. And you'll need to remove the spaces before you do that.

In other words,

$line =~ tr/ //d;          # remove spaces
$line = pack 'H*', $line;  # convert hex to binary
# Now you can use unpack.
Porculus
I actually use this solution due to its cleaner looked (I understood what the code was doing) combined with the unpacking suggested above. and I can also parse the $line if the java code spits an error string.
intiha
+8  A: 

Assuming those ints are in big-endian order, use

#! /usr/bin/perl

use warnings;
use strict;

# for demo only
*ARGV = *DATA;

while (<>) {
  my @fields = unpack "x5C2n7",
               pack "C*",
               map hex, split;

  print "[", join("][" => @fields), "]\n";
}

__DATA__
FF FF 09 7D 10 01 07 01 00 02 00 1D 00 00 00 00 00 06 00 07 00

It starts off by packing in the bytes (C*) according to their values. The unpack template has the following parts:

  • x5 skips five bytes
  • C2 decodes two unsigned char values
  • n7 decodes seven 16-bit big-endian unsigned integers

Output:

$ ./dump-packets
[1][7][256][512][7424][0][0][1536][1792]
Greg Bacon
You can make it look prettier by saying `map { hex } split` :)
friedo
Or even just `map hex, split` — almost Haskell!
Greg Bacon
actually I confirmed that the bytes are in little endian. wierd, as they are sent over a network, I would have thought that should have been in network order. Oh well, the only change then needs to be unpack "x5C2n7" to unpack "x5C2v7", right? And thanks for the reply... this forum is awesome
intiha
You're welcome! Tell your friends! Yes, your template is correct for unpacking little-endian 16-bit unsigned integers. "Network order" is merely the name of a convention: as you've observed, multibyte `int`s aren't automatically converted to network order when transmitted over a network.
Greg Bacon