1 %top {
2 /* Include this before everything else, for various large-file definitions */
3 #include "config.h"
4 }
5
6 /*
7 * We want a reentrant scanner.
8 */
9 %option reentrant
10
11 /*
12 * We don't use input, so don't generate code for it.
13 */
14 %option noinput
15
16 /*
17 * We don't use unput, so don't generate code for it.
18 */
19 %option nounput
20
21 /*
22 * We don't read interactively from the terminal.
23 */
24 %option never-interactive
25
26 /*
27 * We want to stop processing when we get to the end of the input.
28 */
29 %option noyywrap
30
31 /*
32 * The type for the state we keep for a scanner.
33 */
34 %option extra-type="k12text_state_t *"
35
36 /*
37 * Prefix scanner routines with "k12text_" rather than "yy", so this scanner
38 * can coexist with other scanners.
39 */
40 %option prefix="k12text_"
41
42 %option outfile="k12text.c"
43
44 /* Options useful for debugging */
45 /* noline: Prevent generation of #line directives */
46 /* Seems to be required when using the */
47 /* Windows VS debugger so as to be able */
48 /* to properly step through the code and */
49 /* set breakpoints & etc using the */
50 /* k12text.c file rather than the */
51 /* k12text.l file */
52 /* XXX: %option noline gives an error message: */
53 /* "unrecognized %option: line" */
54 /* with flex 2.5.35; the --noline */
55 /* command-line option works OK. */
56 /* */
57 /* debug: Do output of "rule acceptance" info */
58 /* during parse */
59 /* */
60 /* %option noline */
61 /* %option debug */
62
63 /*
64 * We have to override the memory allocators so that we don't get
65 * "unused argument" warnings from the yyscanner argument (which
66 * we don't use, as we have a global memory allocator).
67 *
68 * We provide, as macros, our own versions of the routines generated by Flex,
69 * which just call malloc()/realloc()/free() (as the Flex versions do),
70 * discarding the extra argument.
71 */
72 %option noyyalloc
73 %option noyyrealloc
74 %option noyyfree
75
76 %{
77 /* k12text.l
78 *
79 * Wiretap Library
80 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
81 *
82 * SPDX-License-Identifier: GPL-2.0-or-later
83 */
84
85 /*
86 * TODO:
87 * - fix timestamps after midnight
88 * - verify encapsulations
89 */
90
91 #include <stdlib.h>
92 #include <string.h>
93 #include <errno.h>
94 #include <time.h>
95 #include "wtap-int.h"
96 #include "wtap.h"
97 #include "file_wrappers.h"
98 #include <wsutil/buffer.h>
99 #include "k12.h"
100
101 #ifndef HAVE_UNISTD_H
102 #define YY_NO_UNISTD_H
103 #endif
104
105 /*
106 * Disable diagnostics in the code generated by Flex.
107 */
108 DIAG_OFF_FLEX
109
110 /*
111 * State kept by the scanner.
112 */
113 typedef struct {
114 FILE_T fh;
115 int err;
116 gchar *err_info;
117 int start_state;
118
119 guint g_h;
120 guint g_m;
121 guint g_s;
122 guint g_ms;
123 guint g_ns;
124 gint g_encap;
125 guint8 *bb;
126 guint ii;
127 gboolean is_k12text;
128 gboolean at_eof;
129 guint junk_chars;
130 gchar* error_str;
131 guint64 file_bytes_read;
132 gboolean ok_frame;
133 } k12text_state_t;
134
135 #define KERROR(text) do { yyextra->error_str = g_strdup(text); yyterminate(); } while(0)
136 #define SET_HOURS(text) yyextra->g_h = (guint) strtoul(text,NULL,10)
137 #define SET_MINUTES(text) yyextra->g_m = (guint) strtoul(text,NULL,10)
138 #define SET_SECONDS(text) yyextra->g_s = (guint) strtoul(text,NULL,10)
139 #define SET_MS(text) yyextra->g_ms = (guint) strtoul(text,NULL,10)
140 #define SET_NS(text) yyextra->g_ns = (guint) strtoul(text,NULL,10)
141 #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)
142 #define FINALIZE_FRAME() do { yyextra->ok_frame = TRUE; } while (0)
143 /*~ #define ECHO*/
144 #define YY_USER_ACTION yyextra->file_bytes_read += yyleng;
145 #define YY_USER_INIT { \
146 k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \
147 BEGIN(scanner_state->start_state); \
148 }
149 #define YY_INPUT(buf,result,max_size) { \
150 k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \
151 int c = file_getc(scanner_state->fh); \
152 if (c == EOF) { \
153 scanner_state->err = file_error(scanner_state->fh, \
154 &scanner_state->err_info); \
155 if (scanner_state->err == 0) \
156 scanner_state->err = WTAP_ERR_SHORT_READ; \
157 result = YY_NULL; \
158 } else { \
159 buf[0] = c; \
160 result = 1; \
161 } \
162 }
163 #define MAX_JUNK 400000
164 #define ECHO
165
166 /*
167 * Private per-file data.
168 */
169 typedef struct {
170 /*
171 * The file position after the end of the previous frame processed by
172 * k12text_read.
173 *
174 * We need to keep this around, and seek to it at the beginning of
175 * each call to k12text_read(), since the lexer undoubtedly did some
176 * amount of look-ahead when processing the previous frame.
177 */
178 gint64 next_frame_offset;
179 } k12text_t;
180
181 /*
182 * Sleazy hack to suppress compiler warnings in yy_fatal_error().
183 */
184 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
185
186 /*
187 * Macros for the allocators, to discard the extra argument.
188 */
189 #define k12text_alloc(size, yyscanner) (void *)malloc(size)
190 #define k12text_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
191 #define k12text_free(ptr, yyscanner) free((char *)ptr)
192
193 static int k12text_file_type_subtype = -1;
194
195 void register_k12text(void);
196
197 %}
198 start_timestamp \053[\055]{9}\053[\055]{15,100}\053[\055]{10,100}\053
199 oneormoredigits [0-9]+:
200 twodigits [0-9][0-9]
201 colon :
202 comma ,
203 threedigits [0-9][0-9][0-9]
204 start_bytes \174\060\040\040\040\174
205 bytes_junk \174[A-F0-9][A-F0-9\040][A-F0-9\040][A-F0-9\040]\174
206 byte [a-f0-9][a-f0-9]\174
207 end_bytes \015?\012\015?\012
208 eth ETHER
209 mtp2 MTP-L2
210 sscop SSCOP
211 sscfnni SSCF
212 hdlc HDLC
213
214 %START MAGIC NEXT_FRAME HOURS MINUTES M2S SECONDS S2M MS M2N NS ENCAP STARTBYTES BYTE
215 %%
216 <MAGIC>{start_timestamp} { yyextra->is_k12text = TRUE; yyterminate(); }
217
218 <MAGIC>. { if (++ yyextra->junk_chars > MAX_JUNK) { yyextra->is_k12text = FALSE; yyterminate(); } }
219
220 <NEXT_FRAME>{start_timestamp} {BEGIN(HOURS); }
221 <HOURS>{oneormoredigits} { SET_HOURS(yytext); BEGIN(MINUTES); }
222 <MINUTES>{twodigits} { SET_MINUTES(yytext); BEGIN(M2S);}
223 <M2S>{colon} { BEGIN(SECONDS);}
224 <SECONDS>{twodigits} { SET_SECONDS(yytext); BEGIN(S2M); }
225 <S2M>{comma} { BEGIN(MS); }
226 <MS>{threedigits} { SET_MS(yytext); BEGIN(M2N); }
227 <M2N>{comma} { BEGIN(NS); }
228 <NS>{threedigits} { SET_NS(yytext); BEGIN(ENCAP);}
229 <ENCAP>{eth} {yyextra->g_encap = WTAP_ENCAP_ETHERNET; BEGIN(STARTBYTES); }
230 <ENCAP>{mtp2} {yyextra->g_encap = WTAP_ENCAP_MTP2; BEGIN(STARTBYTES); }
231 <ENCAP>{sscop} {yyextra->g_encap = WTAP_ENCAP_ATM_PDUS; BEGIN(STARTBYTES); }
232 <ENCAP>{sscfnni} {yyextra->g_encap = WTAP_ENCAP_MTP3; BEGIN(STARTBYTES); }
233 <ENCAP>{hdlc} {yyextra->g_encap = WTAP_ENCAP_CHDLC; BEGIN(STARTBYTES); }
234 <ENCAP,STARTBYTES>{start_bytes} { BEGIN(BYTE); }
235 <BYTE>{byte} { ADD_BYTE(yytext); }
236 <BYTE>{bytes_junk} ;
237 <BYTE>{end_bytes} { FINALIZE_FRAME(); yyterminate(); }
238
239 . { if (++yyextra->junk_chars > MAX_JUNK) { KERROR("too much junk"); } }
240 <<EOF>> { yyextra->at_eof = TRUE; yyterminate(); }
241
242 %%
243
244 /*
245 * Turn diagnostics back on, so we check the code that we've written.
246 */
247 DIAG_ON_FLEX
248
249 /* Fill in pkthdr */
250
251 static gboolean
252 k12text_set_headers(wtap_rec *rec, k12text_state_t *state,
253 int *err, gchar **err_info)
254 {
255 rec->rec_type = REC_TYPE_PACKET;
256 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
257 rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
258
259 rec->ts.secs = 946681200 + (3600*state->g_h) + (60*state->g_m) + state->g_s;
260 rec->ts.nsecs = 1000000*state->g_ms + 1000*state->g_ns;
261
262 rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = state->ii;
263
264 rec->rec_header.packet_header.pkt_encap = state->g_encap;
265
266 /* The file-encap is WTAP_ENCAP_PER_PACKET */
267 switch(state->g_encap) {
268 case WTAP_ENCAP_ETHERNET:
269 rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
270 break;
271 case WTAP_ENCAP_MTP3:
272 case WTAP_ENCAP_CHDLC:
273 /* no pseudo_header to fill in for these types */
274 break;
275 case WTAP_ENCAP_MTP2: /* not (yet) supported */
276 /* XXX: I don't know how to fill in the */
277 /* pseudo_header for these types. */
278 *err = WTAP_ERR_UNSUPPORTED;
279 *err_info = g_strdup("k12text: MTP2 packets not yet supported");
280 return FALSE;
281 case WTAP_ENCAP_ATM_PDUS: /* not (yet) supported */
282 /* XXX: I don't know how to fill in the */
283 /* pseudo_header for these types. */
284 *err = WTAP_ERR_UNSUPPORTED;
285 *err_info = g_strdup("k12text: SSCOP packets not yet supported");
286 return FALSE;
287 default:
288 *err = WTAP_ERR_UNSUPPORTED;
289 *err_info = g_strdup("k12text: unknown encapsulation type");
290 return FALSE;
291 }
292 return TRUE;
293 }
294
295 /* Note: k12text_reset is called each time data is to be processed from */
296 /* a file. This ensures that no "state" from a previous read is */
297 /* used (such as the lexer look-ahead buffer, file_handle, file */
298 /* position and so on. This allows a single lexer buffer to be */
299 /* used even when multiple files are open simultaneously (as for */
300 /* a file merge). */
301
302 static gboolean
k12text_run_scanner(k12text_state_t * state,FILE_T fh,int start_state,int * err,gchar ** err_info)303 k12text_run_scanner(k12text_state_t *state, FILE_T fh, int start_state,
304 int *err, gchar **err_info)
305 {
306 yyscan_t scanner = NULL;
307
308 if (yylex_init(&scanner) != 0) {
309 /* errno is set if this fails */
310 *err = errno;
311 *err_info = NULL;
312 return FALSE;
313 }
314 state->fh = fh;
315 state->err = 0;
316 state->err_info = NULL;
317 state->start_state = start_state;
318
319 state->g_encap = WTAP_ENCAP_UNKNOWN;
320 state->ok_frame = FALSE;
321 state->is_k12text = FALSE;
322 state->at_eof = FALSE;
323 state->junk_chars = 0;
324 state->error_str = NULL;
325 state->file_bytes_read=0;
326 state->g_h=0;
327 state->g_m=0;
328 state->g_s=0;
329 state->g_ns=0;
330 state->g_ms=0;
331 state->ii=0;
332
333 /* Associate the state with the scanner */
334 k12text_set_extra(state, scanner);
335
336 yylex(scanner);
337 yylex_destroy(scanner);
338 if (state->err != 0 && state->err != WTAP_ERR_SHORT_READ) {
339 /* I/O error. */
340 *err = state->err;
341 *err_info = state->err_info;
342 return FALSE;
343 }
344 return TRUE;
345 }
346
347 static gboolean
k12text_read(wtap * wth,wtap_rec * rec,Buffer * buf,int * err,char ** err_info,gint64 * data_offset)348 k12text_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char ** err_info, gint64 *data_offset)
349 {
350 k12text_t *k12text = (k12text_t *)wth->priv;
351 k12text_state_t state;
352
353 /*
354 * We seek to the file position after the end of the previous frame
355 * processed by k12text_read(), since the lexer undoubtedly did some
356 * amount of look-ahead when processing the previous frame.
357 *
358 * We also clear out any lexer state (eg: look-ahead buffer) and
359 * init vars set by lexer.
360 */
361
362 if ( file_seek(wth->fh, k12text->next_frame_offset, SEEK_SET, err) == -1) {
363 return FALSE;
364 }
365 state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
366
367 if (!k12text_run_scanner(&state, wth->fh, NEXT_FRAME, err, err_info)) {
368 g_free(state.bb);
369 return FALSE;
370 }
371
372 if (state.ok_frame == FALSE) {
373 if (state.at_eof) {
374 *err = 0;
375 *err_info = NULL;
376 } else {
377 *err = WTAP_ERR_BAD_FILE;
378 *err_info = state.error_str;
379 }
380 g_free(state.bb);
381 return FALSE;
382 }
383
384 *data_offset = k12text->next_frame_offset; /* file position for beginning of this frame */
385 k12text->next_frame_offset += state.file_bytes_read; /* file position after end of this frame */
386
387 if (!k12text_set_headers(rec, &state, err, err_info)) {
388 g_free(state.bb);
389 return FALSE;
390 }
391 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
392 memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen);
393
394 g_free(state.bb);
395 return TRUE;
396 }
397
398 static gboolean
k12text_seek_read(wtap * wth,gint64 seek_off,wtap_rec * rec,Buffer * buf,int * err,char ** err_info)399 k12text_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
400 {
401 k12text_state_t state;
402
403 if ( file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
404 return FALSE;
405 }
406 state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
407
408 if (!k12text_run_scanner(&state, wth->random_fh, NEXT_FRAME, err, err_info)) {
409 g_free(state.bb);
410 return FALSE;
411 }
412
413 if (state.ok_frame == FALSE) {
414 *err = WTAP_ERR_BAD_FILE;
415 if (state.at_eof) {
416 /* What happened ? The desired frame was previously read without a problem */
417 *err_info = g_strdup("Unexpected EOF (program error ?)");
418 } else {
419 *err_info = state.error_str;
420 }
421 g_free(state.bb);
422 return FALSE;
423 }
424
425 if (!k12text_set_headers(rec, &state, err, err_info)) {
426 g_free(state.bb);
427 return FALSE;
428 }
429 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
430 memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen);
431
432 g_free(state.bb);
433 return TRUE;
434 }
435
436 wtap_open_return_val
k12text_open(wtap * wth,int * err,gchar ** err_info)437 k12text_open(wtap *wth, int *err, gchar **err_info)
438 {
439 k12text_t *k12text;
440 k12text_state_t state;
441
442 state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
443 if (!k12text_run_scanner(&state, wth->fh, MAGIC, err, err_info)) {
444 g_free(state.bb);
445 return WTAP_OPEN_ERROR;
446 }
447
448 if (!state.is_k12text) {
449 /* *err might have been set to WTAP_ERR_SHORT_READ */
450 *err = 0;
451 g_free(state.bb);
452 return WTAP_OPEN_NOT_MINE;
453 }
454
455 if ( file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
456 g_free(state.bb);
457 return WTAP_OPEN_ERROR;
458 }
459
460 k12text = g_new(k12text_t, 1);
461 wth->priv = (void *)k12text;
462 k12text->next_frame_offset = 0;
463 wth->file_type_subtype = k12text_file_type_subtype;
464 wth->file_encap = WTAP_ENCAP_PER_PACKET;
465 wth->snapshot_length = 0;
466 wth->subtype_read = k12text_read;
467 wth->subtype_seek_read = k12text_seek_read;
468 wth->file_tsprec = WTAP_TSPREC_NSEC;
469
470 g_free(state.bb);
471 return WTAP_OPEN_MINE;
472 }
473
474
475 static const struct { int e; const char* s; } encaps[] = {
476 { WTAP_ENCAP_ETHERNET, "ETHER" },
477 { WTAP_ENCAP_MTP2, "MTP-L2" },
478 { WTAP_ENCAP_ATM_PDUS, "SSCOP" },
479 { WTAP_ENCAP_MTP3, "SSCF" },
480 { WTAP_ENCAP_CHDLC, "HDLC" },
481 /* ... */
482 { 0, NULL }
483 };
484
485 static gboolean
k12text_dump(wtap_dumper * wdh,const wtap_rec * rec,const guint8 * pd,int * err,gchar ** err_info _U_)486 k12text_dump(wtap_dumper *wdh, const wtap_rec *rec,
487 const guint8 *pd, int *err, gchar **err_info _U_) {
488 #define K12BUF_SIZE 196808
489 char *buf;
490 size_t left = K12BUF_SIZE;
491 size_t wl;
492 char *p;
493 const char* str_enc;
494 guint i;
495 guint ns;
496 guint ms;
497 gboolean ret;
498 struct tm *tmp;
499
500 /* Don't write anything bigger than we're willing to read. */
501 if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
502 *err = WTAP_ERR_PACKET_TOO_LARGE;
503 return FALSE;
504 }
505
506 str_enc = NULL;
507 for(i=0; encaps[i].s; i++) {
508 if (rec->rec_header.packet_header.pkt_encap == encaps[i].e) {
509 str_enc = encaps[i].s;
510 break;
511 }
512 }
513 if (str_enc == NULL) {
514 /*
515 * That encapsulation type is not supported. Fail.
516 */
517 *err = WTAP_ERR_UNWRITABLE_ENCAP;
518 return FALSE;
519 }
520
521 buf = (char *)g_malloc(K12BUF_SIZE);
522 p = buf;
523
524 ms = rec->ts.nsecs / 1000000;
525 ns = (rec->ts.nsecs - (1000000*ms))/1000;
526
527 tmp = gmtime(&rec->ts.secs);
528 if (tmp == NULL)
529 g_snprintf(p, 90, "+---------+---------------+----------+\r\nXX:XX:XX,");
530 else
531 strftime(p, 90, "+---------+---------------+----------+\r\n%H:%M:%S,", tmp);
532 wl = strlen(p);
533 p += wl;
534 left -= wl;
535
536 wl = g_snprintf(p, (gulong)left, "%.3d,%.3d %s\r\n|0 |", ms, ns, str_enc);
537 p += wl;
538 left -= wl;
539
540 for(i = 0; i < rec->rec_header.packet_header.caplen && left > 2; i++) {
541 wl = g_snprintf(p, (gulong)left, "%.2x|", pd[i]);
542 p += wl;
543 left -= wl;
544 }
545
546 wl = g_snprintf(p, (gulong)left, "\r\n\r\n");
547 left -= wl;
548
549 ret = wtap_dump_file_write(wdh, buf, K12BUF_SIZE - left, err);
550
551 g_free(buf);
552 return ret;
553 }
554
555
556 static gboolean
k12text_dump_open(wtap_dumper * wdh,int * err _U_,gchar ** err_info _U_)557 k12text_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
558 {
559 wdh->subtype_write = k12text_dump;
560
561 return TRUE;
562 }
563
564 static int
k12text_dump_can_write_encap(int encap)565 k12text_dump_can_write_encap(int encap)
566 {
567 switch (encap) {
568 case WTAP_ENCAP_PER_PACKET:
569 case WTAP_ENCAP_ETHERNET:
570 case WTAP_ENCAP_MTP3:
571 case WTAP_ENCAP_CHDLC:
572 return 0;
573 case WTAP_ENCAP_MTP2:
574 case WTAP_ENCAP_ATM_PDUS:
575 default:
576 return WTAP_ERR_UNWRITABLE_ENCAP;
577 }
578 }
579
580 static const struct supported_block_type k12text_blocks_supported[] = {
581 /*
582 * We support packet blocks, with no comments or other options.
583 */
584 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
585 };
586
587 static const struct file_type_subtype_info k12text_info = {
588 "K12 text file", "k12text", "txt", NULL,
589 FALSE, BLOCKS_SUPPORTED(k12text_blocks_supported),
590 k12text_dump_can_write_encap, k12text_dump_open, NULL
591 };
592
register_k12text(void)593 void register_k12text(void)
594 {
595 k12text_file_type_subtype = wtap_register_file_type_subtype(&k12text_info);
596
597 /*
598 * Register name for backwards compatibility with the
599 * wtap_filetypes table in Lua.
600 */
601 wtap_register_backwards_compatibility_lua_name("K12TEXT",
602 k12text_file_type_subtype);
603 }
604