Coverage Report

Created: 2026-09-01 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-binary-dumper.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "ostream.h"
6
#include "array.h"
7
#include "buffer.h"
8
9
#include "sieve-common.h"
10
#include "sieve-extensions.h"
11
#include "sieve-dump.h"
12
#include "sieve-script.h"
13
14
#include "sieve-binary-private.h"
15
16
/*
17
 * Binary dumper object
18
 */
19
20
struct sieve_binary_dumper {
21
  pool_t pool;
22
23
  /* Dumptime environment */
24
  struct sieve_dumptime_env dumpenv;
25
};
26
27
struct sieve_binary_dumper *
28
sieve_binary_dumper_create(struct sieve_binary *sbin)
29
0
{
30
0
  pool_t pool;
31
0
  struct sieve_binary_dumper *dumper;
32
33
0
  pool = pool_alloconly_create("sieve_binary_dumper", 4096);
34
0
  dumper = p_new(pool, struct sieve_binary_dumper, 1);
35
0
  dumper->pool = pool;
36
0
  dumper->dumpenv.dumper = dumper;
37
38
0
  dumper->dumpenv.sbin = sbin;
39
0
  sieve_binary_ref(sbin);
40
41
0
  dumper->dumpenv.svinst = sieve_binary_svinst(sbin);
42
43
0
  return dumper;
44
0
}
45
46
void sieve_binary_dumper_free(struct sieve_binary_dumper **dumper)
47
0
{
48
0
  sieve_binary_unref(&(*dumper)->dumpenv.sbin);
49
0
  pool_unref(&((*dumper)->pool));
50
51
0
  *dumper = NULL;
52
0
}
53
54
pool_t sieve_binary_dumper_pool(struct sieve_binary_dumper *dumper)
55
0
{
56
0
  return dumper->pool;
57
0
}
58
59
/*
60
 * Formatted output
61
 */
62
63
void sieve_binary_dumpf(const struct sieve_dumptime_env *denv,
64
      const char *fmt, ...)
65
0
{
66
0
  string_t *outbuf = t_str_new(128);
67
0
  va_list args;
68
69
0
  va_start(args, fmt);
70
0
  str_vprintfa(outbuf, fmt, args);
71
0
  va_end(args);
72
73
0
  o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf));
74
0
}
75
76
void sieve_binary_dump_sectionf(const struct sieve_dumptime_env *denv,
77
        const char *fmt, ...)
78
0
{
79
0
  string_t *outbuf = t_str_new(128);
80
0
  va_list args;
81
82
0
  va_start(args, fmt);
83
0
  str_printfa(outbuf, "\n* ");
84
0
  str_vprintfa(outbuf, fmt, args);
85
0
  str_printfa(outbuf, ":\n\n");
86
0
  va_end(args);
87
88
0
  o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf));
89
0
}
90
91
/*
92
 * Dumping the binary
93
 */
94
95
bool sieve_binary_dumper_run(struct sieve_binary_dumper *dumper,
96
           struct ostream *stream, bool verbose)
97
0
{
98
0
  struct sieve_binary *sbin = dumper->dumpenv.sbin;
99
0
  struct sieve_script *script = sieve_binary_script(sbin);
100
0
  struct sieve_dumptime_env *denv = &(dumper->dumpenv);
101
0
  const struct sieve_binary_header *header = &sbin->header;
102
0
  struct sieve_binary_block *sblock;
103
0
  bool success = TRUE;
104
0
  sieve_size_t offset;
105
0
  int count, i;
106
107
0
  dumper->dumpenv.stream = stream;
108
109
  /* Dump header */
110
111
0
  sieve_binary_dump_sectionf(denv, "Header");
112
113
0
  sieve_binary_dumpf(denv,
114
0
    "version = %"PRIu16".%"PRIu16"\n"
115
0
    "flags = 0x%08"PRIx32"\n",
116
0
    header->version_major, header->version_minor,
117
0
    header->flags);
118
119
  /* Dump list of binary blocks */
120
121
0
  if (verbose) {
122
0
    count = sieve_binary_block_count(sbin);
123
124
0
    sieve_binary_dump_sectionf(denv, "Binary blocks (count: %d)",
125
0
             count);
126
127
0
    for (i = 0; i < count; i++) {
128
0
      struct sieve_binary_block *sblock =
129
0
        sieve_binary_block_get(sbin, i);
130
131
0
      if (sblock == NULL)
132
0
        return FALSE;
133
134
0
      sieve_binary_dumpf(
135
0
        denv, "%3d: size: %zu bytes\n",
136
0
        i, sieve_binary_block_get_size(sblock));
137
0
    }
138
0
  }
139
140
  /* Dump script metadata */
141
142
0
  sieve_binary_dump_sectionf(denv, "Script metadata (block: %d)",
143
0
           SBIN_SYSBLOCK_SCRIPT_DATA);
144
0
  sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA);
145
0
  if (sblock == NULL)
146
0
    return FALSE;
147
148
0
  T_BEGIN {
149
0
    offset = 0;
150
0
    success = sieve_script_binary_dump_metadata(
151
0
      script, denv, sblock, &offset);
152
0
  } T_END;
153
154
0
  if (!success)
155
0
    return FALSE;
156
157
  /* Dump list of used extensions */
158
159
0
  count = sieve_binary_extensions_count(sbin);
