summaryrefslogtreecommitdiffstats
path: root/wiretap/rfc7468.c
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap/rfc7468.c')
-rw-r--r--wiretap/rfc7468.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/wiretap/rfc7468.c b/wiretap/rfc7468.c
new file mode 100644
index 0000000..1c14327
--- /dev/null
+++ b/wiretap/rfc7468.c
@@ -0,0 +1,221 @@
+/* rfc7468.c
+ *
+ * Implements loading of files in the format specified by RFC 7468.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "rfc7468.h"
+
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+#include <wsutil/buffer.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+static int rfc7468_file_type_subtype = -1;
+
+void register_rfc7468(void);
+
+enum line_type {
+ LINE_TYPE_PREEB,
+ LINE_TYPE_POSTEB,
+ LINE_TYPE_OTHER,
+};
+
+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 FALSE;
+ }
+
+ // 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+}
+
+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)
+{
+ /* 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;
+ }
+
+ 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 (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = rfc7468_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_RFC7468;
+
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ 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[] = {
+ /*
+ * We provide one "packet" for each encoded structure in the file,
+ * and don't support any options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info rfc7468_info = {
+ "RFC 7468 files", "rfc7468", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(rfc7468_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_rfc7468(void)
+{
+ rfc7468_file_type_subtype = wtap_register_file_type_subtype(&rfc7468_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("RFC7468",
+ rfc7468_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */