*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include "catapult_dct2000.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <wsutil/strtoi.h>
#include "wtap-int.h"
#include "file_wrappers.h"
#define MAX_FIRST_LINE_LENGTH 150
#define MAX_TIMESTAMP_LINE_LENGTH 50
#define MAX_LINE_LENGTH 131072
#define MAX_SECONDS_CHARS 16
#define MAX_TIMESTAMP_LEN (MAX_SECONDS_CHARS+5)
#define MAX_SUBSECOND_DECIMALS 4
#define MAX_CONTEXT_NAME 64
#define MAX_PROTOCOL_NAME 64
#define MAX_PORT_DIGITS 2
#define MAX_VARIANT_DIGITS 16
#define MAX_OUTHDR_NAME 256
#define AAL_HEADER_CHARS 12
typedef enum packet_direction_t
{
sent,
received
} packet_direction_t;
typedef struct
{
char *before_time;
bool has_l;
} line_prefix_info_t;
typedef struct dct2000_file_externals
{
time_t start_secs;
uint32_t start_usecs;
* The following information is needed only for dumping.
*
* XXX - Wiretap is not *supposed* to require that a packet being
* dumped come from a file of the same type that you currently have
* open; this should be fixed.
*/
char firstline[MAX_FIRST_LINE_LENGTH];
int firstline_length;
char secondline[MAX_TIMESTAMP_LINE_LENGTH];
int secondline_length;
Records (file offset -> line_prefix_info_t)
*/
GHashTable *packet_prefix_table;
} dct2000_file_externals_t;
static const char catapult_dct2000_magic[] = "Session Transcript";
static bool catapult_dct2000_read(wtap *wth, wtap_rec *rec,
Buffer *buf, int *err, char **err_info,
int64_t *data_offset);
static bool catapult_dct2000_seek_read(wtap *wth, int64_t seek_off,
wtap_rec *rec,
Buffer *buf, int *err,
char **err_info);
static void catapult_dct2000_close(wtap *wth);
static bool catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
const uint8_t *pd, int *err, char **err_info);
static bool read_new_line(FILE_T fh, int *length,
char *buf, size_t bufsize, int *err,
char **err_info);
static bool parse_line(char *linebuff, int line_length,
int *seconds, int *useconds,
long *before_time_offset, long *after_time_offset,
long *data_offset,
int *data_chars,
packet_direction_t *direction,
int *encap, bool *is_comment, bool *is_sprint,
char *aal_header_chars,
char *context_name, uint8_t *context_portp,
char *protocol_name, char *variant_name,
char *outhdr_name);
static bool process_parsed_line(wtap *wth,
const dct2000_file_externals_t *file_externals,
wtap_rec *rec,
Buffer *buf, int64_t file_offset,
char *linebuff, long dollar_offset,
int seconds, int useconds,
char *timestamp_string,
packet_direction_t direction, int encap,
char *context_name, uint8_t context_port,
char *protocol_name, char *variant_name,
char *outhdr_name, char *aal_header_chars,
bool is_comment, int data_chars,
int *err, char **err_info);
static uint8_t hex_from_char(char c);
static void prepare_hex_byte_from_chars_table(void);
static uint8_t hex_byte_from_chars(char *c);
static char char_from_hex(uint8_t hex);
static void set_aal_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction,
char *aal_header_chars);
static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction);
static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction);
static int packet_offset_equal(const void *v, const void *v2);
static unsigned packet_offset_hash_func(const void *v);
static bool get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs);
static gboolean free_line_prefix_info(void *key, void *value, void *user_data);
static int dct2000_file_type_subtype = -1;
void register_dct2000(void);
wtap_open_return_val
catapult_dct2000_open(wtap *wth, int *err, char **err_info)
{
time_t timestamp;
uint32_t usecs;
int firstline_length = 0;
dct2000_file_externals_t *file_externals;
static char linebuff[MAX_LINE_LENGTH];
static bool hex_byte_table_values_set = false;
errno = 0;
if (!read_new_line(wth->fh, &firstline_length, linebuff,
sizeof linebuff, err, err_info)) {
if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
return WTAP_OPEN_ERROR;
}
else {
return WTAP_OPEN_NOT_MINE;
}
}
if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
firstline_length >= MAX_FIRST_LINE_LENGTH) {
return WTAP_OPEN_NOT_MINE;
}
if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
return WTAP_OPEN_NOT_MINE;
}
if (!hex_byte_table_values_set) {
prepare_hex_byte_from_chars_table();
hex_byte_table_values_set = true;
}
file_externals = g_new0(dct2000_file_externals_t, 1);
(void) g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
file_externals->firstline_length = firstline_length;
if (!read_new_line(wth->fh, &(file_externals->secondline_length),
linebuff, sizeof linebuff, err, err_info)) {
g_free(file_externals);
if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
return WTAP_OPEN_ERROR;
}
else {
return WTAP_OPEN_NOT_MINE;
}
}
if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
(!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
g_free(file_externals);
return WTAP_OPEN_NOT_MINE;
}
file_externals->start_secs = timestamp;
file_externals->start_usecs = usecs;
(void) g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
wth->file_type_subtype = dct2000_file_type_subtype;
wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
wth->subtype_read = catapult_dct2000_read;
wth->subtype_seek_read = catapult_dct2000_seek_read;
wth->subtype_close = catapult_dct2000_close;
wth->file_tsprec = WTAP_TSPREC_USEC;
file_externals->packet_prefix_table =
g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
wth->priv = (void*)file_externals;
*err = errno;
* Add an IDB; we don't know how many interfaces were
* involved, so we just say one interface, about which
* we only know the link-layer type, snapshot length,
* and time stamp resolution.
*/
wtap_add_generated_idb(wth);
return WTAP_OPEN_MINE;
}
static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
{
int idx = 0;
if (secs < 10) {
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else if (secs < 100) {
timestamp_string[idx++] = ( secs / 10) + '0';
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else if (secs < 1000) {
timestamp_string[idx++] = ((secs) / 100) + '0';
timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else if (secs < 10000) {
timestamp_string[idx++] = ((secs) / 1000) + '0';
timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else if (secs < 100000) {
timestamp_string[idx++] = ((secs) / 10000) + '0';
timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else if (secs < 1000000) {
timestamp_string[idx++] = ((secs) / 100000) + '0';
timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
timestamp_string[idx++] = ((secs % 10)) + '0';
}
else {
snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
return;
}
timestamp_string[idx++] = '.';
timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
timestamp_string[idx] = '\0';
}
static bool
catapult_dct2000_read(wtap *wth, wtap_rec *rec, Buffer *buf,
int *err, char **err_info, int64_t *data_offset)
{
long dollar_offset, before_time_offset, after_time_offset;
packet_direction_t direction;
int encap;
dct2000_file_externals_t *file_externals =
(dct2000_file_externals_t*)wth->priv;
while (1) {
int line_length, seconds, useconds, data_chars;
bool is_comment = false;
bool is_sprint = false;
int64_t this_offset;
static char linebuff[MAX_LINE_LENGTH+1];
char aal_header_chars[AAL_HEADER_CHARS];
char context_name[MAX_CONTEXT_NAME];
uint8_t context_port = 0;
char protocol_name[MAX_PROTOCOL_NAME+1];
char variant_name[MAX_VARIANT_DIGITS+1];
char outhdr_name[MAX_OUTHDR_NAME+1];
this_offset = file_tell(wth->fh);
if (!read_new_line(wth->fh, &line_length, linebuff,
sizeof linebuff, err, err_info)) {
if (*err != 0) {
return false;
}
break;
}
if (parse_line(linebuff, line_length, &seconds, &useconds,
&before_time_offset, &after_time_offset,
&dollar_offset,
&data_chars, &direction, &encap, &is_comment, &is_sprint,
aal_header_chars,
context_name, &context_port,
protocol_name, variant_name, outhdr_name)) {
line_prefix_info_t *line_prefix_info;
int64_t *pkey = NULL;
char timestamp_string[MAX_TIMESTAMP_LEN+1];
write_timestamp_string(timestamp_string, seconds, useconds/100);
This will be the seek_off parameter when this frame is re-read.
*/
*data_offset = this_offset;
if (!process_parsed_line(wth, file_externals,
rec, buf, this_offset,
linebuff, dollar_offset,
seconds, useconds,
timestamp_string,
direction, encap,
context_name, context_port,
protocol_name, variant_name,
outhdr_name, aal_header_chars,
is_comment, data_chars,
err, err_info))
return false;
line_prefix_info = g_new(line_prefix_info_t,1);
line_prefix_info->before_time = (char *)g_malloc(before_time_offset+1);
memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
line_prefix_info->before_time[before_time_offset] = '\0';
line_prefix_info->has_l = ((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
(strncmp(linebuff+after_time_offset, " l ", 3) == 0);
pkey = (int64_t *)g_malloc(sizeof(*pkey));
*pkey = this_offset;
g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
return true;
}
}
return false;
}
static bool
catapult_dct2000_seek_read(wtap *wth, int64_t seek_off,
wtap_rec *rec, Buffer *buf,
int *err, char **err_info)
{
int length;
long dollar_offset, before_time_offset, after_time_offset;
static char linebuff[MAX_LINE_LENGTH+1];
char aal_header_chars[AAL_HEADER_CHARS];
char context_name[MAX_CONTEXT_NAME];
uint8_t context_port = 0;
char protocol_name[MAX_PROTOCOL_NAME+1];
char variant_name[MAX_VARIANT_DIGITS+1];
char outhdr_name[MAX_OUTHDR_NAME+1];
bool is_comment = false;
bool is_sprint = false;
packet_direction_t direction;
int encap;
int seconds, useconds, data_chars;
dct2000_file_externals_t *file_externals =
(dct2000_file_externals_t*)wth->priv;
*err = errno = 0;
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
return false;
}
if (!read_new_line(wth->random_fh, &length, linebuff,
sizeof linebuff, err, err_info)) {
return false;
}
if (parse_line(linebuff, length, &seconds, &useconds,
&before_time_offset, &after_time_offset,
&dollar_offset,
&data_chars, &direction, &encap, &is_comment, &is_sprint,
aal_header_chars,
context_name, &context_port,
protocol_name, variant_name, outhdr_name)) {
char timestamp_string[MAX_TIMESTAMP_LEN+1];
write_timestamp_string(timestamp_string, seconds, useconds/100);
if (!process_parsed_line(wth, file_externals,
rec, buf, seek_off,
linebuff, dollar_offset,
seconds, useconds,
timestamp_string,
direction, encap,
context_name, context_port,
protocol_name, variant_name,
outhdr_name, aal_header_chars,
is_comment, data_chars,
err, err_info)) {
return false;
}
*err = errno = 0;
return true;
}
*err = errno;
*err_info = ws_strdup_printf("catapult dct2000: seek_read failed to read/parse "
"line at position %" PRId64,
seek_off);
return false;
}
static void
catapult_dct2000_close(wtap *wth)
{
dct2000_file_externals_t *file_externals =
(dct2000_file_externals_t*)wth->priv;
g_hash_table_foreach_remove(file_externals->packet_prefix_table,
free_line_prefix_info, NULL);
g_hash_table_destroy(file_externals->packet_prefix_table);
}
typedef struct {
bool first_packet_written;
nstime_t start_time;
} dct2000_dump_t;
static bool
catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_)
{
wdh->subtype_write = catapult_dct2000_dump;
return true;
}
static int
catapult_dct2000_dump_can_write_encap(int encap)
{
switch (encap) {
case WTAP_ENCAP_CATAPULT_DCT2000:
return 0;
default:
return WTAP_ERR_UNWRITABLE_ENCAP;
}
}
static bool
catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
const uint8_t *pd, int *err, char **err_info _U_)
{
const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
uint32_t n;
line_prefix_info_t *prefix = NULL;
char time_string[MAX_TIMESTAMP_LEN];
bool is_comment;
bool is_sprint = false;
dct2000_dump_t *dct2000;
int consecutive_slashes=0;
char *p_c;
dct2000_file_externals_t *file_externals =
(dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
if (rec->rec_type != REC_TYPE_PACKET) {
*err = WTAP_ERR_UNWRITABLE_REC_TYPE;
return false;
}
* Make sure this packet doesn't have a link-layer type that
* differs from the one for the file (which should always
* be WTAP_ENCAP_CATAPULT_DCT2000).
*/
if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
*err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
return false;
}
dct2000 = (dct2000_dump_t *)wdh->priv;
if (dct2000 == NULL) {
if (!wtap_dump_file_write(wdh, file_externals->firstline,
file_externals->firstline_length, err)) {
return false;
}
if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
return false;
}
opening time of the log.
*/
if (!wtap_dump_file_write(wdh, file_externals->secondline,
file_externals->secondline_length, err)) {
return false;
}
if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
return false;
}
dct2000 = g_new(dct2000_dump_t, 1);
wdh->priv = (void *)dct2000;
dct2000->start_time.secs = file_externals->start_secs;
dct2000->start_time.nsecs =
(file_externals->start_usecs * 1000);
dct2000->first_packet_written = true;
}
prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
(const void*)&(pseudo_header->dct2000.seek_off));
if (!wtap_dump_file_write(wdh, prefix->before_time,
strlen(prefix->before_time), err)) {
return false;
}
p_c = prefix->before_time;
while (p_c && (*p_c != '/')) {
p_c++;
}
while (p_c && (*p_c == '/')) {
consecutive_slashes++;
p_c++;
}
is_comment = (consecutive_slashes == 5);
if (rec->ts.nsecs >= dct2000->start_time.nsecs) {
write_timestamp_string(time_string,
(int)(rec->ts.secs - dct2000->start_time.secs),
(rec->ts.nsecs - dct2000->start_time.nsecs) / 100000);
}
else {
write_timestamp_string(time_string,
(int)(rec->ts.secs - dct2000->start_time.secs-1),
((1000000000 + (rec->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
}
if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
return false;
}
if (prefix->has_l) {
if (!wtap_dump_file_write(wdh, " l ", 3, err)) {
return false;
}
}
for (n=0; pd[n] != '\0'; n++);
n++;
n++;
for (; pd[n] != '\0'; n++);
n++;
if (is_comment) {
is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
}
for (; pd[n] != '\0'; n++);
n++;
for (; pd[n] != '\0'; n++);
n++;
for (; pd[n] != '\0'; n++);
n++;
n += 2;
if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
return false;
}
if (!is_comment) {
for (; n < rec->rec_header.packet_header.len; n++) {
char c[2];
c[0] = char_from_hex((uint8_t)(pd[n] >> 4));
c[1] = char_from_hex((uint8_t)(pd[n] & 0x0f));
if (!wtap_dump_file_write(wdh, c, 2, err)) {
return false;
}
}
}
else {
if (!wtap_dump_file_write(wdh, pd+n, rec->rec_header.packet_header.len-n, err)) {
return false;
}
}
if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
return false;
}
return true;
}
static bool
read_new_line(FILE_T fh, int *length,
char *linebuff, size_t linebuffsize, int *err, char **err_info)
{
int64_t pos_before = file_tell(fh);
if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
*err = file_error(fh, err_info);
return false;
}
*length = (int)(file_tell(fh) - pos_before);
if (*length > 0 && linebuff[*length-1] == '\n') {
linebuff[*length-1] = '\0';
*length = *length - 1;
}
if (*length > 0 && linebuff[*length-1] == '\r') {
linebuff[*length-1] = '\0';
*length = *length - 1;
}
return true;
}
static bool
parse_line(char *linebuff, int line_length,
int *seconds, int *useconds,
long *before_time_offset, long *after_time_offset,
long *data_offset, int *data_chars,
packet_direction_t *direction,
int *encap, bool *is_comment, bool *is_sprint,
char *aal_header_chars,
char *context_name, uint8_t *context_portp,
char *protocol_name, char *variant_name,
char *outhdr_name)
{
int n = 0;
int port_digits;
char port_number_string[MAX_PORT_DIGITS+1];
int variant_digits;
int variant = 1;
int protocol_chars;
int outhdr_chars;
char seconds_buff[MAX_SECONDS_CHARS+1];
int seconds_chars;
char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
int subsecond_decimals_chars;
bool skip_first_byte = false;
bool atm_header_present = false;
*is_comment = false;
*is_sprint = false;
for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
if (linebuff[n] == '/') {
context_name[n] = '\0';
if (strncmp(linebuff+n, "/////", 5) != 0) {
return false;
}
(void) g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
*is_comment = true;
break;
}
if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
return false;
}
context_name[n] = linebuff[n];
}
if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
return false;
}
variant_name[0] = '\0';
outhdr_name[0] = '\0';
port_number_string[0] = '\0';
if (!(*is_comment)) {
if (linebuff[n] != '.') {
return false;
}
context_name[n] = '\0';
n++;
for (port_digits = 0;
(linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
n++, port_digits++) {
if (!g_ascii_isdigit(linebuff[n])) {
return false;
}
port_number_string[port_digits] = linebuff[n];
}
if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
return false;
}
if (linebuff[n] != '/')
{
return false;
}
port_number_string[port_digits] = '\0';
if (port_digits == 1) {
*context_portp = port_number_string[0] - '0';
}
else {
whether what follows the number is anything other than
a '\0'. */
if (!ws_strtou8(port_number_string, NULL, context_portp)) {
return false;
}
}
n++;
for (protocol_chars = 0;
(linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
n++, protocol_chars++) {
if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '.')) {
return false;
}
protocol_name[protocol_chars] = linebuff[n];
}
if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
return false;
}
protocol_name[protocol_chars] = '\0';
if (linebuff[n] != '/') {
return false;
}
n++;
for (variant_digits = 0;
(g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
n++, variant_digits++) {
if (!g_ascii_isdigit(linebuff[n])) {
return false;
}
variant_name[variant_digits] = linebuff[n];
}
if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
return false;
}
if (variant_digits > 0) {
variant_name[variant_digits] = '\0';
if (variant_digits == 1) {
variant = variant_name[0] - '0';
}
else {
if (!ws_strtoi32(variant_name, NULL, &variant)) {
return false;
}
}
}
else {
variant_name[0] = '1';
variant_name[1] = '\0';
}
if (linebuff[n] == ',') {
n++;
for (outhdr_chars = 0;
(g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
(outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
n++, outhdr_chars++) {
if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
return false;
}
outhdr_name[outhdr_chars] = linebuff[n];
}
if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
return false;
}
outhdr_name[outhdr_chars] = '\0';
}
}
if ((strcmp(protocol_name, "ip") == 0) ||
(strcmp(protocol_name, "sctp") == 0) ||
(strcmp(protocol_name, "gre") == 0) ||
(strcmp(protocol_name, "mipv6") == 0) ||
(strcmp(protocol_name, "igmp") == 0)) {
*encap = WTAP_ENCAP_RAW_IP;
}
else
if ((strcmp(protocol_name, "fp") == 0) ||
(strncmp(protocol_name, "fp_r", 4) == 0)) {
if ((variant > 256) && (variant % 256 == 3)) {
*encap = 0;
}
else {
*encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
atm_header_present = true;
}
}
else if (strcmp(protocol_name, "fpiur_r5") == 0) {
*encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
atm_header_present = true;
}
else
if (strcmp(protocol_name, "ppp") == 0) {
*encap = WTAP_ENCAP_PPP;
}
else
if (strcmp(protocol_name, "isdn_l3") == 0) {
skip_first_byte = true;
*encap = WTAP_ENCAP_ISDN;
}
else
if (strcmp(protocol_name, "isdn_l2") == 0) {
*encap = WTAP_ENCAP_ISDN;
}
else
if (strcmp(protocol_name, "ethernet") == 0) {
*encap = WTAP_ENCAP_ETHERNET;
}
else
if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
(strcmp(protocol_name, "saaluni_sscop") == 0)) {
*encap = DCT2000_ENCAP_SSCOP;
}
else
if (strcmp(protocol_name, "frelay_l2") == 0) {
*encap = WTAP_ENCAP_FRELAY;
}
else
if (strcmp(protocol_name, "ss7_mtp2") == 0) {
*encap = DCT2000_ENCAP_MTP2;
}
else
if ((strcmp(protocol_name, "nbap") == 0) ||
(strcmp(protocol_name, "nbap_r4") == 0) ||
(strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
*encap = DCT2000_ENCAP_NBAP;
}
else {
in some cases find protocol embedded inside primitive */
*encap = DCT2000_ENCAP_UNHANDLED;
}
if (atm_header_present) {
int header_chars_seen = 0;
for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
n++;
if (n+1 >= line_length) {
return false;
}
for (;
((n < line_length) &&
(linebuff[n] >= '0') && (linebuff[n] <= '?') &&
(header_chars_seen < AAL_HEADER_CHARS));
n++, header_chars_seen++) {
aal_header_chars[header_chars_seen] = linebuff[n];
if (!g_ascii_isdigit(linebuff[n])) {
aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
}
}
if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
return false;
}
}
n++;
TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
and display dct2000 dissector... */
if (g_ascii_isdigit(linebuff[n])) {
while ((n+1 < line_length) && linebuff[n] != '/') {
n++;
}
}
while ((n+1 < line_length) && linebuff[n] == '/') {
n++;
}
if ((n+1 < line_length) && linebuff[n] == ' ') {
n++;
}
if (!(*is_comment)) {
if (linebuff[n] == 's') {
*direction = sent;
}
else
if (linebuff[n] == 'r') {
*direction = received;
}
else {
return false;
}
n++;
}
else {
*direction = sent;
}
for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
if (n >= line_length) {
return false;
}
for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
if (n >= line_length) {
return false;
}
*before_time_offset = n;
for (seconds_chars = 0;
(linebuff[n] != '.') &&
(seconds_chars <= MAX_SECONDS_CHARS) &&
(n < line_length);
n++, seconds_chars++) {
if (!g_ascii_isdigit(linebuff[n])) {
return false;
}
seconds_buff[seconds_chars] = linebuff[n];
}
if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
return false;
}
seconds_buff[seconds_chars] = '\0';
int multiplier = 1;
*seconds = 0;
for (int d=seconds_chars-1; d >= 0; d--) {
*seconds += ((seconds_buff[d]-'0')*multiplier);
multiplier *= 10;
}
if (linebuff[n] != '.') {
return false;
}
n++;
for (subsecond_decimals_chars = 0;
(linebuff[n] != ' ') &&
(subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
(n < line_length);
n++, subsecond_decimals_chars++) {
if (!g_ascii_isdigit(linebuff[n])) {
return false;
}
subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
}
if (subsecond_decimals_chars != MAX_SUBSECOND_DECIMALS || n >= line_length) {
return false;
}
subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
*useconds = ((subsecond_decimals_buff[0]-'0') * 100000) +
((subsecond_decimals_buff[1]-'0') * 10000) +
((subsecond_decimals_buff[2]-'0') * 1000) +
((subsecond_decimals_buff[3]-'0') * 100);
if (linebuff[n] != ' ') {
return false;
}
*after_time_offset = n++;
a sprint line (no '$') */
if (*is_comment) {
if (strncmp(linebuff+n, "l $", 3) != 0) {
*is_sprint = true;
(void) g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
}
}
if (!(*is_sprint)) {
for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
return false;
}
n++;
}
*data_offset = n;
*data_chars = line_length - n;
if (skip_first_byte) {
*data_offset += 2;
*data_chars -= 2;
}
return true;
}
static bool
process_parsed_line(wtap *wth, const dct2000_file_externals_t *file_externals,
wtap_rec *rec,
Buffer *buf, int64_t file_offset,
char *linebuff, long dollar_offset,
int seconds, int useconds, char *timestamp_string,
packet_direction_t direction, int encap,
char *context_name, uint8_t context_port,
char *protocol_name, char *variant_name,
char *outhdr_name, char *aal_header_chars,
bool is_comment, int data_chars,
int *err, char **err_info)
{
int n;
int stub_offset = 0;
size_t length;
uint8_t *frame_buffer;
rec->rec_type = REC_TYPE_PACKET;
rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
rec->presence_flags = WTAP_HAS_TS;
rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
rec->ts.secs = file_externals->start_secs + seconds;
if ((file_externals->start_usecs + useconds) >= 1000000) {
rec->ts.secs++;
}
rec->ts.nsecs =
((file_externals->start_usecs + useconds) % 1000000) *1000;
* Calculate the length of the stub info and the packet data.
* The packet data length is half bytestring length.
*/
rec->rec_header.packet_header.caplen = (unsigned)strlen(context_name)+1 +
1 +
(unsigned)strlen(timestamp_string)+1 +
(unsigned)strlen(variant_name)+1 +
(unsigned)strlen(outhdr_name)+1 +
(unsigned)strlen(protocol_name)+1 +
1 +
1 +
(is_comment ? data_chars : (data_chars/2));
if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
* Probably a corrupt capture file; return an error,
* so that our caller doesn't blow up trying to allocate
* space for an immensely-large packet.
*/
*err = WTAP_ERR_BAD_FILE;
*err_info = ws_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
return false;
}
rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
frame_buffer = ws_buffer_start_ptr(buf);
length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
stub_offset += (int)(length + 1);
frame_buffer[stub_offset] = context_port;
stub_offset++;
length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
stub_offset += (int)(length + 1);
length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
stub_offset += (int)(length + 1);
length = g_strlcpy((char*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
stub_offset += (int)(length + 1);
length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
stub_offset += (int)(length + 1);
frame_buffer[stub_offset++] = direction;
frame_buffer[stub_offset++] = (uint8_t)encap;
if (!is_comment) {
for (n=0; n < data_chars; n+=2) {
frame_buffer[stub_offset + n/2] =
hex_byte_from_chars(linebuff+dollar_offset+n);
}
}
else {
for (n=0; n < data_chars; n++) {
frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
}
}
rec->rec_header.packet_header.pseudo_header.dct2000.seek_off = file_offset;
rec->rec_header.packet_header.pseudo_header.dct2000.wth = wth;
switch (encap) {
case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
set_aal_info(&rec->rec_header.packet_header.pseudo_header, direction, aal_header_chars);
break;
case WTAP_ENCAP_ISDN:
set_isdn_info(&rec->rec_header.packet_header.pseudo_header, direction);
break;
case WTAP_ENCAP_PPP:
set_ppp_info(&rec->rec_header.packet_header.pseudo_header, direction);
break;
default:
break;
}
return true;
}
static void
set_aal_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction,
char *aal_header_chars)
{
Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
Channel Identifier (8 bits) | ...
*/
pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
TODO: Can we infer the correct value here?
Meanwhile, just use the direction to make them distinguishable...
*/
pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
hex_byte_from_chars(aal_header_chars+1);
pseudo_header->dct2000.inner_pseudo_header.atm.vci =
((hex_from_char(aal_header_chars[3]) << 12) |
(hex_from_char(aal_header_chars[4]) << 8) |
(hex_from_char(aal_header_chars[5]) << 4) |
hex_from_char(aal_header_chars[6]));
pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
case cid is derived from last char in ascii */
if (g_ascii_isalnum(aal_header_chars[11])) {
pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
hex_byte_from_chars(aal_header_chars+10);
}
else {
pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
(int)aal_header_chars[11] - '0';
}
}
static void
set_isdn_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction)
{
'User' or 'Network'. If we assume that we're simulating the network,
treat Received messages as being destined for the network.
*/
pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
everything else would be treated as a B-channel
*/
pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
}
static void
set_ppp_info(union wtap_pseudo_header *pseudo_header,
packet_direction_t direction)
{
pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
}
static uint8_t
hex_from_char(char c)
{
if ((c >= '0') && (c <= '9')) {
return c - '0';
}
if ((c >= 'a') && (c <= 'f')) {
return 0x0a + (c - 'a');
}
return 0xff;
}
static uint8_t s_tableValues[256][256];
static void prepare_hex_byte_from_chars_table(void)
{
const unsigned char hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
int i, j;
for (i=0; i < 16; i++) {
for (j=0; j < 16; j++) {
s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
}
}
}
static uint8_t hex_byte_from_chars(char *c)
{
return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
}
static char
char_from_hex(uint8_t hex)
{
static const char hex_lookup[16] =
{ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
if (hex > 15) {
return '?';
}
return hex_lookup[hex];
}
static int
packet_offset_equal(const void *v, const void *v2)
{
return (*(const int64_t*)v == *(const int64_t*)v2);
}
static unsigned
packet_offset_hash_func(const void *v)
{
return (unsigned)(*(const int64_t*)v);
}
static bool
get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs)
{
struct tm tm;
#define MAX_MONTH_LETTERS 9
char month[MAX_MONTH_LETTERS+1];
int day, year, hour, minute, second;
int scan_found;
if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
return false;
}
scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
month, &day, &year, &hour, &minute, &second, usecs);
if (scan_found != 7) {
return false;
}
if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
else {
return false;
}
tm.tm_year = year - 1900;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
tm.tm_isdst = -1;
*secs = mktime(&tm);
*usecs = *usecs * 100;
return true;
}
static gboolean
free_line_prefix_info(void *key, void *value,
void *user_data _U_)
{
line_prefix_info_t *info = (line_prefix_info_t*)value;
g_free(key);
g_free(info->before_time);
g_free(info);
return true;
}
static const struct supported_block_type dct2000_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 dct2000_info = {
"Catapult DCT2000 trace (.out format)", "dct2000", "out", NULL,
false, BLOCKS_SUPPORTED(dct2000_blocks_supported),
catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open, NULL
};
void register_dct2000(void)
{
dct2000_file_type_subtype = wtap_register_file_type_subtype(&dct2000_info);
* Register name for backwards compatibility with the
* wtap_filetypes table in Lua.
*/
wtap_register_backwards_compatibility_lua_name("CATAPULT_DCT2000",
dct2000_file_type_subtype);
}
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/