wireshark/epan/wslua/wslua_dumper.c
Peter Wu 31aba351e2 wiretap: fix memleaks with wtap_rec::opt_comment
The memory ownership of wtap_rec::opt_comment was not clear. Users of
wtap were leaking memory (editcap.c). wtap readers were not sure about
freeing old comments (erf) or simply ignored memleaks (pcapng).

To fix this, ensure opt_comment is owned by wtap_rec and free it with
wtap_rec_cleanup. The erf issue was already addressed since
cf_get_packet_comment properly duplicates wth.opt_comment memory.

- wtap file formats (readers):
  - Should allocate memory for new comments.
  - Should free a comment from an earlier read before writing a new one.
- Users of wth:
  - Can only assume that opt_comment remains valid until the next read.
  - Can assume that wtap_dump does not modify the comment.
  - For random access (wtap_seek_read): should call wtap_rec_cleanup
    to free the comment.

The test_tshark_z_expert_comment and test_text2pcap_sip_pcapng tests now
pass when built with ASAN.

This change was created by carefully looking at all users opt
"opt_comment" and cf_get_packet_comment. Thanks to Vasil Velichkov for
an initial patch which helped validating this version.

Bug: 7515
Change-Id: If3152d1391e7e0d9860f04f3bc2ec41a1f6cc54b
Reviewed-on: https://code.wireshark.org/review/31713
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Vasil Velichkov <vvvelichkov@gmail.com>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
2019-01-25 04:53:10 +00:00

529 lines
16 KiB
C