160
0
  if (count > 0) {
161
0
    sieve_binary_dump_sectionf(
162
0
      denv, "Required extensions (block: %d)",
163
0
      SBIN_SYSBLOCK_EXTENSIONS);
164
165
0
    for (i = 0; i < count; i++) {
166
0
      const struct sieve_extension *ext =
167
0
        sieve_binary_extension_get_by_index(sbin, i);
168
169
0
      sblock = sieve_binary_extension_get_block(sbin, ext);
170
171
0
      if (sblock == NULL) {
172
0
        sieve_binary_dumpf(
173
0
          denv, "%3d: %s (id: %d)\n",
174
0
          i, sieve_extension_name(ext), ext->id);
175
0
      } else {
176
0
        sieve_binary_dumpf(
177
0
          denv, "%3d: %s (id: %d; block: %d)\n",
178
0
          i, sieve_extension_name(ext), ext->id,
179
0
          sieve_binary_block_get_id(sblock));
180
0
      }
181
0
    }
182
0
  }
183
184
  /* Dump extension-specific elements of the binary */
185
186
0
  count = sieve_binary_extensions_count(sbin);
187
0
  if (count > 0) {
188
0
    for (i = 0; i < count; i++) {
189
0
      success = TRUE;
190
191
0
      T_BEGIN {
192
0
        const struct sieve_extension *ext =
193
0
          sieve_binary_extension_get_by_index(
194
0
            sbin, i);
195
196
0
        if (ext->def != NULL &&
197
0
            ext->def->binary_dump != NULL) {
198
0
          success = ext->def->binary_dump(
199
0
            ext, denv);
200
0
        }
201
0
      } T_END;
202
203
0
      if (!success)
204
0
        return FALSE;
205
0
    }
206
0
  }
207
208
  /* Dump main program */
209
210
0
  sieve_binary_dump_sectionf(denv, "Main program (block: %d)",
211
0
           SBIN_SYSBLOCK_MAIN_PROGRAM);
212
213
0
  dumper->dumpenv.sblock =
214
0
    sieve_binary_block_get(sbin, SBIN_SYSBLOCK_MAIN_PROGRAM);
215
0
  if (dumper->dumpenv.sblock == NULL)
216
0
    return FALSE;
217
218
0
  dumper->dumpenv.cdumper = sieve_code_dumper_create(&(dumper->dumpenv));
219
220
0
  if (dumper->dumpenv.cdumper != NULL) {
221
0
    sieve_code_dumper_run(dumper->dumpenv.cdumper);
222
223
0
    sieve_code_dumper_free(&dumper->dumpenv.cdumper);
224
0
  }
225
226
  /* Finish with empty line */
227
0
  sieve_binary_dumpf(denv, "\n");
228
229
0
  return TRUE;
230
0
}
231
232
/*
233
 * Hexdump production
234
 */
235
236
bool sieve_binary_dumper_hexdump(struct sieve_binary_dumper *dumper,
237
         struct ostream *stream)
238
0
{
239
0
  struct sieve_binary *sbin = dumper->dumpenv.sbin;
240
0
  struct sieve_dumptime_env *denv = &(dumper->dumpenv);
241
0
  int count, i;
242
243
0
  dumper->dumpenv.stream = stream;
244
245
0
  count = sieve_binary_block_count(sbin);
246
247
  /* Block overview */
248
249
0
  sieve_binary_dump_sectionf(denv, "Binary blocks (count: %d)", count);
250
251
0
  for (i = 0; i < count; i++) {
252
0
    struct sieve_binary_block *sblock =
253
0
      sieve_binary_block_get(sbin, i);
254
255
0
    if (sblock == NULL)
256
0
      return FALSE;
257
258
0
    sieve_binary_dumpf(denv, "%3d: size: %zu bytes\n",
259
0
           i, sieve_binary_block_get_size(sblock));
260
0
  }
261
262
  /* Hexdump for each block */
263
264
0
  for (i = 0; i < count; i++) {
265
0
    struct sieve_binary_block *sblock =
266
0
      sieve_binary_block_get(sbin, i);
267
0
    buffer_t *blockbuf = sieve_binary_block_get_buffer(sblock);
268
0
    string_t *line;
269
0
    size_t data_size;
270
0
    const unsigned char *data;
271
0
    size_t offset;
272
273
0
    data = buffer_get_data(blockbuf, &data_size);
274
275
    // FIXME: calculate offset more nicely.
276
0
    sieve_binary_dump_sectionf(
277
0
      denv, "Block %d (%zu bytes, file offset %08llx)", i, data_size,
278
0
      (unsigned long long int)sblock->offset + 8);
279
280
0
    line = t_str_new(128);
281
0
    offset = 0;
282
0
    while (offset < data_size) {
283
0
      size_t len = (data_size - offset >= 16 ?
284
0
              16 : data_size - offset);
285
0
      size_t b;
286
287
0
      str_printfa(line, "%08llx  ",
288
0
            (unsigned long long)offset);
289
290
0
      for (b = 0; b < len; b++) {
291
0
        str_printfa(line, "%02x ", data[offset+b]);
292
0
        if (b == 7)
293
0
          str_append_c(line, ' ');
294
0
      }
295
296
0
      if (len < 16) {
297
0
        if (len <= 7)
298
0
          str_append_c(line, ' ');
299
300
0
        for (b = len; b < 16; b++)
301
0
          str_append(line, "   ");
302
0
      }
303
304
0
      str_append(line, " |");
305
306
0
      for (b = 0; b < len; b++) {
307
0
        const unsigned char c = data[offset+b];
308
309
0
        if (c >= 32 && c <= 126)
310
0
          str_append_c(line, (const char)c);
311
0
        else
312
0
          str_append_c(line, '.');
313
0
      }
314
315
0
      str_append(line, "|\n");
316
0
      o_stream_nsend(stream, str_data(line), str_len(line));
317
0
      str_truncate(line, 0);
318
0
      offset += len;
319
0
    }
320
321
0
    str_printfa(line, "%08llx\n", (unsigned long long)offset);
322
0
    o_stream_nsend(stream, str_data(line), str_len(line));
323
0
  }
324
0
  return TRUE;
325
0
}