1 /* A small tool that decodes a raw binary message, while providing useful
2  * info on corrupted messages also. */
3 
4 /* Define _ISOC99_SOURCE to get snprintf() even though otherwise in ansi-C mode */
5 #define _ISOC99_SOURCE 1
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include <pb_decode.h>
10 #include "test_helpers.h"
11 
12 #define HISTORY_LEN 32
13 static pb_byte_t g_history[HISTORY_LEN];
14 static int g_position;
15 
16 /* This binds the pb_istream_t to stdin and logs the most recent bytes read. */
callback(pb_istream_t * stream,uint8_t * buf,size_t count)17 bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
18 {
19     FILE *file = (FILE*)stream->state;
20     size_t len = fread(buf, 1, count, file);
21 
22     if (len < HISTORY_LEN)
23     {
24         memmove(g_history, g_history + len, HISTORY_LEN - len);
25         memcpy(g_history + HISTORY_LEN - len, buf, len);
26     }
27     else
28     {
29         memcpy(g_history, buf + len - HISTORY_LEN, HISTORY_LEN);
30     }
31 
32     g_position += len;
33 
34     if (len == count)
35     {
36         return true;
37     }
38     else
39     {
40         stream->bytes_left = 0;
41         return false;
42     }
43 }
44 
print_history(int position)45 void print_history(int position)
46 {
47     int i;
48 
49     if (position < g_position - HISTORY_LEN)
50         position = g_position - HISTORY_LEN;
51 
52     printf("LATEST BYTES READ (%d to %d): ", position, g_position);
53 
54     for (i = HISTORY_LEN - (g_position - position); i < HISTORY_LEN; i++)
55     {
56         printf("%02x ", g_history[i]);
57     }
58 
59     printf("\n");
60 }
61 
raw_decode(pb_istream_t * stream,const char * indent)62 bool raw_decode(pb_istream_t *stream, const char *indent)
63 {
64     const char *wiretypes[8] = {"VARINT", "64BIT", "STRING", "SGRP", "EGRP", "32BIT", "????", "????"};
65 
66     while (stream->bytes_left)
67     {
68         uint32_t tag;
69         pb_wire_type_t wire_type;
70         bool eof;
71         int position = g_position;
72 
73         if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
74         {
75             if (eof)
76             {
77                 break;
78             }
79             else
80             {
81                 printf("ERROR: Failed to parse tag: %s\n", PB_GET_ERROR(stream));
82                 print_history(position);
83                 return false;
84             }
85         }
86 
87         if (tag == 0)
88         {
89             printf("%sterminating on zero tag\n", indent);
90             return true;
91         }
92 
93         printf("%sAt %d: field tag %d, wire type %d (%s)",
94                indent, position, (int)tag, wire_type, wiretypes[wire_type]);
95 
96         if (wire_type == PB_WT_VARINT)
97         {
98             uint64_t value;
99             position = g_position;
100             if (!pb_decode_varint(stream, &value))
101             {
102                 printf("\n%sERROR: Failed to parse varint: %s\n", indent, PB_GET_ERROR(stream));
103                 print_history(position);
104                 return false;
105             }
106 
107             printf(", varint value (%d bytes): %llu\n",
108                    g_position - position, (unsigned long long)value);
109         }
110         else if (wire_type == PB_WT_64BIT)
111         {
112             uint64_t value;
113             position = g_position;
114             if (!pb_decode_fixed64(stream, &value))
115             {
116                 printf("\n%sERROR: Failed to parse fixed64: %s\n", indent, PB_GET_ERROR(stream));
117                 print_history(position);
118                 return false;
119             }
120 
121             printf(", fixed64 value (%d bytes): 0x%016llx\n",
122                    g_position - position, (unsigned long long)value);
123         }
124         else if (wire_type == PB_WT_32BIT)
125         {
126             uint32_t value;
127             position = g_position;
128             if (!pb_decode_fixed32(stream, &value))
129             {
130                 printf("\n%sERROR: Failed to parse fixed32: %s\n", indent, PB_GET_ERROR(stream));
131                 print_history(position);
132                 return false;
133             }
134 
135             printf(", fixed32 value (%d bytes): 0x%08lx\n",
136                    g_position - position, (unsigned long)value);
137         }
138         else if (wire_type == PB_WT_STRING)
139         {
140             pb_istream_t substream;
141             position = g_position;
142             if (!pb_make_string_substream(stream, &substream))
143             {
144                 printf("ERROR: Failed to parse string length: %s\n", PB_GET_ERROR(stream));
145                 print_history(position);
146                 return false;
147             }
148             else
149             {
150 
151                 if (substream.bytes_left == 0)
152                 {
153                     printf(", empty string\n");
154                 }
155                 else
156                 {
157                     char prefix[8];
158                     snprintf(prefix, sizeof(prefix), "f%d> ", (int)tag);
159 
160                     printf(", string len %d bytes, attempting recursive decode\n",
161                        (int)substream.bytes_left);
162 
163                     if (!raw_decode(&substream, prefix))
164                     {
165                         printf("%sfield %d: recursive decode failed, continuing with upper level\n\n",
166                                indent, (int)tag);
167                     }
168 
169                     pb_close_string_substream(stream, &substream);
170                 }
171             }
172         }
173         else
174         {
175             printf("\n");
176         }
177     }
178 
179     return true;
180 }
181 
main()182 int main()
183 {
184     pb_istream_t stream = {&callback, NULL, SIZE_MAX};
185     stream.state = stdin;
186     SET_BINARY_MODE(stdin);
187 
188     if (!raw_decode(&stream, ""))
189     {
190         return 1;
191     } else {
192         return 0;
193     }
194 }
195