Bluetooth: OBEX: Fix reassemble at transport layer

Transport layer is RFCOMM. OBEX packet may be fragmented over RFCOMM.
If so then (Opcode + Final flag + ) Packet Length determines packet length,
but whole OBEX packet will be framented over parts/packet that first packet
has Packet Length and next packets are sequential parts of whole packet
(no header, etc.) Reassemble on this level is simple concatenation of all packet
up to Packet Length. Let threat packet shorted than 3 bytes as bad packet.

There is still no reasssemble on OBEX level (aka Final flag), so content send
over OBEX (vCards, files, XMLs) is still not reassembled.

Change-Id: I11882eb07480b34d274a4ca6e504e609dcf982c0
Reviewed-on: https://code.wireshark.org/review/8041
Reviewed-by: Michal Labedzki <michal.labedzki@tieto.com>
This commit is contained in:
Michal Labedzki 2015-03-22 18:36:35 +01:00
parent 21757aeadf
commit 71234389ea
1 changed files with 79 additions and 54 deletions

View File

@ -1397,11 +1397,13 @@ dissect_headers(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo,
static gint
dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
proto_item *main_item;
proto_tree *main_tree;
proto_item *sub_item;
fragment_head *frag_msg = NULL;
gboolean save_fragmented, complete;
tvbuff_t* new_tvb = NULL;
tvbuff_t* next_tvb = NULL;
guint32 no_of_segments = 0;
gint offset = 0;
gint profile = PROFILE_UNKNOWN;
gboolean is_obex_over_l2cap = FALSE;
@ -1424,6 +1426,11 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
if (data == NULL)
return 0;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "OBEX");
main_item = proto_tree_add_item(tree, proto_btobex, tvb, 0, tvb_captured_length(tvb), ENC_NA);
main_tree = proto_item_add_subtree(main_item, ett_btobex);
save_fragmented = pinfo->fragmented;
is_obex_over_l2cap = (proto_btl2cap == (gint) GPOINTER_TO_UINT(wmem_list_frame_data(
@ -1476,43 +1483,70 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
profile = obex_profile_data->profile;
}
sub_item = proto_tree_add_uint(main_tree, hf_profile, tvb, 0, 0, profile);
PROTO_ITEM_SET_GENERATED(sub_item);
complete = FALSE;
if (tvb_captured_length(tvb) == tvb_reported_length(tvb)) {
if (fragment_get(&btobex_reassembly_table, pinfo, pinfo->p2p_dir, NULL)) {
/* not the first fragment */
frag_msg = fragment_add_seq_next(&btobex_reassembly_table,
tvb, 0, pinfo, pinfo->p2p_dir, NULL,
tvb_captured_length(tvb), TRUE);
frag_msg = fragment_get_reassembled_id(&btobex_reassembly_table, pinfo, pinfo->p2p_dir);
if (frag_msg && pinfo->fd->num != frag_msg->reassembled_in) {
/* reassembled but not last */
new_tvb = process_reassembled_data(tvb, 0, pinfo,
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, main_tree);
} else if (frag_msg && pinfo->fd->num == frag_msg->reassembled_in) {
/* reassembled and last, so dissect reassembled packet here */
pinfo->fragmented = TRUE;
new_tvb = process_reassembled_data(tvb, 0, pinfo,
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, main_tree);
} else {
if (tvb_reported_length(tvb) < tvb_get_ntohs(tvb, offset+1)) {
/* first fragment in a sequence */
no_of_segments = tvb_get_ntohs(tvb, offset + 1) / tvb_reported_length(tvb);
if (tvb_get_ntohs(tvb, offset+1) > (no_of_segments * tvb_reported_length(tvb)))
no_of_segments++;
frag_msg = fragment_get(&btobex_reassembly_table, pinfo, pinfo->p2p_dir, NULL);
frag_msg = fragment_add_seq_next(&btobex_reassembly_table,
tvb, 0, pinfo, pinfo->p2p_dir, NULL,
tvb_reported_length(tvb), TRUE);
if (frag_msg) {
/* not the first fragment */
fragment_set_tot_len(&btobex_reassembly_table,
pinfo, pinfo->p2p_dir, NULL,
no_of_segments-1);
/* packet stream is guaranted to be sequence of fragments, one by one,
so find last fragment for its offset and length */
while (frag_msg->next) {
frag_msg = frag_msg->next;
}
frag_msg = fragment_add_check(&btobex_reassembly_table,
tvb, 0, pinfo, pinfo->p2p_dir, NULL,
frag_msg->offset + frag_msg->len, tvb_reported_length(tvb),
((frag_msg->offset + frag_msg->len + tvb_reported_length(tvb)) <
fragment_get_tot_len(&btobex_reassembly_table, pinfo, pinfo->p2p_dir, NULL)) ? TRUE : FALSE);
new_tvb = process_reassembled_data(tvb, 0, pinfo,
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, main_tree);
pinfo->fragmented = TRUE;
} else {
if (tvb_reported_length(tvb) < 3) {
/* Packet length is in the second and the third bye of packet, anything shorter than 3 is bad */
col_add_fstr(pinfo->cinfo, COL_INFO, "%s OBEX packet too short",
(pinfo->p2p_dir==P2P_DIR_SENT) ? "Sent" : "Rcvd");
call_dissector(data_handle, tvb, pinfo, main_tree);
return tvb_reported_length(tvb);
} else if (tvb_reported_length(tvb) >= 3 && tvb_reported_length(tvb) < tvb_get_ntohs(tvb, offset+1)) {
/* first fragment in a sequence */
frag_msg = fragment_add_check(&btobex_reassembly_table,
tvb, 0, pinfo, pinfo->p2p_dir, NULL,
0, tvb_reported_length(tvb), TRUE);
fragment_set_tot_len(&btobex_reassembly_table,
pinfo, pinfo->p2p_dir, NULL,
tvb_get_ntohs(tvb, offset + 1));
new_tvb = process_reassembled_data(tvb, 0, pinfo,
"Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, main_tree);
pinfo->fragmented = TRUE;
} else if (tvb_reported_length(tvb) == tvb_get_ntohs(tvb, offset+1)) {
/* non-fragmented */
complete = TRUE;
pinfo->fragmented = FALSE;
}
else if (tvb_reported_length(tvb) == tvb_get_ntohs(tvb, offset+1)) {
/* non-fragmented */
complete = TRUE;
pinfo->fragmented = FALSE;
}
}
}
@ -1525,20 +1559,10 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
}
if (complete) {
proto_item *ti;
proto_tree *st;
proto_item *sub_item;
guint8 code;
guint8 final_flag;
/* fully dissectable packet ready */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "OBEX");
ti = proto_tree_add_item(tree, proto_btobex, next_tvb, 0, tvb_captured_length(tvb), ENC_NA);
st = proto_item_add_subtree(ti, ett_btobex);
sub_item = proto_tree_add_uint(st, hf_profile, next_tvb, 0, 0, profile);
PROTO_ITEM_SET_GENERATED(sub_item);
/* op/response code */
code = tvb_get_guint8(next_tvb, offset) & BTOBEX_CODE_VALS_MASK;
@ -1560,7 +1584,7 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
val_to_str_ext_const(code, &code_vals_ext, "Unknown"));
if (code < BTOBEX_CODE_VALS_CONTINUE || code == BTOBEX_CODE_VALS_ABORT) {
proto_tree_add_item(st, hf_opcode, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_opcode, next_tvb, offset, 1, ENC_BIG_ENDIAN);
if (!pinfo->fd->flags.visited &&
(pinfo->p2p_dir == P2P_DIR_SENT ||
pinfo->p2p_dir == P2P_DIR_RECV)) {
@ -1614,14 +1638,14 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
wmem_tree_insert32_array(obex_last_opcode, key, obex_last_opcode_data);
}
} else {
proto_tree_add_item(st, hf_response_code, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_response_code, next_tvb, offset, 1, ENC_BIG_ENDIAN);
}
proto_tree_add_item(st, hf_final_flag, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_final_flag, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* length */
proto_tree_add_item(st, hf_length, next_tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_length, next_tvb, offset, 2, ENC_BIG_ENDIAN);
length = tvb_get_ntohs(tvb, offset) - 3;
offset += 2;
@ -1669,7 +1693,7 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
obex_last_opcode_data->chandle == chandle &&
obex_last_opcode_data->channel == channel) {
if (obex_last_opcode_data->request_in_frame > 0 && obex_last_opcode_data->request_in_frame != pinfo->fd->num) {
sub_item = proto_tree_add_uint(st, hf_request_in_frame, next_tvb, 0, 0, obex_last_opcode_data->request_in_frame);
sub_item = proto_tree_add_uint(main_tree, hf_request_in_frame, next_tvb, 0, 0, obex_last_opcode_data->request_in_frame);
PROTO_ITEM_SET_GENERATED(sub_item);
}
@ -1678,7 +1702,7 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
}
if (obex_last_opcode_data->response_in_frame > 0 && obex_last_opcode_data->response_in_frame != pinfo->fd->num) {
sub_item = proto_tree_add_uint(st, hf_response_in_frame, next_tvb, 0, 0, obex_last_opcode_data->response_in_frame);
sub_item = proto_tree_add_uint(main_tree, hf_response_in_frame, next_tvb, 0, 0, obex_last_opcode_data->response_in_frame);
PROTO_ITEM_SET_GENERATED(sub_item);
}
}
@ -1686,13 +1710,13 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
switch(code)
{
case BTOBEX_CODE_VALS_CONNECT:
proto_tree_add_item(st, hf_version, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_version, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_max_pkt_len, next_tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
break;
@ -1702,12 +1726,12 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
break;
case BTOBEX_CODE_VALS_SET_PATH:
proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(st, hf_set_path_flags_0, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(st, hf_set_path_flags_1, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_set_path_flags_0, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_set_path_flags_1, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(st, hf_constants, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_constants, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
break;
@ -1717,31 +1741,32 @@ dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
default:
if (length == 0 && tvb_reported_length_remaining(tvb, offset) > 0) {
proto_tree_add_expert(st, pinfo, &ei_unexpected_data, tvb, offset, tvb_reported_length_remaining(tvb, offset));
proto_tree_add_expert(main_tree, pinfo, &ei_unexpected_data, tvb, offset, tvb_reported_length_remaining(tvb, offset));
offset += tvb_reported_length_remaining(tvb, offset);
break;
} else if (length == 0) break;
if (obex_last_opcode_data && obex_last_opcode_data->code == BTOBEX_CODE_VALS_CONNECT) {
proto_tree_add_item(st, hf_version, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_version, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_flags, next_tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(main_tree, hf_max_pkt_len, next_tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
break;
}
dissect_headers(st, next_tvb, offset, pinfo, profile, is_obex_over_l2cap, data);
dissect_headers(main_tree, next_tvb, offset, pinfo, profile, is_obex_over_l2cap, data);
} else {
/* packet fragment */
col_add_fstr(pinfo->cinfo, COL_INFO, "%s Obex fragment",
col_add_fstr(pinfo->cinfo, COL_INFO, "%s OBEX fragment",
(pinfo->p2p_dir==P2P_DIR_SENT) ? "Sent" : "Rcvd");
call_dissector(data_handle, next_tvb, pinfo, main_tree);
call_dissector(data_handle, next_tvb, pinfo, tree);
offset = tvb_reported_length(tvb);
}
pinfo->fragmented = save_fragmented;