%top { /* Include this before everything else, for various large-file definitions */ #include "config.h" #include } /* * 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 * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * TODO: * - fix timestamps after midnight * - verify encapsulations */ #include #include #include #include #include "wtap-int.h" #include "wtap.h" #include "file_wrappers.h" #include #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; char *err_info; int start_state; unsigned g_h; unsigned g_m; unsigned g_s; unsigned g_ms; unsigned g_ns; int g_encap; uint8_t *bb; unsigned ii; bool is_k12text; bool at_eof; unsigned junk_chars; char* error_str; uint64_t file_bytes_read; bool 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 = (unsigned) strtoul(text,NULL,10) #define SET_MINUTES(text) yyextra->g_m = (unsigned) strtoul(text,NULL,10) #define SET_SECONDS(text) yyextra->g_s = (unsigned) strtoul(text,NULL,10) #define SET_MS(text) yyextra->g_ms = (unsigned) strtoul(text,NULL,10) #define SET_NS(text) yyextra->g_ns = (unsigned) 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++] = (uint8_t)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. */ int64_t 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 %% {start_timestamp} { yyextra->is_k12text = true; yyterminate(); } . { if (++ yyextra->junk_chars > MAX_JUNK) { yyextra->is_k12text = false; yyterminate(); } } {start_timestamp} {BEGIN(HOURS); } {oneormoredigits} { SET_HOURS(yytext); BEGIN(MINUTES); } {twodigits} { SET_MINUTES(yytext); BEGIN(M2S);} {colon} { BEGIN(SECONDS);} {twodigits} { SET_SECONDS(yytext); BEGIN(S2M); } {comma} { BEGIN(MS); } {threedigits} { SET_MS(yytext); BEGIN(M2N); } {comma} { BEGIN(NS); } {threedigits} { SET_NS(yytext); BEGIN(ENCAP);} {eth} {yyextra->g_encap = WTAP_ENCAP_ETHERNET; BEGIN(STARTBYTES); } {mtp2} {yyextra->g_encap = WTAP_ENCAP_MTP2; BEGIN(STARTBYTES); } {sscop} {yyextra->g_encap = WTAP_ENCAP_ATM_PDUS; BEGIN(STARTBYTES); } {sscfnni} {yyextra->g_encap = WTAP_ENCAP_MTP3; BEGIN(STARTBYTES); } {hdlc} {yyextra->g_encap = WTAP_ENCAP_CHDLC; BEGIN(STARTBYTES); } {start_bytes} { BEGIN(BYTE); } {byte} { ADD_BYTE(yytext); } {bytes_junk} ; {end_bytes} { FINALIZE_FRAME(); yyterminate(); } . { if (++yyextra->junk_chars > MAX_JUNK) { KERROR("too much junk"); } } <> { 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 bool k12text_set_headers(wtap_rec *rec, k12text_state_t *state, int *err, char **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 bool k12text_run_scanner(k12text_state_t *state, FILE_T fh, int start_state, int *err, char **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 bool k12text_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char ** err_info, int64_t *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 = (uint8_t*)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 bool k12text_seek_read(wtap *wth, int64_t 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 = (uint8_t*)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, char **err_info) { k12text_t *k12text; k12text_state_t state; state.bb = (uint8_t*)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 bool k12text_dump(wtap_dumper *wdh, const wtap_rec *rec, const uint8_t *pd, int *err, char **err_info _U_) { #define K12BUF_SIZE 196808 char *buf; size_t left = K12BUF_SIZE; size_t wl; char *p; const char* str_enc; unsigned i; unsigned ns; unsigned ms; bool 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 bool k12text_dump_open(wtap_dumper *wdh, int *err _U_, char **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); }