Coverage Report

Created: 2026-04-12 07:00

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