/* 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 #include #include #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; } }