text2pcap: Support parsing of iso-8601 dates

This commit is contained in:
Jörg Mayer 2021-12-02 19:34:45 +00:00
parent 76ff47152b
commit a0173cd7cf
2 changed files with 79 additions and 57 deletions

View File

@ -75,11 +75,15 @@ a hex number longer than two characters. Any text after the bytes is
ignored (e.g. the character dump). Any hex numbers in this text are ignored (e.g. the character dump). Any hex numbers in this text are
also ignored. An offset of zero is indicative of starting a new also ignored. An offset of zero is indicative of starting a new
packet, so a single text file with a series of hexdumps can be packet, so a single text file with a series of hexdumps can be
converted into a packet capture with multiple packets. Packets may be converted into a packet capture with multiple packets.
preceded by a timestamp. These are interpreted according to the format
given on the command line (see *-t*). If not, the first packet Packets may be preceded by a direction indicator and a timestamp if
is timestamped with the current time the conversion takes place. Multiple indicated by the command line (see *-D* and *-t*). The format of the
packets are written with timestamps differing by one microsecond each. timestamps is specified as a mandatory parameter to *-t*. If timestamp
parsing is not enabled or failed, the first packet is timestamped
with the current time the conversion takes place. Multiple packets
are written with timestamps differing by one microsecond each.
In general, short of these restrictions, *text2pcap* is pretty liberal In general, short of these restrictions, *text2pcap* is pretty liberal
about reading in hexdumps and has been tested with a variety of about reading in hexdumps and has been tested with a variety of
mangled outputs (including being forwarded through email multiple mangled outputs (including being forwarded through email multiple
@ -122,7 +126,7 @@ multiple times to generate more debugging information.
-D:: -D::
+ +
-- --
The text before the packet starts either with an I or O indicating that The text before the packet may start either with an I or O indicating that
the packet is inbound or outbound. This is used when generating dummy headers. the packet is inbound or outbound. This is used when generating dummy headers.
The indication is only stored if the output format is pcapng. The indication is only stored if the output format is pcapng.
-- --
@ -239,8 +243,10 @@ into the SCTP header.
+ +
-- --
Treats the text before the packet as a date/time code; __timefmt__ is a Treats the text before the packet as a date/time code; __timefmt__ is a
format string of the sort supported by strptime(3). format string of the sort supported by strftime(3).
Example: The time "10:15:14.5476" has the format code "%H:%M:%S." Example: The time "10:15:14.5476" has the format code "%H:%M:%S."
The special format string __ISO__ indicates that the string should be
parsed according to the ISO-8601 specification.
*NOTE:* The subsecond component delimiter must be specified (.) but no *NOTE:* The subsecond component delimiter must be specified (.) but no
pattern is required; the remaining number is assumed to be fractions of pattern is required; the remaining number is assumed to be fractions of
@ -300,7 +306,7 @@ use fe80::202:b3ff:fe1e:8329 and 2001:0db8:85a3::8a2e:0370:7334 for all IP packe
== SEE ALSO == SEE ALSO
od(1), xref:https://www.tcpdump.org/manpages/pcap.3pcap.html[pcap](3), xref:wireshark.html[wireshark](1), xref:tshark.html[tshark](1), xref:dumpcap.html[dumpcap](1), xref:mergecap.html[mergecap](1), od(1), xref:https://www.tcpdump.org/manpages/pcap.3pcap.html[pcap](3), xref:wireshark.html[wireshark](1), xref:tshark.html[tshark](1), xref:dumpcap.html[dumpcap](1), xref:mergecap.html[mergecap](1),
xref:editcap.html[editcap](1), strptime(3), xref:https://www.tcpdump.org/manpages/pcap-filter.7.html[pcap-filter](7) or xref:https://www.tcpdump.org/manpages/tcpdump.1.html[tcpdump](8) xref:editcap.html[editcap](1), strftime(3), xref:https://www.tcpdump.org/manpages/pcap-filter.7.html[pcap-filter](7) or xref:https://www.tcpdump.org/manpages/tcpdump.1.html[tcpdump](8)
== NOTES == NOTES

View File

@ -104,6 +104,7 @@
#include <ui/version_info.h> #include <ui/version_info.h>
#include <wsutil/inet_addr.h> #include <wsutil/inet_addr.h>
#include <wsutil/wslog.h> #include <wsutil/wslog.h>
#include <wsutil/nstime.h>
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> /* for _setmode */ #include <io.h> /* for _setmode */
@ -205,7 +206,7 @@ static int start_new_packet(gboolean);
/* This buffer contains strings present before the packet offset 0 */ /* This buffer contains strings present before the packet offset 0 */
#define PACKET_PREAMBLE_MAX_LEN 2048 #define PACKET_PREAMBLE_MAX_LEN 2048
static guint8 packet_preamble[PACKET_PREAMBLE_MAX_LEN+1]; static char packet_preamble[PACKET_PREAMBLE_MAX_LEN+1];
static int packet_preamble_len = 0; static int packet_preamble_len = 0;
/* Number of packets read and written */ /* Number of packets read and written */
@ -217,6 +218,7 @@ static guint64 bytes_written = 0;
static time_t ts_sec = 0; static time_t ts_sec = 0;
static guint32 ts_nsec = 0; static guint32 ts_nsec = 0;
static char *ts_fmt = NULL; static char *ts_fmt = NULL;
static int ts_fmt_iso = 0;
static struct tm timecode_default; static struct tm timecode_default;
static guint8* pkt_lnstart; static guint8* pkt_lnstart;
@ -953,9 +955,6 @@ static void
parse_preamble (void) parse_preamble (void)
{ {
struct tm timecode; struct tm timecode;
char *subsecs;
char *p;
int subseclen;
int i; int i;
/* /*
@ -1015,56 +1014,71 @@ parse_preamble (void)
* This should cover line breaks etc that get counted. * This should cover line breaks etc that get counted.
*/ */
if (strlen(packet_preamble) > 2) { if (strlen(packet_preamble) > 2) {
/* Get Time leaving subseconds */ if (ts_fmt_iso) {
subsecs = strptime( packet_preamble, ts_fmt, &timecode ); nstime_t ts_iso;
if (subsecs != NULL) { if (0 < iso8601_to_nstime(&ts_iso, packet_preamble, ISO8601_DATETIME_AUTO)) {
/* Get the long time from the tm structure */ ts_sec = ts_iso.secs;
/* (will return -1 if failure) */ ts_nsec = ts_iso.nsecs;
ts_sec = mktime( &timecode ); } else {
} else ts_sec = 0; /* Jan 1,1970: 00:00 GMT; tshark/wireshark will display date/time as adjusted by timezone */
ts_sec = -1; /* we failed to parse it */ ts_nsec = 0;
/* This will ensure incorrectly parsed dates get set to zero */
if (-1 == ts_sec) {
/* Sanitize - remove all '\r' */
char *c;
while ((c = strchr(packet_preamble, '\r')) != NULL) *c=' ';
fprintf (stderr, "Failure processing time \"%s\" using time format \"%s\"\n (defaulting to Jan 1,1970 00:00:00 GMT)\n",
packet_preamble, ts_fmt);
if (debug >= 2) {
fprintf(stderr, "timecode: %02d/%02d/%d %02d:%02d:%02d %d\n",
timecode.tm_mday, timecode.tm_mon, timecode.tm_year,
timecode.tm_hour, timecode.tm_min, timecode.tm_sec, timecode.tm_isdst);
} }
ts_sec = 0; /* Jan 1,1970: 00:00 GMT; tshark/wireshark will display date/time as adjusted by timezone */
ts_nsec = 0;
} else { } else {
/* Parse subseconds */ char *subsecs;
ts_nsec = (guint32)strtol(subsecs, &p, 10); char *p;
if (subsecs == p) { int subseclen;
/* Error */
/* Get Time leaving subseconds */
subsecs = strptime( packet_preamble, ts_fmt, &timecode );
if (subsecs != NULL) {
/* Get the long time from the tm structure */
/* (will return -1 if failure) */
ts_sec = mktime( &timecode );
} else
ts_sec = -1; /* we failed to parse it */
/* This will ensure incorrectly parsed dates get set to zero */
if (-1 == ts_sec) {
/* Sanitize - remove all '\r' */
char *c;
while ((c = strchr(packet_preamble, '\r')) != NULL) *c=' ';
fprintf (stderr, "Failure processing time \"%s\" using time format \"%s\"\n (defaulting to Jan 1,1970 00:00:00 GMT)\n",
packet_preamble, ts_fmt);
if (debug >= 2) {
fprintf(stderr, "timecode: %02d/%02d/%d %02d:%02d:%02d %d\n",
timecode.tm_mday, timecode.tm_mon, timecode.tm_year,
timecode.tm_hour, timecode.tm_min, timecode.tm_sec, timecode.tm_isdst);
}
ts_sec = 0; /* Jan 1,1970: 00:00 GMT; tshark/wireshark will display date/time as adjusted by timezone */
ts_nsec = 0; ts_nsec = 0;
} else { } else {
/* /* Parse subseconds */
* Convert that number to a number ts_nsec = (guint32)strtol(subsecs, &p, 10);
* of nanoseconds; if it's N digits if (subsecs == p) {
* long, it's in units of 10^(-N) seconds, /* Error */
* so, to convert it to units of ts_nsec = 0;
* 10^-9 seconds, we multiply by } else {
* 10^(9-N).
*/
subseclen = (int) (p - subsecs);
if (subseclen > 9) {
/* /*
* *More* than 9 digits; 9-N is * Convert that number to a number
* negative, so we divide by * of nanoseconds; if it's N digits
* 10^(N-9). * long, it's in units of 10^(-N) seconds,
* so, to convert it to units of
* 10^-9 seconds, we multiply by
* 10^(9-N).
*/ */
for (i = subseclen - 9; i != 0; i--) subseclen = (int) (p - subsecs);
ts_nsec /= 10; if (subseclen > 9) {
} else if (subseclen < 9) { /*
for (i = 9 - subseclen; i != 0; i--) * *More* than 9 digits; 9-N is
ts_nsec *= 10; * negative, so we divide by
* 10^(N-9).
*/
for (i = subseclen - 9; i != 0; i--)
ts_nsec /= 10;
} else if (subseclen < 9) {
for (i = 9 - subseclen; i != 0; i--)
ts_nsec *= 10;
}
} }
} }
} }
@ -1587,6 +1601,8 @@ parse_options (int argc, char *argv[])
case 't': case 't':
ts_fmt = ws_optarg; ts_fmt = ws_optarg;
if (!strcmp(ws_optarg, "ISO"))
ts_fmt_iso = 1;
break; break;
case 'u': case 'u':
@ -1794,7 +1810,7 @@ parse_options (int argc, char *argv[])
* on Windows, it won't work if ts_sec is before the Epoch, * on Windows, it won't work if ts_sec is before the Epoch,
* but it's long after 1970, so.... * but it's long after 1970, so....
*/ */
fprintf(stderr, "localtime(right now) failed\n"); fprintf(stderr, "localtime (right now) failed\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
timecode_default = *now_tm; timecode_default = *now_tm;