/*
* wslua_dumper.c
*
* Wireshark's interface to the Lua Programming Language
*
* (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <epan/wmem/wmem.h>
/* WSLUA_MODULE Dumper Saving capture files
The classes/functions defined in this module are for using a `Dumper` object to
make Wireshark save a capture file to disk. `Dumper` represents Wireshark's built-in
file format writers (see the `wtap_filetypes` table in `init.lua`).
To have a Lua script create its own file format writer, see the chapter titled
"Custom file format reading/writing".
*/
#include "wslua.h"
#include <math.h>
WSLUA_CLASS_DEFINE(PseudoHeader,NOP);
/*
A pseudoheader to be used to save captured frames.
*/
enum lua_pseudoheader_type {
PHDR_NONE,
PHDR_ETH,
PHDR_X25,
PHDR_ISDN,
PHDR_ATM,
PHDR_ASCEND,
PHDR_P2P,
PHDR_WIFI,
PHDR_COSINE,
PHDR_IRDA,
PHDR_NETTL,
PHDR_MTP2,
PHDR_K12
};
struct lua_pseudo_header {
enum lua_pseudoheader_type type;
union wtap_pseudo_header* wph;
};
WSLUA_CONSTRUCTOR PseudoHeader_none(lua_State* L) {
/*
Creates a "no" pseudoheader.
*/
PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header));
ph->type = PHDR_NONE;
ph->wph = NULL;
pushPseudoHeader(L,ph);
WSLUA_RETURN(1);
/* A null pseudoheader */
}
WSLUA_CONSTRUCTOR PseudoHeader_eth(lua_State* L) {
/*
Creates an ethernet pseudoheader.
*/
#define WSLUA_OPTARG_PseudoHeader_eth_FCSLEN 1 /* The fcs length */
PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header));
ph->type = PHDR_ETH;
ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header));
ph->wph->eth.fcs_len = (gint)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_eth_FCSLEN,-1);
pushPseudoHeader(L,ph);
WSLUA_RETURN(1); /* The ethernet pseudoheader */
}
WSLUA_CONSTRUCTOR PseudoHeader_atm(lua_State* L) {
/*
Creates an ATM pseudoheader.
*/
#define WSLUA_OPTARG_PseudoHeader_atm_AAL 1 /* AAL number */
#define WSLUA_OPTARG_PseudoHeader_atm_VPI 2 /* VPI */
#define WSLUA_OPTARG_PseudoHeader_atm_VCI 3 /* VCI */
#define WSLUA_OPTARG_PseudoHeader_atm_CHANNEL 4 /* Channel */
#define WSLUA_OPTARG_PseudoHeader_atm_CELLS 5 /* Number of cells in the PDU */
#define WSLUA_OPTARG_PseudoHeader_atm_AAL5U2U 6 /* AAL5 User to User indicator */
#define WSLUA_OPTARG_PseudoHeader_atm_AAL5LEN 7 /* AAL5 Len */
PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header));
ph->type = PHDR_ATM;
ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header));
ph->wph->atm.aal = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL,5);
ph->wph->atm.vpi = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_VPI,1);
ph->wph->atm.vci = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_VCI,1);
ph->wph->atm.channel = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_CHANNEL,0);
ph->wph->atm.cells = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_CELLS,1);
ph->wph->atm.aal5t_u2u = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL5U2U,1);
ph->wph->atm.aal5t_len = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL5LEN,0);
pushPseudoHeader(L,ph);
WSLUA_RETURN(1);
/* The ATM pseudoheader */
}
WSLUA_CONSTRUCTOR PseudoHeader_mtp2(lua_State* L) {
/* Creates an MTP2 PseudoHeader. */
#define WSLUA_OPTARG_PseudoHeader_mtp2_SENT 1 /* True if the packet is sent, False if received. */
#define WSLUA_OPTARG_PseudoHeader_mtp2_ANNEXA 2 /* True if annex A is used. */
#define WSLUA_OPTARG_PseudoHeader_mtp2_LINKNUM 3 /* Link Number. */
PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header));
ph->type = PHDR_MTP2;
ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header));
ph->wph->mtp2.sent = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_SENT,0);
ph->wph->mtp2.annex_a_used = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_ANNEXA,0);
ph->wph->mtp2.link_number = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_LINKNUM,0);
pushPseudoHeader(L,ph);
WSLUA_RETURN(1); /* The MTP2 pseudoheader */
}
#if 0
static int PseudoHeader_x25(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_isdn(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_ascend(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_wifi(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_cosine(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_irda(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_nettl(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
static int PseudoHeader_k12(lua_State* L) { luaL_error(L,"not implemented"); return 0; }
#endif
/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */
static int PseudoHeader__gc(lua_State* L _U_) {
/* do NOT free PseudoHeader */
return 0;
}
WSLUA_METHODS PseudoHeader_methods[] = {
WSLUA_CLASS_FNREG(PseudoHeader,mtp2),
WSLUA_CLASS_FNREG(PseudoHeader,atm),
WSLUA_CLASS_FNREG(PseudoHeader,eth),
WSLUA_CLASS_FNREG(PseudoHeader,none),
{0,0}
};
WSLUA_META PseudoHeader_meta[] = {
{0,0}
};
int PseudoHeader_register(lua_State* L) {
WSLUA_REGISTER_CLASS(PseudoHeader)
return 0;
}
WSLUA_CLASS_DEFINE(Dumper,FAIL_ON_NULL("Dumper already closed"));
static GHashTable* dumper_encaps = NULL;
#define DUMPER_ENCAP(d) GPOINTER_TO_INT(g_hash_table_lookup(dumper_encaps,d))
static const char* cross_plat_fname(const char* fname) {
static char fname_clean[256];
char* f;
g_strlcpy(fname_clean,fname,255);
fname_clean[255] = '\0';
for(f = fname_clean; *f; f++) {
switch(*f) {
case '/': case '\\':
*f = *(G_DIR_SEPARATOR_S);
break;
default:
break;
}
}
return fname_clean;
}
WSLUA_CONSTRUCTOR Dumper_new(lua_State* L) {
/*
Creates a file to write packets.
`Dumper:new_for_current()` will probably be a better choice.
*/
#define WSLUA_ARG_Dumper_new_FILENAME 1 /* The name of the capture file to be created. */
#define WSLUA_OPTARG_Dumper_new_FILETYPE 2 /* The type of the file to be created - a number entry from the `wtap_filetypes` table in `init.lua`. */
#define WSLUA_OPTARG_Dumper_new_ENCAP 3 /* The encapsulation to be used in the file to be created - a number entry from the `wtap_encaps` table in `init.lua`. */
Dumper d;
const char* fname = luaL_checkstring(L,WSLUA_ARG_Dumper_new_FILENAME);
int filetype = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_FILETYPE,WTAP_FILE_TYPE_SUBTYPE_PCAP);
int encap = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_ENCAP,WTAP_ENCAP_ETHERNET);
int err = 0;
const char* filename = cross_plat_fname(fname);
wtap_dump_params params = WTAP_DUMP_PARAMS_INIT;
params.encap = encap;
d = wtap_dump_open(filename, filetype, WTAP_UNCOMPRESSED, &params, &err);
if (! d ) {
/* WSLUA_ERROR("Error while opening file for writing"); */
switch (err) {
case WTAP_ERR_UNWRITABLE_FILE_TYPE:
luaL_error(L,"Files of file type %s cannot be written",
wtap_file_type_subtype_string(filetype));
break;
case WTAP_ERR_UNWRITABLE_ENCAP:
luaL_error(L,"Files of file type %s don't support encapsulation %s",
wtap_file_type_subtype_string(filetype),
wtap_encap_name(encap));
break;
default:
luaL_error(L,"error while opening `%s': %s",
filename,
wtap_strerror(err));
break;
}
return 0;
}
g_hash_table_insert(dumper_encaps,d,GINT_TO_POINTER(encap));
pushDumper(L,d);
WSLUA_RETURN(1);
/* The newly created Dumper object */
}
WSLUA_METHOD Dumper_close(lua_State* L) {
/* Closes a dumper. */
Dumper* dp = (Dumper*)luaL_checkudata(L, 1, "Dumper");
int err;
if (! *dp) {
WSLUA_ERROR(Dumper_close,"Cannot operate on a closed dumper");
return 0;
}
g_hash_table_remove(dumper_encaps,*dp);
if (!wtap_dump_close(*dp, &err)) {
luaL_error(L,"error closing: %s",
wtap_strerror(err));
}
/* this way if we close a dumper any attempt to use it (for everything but GC) will yield an error */
*dp = NULL;
return 0;
}
WSLUA_METHOD Dumper_flush(lua_State* L) {
/*
Writes all unsaved data of a dumper to the disk.
*/
Dumper d = checkDumper(L,1);
if (!d) return 0;
wtap_dump_flush(d);
return 0;
}
WSLUA_METHOD Dumper_dump(lua_State* L) {
/*
Dumps an arbitrary packet.
Note: Dumper:dump_current() will fit best in most cases.
*/
#define WSLUA_ARG_Dumper_dump_TIMESTAMP 2 /* The absolute timestamp the packet will have. */
#define WSLUA_ARG_Dumper_dump_PSEUDOHEADER 3 /* The `PseudoHeader` to use. */
#define WSLUA_ARG_Dumper_dump_BYTEARRAY 4 /* the data to be saved */
Dumper d = checkDumper(L,1);
PseudoHeader ph;
ByteArray ba;
wtap_rec rec;
double ts;
int err;
gchar *err_info;
if (!d) return 0;
ts = luaL_checknumber(L,WSLUA_ARG_Dumper_dump_TIMESTAMP);
ph = checkPseudoHeader(L,WSLUA_ARG_Dumper_dump_PSEUDOHEADER);
if (!ph) {
WSLUA_ARG_ERROR(Dumper_dump,PSEUDOHEADER,"need a PseudoHeader");
return 0;
}
ba = checkByteArray(L,WSLUA_ARG_Dumper_dump_BYTEARRAY);
if (! ba) {
WSLUA_ARG_ERROR(Dumper_dump,BYTEARRAY,"must be a ByteArray");
return 0;
}
memset(&rec, 0, sizeof rec);
rec.rec_type = REC_TYPE_PACKET;
rec.presence_flags = WTAP_HAS_TS;
rec.ts.secs = (unsigned int)(floor(ts));
rec.ts.nsecs = (unsigned int)(floor((ts - (double)rec.ts.secs) * 1000000000));
rec.rec_header.packet_header.len = ba->len;
rec.rec_header.packet_header.caplen = ba->len;
rec.rec_header.packet_header.pkt_encap = DUMPER_ENCAP(d);
if (ph->wph) {
rec.rec_header.packet_header.pseudo_header = *ph->wph;
}
/* TODO: Can we get access to pinfo->pkt_comment here somehow? We
* should be copying it to pkthdr.opt_comment if we can. */
if (! wtap_dump(d, &rec, ba->data, &err, &err_info)) {
switch (err) {
case WTAP_ERR_UNWRITABLE_REC_DATA:
luaL_error(L,"error while dumping: %s (%s)",
wtap_strerror(err), err_info);
g_free(err_info);
break;
default:
luaL_error(L,"error while dumping: %s",
wtap_strerror(err));
break;
}
}
return 0;
}
WSLUA_METHOD Dumper_new_for_current(lua_State* L) {
/*
Creates a capture file using the same encapsulation as the one of the current packet.
*/
#define WSLUA_OPTARG_Dumper_new_for_current_FILETYPE 2 /* The file type. Defaults to pcap. */
Dumper d;
const char* fname = luaL_checkstring(L,1);
int filetype = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_for_current_FILETYPE,WTAP_FILE_TYPE_SUBTYPE_PCAP);
int encap;
int err = 0;
const char* filename = cross_plat_fname(fname);
wtap_dump_params params = WTAP_DUMP_PARAMS_INIT;
if (! lua_pinfo ) {
WSLUA_ERROR(Dumper_new_for_current,"Cannot be used outside a tap or a dissector");
return 0;
}
if (lua_pinfo->rec->rec_type != REC_TYPE_PACKET) {
return 0;
}
encap = lua_pinfo->rec->rec_header.packet_header.pkt_encap;
params.encap = encap;
d = wtap_dump_open(filename, filetype, WTAP_UNCOMPRESSED, &params, &err);
if (! d ) {
switch (err) {
case WTAP_ERR_UNWRITABLE_FILE_TYPE:
luaL_error(L,"Files of file type %s cannot be written",
wtap_file_type_subtype_string(filetype));
break;
case WTAP_ERR_UNWRITABLE_ENCAP:
luaL_error(L,"Files of file type %s don't support encapsulation %s",
wtap_file_type_subtype_string(filetype),
wtap_encap_name(encap));
break;
default:
luaL_error(L,"error while opening `%s': %s",
filename,
wtap_strerror(err));
break;
}
return 0;
}
pushDumper(L,d);
WSLUA_RETURN(1); /* The newly created Dumper Object */
}
WSLUA_METHOD Dumper_dump_current(lua_State* L) {
/*
Dumps the current packet as it is.
*/
Dumper d = checkDumper(L,1);
wtap_rec rec;
const guchar* data;
tvbuff_t* tvb;
struct data_source *data_src;
int err = 0;
gchar *err_info;
if (!d) return 0;
if (! lua_pinfo ) {
WSLUA_ERROR(Dumper_new_for_current,"Cannot be used outside a tap or a dissector");
return 0;
}
if (lua_pinfo->rec->rec_type != REC_TYPE_PACKET) {
return 0;
}
data_src = (struct data_source*) (lua_pinfo->data_src->data);
if (!data_src)
return 0;
tvb = get_data_source_tvb(data_src);
memset(&rec, 0, sizeof rec);
rec.rec_type = REC_TYPE_PACKET;
rec.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
rec.ts = lua_pinfo->abs_ts;
rec.rec_header.packet_header.len = tvb_reported_length(tvb);
rec.rec_header.packet_header.caplen = tvb_captured_length(tvb);
rec.rec_header.packet_header.pkt_encap = lua_pinfo->rec->rec_header.packet_header.pkt_encap;
rec.rec_header.packet_header.pseudo_header = *lua_pinfo->pseudo_header;
/*
* wtap_dump does not modify rec.opt_comment, so it should be possible to
* pass epan_get_user_comment() or lua_pinfo->rec->opt_comment directly.
* Temporarily duplicating the memory should not hurt though.
*/
if (lua_pinfo->fd->has_user_comment) {
rec.opt_comment = wmem_strdup(wmem_packet_scope(), epan_get_user_comment(lua_pinfo->epan, lua_pinfo->fd));
rec.has_comment_changed = TRUE;
} else if (lua_pinfo->fd->has_phdr_comment) {
rec.opt_comment = wmem_strdup(wmem_packet_scope(), lua_pinfo->rec->opt_comment);
}
data = (const guchar *)tvb_memdup(wmem_packet_scope(),tvb,0,rec.rec_header.packet_header.caplen);
if (! wtap_dump(d, &rec, data, &err, &err_info)) {
switch (err) {
case WTAP_ERR_UNWRITABLE_REC_DATA:
luaL_error(L,"error while dumping: %s (%s)",
wtap_strerror(err), err_info);
g_free(err_info);
break;
default:
luaL_error(L,"error while dumping: %s",
wtap_strerror(err));
break;
}
}
return 0;
}
/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */
static int Dumper__gc(lua_State* L) {
Dumper* dp = (Dumper*)luaL_checkudata(L, 1, "Dumper");
int err;
/* If we are Garbage Collected it means the Dumper is no longer usable. Close it */
if (! *dp)
return 0; /* already closed, nothing to do! */
g_hash_table_remove(dumper_encaps,*dp);
if (!wtap_dump_close(*dp, &err)) {
luaL_error(L,"error closing: %s",
wtap_strerror(err));
}
return 0;
}
WSLUA_METHODS Dumper_methods[] = {
WSLUA_CLASS_FNREG(Dumper,new),
WSLUA_CLASS_FNREG(Dumper,new_for_current),
WSLUA_CLASS_FNREG(Dumper,close),
WSLUA_CLASS_FNREG(Dumper,flush),
WSLUA_CLASS_FNREG(Dumper,dump),
WSLUA_CLASS_FNREG(Dumper,dump_current),
{ NULL, NULL }
};
WSLUA_META Dumper_meta[] = {
{ NULL, NULL }
};
int Dumper_register(lua_State* L) {
dumper_encaps = g_hash_table_new(g_direct_hash,g_direct_equal);
WSLUA_REGISTER_CLASS(Dumper);
return 0;
}
/*
* Editor modelines - http://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:
*/