3 * Implements loading of files in the format specified by RFC 7468.
5 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "file_wrappers.h"
15 #include <wsutil/buffer.h>
21 static int rfc7468_file_type_subtype
= -1;
23 void register_rfc7468(void);
31 const char PREEB_BEGIN
[] = "-----BEGIN ";
32 #define PREEB_BEGIN_LEN (sizeof PREEB_BEGIN - 1)
33 const char POSTEB_BEGIN
[] = "-----END ";
34 #define POSTEB_BEGIN_LEN (sizeof POSTEB_BEGIN - 1)
36 static bool rfc7468_read_line(FILE_T fh
, enum line_type
*line_type
, Buffer
*buf
,
37 int* err
, char** err_info
)
39 /* Make the chunk size large enough that most lines can fit in a single chunk.
40 Strict RFC 7468 syntax only allows up to 64 characters per line, but we provide
41 some leeway to accommodate nonconformant producers and explanatory text.
42 The 3 extra bytes are for the trailing CR+LF and NUL terminator. */
43 char line_chunk
[128 + 3];
46 if (!(line_chunk_end
= file_getsp(line_chunk
, sizeof line_chunk
, fh
))) {
47 *err
= file_error(fh
, err_info
);
51 // First chunk determines the line type.
52 if (memcmp(line_chunk
, PREEB_BEGIN
, PREEB_BEGIN_LEN
) == 0)
53 *line_type
= LINE_TYPE_PREEB
;
54 else if (memcmp(line_chunk
, POSTEB_BEGIN
, POSTEB_BEGIN_LEN
) == 0)
55 *line_type
= LINE_TYPE_POSTEB
;
57 *line_type
= LINE_TYPE_OTHER
;
60 size_t line_chunk_len
= line_chunk_end
- line_chunk
;
61 if (line_chunk_len
> INT_MAX
- ws_buffer_length(buf
)) {
62 *err
= WTAP_ERR_BAD_FILE
;
63 *err_info
= g_strdup_printf(
64 "File contains an encoding larger than the maximum of %d bytes",
69 ws_buffer_append(buf
, line_chunk
, line_chunk_len
);
71 if (line_chunk_end
[-1] == '\n' || file_eof(fh
))
74 if (!(line_chunk_end
= file_getsp(line_chunk
, sizeof line_chunk
, fh
))) {
75 *err
= file_error(fh
, err_info
);
83 static bool rfc7468_read_impl(FILE_T fh
, wtap_rec
*rec
, Buffer
*buf
,
84 int *err
, char **err_info
)
88 bool saw_preeb
= false;
91 enum line_type line_type
;
93 if (!rfc7468_read_line(fh
, &line_type
, buf
, err
, err_info
)) {
94 if (*err
!= 0 || !saw_preeb
) return false;
96 *err
= WTAP_ERR_BAD_FILE
;
97 *err_info
= g_strdup("Missing post-encapsulation boundary at end of file");
102 if (line_type
== LINE_TYPE_POSTEB
) break;
104 if (line_type
== LINE_TYPE_PREEB
) saw_preeb
= true;
108 rec
->rec_type
= REC_TYPE_PACKET
;
109 rec
->presence_flags
= 0;
112 rec
->rec_header
.packet_header
.caplen
= (uint32_t)ws_buffer_length(buf
);
113 rec
->rec_header
.packet_header
.len
= (uint32_t)ws_buffer_length(buf
);
118 static bool rfc7468_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
119 int *err
, char **err_info
, int64_t *data_offset
)
121 *data_offset
= file_tell(wth
->fh
);
123 return rfc7468_read_impl(wth
->fh
, rec
, buf
, err
, err_info
);
126 static bool rfc7468_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
,
127 Buffer
*buf
, int *err
, char **err_info
)
129 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) < 0)
132 return rfc7468_read_impl(wth
->random_fh
, rec
, buf
, err
, err_info
);
135 wtap_open_return_val
rfc7468_open(wtap
*wth
, int *err
, char **err_info
)
137 /* To detect whether this file matches our format, we need to find the
138 first pre-encapsulation boundary, which may be located anywhere in the file,
139 since it may be preceded by explanatory text. However, we don't want to
140 read the entire file to find it, since the file may be huge, and detection
141 needs to be fast. Therefore, we'll assume that if the boundary exists,
142 it's located within a small initial chunk of the file. The size of
143 the chunk was chosen arbitrarily. */
144 char initial_chunk
[2048];
145 int initial_chunk_size
= file_read(&initial_chunk
, sizeof initial_chunk
, wth
->fh
);
147 if (initial_chunk_size
< 0) {
148 *err
= file_error(wth
->fh
, err_info
);
149 return WTAP_OPEN_ERROR
;
152 char *chunk_end_ptr
= initial_chunk
+ initial_chunk_size
;
154 // Try to find a line that starts with PREEB_BEGIN in the initial chunk.
155 for (char *line_ptr
= initial_chunk
; ; ) {
156 if ((unsigned)(chunk_end_ptr
- line_ptr
) < PREEB_BEGIN_LEN
)
157 return WTAP_OPEN_NOT_MINE
;
159 if (memcmp(line_ptr
, PREEB_BEGIN
, PREEB_BEGIN_LEN
) == 0)
163 char *lf_ptr
= memchr(line_ptr
, '\n', chunk_end_ptr
- line_ptr
);
165 return WTAP_OPEN_NOT_MINE
;
166 line_ptr
= lf_ptr
+ 1;
169 if (file_seek(wth
->fh
, 0, SEEK_SET
, err
) == -1)
170 return WTAP_OPEN_ERROR
;
172 wth
->file_type_subtype
= rfc7468_file_type_subtype
;
173 wth
->file_encap
= WTAP_ENCAP_RFC7468
;
175 wth
->snapshot_length
= 0;
176 wth
->file_tsprec
= WTAP_TSPREC_SEC
;
178 wth
->subtype_read
= rfc7468_read
;
179 wth
->subtype_seek_read
= rfc7468_seek_read
;
181 return WTAP_OPEN_MINE
;
184 static const struct supported_block_type rfc7468_blocks_supported
[] = {
186 * We provide one "packet" for each encoded structure in the file,
187 * and don't support any options.
189 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
192 static const struct file_type_subtype_info rfc7468_info
= {
193 "RFC 7468 files", "rfc7468", NULL
, NULL
,
194 false, BLOCKS_SUPPORTED(rfc7468_blocks_supported
),
198 void register_rfc7468(void)
200 rfc7468_file_type_subtype
= wtap_register_file_type_subtype(&rfc7468_info
);
203 * Register name for backwards compatibility with the
204 * wtap_filetypes table in Lua.
206 wtap_register_backwards_compatibility_lua_name("RFC7468",
207 rfc7468_file_type_subtype
);
211 * Editor modelines - https://www.wireshark.org/tools/modelines.html
216 * indent-tabs-mode: nil
219 * vi: set shiftwidth=4 tabstop=8 expandtab:
220 * :indentSize=4:tabSize=8:noTabs=true: