wireshark/epan/dissectors/packet-dbus.c
Guy Harris 8ed7a73e22 Fix a bunch of warnings.
Cast away some implicit 64-bit-to-32-bit conversion errors due to use of
sizeof.

Cast away some implicit 64-bit-to-32-bit conversion errors due to use of
strtol() and strtoul().

Change some data types to avoid those implicit conversion warnings.

When assigning a constant to a float, make sure the constant isn't a
double, by appending "f" to the constant.

Constify a bunch of variables, parameters, and return values to
eliminate warnings due to strings being given const qualifiers.  Cast
away those warnings in some cases where an API we don't control forces
us to do so.

Enable a bunch of additional warnings by default.  Note why at least
some of the other warnings aren't enabled.

randpkt.c and text2pcap.c are used to build programs, so they don't need
to be in EXTRA_DIST.

If the user specifies --enable-warnings-as-errors, add -Werror *even if
the user specified --enable-extra-gcc-flags; assume they know what
they're doing and are willing to have the compile fail due to the extra
GCC warnings being treated as errors.

svn path=/trunk/; revision=46748
2012-12-26 05:57:06 +00:00

664 lines
17 KiB
C

/* packet-dbus.c
* Routines for D-Bus dissection
* Copyright 2012, Jakub Zawadzki <darkjames-ws@darkjames.pl>
*
* $Id$
*
* Protocol specification available at http://dbus.freedesktop.org/doc/dbus-specification.html
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/strutil.h>
#include <epan/expert.h>
#include <epan/dissectors/packet-tcp.h>
static gboolean dbus_desegment = TRUE;
static int hf_dbus_hdr = -1;
static int hf_dbus_hdr_endianess = -1;
static int hf_dbus_hdr_type = -1;
static int hf_dbus_hdr_flags = -1;
static int hf_dbus_hdr_version = -1;
static int hf_dbus_hdr_body_length = -1;
static int hf_dbus_hdr_serial = -1;
static int hf_dbus_hdr_fields_length = -1;
static int hf_dbus_hdr_field = -1;
static int hf_dbus_hdr_field_code = -1;
static int hf_dbus_value_bool = -1;
static int hf_dbus_value_int = -1;
static int hf_dbus_value_uint = -1;
static int hf_dbus_value_str = -1;
static int hf_dbus_value_double = -1;
static int hf_dbus_body = -1;
static int hf_dbus_type_signature = -1;
static int ett_dbus = -1;
static int ett_dbus_hdr = -1;
static int ett_dbus_body = -1;
static int ett_dbus_field = -1;
static int proto_dbus = -1;
#define DBUS_MESSAGE_TYPE_INVALID 0
#define DBUS_MESSAGE_TYPE_METHOD_CALL 1
#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
#define DBUS_MESSAGE_TYPE_ERROR 3
#define DBUS_MESSAGE_TYPE_SIGNAL 4
static const value_string message_type_vals[] = {
{ DBUS_MESSAGE_TYPE_INVALID, "Invalid" },
{ DBUS_MESSAGE_TYPE_METHOD_CALL, "Method call" },
{ DBUS_MESSAGE_TYPE_METHOD_RETURN, "Method reply" },
{ DBUS_MESSAGE_TYPE_ERROR, "Error reply" },
{ DBUS_MESSAGE_TYPE_SIGNAL, "Signal emission" },
{ 0, NULL }
};
#define DBUS_HEADER_FIELD_INVALID 0
#define DBUS_HEADER_FIELD_PATH 1
#define DBUS_HEADER_FIELD_INTERFACE 2
#define DBUS_HEADER_FIELD_MEMBER 3
#define DBUS_HEADER_FIELD_ERROR_NAME 4
#define DBUS_HEADER_FIELD_REPLY_SERIAL 5
#define DBUS_HEADER_FIELD_DESTINATION 6
#define DBUS_HEADER_FIELD_SENDER 7
#define DBUS_HEADER_FIELD_SIGNATURE 8
#define DBUS_HEADER_FIELD_UNIX_FDS 9
static const value_string field_code_vals[] = {
{ DBUS_HEADER_FIELD_INVALID, "INVALID" },
{ DBUS_HEADER_FIELD_PATH, "PATH" },
{ DBUS_HEADER_FIELD_INTERFACE, "INTERFACE" },
{ DBUS_HEADER_FIELD_MEMBER, "MEMBER" },
{ DBUS_HEADER_FIELD_ERROR_NAME, "ERROR_NAME" },
{ DBUS_HEADER_FIELD_REPLY_SERIAL, "REPLY_SERIAL" },
{ DBUS_HEADER_FIELD_DESTINATION, "DESTINATION" },
{ DBUS_HEADER_FIELD_SENDER, "SENDER" },
{ DBUS_HEADER_FIELD_SIGNATURE, "SIGNATURE" },
{ DBUS_HEADER_FIELD_UNIX_FDS, "UNIX_FDS" },
{ 0, NULL }
};
typedef struct {
packet_info *pinfo;
guint16 (*get16)(tvbuff_t *, const gint);
guint32 (*get32)(tvbuff_t *, const gint);
gdouble (*getdouble)(tvbuff_t *, const gint);
int enc;
guint32 body_len;
guint32 fields_len;
const char *body_sig;
} dbus_info_t;
typedef union {
char *str;
guint uint;
} dbus_val_t;
static gboolean
dbus_validate_object_path(const char *path)
{
/* XXX check */
if (*path != '/')
return FALSE;
do {
path++;
if (*path == '/')
return FALSE;
while ((*path >= 'A' && *path <= 'Z') || (*path >= 'a' && *path <= 'z') || (*path >= '0' && *path <= '9') || *path == '_')
path++;
if (*path == '\0')
return TRUE;
} while (*path == '/');
return FALSE;
}
static gboolean
dbus_validate_signature(const char *sig _U_)
{
/* XXX implement */
return TRUE;
}
static int
dissect_dbus_sig(tvbuff_t *tvb, dbus_info_t *dinfo, proto_tree *tree, int offset, char sig, dbus_val_t *ret)
{
const int org_offset = offset;
proto_item *ti;
switch (sig) {
case 'y': /* BYTE */
{
guint8 val;
val = tvb_get_guint8(tvb, offset);
offset += 1;
proto_tree_add_uint_format(tree, hf_dbus_value_uint, tvb, org_offset, offset - org_offset, val, "BYTE: %u", val);
ret->uint = val;
return offset;
}
case 'b': /* BOOLEAN */
{
guint32 val;
val = dinfo->get32(tvb, offset);
offset += 4;
ti = proto_tree_add_boolean_format(tree, hf_dbus_value_bool, tvb, org_offset, offset - org_offset, val, "BOOLEAN: %s", val ? "True" : "False");
if (val != 0 && val != 1) {
expert_add_info_format(dinfo->pinfo, ti, PI_PROTOCOL, PI_WARN, "Invalid boolean value (must be 0 or 1 is: %u)", val);
return -1;
}
ret->uint = val;
return offset;
}
case 'n': /* INT16 */
{
gint16 val;
val = (gint16 )dinfo->get16(tvb, offset);
offset += 2;
proto_tree_add_uint_format(tree, hf_dbus_value_int, tvb, org_offset, offset - org_offset, val, "INT16: %d", val);
/* XXX ret */
return offset;
}
case 'q': /* UINT16 */
{
guint16 val;
val = dinfo->get16(tvb, offset);
offset += 2;
proto_tree_add_uint_format(tree, hf_dbus_value_uint, tvb, org_offset, offset - org_offset, val, "UINT16: %u", val);
ret->uint = val;
return offset;
}
case 'i': /* INT32 */
{
gint32 val;
val = (gint32) dinfo->get32(tvb, offset);
offset += 4;
proto_tree_add_int_format(tree, hf_dbus_value_int, tvb, org_offset, offset - org_offset, val, "INT32: %d", val);
/* XXX ret */
return offset;
}
case 'u': /* UINT32 */
{
guint32 val;
val = dinfo->get32(tvb, offset);
offset += 4;
proto_tree_add_uint_format(tree, hf_dbus_value_uint, tvb, org_offset, offset - org_offset, val, "UINT32: %u", val);
ret->uint = val;
return offset;
}
case 'x': /* INT64 */
case 't': /* UINT64 */
return -1;
case 'd': /* DOUBLE */
{
gdouble val;
val = dinfo->getdouble(tvb, offset);
offset += 8;
proto_tree_add_double_format(tree, hf_dbus_value_double, tvb, org_offset, offset - org_offset, val, "DOUBLE: %." STRINGIFY(DBL_DIG) "g", val);
/* XXX ret */
return offset;
}
case 's': /* STRING */
case 'o': /* OBJECT_PATH */
{
guint32 len;
char *val;
len = dinfo->get32(tvb, offset);
offset += 4;
val = tvb_get_ephemeral_string(tvb, offset, len);
offset += (len + 1 /* NUL-byte */ + 3) & ~3;
if (sig == 's') {
ti = proto_tree_add_string_format(tree, hf_dbus_value_str, tvb, org_offset, offset - org_offset, val, "STRING: %s", val);
if (!g_utf8_validate(val, -1, NULL)) {
expert_add_info_format(dinfo->pinfo, ti, PI_PROTOCOL, PI_WARN, "Invalid string (not UTF-8)");
return -1;
}
} else {
ti = proto_tree_add_string_format(tree, hf_dbus_value_str, tvb, org_offset, offset - org_offset, val, "OBJECT_PATH: %s", val);
if (!dbus_validate_object_path(val)) {
expert_add_info_format(dinfo->pinfo, ti, PI_PROTOCOL, PI_WARN, "Invalid object_path");
return -1;
}
}
ret->str = val;
return offset;
}
case 'g': /* SIGNATURE */
{
guint8 len;
char *val;
len = tvb_get_guint8(tvb, offset);
offset += 1;
val = tvb_get_ephemeral_string(tvb, offset, len);
offset += (len + 1);
ti = proto_tree_add_string_format(tree, hf_dbus_value_str, tvb, org_offset, offset - org_offset, val, "SIGNATURE: %s", val);
if (!dbus_validate_signature(val)) {
expert_add_info_format(dinfo->pinfo, ti, PI_PROTOCOL, PI_WARN, "Invalid signature");
return -1;
}
ret->str = val;
return offset;
}
/* ... */
}
return -1;
}
static int
dissect_dbus_field_signature(tvbuff_t *tvb, dbus_info_t *dinfo, proto_tree *tree, int offset, int field_code)
{
const int org_offset = offset;
proto_item *ti;
guint sig_len;
char *sig;
sig_len = tvb_get_guint8(tvb, offset);
offset += 1;
/* sig_len = tvb_strsize(tvb, offset); */
sig = tvb_get_ephemeral_string(tvb, offset, sig_len);
offset += (sig_len + 1);
ti = proto_tree_add_string(tree, hf_dbus_type_signature, tvb, org_offset, offset - org_offset, sig);
if (!dbus_validate_signature(sig)) {
expert_add_info_format(dinfo->pinfo, ti, PI_PROTOCOL, PI_WARN, "Invalid signature");
return -1;
}
switch (field_code) {
case DBUS_HEADER_FIELD_REPLY_SERIAL:
if (!strcmp(sig, "u")) { /* UINT32 */
dbus_val_t serial_val;
offset = dissect_dbus_sig(tvb, dinfo, tree, offset, 'u', &serial_val);
if (offset != -1)
{ /* XXX link with sending frame (serial_val.uint) */ }
return offset;
}
break;
case DBUS_HEADER_FIELD_DESTINATION:
case DBUS_HEADER_FIELD_SENDER:
if (!strcmp(sig, "s")) { /* STRING */
dbus_val_t addr_val;
offset = dissect_dbus_sig(tvb, dinfo, tree, offset, 's', &addr_val);
if (offset != -1)
SET_ADDRESS((field_code == DBUS_HEADER_FIELD_DESTINATION) ? &dinfo->pinfo->dst : &dinfo->pinfo->src,
AT_STRINGZ, (int)strlen(addr_val.str)+1, addr_val.str);
return offset;
}
break;
case DBUS_HEADER_FIELD_SIGNATURE:
if (!strcmp(sig, "g")) { /* SIGNATURE */
dbus_val_t sig_val;
offset = dissect_dbus_sig(tvb, dinfo, tree, offset, 'g', &sig_val);
if (offset != -1)
dinfo->body_sig = sig_val.str;
return offset;
}
break;
}
while (*sig) {
dbus_val_t val;
offset = dissect_dbus_sig(tvb, dinfo, tree, offset, *sig, &val);
if (offset == -1)
return -1;
sig++;
}
return offset;
}
static int
dissect_dbus_hdr_fields(tvbuff_t *tvb, dbus_info_t *dinfo, proto_tree *tree, int offset)
{
int end_offset;
end_offset = offset + dinfo->fields_len;
while (offset < end_offset) {
proto_tree *field_tree;
proto_item *ti;
guint8 field_code;
ti = proto_tree_add_item(tree, hf_dbus_hdr_field, tvb, offset, 0, ENC_NA);
field_tree = proto_item_add_subtree(ti, ett_dbus_field);
field_code = tvb_get_guint8(tvb, offset);
proto_tree_add_item(field_tree, hf_dbus_hdr_field_code, tvb, offset, 1, dinfo->enc);
proto_item_append_text(ti, ": %s", val_to_str(field_code, field_code_vals, "Unknown: %d"));
offset += 1;
offset = dissect_dbus_field_signature(tvb, dinfo, field_tree, offset, field_code);
if (offset == -1)
break;
offset = (offset + 7) & ~7; /* XXX ? */
proto_item_set_end(ti, tvb, offset);
}
/* XXX, verify if all required fields are preset */
if (offset >= end_offset) {
/* XXX expert */
}
return end_offset;
}
static int
dissect_dbus_hdr(tvbuff_t *tvb, dbus_info_t *dinfo, proto_tree *tree, int offset)
{
proto_tree *hdr_tree;
proto_item *ti;
guint8 type;
ti = proto_tree_add_item(tree, hf_dbus_hdr, tvb, offset, 0, ENC_NA);
hdr_tree = proto_item_add_subtree(ti, ett_dbus_hdr);
proto_tree_add_item(hdr_tree, hf_dbus_hdr_endianess, tvb, offset, 1, ENC_ASCII | ENC_NA);
offset += 1;
type = tvb_get_guint8(tvb, offset);
col_add_str(dinfo->pinfo->cinfo, COL_INFO, val_to_str_const(type, message_type_vals, ""));
proto_tree_add_item(hdr_tree, hf_dbus_hdr_type, tvb, offset, 1, dinfo->enc);
offset += 1;
proto_tree_add_item(hdr_tree, hf_dbus_hdr_flags, tvb, offset, 1, dinfo->enc);
offset += 1;
proto_tree_add_item(hdr_tree, hf_dbus_hdr_version, tvb, offset, 1, dinfo->enc);
offset += 1;
dinfo->body_len = dinfo->get32(tvb, offset);
proto_tree_add_item(hdr_tree, hf_dbus_hdr_body_length, tvb, offset, 4, dinfo->enc);
offset += 4;
proto_tree_add_item(hdr_tree, hf_dbus_hdr_serial, tvb, offset, 4, dinfo->enc);
offset += 4;
dinfo->fields_len = dinfo->get32(tvb, offset);
proto_tree_add_item(hdr_tree, hf_dbus_hdr_fields_length, tvb, offset, 4, dinfo->enc);
offset += 4;
return offset;
}
static int
dissect_dbus_body(tvbuff_t *tvb, dbus_info_t *dinfo, proto_tree *tree, int offset)
{
proto_tree *body_tree;
proto_item *ti;
if (dinfo->body_len && dinfo->body_sig[0]) {
const char *sig = dinfo->body_sig;
ti = proto_tree_add_item(tree, hf_dbus_body, tvb, offset, 0, ENC_NA);
body_tree = proto_item_add_subtree(ti, ett_dbus_body);
while (*sig) {
dbus_val_t val;
offset = dissect_dbus_sig(tvb, dinfo, body_tree, offset, *sig, &val);
if (offset == -1)
return -1;
sig++;
}
proto_item_set_end(ti, tvb, offset);
} else if (dinfo->body_len || dinfo->body_sig[0]) {
/* XXX smth wrong */
}
return offset;
}
static int
dissect_dbus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
proto_tree *dbus_tree = NULL;
dbus_info_t dinfo;
int offset;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "D-BUS");
col_clear(pinfo->cinfo, COL_INFO);
memset(&dinfo, 0, sizeof(dinfo));
dinfo.pinfo = pinfo;
switch (tvb_get_guint8(tvb, 0)) {
case 'l':
dinfo.enc = ENC_LITTLE_ENDIAN;
dinfo.get16 = tvb_get_letohs;
dinfo.get32 = tvb_get_letohl;
dinfo.getdouble = tvb_get_letohieee_double;
break;
case 'B':
dinfo.enc = ENC_BIG_ENDIAN;
dinfo.get16 = tvb_get_ntohs;
dinfo.get32 = tvb_get_ntohl;
dinfo.getdouble = tvb_get_ntohieee_double;
break;
default: /* same as BIG_ENDIAN */
/* XXX we should probably return 0; */
dinfo.enc = ENC_NA;
dinfo.get16 = tvb_get_ntohs;
dinfo.get32 = tvb_get_ntohl;
dinfo.getdouble = tvb_get_ntohieee_double;
}
if (tree) {
proto_item *ti = proto_tree_add_item(tree, proto_dbus, tvb, 0, -1, ENC_NA);
dbus_tree = proto_item_add_subtree(ti, ett_dbus);
}
offset = 0;
offset = dissect_dbus_hdr(tvb, &dinfo, dbus_tree, offset);
offset = dissect_dbus_hdr_fields(tvb, &dinfo, dbus_tree, offset);
/* header aligned to 8B */
offset = (offset + 7) & ~7;
if (!dinfo.body_sig)
dinfo.body_sig = "";
offset = dissect_dbus_body(tvb, &dinfo, dbus_tree, offset);
return offset;
}
#define DBUS_HEADER_LEN 16
static guint
get_dbus_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
{
guint32 (*get_guint32)(tvbuff_t *, const gint);
guint32 len_body, len_hdr;
switch (tvb_get_guint8(tvb, offset)) {
case 'l':
get_guint32 = tvb_get_letohl;
break;
case 'B':
default:
get_guint32 = tvb_get_ntohl;
break;
}
len_hdr = DBUS_HEADER_LEN + get_guint32(tvb, offset + 12);
len_hdr = (len_hdr + 7) & ~7;
len_body = get_guint32(tvb, offset + 4);
return len_hdr + len_body;
}
static void
dissect_dbus_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
dissect_dbus(tvb, pinfo, tree, NULL);
}
static int
dissect_dbus_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
tcp_dissect_pdus(tvb, pinfo, tree, dbus_desegment, DBUS_HEADER_LEN, get_dbus_message_len, dissect_dbus_pdu);
return tvb_length(tvb);
}
void
proto_register_dbus(void)
{
/* XXX, FT_NONE -> FT_BYTES? */
static hf_register_info hf[] = {
/* Header */
{ &hf_dbus_hdr,
{ "Header", "dbus.header", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_endianess,
{ "Endianess Flag", "dbus.endianess", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_type,
{ "Message Type", "dbus.type", FT_UINT8, BASE_DEC, VALS(message_type_vals), 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_flags,
{ "Message Flags", "dbus.flags", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_version,
{ "Protocol Version", "dbus.version", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_body_length,
{ "Message body Length", "dbus.length", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_serial,
{ "Message Serial (cookie)", "dbus.serial", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_fields_length,
{ "Header fields Length", "dbus.fields_length", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
/* Header field */
{ &hf_dbus_hdr_field,
{ "Header Field", "dbus.field", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_hdr_field_code,
{ "Field code", "dbus.field.code", FT_UINT8, BASE_DEC, VALS(field_code_vals), 0x00, NULL, HFILL }
},
{ &hf_dbus_type_signature,
{ "Type signature", "dbus.type_signature", FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_body,
{ "Body", "dbus.body", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
/* Values */
{ &hf_dbus_value_bool,
{ "Value", "dbus.value.bool", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_value_int,
{ "Value", "dbus.value.int", FT_INT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_value_uint,
{ "Value", "dbus.value.uint", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_value_str,
{ "Value", "dbus.value.str", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL }
},
{ &hf_dbus_value_double,
{ "Value", "dbus.value.double", FT_DOUBLE, BASE_NONE, NULL, 0x00, NULL, HFILL }
}
};
static gint *ett[] = {
&ett_dbus,
&ett_dbus_hdr,
&ett_dbus_body,
&ett_dbus_field
};
proto_dbus = proto_register_protocol("D-Bus", "D-BUS", "dbus");
proto_register_field_array(proto_dbus, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
void
proto_reg_handoff_dbus(void)
{
dissector_handle_t dbus_handle = new_create_dissector_handle(dissect_dbus, proto_dbus);
dissector_handle_t dbus_handle_tcp = new_create_dissector_handle(dissect_dbus_tcp, proto_dbus);
dissector_add_uint("wtap_encap", WTAP_ENCAP_DBUS, dbus_handle);
dissector_add_handle("tcp.port", dbus_handle_tcp);
}