static rtpproxy_info_t *
rtpproxy_add_tid(gboolean is_request, tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtpproxy_tree, rtpproxy_conv_info_t *rtpproxy_conv, gchar* cookie)
{
    rtpproxy_info_t *rtpproxy_info;
    proto_item *pi;

    if (!PINFO_FD_VISITED(pinfo)) {
        if (is_request) {
            rtpproxy_info = wmem_new(wmem_file_scope(), rtpproxy_info_t);
            rtpproxy_info->req_frame = PINFO_FD_NUM(pinfo);
            rtpproxy_info->resp_frame = 0;
            rtpproxy_info->req_time = pinfo->fd->abs_ts;
            rtpproxy_info->callid = NULL;
            wmem_tree_insert_string(rtpproxy_conv->trans, cookie, rtpproxy_info, 0);
        } else {
            rtpproxy_info = (rtpproxy_info_t *)wmem_tree_lookup_string(rtpproxy_conv->trans, cookie, 0);
            if (rtpproxy_info) {
                rtpproxy_info->resp_frame = PINFO_FD_NUM(pinfo);
            }
        }
    } else {
        rtpproxy_info = (rtpproxy_info_t *)wmem_tree_lookup_string(rtpproxy_conv->trans, cookie, 0);
        if (rtpproxy_info && (is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame)) {
            nstime_t ns;

            pi = proto_tree_add_uint(rtpproxy_tree, is_request ? hf_rtpproxy_response_in : hf_rtpproxy_request_in, tvb, 0, 0, is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame);
            PROTO_ITEM_SET_GENERATED(pi);

            /* If reply then calculate response time */
            if (!is_request) {
                nstime_delta(&ns, &pinfo->fd->abs_ts, &rtpproxy_info->req_time);
                pi = proto_tree_add_time(rtpproxy_tree, hf_rtpproxy_response_time, tvb, 0, 0, &ns);
                PROTO_ITEM_SET_GENERATED(pi);
                if (nstime_cmp(&rtpproxy_timeout_ns, &ns) < 0)
                    expert_add_info_format(pinfo, rtpproxy_tree, &ei_rtpproxy_timeout, "Response timeout %.3f seconds", nstime_to_sec(&ns));
            }
        }
    }
    /* Could be NULL so we should check it before dereferencing */
    return rtpproxy_info;
}
Beispiel #2
0
static int
usbip_dissect_urb(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree,
                  proto_tree *orig, int offset,
                  usbip_conv_info_t *usbip_info)
{
    proto_item *ti = NULL;
    usbip_transaction_t *usbip_trans;
    guint32 command;
    guint32 devid;
    guint32 seqnum;
    guint32 dir;
    guint32 ep;
    struct usbip_header header;

    proto_tree_add_item_ret_uint(tree, hf_usbip_command, tvb, offset, 4, ENC_BIG_ENDIAN, &command);
    offset += 4;
    proto_tree_add_item_ret_uint(tree, hf_usbip_seqnum, tvb, offset, 4, ENC_BIG_ENDIAN, &seqnum);
    offset += 4;

    dir = tvb_get_ntohl(tvb, offset + 4);
    ep = tvb_get_ntohl(tvb, offset + 8);
    devid = tvb_get_ntohl(tvb, offset);

    if (!PINFO_FD_VISITED(pinfo)) {
        if (command == OP_CMD_SUBMIT || command == OP_CMD_UNLINK) {
            usbip_trans = wmem_new(wmem_file_scope(), usbip_transaction_t);
            usbip_trans->devid = devid;
            usbip_trans->dir = dir;
            usbip_trans->ep = ep;
            usbip_trans->seqnum = seqnum;
            usbip_trans->cmd_frame = pinfo->num;
            usbip_trans->ret_frame = 0;
            usbip_trans->unlink_seqnum = 0;
            wmem_tree_insert32(usbip_info->pdus, seqnum, (void *) usbip_trans);
        } else {
            usbip_trans = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum);
            if (usbip_trans)
                usbip_trans->ret_frame = pinfo->num;
        }
    } else {
        usbip_trans = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum);
    }

    if (!usbip_trans) {
        usbip_trans = wmem_new(wmem_packet_scope(), usbip_transaction_t);
        usbip_trans->cmd_frame = 0;
        usbip_trans->ret_frame = 0;
        usbip_trans->devid = 0;
        usbip_trans->unlink_seqnum = 0;
        usbip_trans->seqnum = seqnum;
    }

    /* only the OP_CMD_SUBMIT has a valid devid - in all other case we have to restore it from the transaction */
    if (command == OP_RET_SUBMIT || command == OP_RET_UNLINK) {
        devid = usbip_trans->devid;
        ep = usbip_trans->ep;
        dir = usbip_trans->dir;
    }

    ti = proto_tree_add_uint(tree, hf_usbip_cmd_frame, NULL, 0, 0,
                             usbip_trans->cmd_frame);
    PROTO_ITEM_SET_GENERATED(ti);
    ti = proto_tree_add_uint(tree, hf_usbip_ret_frame, NULL, 0, 0,
                             usbip_trans->ret_frame);
    PROTO_ITEM_SET_GENERATED(ti);
    ti = proto_tree_add_uint(tree, hf_usbip_devid, NULL, 0, 0, devid);
    PROTO_ITEM_SET_GENERATED(ti);
    ti = proto_tree_add_uint(tree, hf_usbip_direction, NULL, 0, 0, dir);
    PROTO_ITEM_SET_GENERATED(ti);
    ti = proto_tree_add_uint(tree, hf_usbip_ep, NULL, 0, 0, ep);
    PROTO_ITEM_SET_GENERATED(ti);

    proto_tree_add_item(tree, hf_usbip_devid, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(tree, hf_usbip_direction, tvb, offset, 4,
                        ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(tree, hf_usbip_ep, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;

    header.ep = ep;
    header.dir = dir;
    header.devid = devid & 0x00ff;
    header.busid = devid >> 16;

    switch (command) {

    case OP_CMD_SUBMIT:
        offset = dissect_cmd_submit(pinfo, tree, tvb, offset);
        dissect_usb_common(tvb, pinfo, orig, USB_HEADER_USBIP, &header);
        break;

    case OP_CMD_UNLINK:
        offset = dissect_cmd_unlink(pinfo, tree, tvb, offset, usbip_info,
                                    usbip_trans);
        break;

    case OP_RET_SUBMIT: {
        guint32 status;

        status = tvb_get_ntohl(tvb, offset);
        offset = dissect_ret_submit(pinfo, tree, tvb, offset);
        if (status == 0)
            dissect_usb_common(tvb, pinfo, orig, USB_HEADER_USBIP, &header);
        break;
    }

    case OP_RET_UNLINK:
        offset = dissect_ret_unlink(pinfo, tree, tvb, offset, usbip_info,
                                    usbip_trans->unlink_seqnum);
        break;

    default:
        proto_tree_add_item(tree, hf_usbip_urb_data, tvb, offset, -1, ENC_NA);
        offset = tvb_reported_length_remaining(tvb, offset);
        expert_add_info_format(
            pinfo, ti, &ei_usbip,
            "Dissector for USBIP Command"
            " (%x) code not implemented, Contact"
            " Wireshark developers if you want this supported",
            command);
        proto_item_append_text(ti, ": Undecoded");
        break;
    }
    return offset;
}
static void dissect_usb_i1d3_response(
        tvbuff_t *tvb, packet_info *pinfo,
        usb_i1d3_conversation_t *conversation, proto_tree *tree) {
    // The response packet does not contain any information about the command
    // it is a response to, so we need to reconstruct this information using the
    // previous packet that we saw.
    //
    // Note: currently, for simplicity's sake, this assumes that there is only
    // one inflight request at any given time - in other words, that there is no
    // pipelining going on. It is not clear if the device would even be able to
    // service more than one request at the same time in the first place.
    usb_i1d3_transaction_t *transaction;
    if (!PINFO_FD_VISITED(pinfo)) {
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
                conversation->request_to_transaction,
                GUINT_TO_POINTER(conversation->previous_packet));
        if (transaction) {
            DISSECTOR_ASSERT(transaction->response == 0);
            transaction->response = pinfo->num;
            wmem_map_insert(
                    conversation->response_to_transaction,
                    GUINT_TO_POINTER(transaction->response),
                    (void *)transaction);
        }
    } else {
        // After the first pass, we can't use previous_packet anymore since
        // there is no guarantee the dissector is called in order, so we use
        // the reverse mapping that we populated above.
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
                conversation->response_to_transaction,
                GUINT_TO_POINTER(pinfo->num));
    }
    if (transaction) {
        DISSECTOR_ASSERT(transaction->response == pinfo->num);
        DISSECTOR_ASSERT(transaction->request != 0);
    }

    proto_item *request_item = proto_tree_add_uint(
            tree, hf_usb_i1d3_request_in, tvb, 0, 0,
            transaction ? transaction->request : 0);
    PROTO_ITEM_SET_GENERATED(request_item);
    if (!transaction) {
        expert_add_info(pinfo, request_item, &ei_usb_i1d3_unexpected_response);
    } else {
        proto_item *command_code_item = proto_tree_add_uint(
                tree, hf_usb_i1d3_command_code, tvb, 0, 0,
                transaction->command_code);
        PROTO_ITEM_SET_GENERATED(command_code_item);
    }

    const gchar *command_string = transaction ? try_val_to_str(
            transaction->command_code, usb_i1d3_command_code_strings) : NULL;
    if (!command_string) command_string = "unknown";

    guint32 response_code;
    proto_item *response_code_item = proto_tree_add_item_ret_uint(
            tree, hf_usb_i1d3_response_code, tvb, 0, 1, ENC_NA, &response_code);
    proto_item_append_text(
            response_code_item, " (%s)", (response_code == 0) ? "OK" : "error");
    if (response_code != 0) {
        col_add_fstr(
                pinfo->cinfo, COL_INFO, "Error code %u (%s)",
                response_code, command_string);
        expert_add_info(pinfo, response_code_item, &ei_usb_i1d3_error);
        return;
    }

    col_add_fstr(pinfo->cinfo, COL_INFO, "OK (%s)", command_string);

    if (!transaction) return;

    // As mentioned in ArgyllCMS spectro/i1d3.c, the second byte is usually the
    // first byte of the command code, except for GET_DIFF.
    if (transaction->command_code != USB_I1D3_GET_DIFF) {
        guint32 echoed_command_code;
        proto_item *echoed_command_code_item = proto_tree_add_item_ret_uint(
                tree, hf_usb_i1d3_echoed_command_code, tvb, 1, 1, ENC_NA,
                &echoed_command_code);
        guint8 expected_command_code = transaction->command_code >> 8;
        proto_item_append_text(
                echoed_command_code_item, " [expected 0x%02x]",
                expected_command_code);
        if (echoed_command_code != expected_command_code) {
            expert_add_info(
                    pinfo, echoed_command_code_item,
                    &ei_usb_i1d3_echoed_command_code_mismatch);
        }
    }
static void dissect_usb_i1d3_command(
        tvbuff_t *tvb, packet_info *pinfo,
        usb_i1d3_conversation_t *conversation, proto_tree *tree) {
    // Parsing the command code is a bit tricky: if the most significant
    // byte is non-zero, the command code is the most significant byte,
    // *and* the next byte is the first byte of the payload.
    guint32 command_code = tvb_get_ntohs(tvb, 0);
    guint32 command_code_msb = command_code & 0xff00;
    gint command_code_length = 2;
    if (command_code_msb) {
        command_code = command_code_msb;
        command_code_length = 1;
    }
    proto_item *command_code_item = proto_tree_add_uint(
            tree, hf_usb_i1d3_command_code, tvb, 0, command_code_length,
            command_code);

    usb_i1d3_transaction_t *transaction;
    if (!PINFO_FD_VISITED(pinfo)) {
        transaction = usb_i1d3_create_transaction(conversation, pinfo->num);
        transaction->command_code = command_code;
    } else {
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
                conversation->request_to_transaction,
                GUINT_TO_POINTER(pinfo->num));
    }
    DISSECTOR_ASSERT(transaction);

    if (transaction->response != 0) {
        proto_item *response_item = proto_tree_add_uint(
                tree, hf_usb_i1d3_response_in, tvb, 0, 0,
                transaction->response);
        PROTO_ITEM_SET_GENERATED(response_item);
    }

    const gchar *command_code_string = try_val_to_str(
            command_code, usb_i1d3_command_code_strings);
    if (command_code_string) {
        col_set_str(pinfo->cinfo, COL_INFO, command_code_string);
    } else {
        expert_add_info(pinfo, command_code_item,
                &ei_usb_i1d3_unknown_command);
        col_set_str(pinfo->cinfo, COL_INFO, "Unknown command");
    }

    switch (command_code) {
        case USB_I1D3_LOCKRESP: {
            // TODO: verify that the challenge response is correct
            proto_tree_add_item(
                    tree, hf_usb_i1d3_challenge_response, tvb, 24, 16, ENC_NA);
            break;
        }

        case USB_I1D3_READINTEE: {
            guint32 offset, length;
            proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_readintee_offset, tvb,
                    1, 1, ENC_NA, &offset);
            proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_readintee_length, tvb,
                    2, 1, ENC_NA, &length);
            col_add_fstr(pinfo->cinfo, COL_INFO, "%s (offset: %u, length: %u)",
                    command_code_string, offset, length);
            if (!PINFO_FD_VISITED(pinfo)) {
                transaction->offset = offset;
                transaction->length = length;
            }
            break;
        }

        case USB_I1D3_READEXTEE: {
            guint32 offset, length;
            proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_readextee_offset, tvb,
                    1, 2, ENC_BIG_ENDIAN, &offset);
            proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_readextee_length, tvb,
                    3, 1, ENC_NA, &length);
            col_add_fstr(pinfo->cinfo, COL_INFO, "%s (offset: %u, length: %u)",
                    command_code_string, offset, length);
            if (!PINFO_FD_VISITED(pinfo)) {
                transaction->offset = offset;
                transaction->length = length;
            }
            break;
        }

        case USB_I1D3_MEASURE1: {
            guint32 integration_time;
            proto_item *integration_time_item = proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_requested_integration_time, tvb, 1, 4,
                    ENC_LITTLE_ENDIAN, &integration_time);
            double integration_time_seconds =
                integration_time / USB_I1D3_CLOCK_FREQUENCY;
            proto_item_append_text(
                    integration_time_item,
                    " [%.6f seconds]", integration_time_seconds);
            col_add_fstr(pinfo->cinfo, COL_INFO,
                    "Measure for %.6fs", integration_time_seconds);
            break;
        }
        case USB_I1D3_MEASURE2: {
            proto_item *edge_count_item = proto_tree_add_item(
                    tree, hf_usb_i1d3_requested_edge_count, tvb, 1, 6, ENC_NA);
            proto_tree *edge_count_tree = proto_item_add_subtree(
                    edge_count_item, ett_usb_i1d3_requested_edge_count);
            guint32 edge_count_red, edge_count_green, edge_count_blue;
            proto_tree_add_item_ret_uint(
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_red, tvb,
                    1, 2, ENC_LITTLE_ENDIAN, &edge_count_red);
            proto_tree_add_item_ret_uint(
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_green, tvb,
                    3, 2, ENC_LITTLE_ENDIAN, &edge_count_green);
            proto_tree_add_item_ret_uint(
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_blue, tvb,
                    5, 2, ENC_LITTLE_ENDIAN, &edge_count_blue);
            proto_item_append_text(
                    edge_count_item, ": R%u G%u B%u",
                    edge_count_red, edge_count_green, edge_count_blue);
            col_add_fstr(pinfo->cinfo, COL_INFO, "Measure R%u G%u B%u edges",
                    edge_count_red, edge_count_green, edge_count_blue);
            break;
        }
        case USB_I1D3_SETLED: {
            guint32 led_mode, led_offtime, led_ontime, pulse_count;
            proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_led_mode, tvb, 1, 1, ENC_NA, &led_mode);
            proto_item *led_offtime_item = proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_led_offtime, tvb, 2, 1, ENC_NA,
                    &led_offtime);
            double led_offtime_seconds =
                led_offtime / USB_I1D3_LED_OFFTIME_FACTOR;
            proto_item_append_text(
                    led_offtime_item, " [%.6f seconds]", led_offtime_seconds);
            proto_item *led_ontime_item = proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_led_ontime, tvb, 3, 1, ENC_NA,
                    &led_ontime);
            double led_ontime_seconds =
                led_ontime / ((led_mode == USB_I1D3_LED_BLINK) ?
                        USB_I1D3_LED_ONTIME_FACTOR :
                        USB_I1D3_LED_ONTIME_FADE_FACTOR);
            proto_item_append_text(
                    led_ontime_item, " [%.6f seconds]", led_ontime_seconds);
            proto_item *pulse_count_item = proto_tree_add_item_ret_uint(
                    tree, hf_usb_i1d3_led_pulse_count, tvb, 4, 1, ENC_NA,
                    &pulse_count);
            if (pulse_count == 0x80) {
                proto_item_append_text(pulse_count_item, " [infinity]");
                col_add_fstr(pinfo->cinfo, COL_INFO,
                        "Pulse LED off (%.6fs) and on (%.6fs%s) "
                        "indefinitely", led_offtime_seconds, led_ontime_seconds,
                        (led_mode == USB_I1D3_LED_BLINK_FADE_ON) ?
                        " fading" : "");
            } else {
                col_add_fstr(pinfo->cinfo, COL_INFO,
                        "Pulse LED off (%.6fs) and on (%.6fs%s) "
                        "%u times", led_offtime_seconds, led_ontime_seconds,
                        (led_mode == USB_I1D3_LED_BLINK_FADE_ON) ?
                        " fading" : "", pulse_count);
            }
        }
    }
}
/** Dissector for SoupBinTCP messages */
static void
dissect_soupbintcp_common(
    tvbuff_t    *tvb,
    packet_info *pinfo,
    proto_tree  *tree)
{
    struct conv_data *conv_data;
    struct pdu_data  *pdu_data;
    const char       *pkt_name;
    const char       *tmp_buf;
    proto_item       *ti;
    proto_tree       *soupbintcp_tree = NULL;
    conversation_t   *conv            = NULL;
    guint16           expected_len;
    guint8            pkt_type;
    gint              offset          = 0;
    guint             this_seq        = 0, next_seq;
    heur_dtbl_entry_t *hdtbl_entry;

    /* Get the 16-bit big-endian SOUP packet length */
    expected_len = tvb_get_ntohs(tvb, 0);

    /* Get the 1-byte SOUP message type */
    pkt_type = tvb_get_guint8(tvb, 2);

    /* Since we use the packet name a few times, get and save that value */
    pkt_name = val_to_str(pkt_type, pkt_type_val, "Unknown (%u)");

    /* Set the protocol name in the summary display */
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "SoupBinTCP");

    /* Set the packet name in the info column */
    col_add_str(pinfo->cinfo, COL_INFO, pkt_name);

    /* Sequence number tracking
     *
     * SOUP does not number packets from client to server (the server
     * acknowledges all important messages, so the client should use
     * the acks to figure out if the server received the message, and
     * otherwise resend it).
     *
     * Packets from server to client are numbered, but it's implicit.
     * The Login Accept packet contains the next sequence number that
     * the server will send, and the client needs to count the
     * Sequenced Data packets that it receives to know what their
     * sequence numbers are.
     *
     * So, we grab the next sequence number from the Login Acceptance
     * packet, and save it in a conversation_t we associate with the
     * TCP session.  Then, for each Sequenced Data packet we receive,
     * the first time it's processed (when PINFO_FD_VISITED() is
     * false), we write it into the PDU's frame's private data pointer
     * and increment the saved sequence number (in the conversation_t).
     *
     * If the visited flag is true, then we've dissected this packet
     * already, and so we can fetch the sequence number from the
     * frame's private data area.
     *
     * In either case, if there's any problem, we report zero as the
     * sequence number, and try to continue dissecting. */

    /* If first dissection of Login Accept, save sequence number */
    if (pkt_type == 'A' && !PINFO_FD_VISITED(pinfo)) {
        tmp_buf = tvb_get_string_enc(wmem_packet_scope(), tvb, 13, 20, ENC_ASCII);
        next_seq = atoi(tmp_buf);

        /* Create new conversation for this session */
        conv = conversation_new(PINFO_FD_NUM(pinfo),
                                &pinfo->src,
                                &pinfo->dst,
                                pinfo->ptype,
                                pinfo->srcport,
                                pinfo->destport,
                                0);

        /* Store starting sequence number for session's packets */
        conv_data = (struct conv_data *)wmem_alloc(wmem_file_scope(), sizeof(struct conv_data));
        conv_data->next_seq = next_seq;
        conversation_add_proto_data(conv, proto_soupbintcp, conv_data);
    }

    /* Handle sequence numbering for a Sequenced Data packet */
    if (pkt_type == 'S') {
        if (!PINFO_FD_VISITED(pinfo)) {
            /* Get next expected sequence number from conversation */
            conv = find_conversation(PINFO_FD_NUM(pinfo),
                                     &pinfo->src,
                                     &pinfo->dst,
                                     pinfo->ptype,
                                     pinfo->srcport,
                                     pinfo->destport,
                                     0);
            if (!conv) {
                this_seq = 0;
            } else {
                conv_data = (struct conv_data *)conversation_get_proto_data(conv,
                                                        proto_soupbintcp);
                if (conv_data) {
                    this_seq = conv_data->next_seq++;
                } else {
                    this_seq = 0;
                }

                pdu_data = (struct pdu_data *)wmem_alloc(
                    wmem_file_scope(),
                    sizeof(struct pdu_data));
                pdu_data->seq_num = this_seq;
                p_add_proto_data(wmem_file_scope(), pinfo, proto_soupbintcp, 0, pdu_data);
            }
        } else {
            pdu_data = (struct pdu_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_soupbintcp, 0);
            if (pdu_data) {
                this_seq = pdu_data->seq_num;
            } else {
                this_seq = 0;
            }
        }

        col_append_fstr(pinfo->cinfo, COL_INFO, ", SeqNum = %u", this_seq);
    }

    if (tree) {
        /* Create sub-tree for SoupBinTCP details */
        ti = proto_tree_add_item(tree,
                                 proto_soupbintcp,
                                 tvb, 0, -1, ENC_NA);

        soupbintcp_tree = proto_item_add_subtree(ti, ett_soupbintcp);

        /* Append the packet name to the sub-tree item */
        proto_item_append_text(ti, ", %s", pkt_name);

        /* Length */
        proto_tree_add_item(soupbintcp_tree,
                            hf_soupbintcp_packet_length,
                            tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;

        /* Type */
        proto_tree_add_item(soupbintcp_tree,
                            hf_soupbintcp_packet_type,
                            tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;

        switch (pkt_type) {
        case '+': /* Debug Message */
            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_text,
                                tvb, offset, expected_len - 1, ENC_ASCII|ENC_NA);
            break;

        case 'A': /* Login Accept */
            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_session,
                                tvb, offset, 10, ENC_ASCII|ENC_NA);
            offset += 10;

            tmp_buf = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 20, ENC_ASCII);
            proto_tree_add_string_format_value(soupbintcp_tree,
                                               hf_soupbintcp_next_seq_num,
                                               tvb, offset, 20,
                                               "X", "%d", atoi(tmp_buf));
            break;

        case 'J': /* Login Reject */
            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_reject_code,
                                tvb, offset, 1, ENC_BIG_ENDIAN);
            break;

        case 'U': /* Unsequenced Data */
            /* Display handled by sub-dissector */
            break;

        case 'S': /* Sequenced Data */
            proto_item_append_text(ti, ", SeqNum=%u", this_seq);
            proto_tree_add_string_format_value(soupbintcp_tree,
                                               hf_soupbintcp_seq_num,
                                               tvb, offset, 0,
                                               "X",
                                               "%u (Calculated)",
                                               this_seq);

            /* Display handled by sub-dissector */
            break;

        case 'L': /* Login Request */
            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_username,
                                tvb, offset, 6, ENC_ASCII|ENC_NA);
            offset += 6;

            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_password,
                                tvb, offset, 10, ENC_ASCII|ENC_NA);
            offset += 10;

            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_session,
                                tvb, offset, 10, ENC_ASCII|ENC_NA);
            offset += 10;

            tmp_buf = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 20, ENC_ASCII);
            proto_tree_add_string_format_value(soupbintcp_tree,
                                               hf_soupbintcp_req_seq_num,
                                               tvb, offset, 20,
                                               "X", "%d", atoi(tmp_buf));
            break;

        case 'H': /* Server Heartbeat */
            break;

        case 'O': /* Logout Request */
            break;

        case 'R': /* Client Heartbeat */
            break;

        case 'Z': /* End of Session */
            break;

        default:
            /* Unknown */
            proto_tree_add_item(tree,
                                hf_soupbintcp_message,
                                tvb, offset, -1, ENC_NA);
            break;
        }
    }

    /* Call sub-dissector for encapsulated data */
    if (pkt_type == 'S' || pkt_type == 'U') {
        tvbuff_t         *sub_tvb;

        /* Sub-dissector tvb starts at 3 (length (2) + pkt_type (1)) */
        sub_tvb = tvb_new_subset_remaining(tvb, 3);

#if 0   /* XXX: It's not valid for a soupbintcp subdissector to call       */
        /*  conversation_set_dissector() since the conversation is really  */
        /*  a TCP conversation.  (A 'soupbintcp' port type would need to   */
        /*  be defined to be able to use conversation_set_dissector()).    */
        /* In addition, no current soupbintcp subdissector calls           */
        /*  conversation_set_dissector().                                  */

        /* If this packet is part of a conversation, call dissector
         * for the conversation if available */
        if (try_conversation_dissector(&pinfo->dst, &pinfo->src, pinfo->ptype,
                                       pinfo->srcport, pinfo->destport,
                                       sub_tvb, pinfo, tree, NULL)) {
            return;
        }
#endif

        /* Otherwise, try heuristic dissectors */
        if (dissector_try_heuristic(heur_subdissector_list,
                                    sub_tvb,
                                    pinfo,
                                    tree,
                                    &hdtbl_entry,
                                    NULL)) {
            return;
        }

        /* Otherwise, give up, and just print the bytes in hex */
        if (tree) {
            proto_tree_add_item(soupbintcp_tree,
                                hf_soupbintcp_message,
                                sub_tvb, 0, -1,
                                ENC_NA);
        }
    }
}