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