5

I have the following struct, from the NRPE daemon code in C:

typedef struct packet_struct {
  int16_t packet_version;
  int16_t packet_type;
  uint32_t crc32_value;
  int16_t result_code;
  char buffer[1024];
} packet;

I want to send this data format to the C daemon from Python. The CRC is calculated when crc32_value is 0, then it is put into the struct. My Python code to do this is as follows:

cmd = '_NRPE_CHECK'
pkt = struct.pack('hhIh1024s', 2, 1, 0, 0, cmd)
# pkt has length of 1034, as it should
checksum = zlib.crc32(pkt) & 0xFFFFFFFF
pkt = struct.pack('hhIh1024s', 2, 1, checksum, 0, cmd)
socket.send(....)

The daemon is receiving these values: version=2 type=1 crc=FE4BBC49 result=0

But it is calculating crc=3731C3FD

The actual C code to compute the CRC is:

https://github.com/KristianLyng/nrpe/blob/master/src/utils.c

and it is called via:

calculate_crc32((char *)packet, sizeof(packet));

When I ported those two functions to Python, I get the same as what zlib.crc32 returns.

Is my struct.pack call correct? Why is my CRC computation differing from the server's?

Santosh Kumar
  • 26,475
  • 20
  • 67
  • 118
verideskstar
  • 183
  • 2
  • 8
  • 1
    Are you sure both platforms are using the same alignment? – Paulo Scardine Aug 17 '12 at 01:32
  • I would believe so as the two numerical values in the front of the packed bytestring came out to be correct. – verideskstar Aug 17 '12 at 01:38
  • It could be a byte ordering problem. The server is probably using network (= big-endian) order but your script is using native since it doesn't specify anything at the beginning of the format string and override that default -- see [this](http://docs.python.org/library/struct.html#byte-order-size-and-alignment) section of the online docs. – martineau Aug 17 '12 at 03:13
  • Possibilities: (1) endian differences (2) byte-swapping differences (3) padding [alignment] differences – wberry Aug 17 '12 at 03:14
  • 1
    @wberry: Aren't 1) and 2) basically the same thing? – martineau Aug 17 '12 at 03:16
  • Actually no. In #1 you could read the same single byte as either 0xE2 or 0x47. In #2 bytes could be stored as "UNIX" or "NUXI". – wberry Aug 17 '12 at 03:28
  • @wberry: _endianness_ usually means byte order. Your example #1 is commonly called [bit numbering](http://en.wikipedia.org/wiki/Bit_numbering) (although technically it could be called "bit endianness"). Regardless, the `struct` module only deals with byte streams -- but, fortunately, that's unlikely the problem here. – martineau Aug 18 '12 at 16:43

1 Answers1

2

From the Python struct documentation:

To handle platform-independent data formats or omit implicit pad bytes, use standard size and alignment instead of native size and alignment: see Byte Order, Size, and Alignment for details.

Use '!' as the first format character to make the packed structure platform-independent. It forces big-endian, standard type sizes, and no pad bytes. Then the CRCs should be consistent.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • Using '>' at the front got the Endianness correct, but for some reason I also had to add one bytes to the end. The size of that C-struct is 1036, not 1034; no idea why... – verideskstar Aug 17 '12 at 04:32
  • 1
    @sadris: It's likely padding the size of the C-struct up to the nearest whole 32-bits (4-bytes) native word size due to [C/C++ structure padding](http://en.wikipedia.org/wiki/Sizeof#Structure_padding). Some compilers have an option to turn this on and off. – martineau Aug 18 '12 at 17:15
  • Just compare the 1034 bytes instead of trying to pad it to 1036 bytes. The last two pad bytes of C structure are probably not initialized, so the padding you add won't match. Unless you copy it from the C structure, which would just be silly. – Mark Adler Aug 19 '12 at 20:18