blob: 4dd76d85a75e7387742d9176611dc0e2ed61c16c [file] [log] [blame]
/* A small tool that decodes a raw binary message, while providing useful
* info on corrupted messages also. */
/* Define _ISOC99_SOURCE to get snprintf() even though otherwise in ansi-C mode */
#define _ISOC99_SOURCE 1
#include <stdio.h>
#include <string.h>
#include <pb_decode.h>
#include "test_helpers.h"
#define HISTORY_LEN 32
static pb_byte_t g_history[HISTORY_LEN];
static int g_position;
/* This binds the pb_istream_t to stdin and logs the most recent bytes read. */
bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
{
FILE *file = (FILE*)stream->state;
size_t len = fread(buf, 1, count, file);
if (len < HISTORY_LEN)
{
memmove(g_history, g_history + len, HISTORY_LEN - len);
memcpy(g_history + HISTORY_LEN - len, buf, len);
}
else
{
memcpy(g_history, buf + len - HISTORY_LEN, HISTORY_LEN);
}
g_position += len;
if (len == count)
{
return true;
}
else
{
stream->bytes_left = 0;
return false;
}
}
void print_history(int position)
{
int i;
if (position < g_position - HISTORY_LEN)
position = g_position - HISTORY_LEN;
printf("LATEST BYTES READ (%d to %d): ", position, g_position);
for (i = HISTORY_LEN - (g_position - position); i < HISTORY_LEN; i++)
{
printf("%02x ", g_history[i]);
}
printf("\n");
}
bool raw_decode(pb_istream_t *stream, const char *indent)
{
const char *wiretypes[8] = {"VARINT", "64BIT", "STRING", "SGRP", "EGRP", "32BIT", "????", "????"};
while (stream->bytes_left)
{
uint32_t tag;
pb_wire_type_t wire_type;
bool eof;
int position = g_position;
if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
{
if (eof)
{
break;
}
else
{
printf("ERROR: Failed to parse tag: %s\n", PB_GET_ERROR(stream));
print_history(position);
return false;
}
}
if (tag == 0)
{
printf("%sterminating on zero tag\n", indent);
return true;
}
printf("%sAt %d: field tag %d, wire type %d (%s)",
indent, position, (int)tag, wire_type, wiretypes[wire_type]);
if (wire_type == PB_WT_VARINT)
{
uint64_t value;
position = g_position;
if (!pb_decode_varint(stream, &value))
{
printf("\n%sERROR: Failed to parse varint: %s\n", indent, PB_GET_ERROR(stream));
print_history(position);
return false;
}
printf(", varint value (%d bytes): %llu\n",
g_position - position, (unsigned long long)value);
}
else if (wire_type == PB_WT_64BIT)
{
uint64_t value;
position = g_position;
if (!pb_decode_fixed64(stream, &value))
{
printf("\n%sERROR: Failed to parse fixed64: %s\n", indent, PB_GET_ERROR(stream));
print_history(position);
return false;
}
printf(", fixed64 value (%d bytes): 0x%016llx\n",
g_position - position, (unsigned long long)value);
}
else if (wire_type == PB_WT_32BIT)
{
uint32_t value;
position = g_position;
if (!pb_decode_fixed32(stream, &value))
{
printf("\n%sERROR: Failed to parse fixed32: %s\n", indent, PB_GET_ERROR(stream));
print_history(position);
return false;
}
printf(", fixed32 value (%d bytes): 0x%08lx\n",
g_position - position, (unsigned long)value);
}
else if (wire_type == PB_WT_STRING)
{
pb_istream_t substream;
position = g_position;
if (!pb_make_string_substream(stream, &substream))
{
printf("ERROR: Failed to parse string length: %s\n", PB_GET_ERROR(stream));
print_history(position);
return false;
}
else
{
if (substream.bytes_left == 0)
{
printf(", empty string\n");
}
else
{
char prefix[8];
snprintf(prefix, sizeof(prefix), "f%d> ", (int)tag);
printf(", string len %d bytes, attempting recursive decode\n",
(int)substream.bytes_left);
if (!raw_decode(&substream, prefix))
{
printf("%sfield %d: recursive decode failed, continuing with upper level\n\n",
indent, (int)tag);
}
pb_close_string_substream(stream, &substream);
}
}
}
else
{
printf("\n");
}
}
return true;
}
int main()
{
pb_istream_t stream = {&callback, NULL, SIZE_MAX};
stream.state = stdin;
SET_BINARY_MODE(stdin);
if (!raw_decode(&stream, ""))
{
return 1;
} else {
return 0;
}
}