aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--wiretap/rfc7468.c183
1 files changed, 132 insertions, 51 deletions
diff --git a/wiretap/rfc7468.c b/wiretap/rfc7468.c
index c8afb238aa..1c143273e8 100644
--- a/wiretap/rfc7468.c
+++ b/wiretap/rfc7468.c
@@ -18,71 +18,153 @@
#include <string.h>
-/* 128 bytes should be enough to contain any line. Strictly speaking, 64 is
- enough, but we provide some leeway to accommodate nonconformant producers and
- trailing whitespace. The 2 extra bytes are for the trailing newline and NUL
- terminator. */
-#define MAX_LINE_LENGTH (128 + 2)
-
static int rfc7468_file_type_subtype = -1;
void register_rfc7468(void);
-static char *read_complete_text_line(char line[MAX_LINE_LENGTH], FILE_T fh, int *err, gchar **err_info)
-{
- char *line_end;
+enum line_type {
+ LINE_TYPE_PREEB,
+ LINE_TYPE_POSTEB,
+ LINE_TYPE_OTHER,
+};
- if (!(line_end = file_getsp(line, MAX_LINE_LENGTH, fh))) {
+const char PREEB_BEGIN[] = "-----BEGIN ";
+#define PREEB_BEGIN_LEN (sizeof PREEB_BEGIN - 1)
+const char POSTEB_BEGIN[] = "-----END ";
+#define POSTEB_BEGIN_LEN (sizeof POSTEB_BEGIN - 1)
+
+static gboolean rfc7468_read_line(FILE_T fh, enum line_type *line_type, Buffer *buf,
+ int* err, gchar** err_info)
+{
+ /* Make the chunk size large enough that most lines can fit in a single chunk.
+ Strict RFC 7468 syntax only allows up to 64 characters per line, but we provide
+ some leeway to accommodate nonconformant producers and explanatory text.
+ The 3 extra bytes are for the trailing CR+LF and NUL terminator. */
+ char line_chunk[128 + 3];
+ char *line_chunk_end;
+
+ if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
*err = file_error(fh, err_info);
- return NULL;
+ return FALSE;
}
- if (strlen(line) != (size_t)(line_end - line)) {
- *err = 0;
- return NULL;
+ // First chunk determines the line type.
+ if (memcmp(line_chunk, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
+ *line_type = LINE_TYPE_PREEB;
+ else if (memcmp(line_chunk, POSTEB_BEGIN, POSTEB_BEGIN_LEN) == 0)
+ *line_type = LINE_TYPE_POSTEB;
+ else
+ *line_type = LINE_TYPE_OTHER;
+
+ for (;;) {
+ gsize line_chunk_len = line_chunk_end - line_chunk;
+ if (line_chunk_len > G_MAXINT - ws_buffer_length(buf)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup_printf(
+ "File contains an encoding larger than the maximum of %d bytes",
+ G_MAXINT);
+ return FALSE;
+ }
+
+ ws_buffer_append(buf, line_chunk, line_chunk_len);
+
+ if (line_chunk_end[-1] == '\n' || file_eof(fh))
+ break;
+
+ if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
}
- if (line_end[-1] != '\n' && !file_eof(fh)) {
- *err = 0;
- return NULL;
+ return TRUE;
+}
+
+static gboolean rfc7468_read_impl(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ ws_buffer_clean(buf);
+
+ gboolean saw_preeb = FALSE;
+
+ for (;;) {
+ enum line_type line_type;
+
+ if (!rfc7468_read_line(fh, &line_type, buf, err, err_info)) {
+ if (*err != 0 || !saw_preeb) return FALSE;
+
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Missing post-encapsulation boundary at end of file");
+ return FALSE;
+ }
+
+ if (saw_preeb) {
+ if (line_type == LINE_TYPE_POSTEB) break;
+ } else {
+ if (line_type == LINE_TYPE_PREEB) saw_preeb = TRUE;
+ }
}
- return line_end;
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = 0;
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->rec_header.packet_header.caplen = (guint32)ws_buffer_length(buf);
+ rec->rec_header.packet_header.len = (guint32)ws_buffer_length(buf);
+
+ return TRUE;
}
-//
-// Arbitrary value - we don't want to read all of a huge non-RFC 7468 file
-// only to find no pre-encapsulation boundary.
-//
-#define MAX_EXPLANATORY_TEXT_LINES 20
+static gboolean rfc7468_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return rfc7468_read_impl(wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean rfc7468_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) < 0)
+ return FALSE;
+
+ return rfc7468_read_impl(wth->random_fh, rec, buf, err, err_info);
+}
wtap_open_return_val rfc7468_open(wtap *wth, int *err, gchar **err_info)
{
- gboolean found_preeb;
- static const char preeb_begin[] = "-----BEGIN ";
- char line[MAX_LINE_LENGTH];
-
- //
- // Skip up to MAX_EXPLANATORY_TEXT_LINES worth of lines that don't
- // look like pre-encapsulation boundaries.
- //
- found_preeb = FALSE;
- for (unsigned int i = 0; i < MAX_EXPLANATORY_TEXT_LINES; i++) {
- if (!read_complete_text_line(line, wth->fh, err, err_info)) {
- if (*err == 0 || *err == WTAP_ERR_SHORT_READ)
- return WTAP_OPEN_NOT_MINE;
- return WTAP_OPEN_ERROR;
- }
+ /* To detect whether this file matches our format, we need to find the
+ first pre-encapsulation boundary, which may be located anywhere in the file,
+ since it may be preceded by explanatory text. However, we don't want to
+ read the entire file to find it, since the file may be huge, and detection
+ needs to be fast. Therefore, we'll assume that if the boundary exists,
+ it's located within a small initial chunk of the file. The size of
+ the chunk was chosen arbitrarily. */
+ char initial_chunk[2048];
+ int initial_chunk_size = file_read(&initial_chunk, sizeof initial_chunk, wth->fh);
+
+ if (initial_chunk_size < 0) {
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
- // Does the line look like a pre-encapsulation boundary?
- if (memcmp(line, preeb_begin, sizeof preeb_begin - 1) == 0) {
- // Yes.
- found_preeb = TRUE;
+ char *chunk_end_ptr = initial_chunk + initial_chunk_size;
+
+ // Try to find a line that starts with PREEB_BEGIN in the initial chunk.
+ for (char *line_ptr = initial_chunk; ; ) {
+ if ((unsigned)(chunk_end_ptr - line_ptr) < PREEB_BEGIN_LEN)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (memcmp(line_ptr, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
break;
- }
+
+ // Try next line.
+ char *lf_ptr = memchr(line_ptr, '\n', chunk_end_ptr - line_ptr);
+ if (!lf_ptr)
+ return WTAP_OPEN_NOT_MINE;
+ line_ptr = lf_ptr + 1;
}
- if (!found_preeb)
- return WTAP_OPEN_NOT_MINE;
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
return WTAP_OPEN_ERROR;
@@ -93,19 +175,18 @@ wtap_open_return_val rfc7468_open(wtap *wth, int *err, gchar **err_info)
wth->snapshot_length = 0;
wth->file_tsprec = WTAP_TSPREC_SEC;
- wth->subtype_read = wtap_full_file_read;
- wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->subtype_read = rfc7468_read;
+ wth->subtype_seek_read = rfc7468_seek_read;
return WTAP_OPEN_MINE;
}
static const struct supported_block_type rfc7468_blocks_supported[] = {
/*
- * This is a file format that we dissect, so we provide only one
- * "packet" with the file's contents, and don't support any
- * options.
+ * We provide one "packet" for each encoded structure in the file,
+ * and don't support any options.
*/
- { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
};
static const struct file_type_subtype_info rfc7468_info = {