/* packet-uaudp.c
* Routines for UA/UDP (Universal Alcatel UDP) packet dissection.
* Copyright 2011, Marek Tews <marek@trx.com.pl>
*
* $Id: packet-uaudp.c 39691 2011-10-31 17:26:02Z alagoutte $
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED"
* is a dissector file; if you just copied this from README.developer,
* don't bother with the "Copied from" - you don't even need to put
* in a "Copied from" if you copied an existing dissector, especially
* if the bulk of the code in the new dissector is your code)
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <epan/packet.h>
#include <epan/prefs.h>

#include "packet-ua.h"

/*
* Here are the global variables associated with
* the various user definable characteristics of the dissection
*/
static gboolean use_heuristic_dissector = TRUE;
static range_t *global_uaudp_port_range;

/* Define the UAUDP proto */
static int proto_uaudp = -1;
static dissector_handle_t uaudp_handle;
static dissector_table_t uaudp_dissector_table;

/* Define many header fields for UAUDP */
static int hf_uaudp_opcode = -1;
static int hf_uaudp_expected = -1;
static int hf_uaudp_send = -1;

/*
* Define the trees for UAUDP
*/
static int ett_uaudp_header = -1;

/**
* Opcode
*/
static const value_string szUaOpcode[] =
{
    { 0, "Connect" },
    { 1, "Connect ACK" },
    { 2, "Release" },
    { 3, "Release ACK" },
    { 4, "Keepalive" },
    { 5, "Keepalive ACK" },
    { 6, "NACK" },
    { 7, "Data" },
    { 0, NULL }
};

/*
* dissect_uaudp - The dissector for the UA/UDP protocol
*/
static int dissect_uaudp(tvbuff_t *pTvb, packet_info *pInfo, proto_tree *pTree)
{
    gint nLen;
    guint8 u8Opcode;
    proto_item *pUAUDP, *pHeaderSubTree;

    /* PROTOCOL column */
    col_set_str(pInfo->cinfo, COL_PROTOCOL, "UAUDP");

    nLen = tvb_reported_length(pTvb);
    u8Opcode = tvb_get_guint8(pTvb, 0);

    /* INFO column */
    col_set_str(pInfo->cinfo, COL_INFO, val_to_str_const(u8Opcode, szUaOpcode, "Unknown"));

    /* opcode "UA/UDP Protocol, ..." */
    pUAUDP = proto_tree_add_item(pTree, proto_uaudp, pTvb, 0, -1, ENC_NA);
    proto_item_append_text(pUAUDP, ", %s (%d)", val_to_str_const(u8Opcode, szUaOpcode, "Unknown"), u8Opcode);

    pHeaderSubTree = proto_item_add_subtree(pUAUDP, ett_uaudp_header);
    proto_tree_add_item(pHeaderSubTree, hf_uaudp_opcode, pTvb, 0, 1, ENC_BIG_ENDIAN);

    switch(u8Opcode)
    {
    case 6:
        {
            /* Sequence Number (expected) */
            proto_tree_add_item(pHeaderSubTree, hf_uaudp_expected, pTvb, 1, 2, ENC_BIG_ENDIAN);
            break;
        }
    case 7:
        {
            int iOffs = 1;

            /* Sequence Number (expected) */
            proto_tree_add_item(pHeaderSubTree, hf_uaudp_expected, pTvb, iOffs, 2, ENC_BIG_ENDIAN);
            iOffs += 2;

            /* Sequence Number (sent) */
            proto_tree_add_item(pHeaderSubTree, hf_uaudp_send, pTvb, iOffs, 2, ENC_BIG_ENDIAN);
            iOffs += 2;

            /* Create the tvbuffer for the next dissector */
            if(nLen > iOffs)
            {
                if(dissector_try_uint(uaudp_dissector_table, 7, tvb_new_subset_remaining(pTvb, iOffs), pInfo, pTree))
                    iOffs = nLen;
                return iOffs;
            }
            else
            {
                col_append_str(pInfo->cinfo, COL_INFO, " ACK");
            }
            break;
        }
    }
    return nLen;
}

/*
 * UAUDP-over-UDP
 */
