diff options
Diffstat (limited to 'wiretap/k12text.l')
-rw-r--r-- | wiretap/k12text.l | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/wiretap/k12text.l b/wiretap/k12text.l new file mode 100644 index 00000000..7ba4587c --- /dev/null +++ b/wiretap/k12text.l @@ -0,0 +1,604 @@ +%top { +/* Include this before everything else, for various large-file definitions */ +#include "config.h" +#include <wireshark.h> +} + +/* + * We want a reentrant scanner. + */ +%option reentrant + +/* + * We don't use input, so don't generate code for it. + */ +%option noinput + +/* + * We don't use unput, so don't generate code for it. + */ +%option nounput + +/* + * We don't read interactively from the terminal. + */ +%option never-interactive + +/* + * We want to stop processing when we get to the end of the input. + */ +%option noyywrap + +/* + * The type for the state we keep for a scanner. + */ +%option extra-type="k12text_state_t *" + +/* + * Prefix scanner routines with "k12text_" rather than "yy", so this scanner + * can coexist with other scanners. + */ +%option prefix="k12text_" + +%option outfile="k12text.c" + +/* Options useful for debugging */ +/* noline: Prevent generation of #line directives */ +/* Seems to be required when using the */ +/* Windows VS debugger so as to be able */ +/* to properly step through the code and */ +/* set breakpoints & etc using the */ +/* k12text.c file rather than the */ +/* k12text.l file */ +/* XXX: %option noline gives an error message: */ +/* "unrecognized %option: line" */ +/* with flex 2.5.35; the --noline */ +/* command-line option works OK. */ +/* */ +/* debug: Do output of "rule acceptance" info */ +/* during parse */ +/* */ +/* %option noline */ +/* %option debug */ + +/* + * We have to override the memory allocators so that we don't get + * "unused argument" warnings from the yyscanner argument (which + * we don't use, as we have a global memory allocator). + * + * We provide, as macros, our own versions of the routines generated by Flex, + * which just call malloc()/realloc()/free() (as the Flex versions do), + * discarding the extra argument. + */ +%option noyyalloc +%option noyyrealloc +%option noyyfree + +%{ +/* k12text.l + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + /* + * TODO: + * - fix timestamps after midnight + * - verify encapsulations + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include "wtap-int.h" +#include "wtap.h" +#include "file_wrappers.h" +#include <wsutil/buffer.h> +#include "k12.h" + +#ifndef HAVE_UNISTD_H +#define YY_NO_UNISTD_H +#endif + +/* + * Disable diagnostics in the code generated by Flex. + */ +DIAG_OFF_FLEX() + +/* + * State kept by the scanner. + */ +typedef struct { + FILE_T fh; + int err; + gchar *err_info; + int start_state; + + guint g_h; + guint g_m; + guint g_s; + guint g_ms; + guint g_ns; + gint g_encap; + guint8 *bb; + guint ii; + gboolean is_k12text; + gboolean at_eof; + guint junk_chars; + gchar* error_str; + guint64 file_bytes_read; + gboolean ok_frame; +} k12text_state_t; + +#define KERROR(text) do { yyextra->error_str = g_strdup(text); yyterminate(); } while(0) +#define SET_HOURS(text) yyextra->g_h = (guint) strtoul(text,NULL,10) +#define SET_MINUTES(text) yyextra->g_m = (guint) strtoul(text,NULL,10) +#define SET_SECONDS(text) yyextra->g_s = (guint) strtoul(text,NULL,10) +#define SET_MS(text) yyextra->g_ms = (guint) strtoul(text,NULL,10) +#define SET_NS(text) yyextra->g_ns = (guint) strtoul(text,NULL,10) +#define ADD_BYTE(text) do {if (yyextra->ii >= WTAP_MAX_PACKET_SIZE_STANDARD) {KERROR("frame too large");} yyextra->bb[yyextra->ii++] = (guint8)strtoul(text,NULL,16); } while(0) +#define FINALIZE_FRAME() do { yyextra->ok_frame = TRUE; } while (0) +/*~ #define ECHO*/ +#define YY_USER_ACTION yyextra->file_bytes_read += yyleng; +#define YY_USER_INIT { \ + k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \ + BEGIN(scanner_state->start_state); \ +} +#define YY_INPUT(buf,result,max_size) { \ + k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \ + int c = file_getc(scanner_state->fh); \ + if (c == EOF) { \ + scanner_state->err = file_error(scanner_state->fh, \ + &scanner_state->err_info); \ + if (scanner_state->err == 0) \ + scanner_state->err = WTAP_ERR_SHORT_READ; \ + result = YY_NULL; \ + } else { \ + buf[0] = c; \ + result = 1; \ + } \ +} +#define MAX_JUNK 400000 +#define ECHO + +/* + * Private per-file data. + */ +typedef struct { + /* + * The file position after the end of the previous frame processed by + * k12text_read. + * + * We need to keep this around, and seek to it at the beginning of + * each call to k12text_read(), since the lexer undoubtedly did some + * amount of look-ahead when processing the previous frame. + */ + gint64 next_frame_offset; +} k12text_t; + +/* + * Sleazy hack to suppress compiler warnings in yy_fatal_error(). + */ +#define YY_EXIT_FAILURE ((void)yyscanner, 2) + +/* + * Macros for the allocators, to discard the extra argument. + */ +#define k12text_alloc(size, yyscanner) (void *)malloc(size) +#define k12text_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size)) +#define k12text_free(ptr, yyscanner) free((char *)ptr) + +static int k12text_file_type_subtype = -1; + +void register_k12text(void); + +%} +start_timestamp \053[\055]{9}\053[\055]{15,100}\053[\055]{10,100}\053 +oneormoredigits [0-9]+: +twodigits [0-9][0-9] +colon : +comma , +threedigits [0-9][0-9][0-9] +start_bytes \174\060\040\040\040\174 +bytes_junk \174[A-F0-9][A-F0-9\040][A-F0-9\040][A-F0-9\040]\174 +byte [a-f0-9][a-f0-9]\174 +end_bytes \015?\012\015?\012 +eth ETHER +mtp2 MTP-L2 +sscop SSCOP +sscfnni SSCF +hdlc HDLC + +%START MAGIC NEXT_FRAME HOURS MINUTES M2S SECONDS S2M MS M2N NS ENCAP STARTBYTES BYTE +%% +<MAGIC>{start_timestamp} { yyextra->is_k12text = TRUE; yyterminate(); } + +<MAGIC>. { if (++ yyextra->junk_chars > MAX_JUNK) { yyextra->is_k12text = FALSE; yyterminate(); } } + +<NEXT_FRAME>{start_timestamp} {BEGIN(HOURS); } +<HOURS>{oneormoredigits} { SET_HOURS(yytext); BEGIN(MINUTES); } +<MINUTES>{twodigits} { SET_MINUTES(yytext); BEGIN(M2S);} +<M2S>{colon} { BEGIN(SECONDS);} +<SECONDS>{twodigits} { SET_SECONDS(yytext); BEGIN(S2M); } +<S2M>{comma} { BEGIN(MS); } +<MS>{threedigits} { SET_MS(yytext); BEGIN(M2N); } +<M2N>{comma} { BEGIN(NS); } +<NS>{threedigits} { SET_NS(yytext); BEGIN(ENCAP);} +<ENCAP>{eth} {yyextra->g_encap = WTAP_ENCAP_ETHERNET; BEGIN(STARTBYTES); } +<ENCAP>{mtp2} {yyextra->g_encap = WTAP_ENCAP_MTP2; BEGIN(STARTBYTES); } +<ENCAP>{sscop} {yyextra->g_encap = WTAP_ENCAP_ATM_PDUS; BEGIN(STARTBYTES); } +<ENCAP>{sscfnni} {yyextra->g_encap = WTAP_ENCAP_MTP3; BEGIN(STARTBYTES); } +<ENCAP>{hdlc} {yyextra->g_encap = WTAP_ENCAP_CHDLC; BEGIN(STARTBYTES); } +<ENCAP,STARTBYTES>{start_bytes} { BEGIN(BYTE); } +<BYTE>{byte} { ADD_BYTE(yytext); } +<BYTE>{bytes_junk} ; +<BYTE>{end_bytes} { FINALIZE_FRAME(); yyterminate(); } + +. { if (++yyextra->junk_chars > MAX_JUNK) { KERROR("too much junk"); } } +<<EOF>> { yyextra->at_eof = TRUE; yyterminate(); } + +%% + +/* + * Turn diagnostics back on, so we check the code that we've written. + */ +DIAG_ON_FLEX() + +/* Fill in pkthdr */ + +static gboolean +k12text_set_headers(wtap_rec *rec, k12text_state_t *state, + int *err, gchar **err_info) +{ + rec->rec_type = REC_TYPE_PACKET; + rec->block = wtap_block_create(WTAP_BLOCK_PACKET); + rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; + + rec->ts.secs = 946681200 + (3600*state->g_h) + (60*state->g_m) + state->g_s; + rec->ts.nsecs = 1000000*state->g_ms + 1000*state->g_ns; + + rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = state->ii; + + rec->rec_header.packet_header.pkt_encap = state->g_encap; + + /* The file-encap is WTAP_ENCAP_PER_PACKET */ + switch(state->g_encap) { + case WTAP_ENCAP_ETHERNET: + rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; + break; + case WTAP_ENCAP_MTP3: + case WTAP_ENCAP_CHDLC: + /* no pseudo_header to fill in for these types */ + break; + case WTAP_ENCAP_MTP2: /* not (yet) supported */ + /* XXX: I don't know how to fill in the */ + /* pseudo_header for these types. */ + *err = WTAP_ERR_UNSUPPORTED; + *err_info = g_strdup("k12text: MTP2 packets not yet supported"); + return FALSE; + case WTAP_ENCAP_ATM_PDUS: /* not (yet) supported */ + /* XXX: I don't know how to fill in the */ + /* pseudo_header for these types. */ + *err = WTAP_ERR_UNSUPPORTED; + *err_info = g_strdup("k12text: SSCOP packets not yet supported"); + return FALSE; + default: + *err = WTAP_ERR_UNSUPPORTED; + *err_info = g_strdup("k12text: unknown encapsulation type"); + return FALSE; + } + return TRUE; +} + +/* Note: k12text_reset is called each time data is to be processed from */ +/* a file. This ensures that no "state" from a previous read is */ +/* used (such as the lexer look-ahead buffer, file_handle, file */ +/* position and so on. This allows a single lexer buffer to be */ +/* used even when multiple files are open simultaneously (as for */ +/* a file merge). */ + +static gboolean +k12text_run_scanner(k12text_state_t *state, FILE_T fh, int start_state, + int *err, gchar **err_info) +{ + yyscan_t scanner = NULL; + + if (yylex_init(&scanner) != 0) { + /* errno is set if this fails */ + *err = errno; + *err_info = NULL; + return FALSE; + } + state->fh = fh; + state->err = 0; + state->err_info = NULL; + state->start_state = start_state; + + state->g_encap = WTAP_ENCAP_UNKNOWN; + state->ok_frame = FALSE; + state->is_k12text = FALSE; + state->at_eof = FALSE; + state->junk_chars = 0; + state->error_str = NULL; + state->file_bytes_read=0; + state->g_h=0; + state->g_m=0; + state->g_s=0; + state->g_ns=0; + state->g_ms=0; + state->ii=0; + + /* Associate the state with the scanner */ + k12text_set_extra(state, scanner); + + yylex(scanner); + yylex_destroy(scanner); + if (state->err != 0 && state->err != WTAP_ERR_SHORT_READ) { + /* I/O error. */ + *err = state->err; + *err_info = state->err_info; + return FALSE; + } + return TRUE; +} + +static gboolean +k12text_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char ** err_info, gint64 *data_offset) +{ + k12text_t *k12text = (k12text_t *)wth->priv; + k12text_state_t state; + + /* + * We seek to the file position after the end of the previous frame + * processed by k12text_read(), since the lexer undoubtedly did some + * amount of look-ahead when processing the previous frame. + * + * We also clear out any lexer state (eg: look-ahead buffer) and + * init vars set by lexer. + */ + + if ( file_seek(wth->fh, k12text->next_frame_offset, SEEK_SET, err) == -1) { + return FALSE; + } + state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); + + if (!k12text_run_scanner(&state, wth->fh, NEXT_FRAME, err, err_info)) { + g_free(state.bb); + return FALSE; + } + + if (state.ok_frame == FALSE) { + if (state.at_eof) { + *err = 0; + *err_info = NULL; + } else { + *err = WTAP_ERR_BAD_FILE; + *err_info = state.error_str; + } + g_free(state.bb); + return FALSE; + } + + *data_offset = k12text->next_frame_offset; /* file position for beginning of this frame */ + k12text->next_frame_offset += state.file_bytes_read; /* file position after end of this frame */ + + if (!k12text_set_headers(rec, &state, err, err_info)) { + g_free(state.bb); + return FALSE; + } + ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen); + memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen); + + g_free(state.bb); + return TRUE; +} + +static gboolean +k12text_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, char **err_info) +{ + k12text_state_t state; + + if ( file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) { + return FALSE; + } + state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); + + if (!k12text_run_scanner(&state, wth->random_fh, NEXT_FRAME, err, err_info)) { + g_free(state.bb); + return FALSE; + } + + if (state.ok_frame == FALSE) { + *err = WTAP_ERR_BAD_FILE; + if (state.at_eof) { + /* What happened ? The desired frame was previously read without a problem */ + *err_info = g_strdup("Unexpected EOF (program error ?)"); + } else { + *err_info = state.error_str; + } + g_free(state.bb); + return FALSE; + } + + if (!k12text_set_headers(rec, &state, err, err_info)) { + g_free(state.bb); + return FALSE; + } + ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen); + memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen); + + g_free(state.bb); + return TRUE; +} + +wtap_open_return_val +k12text_open(wtap *wth, int *err, gchar **err_info) +{ + k12text_t *k12text; + k12text_state_t state; + + state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); + if (!k12text_run_scanner(&state, wth->fh, MAGIC, err, err_info)) { + g_free(state.bb); + return WTAP_OPEN_ERROR; + } + + if (!state.is_k12text) { + /* *err might have been set to WTAP_ERR_SHORT_READ */ + *err = 0; + g_free(state.bb); + return WTAP_OPEN_NOT_MINE; + } + + if ( file_seek(wth->fh, 0, SEEK_SET, err) == -1) { + g_free(state.bb); + return WTAP_OPEN_ERROR; + } + + k12text = g_new(k12text_t, 1); + wth->priv = (void *)k12text; + k12text->next_frame_offset = 0; + wth->file_type_subtype = k12text_file_type_subtype; + wth->file_encap = WTAP_ENCAP_PER_PACKET; + wth->snapshot_length = 0; + wth->subtype_read = k12text_read; + wth->subtype_seek_read = k12text_seek_read; + wth->file_tsprec = WTAP_TSPREC_NSEC; + + g_free(state.bb); + return WTAP_OPEN_MINE; +} + + +static const struct { int e; const char* s; } encaps[] = { + { WTAP_ENCAP_ETHERNET, "ETHER" }, + { WTAP_ENCAP_MTP2, "MTP-L2" }, + { WTAP_ENCAP_ATM_PDUS, "SSCOP" }, + { WTAP_ENCAP_MTP3, "SSCF" }, + { WTAP_ENCAP_CHDLC, "HDLC" }, + /* ... */ + { 0, NULL } +}; + +static gboolean +k12text_dump(wtap_dumper *wdh, const wtap_rec *rec, + const guint8 *pd, int *err, gchar **err_info _U_) { +#define K12BUF_SIZE 196808 + char *buf; + size_t left = K12BUF_SIZE; + size_t wl; + char *p; + const char* str_enc; + guint i; + guint ns; + guint ms; + gboolean ret; + struct tm *tmp; + + /* Don't write anything bigger than we're willing to read. */ + if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) { + *err = WTAP_ERR_PACKET_TOO_LARGE; + return FALSE; + } + + str_enc = NULL; + for(i=0; encaps[i].s; i++) { + if (rec->rec_header.packet_header.pkt_encap == encaps[i].e) { + str_enc = encaps[i].s; + break; + } + } + if (str_enc == NULL) { + /* + * That encapsulation type is not supported. Fail. + */ + *err = WTAP_ERR_UNWRITABLE_ENCAP; + return FALSE; + } + + buf = (char *)g_malloc(K12BUF_SIZE); + p = buf; + + ms = rec->ts.nsecs / 1000000; + ns = (rec->ts.nsecs - (1000000*ms))/1000; + + tmp = gmtime(&rec->ts.secs); + if (tmp == NULL) + snprintf(p, 90, "+---------+---------------+----------+\r\nXX:XX:XX,"); + else + strftime(p, 90, "+---------+---------------+----------+\r\n%H:%M:%S,", tmp); + wl = strlen(p); + p += wl; + left -= wl; + + wl = snprintf(p, left, "%.3d,%.3d %s\r\n|0 |", ms, ns, str_enc); + p += wl; + left -= wl; + + for(i = 0; i < rec->rec_header.packet_header.caplen && left > 2; i++) { + wl = snprintf(p, left, "%.2x|", pd[i]); + p += wl; + left -= wl; + } + + wl = snprintf(p, left, "\r\n\r\n"); + left -= wl; + + ret = wtap_dump_file_write(wdh, buf, K12BUF_SIZE - left, err); + + g_free(buf); + return ret; +} + + +static gboolean +k12text_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) +{ + wdh->subtype_write = k12text_dump; + + return TRUE; +} + +static int +k12text_dump_can_write_encap(int encap) +{ + switch (encap) { + case WTAP_ENCAP_PER_PACKET: + case WTAP_ENCAP_ETHERNET: + case WTAP_ENCAP_MTP3: + case WTAP_ENCAP_CHDLC: + return 0; + case WTAP_ENCAP_MTP2: + case WTAP_ENCAP_ATM_PDUS: + default: + return WTAP_ERR_UNWRITABLE_ENCAP; + } +} + +static const struct supported_block_type k12text_blocks_supported[] = { + /* + * We support packet blocks, with no comments or other options. + */ + { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } +}; + +static const struct file_type_subtype_info k12text_info = { + "K12 text file", "k12text", "txt", NULL, + FALSE, BLOCKS_SUPPORTED(k12text_blocks_supported), + k12text_dump_can_write_encap, k12text_dump_open, NULL +}; + +void register_k12text(void) +{ + k12text_file_type_subtype = wtap_register_file_type_subtype(&k12text_info); + + /* + * Register name for backwards compatibility with the + * wtap_filetypes table in Lua. + */ + wtap_register_backwards_compatibility_lua_name("K12TEXT", + k12text_file_type_subtype); +} |