static gboolean 
dissect_uaudp_heur(tvbuff_t *pTvb, packet_info *pInfo, proto_tree *pTree)
{
    guint8 u8Opcode;

    if(!use_heuristic_dissector)
        return FALSE;

    /* The opcode must be in range */
    u8Opcode = tvb_get_guint8(pTvb, 0);
    if(u8Opcode > 7)
        return FALSE;

    /* The minimum length of a UAUDP message */
    switch(u8Opcode)
    {
    case 4:
    case 5:
        {
            if(tvb_reported_length(pTvb) != 1)
                return FALSE;
            break;
        }
    case 6:
        {
            if(tvb_reported_length(pTvb) != 3)
                return FALSE;
            break;
        }
    case 7:
        {
            guint nLen = tvb_reported_length(pTvb);
            if(nLen < 5)
                return FALSE;

            if(nLen > 5 && !is_ua(tvb_new_subset_remaining(pTvb, 5)))
                return FALSE;

            break;
        }
    /*
     * There I met with other opcodes 
     * and do not know how much data is transmitted.
     */
    default: return FALSE;
    }

    dissect_uaudp(pTvb, pInfo, pTree);
    return TRUE;
}

/* The registration hand-off routine is called at startup */
static void range_delete_callback(guint32 port)
{
    dissector_delete_uint("udp.port", port, uaudp_handle);
}

static void range_add_callback (guint32 port)
{
    dissector_add_uint("udp.port", port, uaudp_handle);
}

void proto_reg_handoff_uaudp(void)
{
    static range_t *uaudp_port_range;
    static gboolean uaudp_initialized = FALSE;

    if (!uaudp_initialized)
    {
        /*
         * For UAUDP-over-UDP.
         */
        heur_dissector_add("udp", dissect_uaudp_heur, proto_uaudp);

        uaudp_handle = find_dissector("uaudp");
        uaudp_initialized = TRUE;
    }
    else
    {
        range_foreach(uaudp_port_range, range_delete_callback);
        g_free(uaudp_port_range);
    }

    uaudp_port_range = range_copy(global_uaudp_port_range);
    range_foreach(uaudp_port_range, range_add_callback);
}

/* Register all the bits needed by the filtering engine */
void proto_register_uaudp(void)
{
    static hf_register_info hf[] =
    {
        { &hf_uaudp_opcode,
            { "Opcode", "uaudp.opcode",
                FT_UINT8, BASE_DEC, VALS(szUaOpcode), 0x0,
                "UA/UDP Opcode", HFILL }
        },
        { &hf_uaudp_expected,
            { "Sequence Number (expected)", "uaudp.expected",
                FT_UINT16, BASE_DEC, NULL, 0x0,
                NULL, HFILL }
        },
        { &hf_uaudp_send,
            { "Sequence Number (sent)", "uaudp.sent",
                FT_UINT16, BASE_DEC, NULL, 0x0,
                NULL, HFILL }
        }
    };
    static gint *ett[] =
    {
        &ett_uaudp_header,
    };

    module_t* uaudp_module;

    proto_uaudp = proto_register_protocol("Universal Alcatel UDP Protocol", "UAUDP", "uaudp");

    proto_register_field_array(proto_uaudp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    new_register_dissector("uaudp", dissect_uaudp, proto_uaudp);

    /* Register our configuration options */
    uaudp_module = prefs_register_protocol(proto_uaudp, proto_reg_handoff_uaudp);
    prefs_register_bool_preference(uaudp_module, "use_heuristic_dissector",
        "Use heuristic dissector",
        "Use to decode a packet a heuristic dissector. "
        "Otherwise, they are decoded only those packets that will come from the specified ports.",
        &use_heuristic_dissector);
    prefs_register_range_preference(uaudp_module, "udp_ports",
        "UAUDP port numbers",
        "Port numbers used for UAUDP traffic (examples: 5001, 32512)",
        &global_uaudp_port_range, MAX_UDP_PORT);

    uaudp_dissector_table = register_dissector_table("uaudp.opcode", "UA/UDP Opcode", FT_UINT8, BASE_DEC);
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/
