/* packet-freepastry-core-v0.c
* Routines for the FreePastry Binary protocol 
ion
* Copyright 2007, David Dugoujon <dav176fr@yahoo.fr>
*
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

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

#include <gmodule.h>

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

#include "packet-freepastry.h"

static int proto_freepastry_core_v0 = -1;

static int hf_freepastry_router_sub_address = -1;
static int hf_freepastry_router_target = -1;
static int hf_freepastry_router_has_destination_handle  = -1;
static int hf_freepastry_direct_nodeid_resp_id_value = -1;
static int hf_freepastry_direct_nodeid_resp_epoch = -1;
static int hf_freepastry_direct_routerow_row = -1;
static int hf_freepastry_direct_routerow_numroutesets = -1;
static int hf_freepastry_direct_routerow_notnull = -1;
static int hf_freepastry_direct_sourceroute_numhops = -1;
static int hf_freepastry_direct_sourceroute_resp_numsourceroutes = -1;
static int hf_freepastry_join_consistent_join_is_request  = -1;
static int hf_freepastry_join_consistent_join_num_failed  = -1;
static int hf_freepastry_join_join_req_rtbasebitlength  = -1;
static int hf_freepastry_join_join_req_has_join_handle  = -1;
static int hf_freepastry_join_join_req_last_row  = -1;
static int hf_freepastry_join_join_req_has_row  = -1;
static int hf_freepastry_join_join_req_has_col  = -1;
static int hf_freepastry_join_join_req_has_leafset  = -1;
static int hf_freepastry_leafset_request_leafset_timestamp  = -1;
static int hf_freepastry_leafset_broadcast_leafset_type  = -1;
static int hf_freepastry_leafset_broadcast_leafset_timestamp  = -1;
static int hf_freepastry_routingtable_routerow_req_row  = -1;
static int hf_freepastry_routingtable_broadcast_routerow_num_row  = -1;
static int hf_freepastry_routingtable_broadcast_routerow_notnull  = -1;
static int hf_freepastry_msg_pastry_endpoint_priority = -1;
static int hf_freepastry_direct_v0 = -1;
static int hf_freepastry_router_v0  = -1;
static int hf_freepastry_commonapi_v0 = -1;
static int hf_freepastry_join_v0 = -1;
static int hf_freepastry_routingtable_v0 = -1;
static int hf_freepastry_leafset_proto_v0 = -1;
static int hf_freepastry_direct_leafset_req = -1;
static int hf_freepastry_direct_leafset_resp = -1;
static int hf_freepastry_direct_nodeid_req = -1;
static int hf_freepastry_direct_nodeid_resp = -1;
static int hf_freepastry_direct_routerow_req = -1;
static int hf_freepastry_direct_routerow_resp = -1;
static int hf_freepastry_direct_sourceroute_req = -1;
static int hf_freepastry_direct_sourceroute_resp = -1;
static int hf_freepastry_direct_sourceroute = -1;
static int hf_freepastry_router_route  = -1;
static int hf_freepastry_commonapi_pastry_endpoint = -1;
static int hf_freepastry_join_join_req = -1;
static int hf_freepastry_join_consistent_join = -1;
static int hf_freepastry_routingtable_routerow_req = -1;
static int hf_freepastry_routingtable_broadcast_routerow = -1;
static int hf_freepastry_leafset_proto_leafset_req = -1;
static int hf_freepastry_leafset_proto_broadcast_leafset = -1;

static gint ett_freepastry_core_v0 = -1;
static gint ett_freepastry_core_v0_sr = -1;

static dissector_handle_t freepastry_core_v0_handle;

/*To dissect common API applications messages*/
static dissector_table_t subdissector_commonapi_table;

/*TODO duplicate data in packet-freepastry*/
/* Address mapping */
static const value_string freepastry_address[] = {
  { DIRECT_ACCESS, 	       "Direct Access" },
  { ROUTER, 	             "Router" },
  { JOIN_PROTOCOL, 	       "Join Protocol" },
  { LEAFSET_PROTOCOL, 	   "Leafset Protocol" },
  { ROUTINGTABLE_PROTOCOL, "Route Protocol" }, 
  { 0, NULL }
};

/* Message Priority */
static const value_string freepastry_priority[] = {
  { -15,  "Max" },
  { -10,  "High" },
  { -5, "Medium High" },
  { 0, "Medium" },
  { 5, "Medium Low" }, 
  { 10, "Low" },
  { 0, NULL }
};

/**
*   Decode a "Source Route" object.
*   @return the new offset or -1 on error.
**/
static gint
decode_sourceroute(tvbuff_t *tvb, proto_tree *parent_tree, gint offset, gchar *attribute_name)
{
  proto_item *ti = NULL;
  proto_tree *sourceroute_tree = NULL; 
  guint32 i;
  guint32 sourceroute_size = tvb_get_ntohl(tvb, offset);
  ti = proto_tree_add_text(parent_tree, tvb, offset, 1, attribute_name);
  sourceroute_tree = proto_item_add_subtree(ti, ett_freepastry_core_v0_sr);
  proto_tree_add_uint(sourceroute_tree, hf_freepastry_direct_sourceroute_numhops, tvb, offset, 4, sourceroute_size);
  offset += 4;
  /*for each hop (do not parse the last hop)*/
  for (i=0; i < (sourceroute_size - 1); ++i){
    offset = decode_epoch_inet_socket_address(tvb, sourceroute_tree, offset, "Hop");
    if (offset == -1){
      return -1;
    }
  }
  /*Parse the last node in the sourceroute*/
  if (sourceroute_size > 0) {
    gchar *ip_str;
    guint16 port_number;
    gint former_offset = offset;
    offset = decode_epoch_inet_socket_address(tvb, sourceroute_tree, offset, "Hop");
    if (offset == -1){
      return -1;
    }
    /*Print final destination on the subtree root*/
    ip_str = ip_to_str(tvb_get_ptr(tvb, former_offset + 1, 4));
    former_offset += 5;
    port_number = tvb_get_ntohs(tvb, former_offset);
    proto_item_append_text(ti, " -> %s:%d", ip_str, port_number);
  }
  proto_item_set_end(ti, tvb, offset);
  return offset;
}

/*
 * Start: TCP FreePastry Core Message dissection.
 */

/*Direct Acces Messages*/
static void
decode_msg_leafset_request(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
  }
}

static void
decode_msg_leafset_response(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    offset = decode_leafset(tvb, tree, offset, "Leafset");
  }
}

static void
decode_msg_id_request(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
  }
}

static void
decode_msg_id_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  gint offset = 0;
  if (tvb_reported_length_remaining(tvb, offset) < 29){
    proto_tree_add_text(tree, tvb, offset, -1, "Malformed message!");
    return;
  }

  if (check_col(pinfo->cinfo, COL_INFO)){
    col_append_str(pinfo->cinfo, COL_INFO, get_id(tvb, offset + 1));
  }

  if (tree){
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    proto_tree_add_string(tree, hf_freepastry_direct_nodeid_resp_id_value, tvb, offset, 20, get_id_full(tvb, offset));
    proto_tree_add_item(tree, hf_freepastry_direct_nodeid_resp_epoch, tvb, offset+20, 8, FALSE);
  }
}

static void
decode_msg_row_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  gint offset = 0;
  guint32 requested_row = tvb_get_ntohl(tvb, offset +1);
  if (check_col(pinfo->cinfo, COL_INFO)){
    col_append_fstr(pinfo->cinfo, COL_INFO, " %d", requested_row);
  }
  if (tree){
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    proto_tree_add_uint(tree, hf_freepastry_direct_routerow_row, tvb, offset, 4, requested_row);
  }
}

static void
decode_msg_row_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  gint offset = 0;
  guint32 num_routeset = tvb_get_ntohl(tvb, offset +1);
  if (check_col(pinfo->cinfo, COL_INFO)){
    col_append_fstr(pinfo->cinfo, COL_INFO, " (%d Route Sets)", num_routeset);
  }
  if (tree)
  {
    guint32 i;
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    proto_tree_add_uint(tree, hf_freepastry_direct_routerow_numroutesets, tvb, offset, 4, num_routeset);
    offset += 4;
    for (i = 0; i < num_routeset; ++i){
      gboolean not_null = FALSE;
      /*has sender?*/
      if (tvb_get_guint8(tvb, offset) != 0){
        not_null = TRUE;
      }
      proto_tree_add_boolean(tree, hf_freepastry_direct_routerow_notnull, tvb, offset, 1, not_null);
      offset++;
      if (not_null){
        offset = decode_routeset(tvb, tree, offset, ep_strdup_printf("RouteSet for col 0x%X", i));
        if (offset == -1){
          return;
        }
      }
    }
  }
}

static void
decode_msg_routes_request(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
  }
}

static void
decode_msg_routes_response(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    guint32 i;
    guint32 num_sourceroutes = tvb_get_ntohl(tvb, offset+1);
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    proto_tree_add_uint(tree, hf_freepastry_direct_sourceroute_resp_numsourceroutes, 
      tvb, offset, 4, num_sourceroutes);
    offset += 4;
    for (i=0; i < num_sourceroutes; ++i){
      /*TODO check version*/
      offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
      offset = decode_sourceroute(tvb, tree, offset, "Source Route");
      if (offset == -1){
        return;
      }
    }
  }
}

static void
decode_msg_source_route(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    offset = decode_message_version(tvb,tree, offset, DIRECT_ACCESS);
    decode_sourceroute(tvb, tree, offset, "Source Route");
  }
}

/* Join Messages */

static void
decode_msg_join_request(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    gboolean has_join_handle = FALSE;
    gboolean has_leafset = FALSE;
    guint8 rt_base_bit_length;
    guint8 num_row;
    guint8 num_col;
    guint8 i;
    guint8 j;

    rt_base_bit_length = tvb_get_guint8(tvb, offset + 1);
    offset = decode_message_version(tvb,tree, offset, JOIN_PROTOCOL);
    proto_tree_add_item(tree, hf_freepastry_join_join_req_rtbasebitlength, tvb, offset, 1, rt_base_bit_length);
    offset += 1;
    offset = decode_nodehandle(tvb, tree, offset, "Node Handle");
    if (offset == -1) {
      return;
    }
    if (tvb_get_guint8(tvb, offset) != 0){
      has_join_handle = TRUE;
    }
    proto_tree_add_boolean(tree, hf_freepastry_join_join_req_has_join_handle, tvb, offset, 1, has_join_handle);
    offset++;
    if (has_join_handle) {
      offset = decode_nodehandle(tvb, tree, offset, "Join Handle");
    }
    if (offset == -1) {
      return;
    }
    proto_tree_add_item(tree, hf_freepastry_join_join_req_last_row, tvb, offset, 2, FALSE);
    offset += 2;
    if (rt_base_bit_length) {
      num_row = 160/rt_base_bit_length;
    } else {/*division by zero*/
      return;
    }
    num_col = 1 << rt_base_bit_length;
    for (i = 0; i < num_row; ++i){
      gboolean has_row = FALSE;
      if (tvb_get_guint8(tvb, offset) != 0){
        has_row = TRUE;
      }
      proto_tree_add_boolean(tree, hf_freepastry_join_join_req_has_row, tvb, offset, 1, has_row);
      offset++;
      if (has_row) {
        for (j = 0; j < num_col; ++j){
          gboolean has_col = FALSE;
          if (tvb_get_guint8(tvb, offset) != 0){
            has_col = TRUE;
          }
          proto_tree_add_boolean(tree, hf_freepastry_join_join_req_has_col, tvb, offset, 1, has_col);
          offset++;
          if (has_col) {
            offset = decode_routeset(tvb, tree, offset, ep_strdup_printf("RouteSet (%d, 0x%X)", i+1, j));
            if (offset == -1){
              return;
            }
          }
        }/*end for each col*/
      }/*end has row entry*/
    }/*end for each row*/

    if (tvb_get_guint8(tvb, offset) != 0){
      has_leafset = TRUE;
    }
    proto_tree_add_boolean(tree, hf_freepastry_join_join_req_has_leafset, tvb, offset, 1, has_leafset);
    if (has_leafset) {
      offset = decode_leafset(tvb, tree, offset + 1, "LeafSet");
    }
  }
}

static void
decode_msg_consistent_join(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    guint32 num_failed;
    guint32 i;

    offset = decode_message_version(tvb,tree, offset, JOIN_PROTOCOL);
    offset = decode_leafset(tvb, tree, offset, "Leafset");
    if (offset == -1){
      return;
    }
    proto_tree_add_boolean(tree, hf_freepastry_join_consistent_join_is_request, tvb, offset, 1, FALSE);
    num_failed = tvb_get_ntohl(tvb, offset + 1);
    proto_tree_add_uint(tree, hf_freepastry_join_consistent_join_num_failed, tvb, offset+1, 4, num_failed);
    offset += 5;
    for (i = 0; i < num_failed; ++i){
      if (offset == -1) {
        return;
      }
      offset = decode_nodehandle(tvb, tree, offset, "Failed Node Handle");
    }
  }
}

/* Leafset Maintenance Messages*/

static void
decode_msg_request_leafset(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    offset = decode_message_version(tvb,tree, offset, LEAFSET_PROTOCOL);
    proto_tree_add_item(tree, hf_freepastry_leafset_request_leafset_timestamp, tvb, offset, 8, FALSE);
  }
}

static void
decode_msg_broadcast_leafset(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) {
  gint offset = 0;
  if (tree){
    offset = decode_message_version(tvb,tree, offset, LEAFSET_PROTOCOL);
    offset = decode_nodehandle(tvb, tree, offset, "From Handle");
    if (offset == -1) {
      return;
    }
    offset = decode_leafset(tvb, tree, offset, "LeafSet");
    if (offset == -1) {
      return;
    }
    proto_tree_add_item(tree, hf_freepastry_leafset_broadcast_leafset_type, tvb, offset, 1, FALSE);
    proto_tree_add_item(tree, hf_freepastry_leafset_broadcast_leafset_timestamp, tvb, offset + 1, 8, FALSE);
  }
}

/* Routing Table Maintenance Messages*/

static void
decode_msg_request_route_row(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  gint offset = 0;
  guint32 requested_row = tvb_get_ntohl(tvb, offset +1);
  if (check_col(pinfo->cinfo, COL_INFO)){
    col_append_fstr(pinfo->cinfo, COL_INFO, " %d", requested_row);
  }
  if (tree){
    offset = decode_message_version(tvb,tree, offset, ROUTINGTABLE_PROTOCOL);
    proto_tree_add_uint(tree, hf_freepastry_routingtable_routerow_req_row, tvb, offset, 4, requested_row);
  }
}

static void
decode_msg_route_row(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  guint8 num_rows;
  gint offset = 0;
  if (tree)
  {
    offset = decode_message_version(tvb,tree, offset, ROUTINGTABLE_PROTOCOL);
    offset = decode_nodehandle(tvb, tree, offset, "From Handle");
  } else {
    offset++;
    offset += get_node_handle_len(tvb, offset);
  }
  
  
  if (offset == -1) {
    return;
  }

  num_rows = tvb_get_guint8(tvb, offset);
  if (check_col(pinfo->cinfo, COL_INFO)){
    col_append_fstr(pinfo->cinfo, COL_INFO, " (%d Rows)", num_rows);
  }

  if (tree)
  {
    guint32 i;
    proto_tree_add_uint(tree, hf_freepastry_routingtable_broadcast_routerow_num_row, tvb, offset, 1, num_rows);
    offset++;
    for (i = 0; i < num_rows; ++i){
      gboolean not_null = FALSE;
      /*row is not null?*/
      if (tvb_get_guint8(tvb, offset) != 0){
        not_null = TRUE;
      }
      proto_tree_add_boolean(tree, hf_freepastry_routingtable_broadcast_routerow_notnull, tvb, offset, 1, not_null);
      offset++;
      if (not_null){
        offset = decode_routeset(tvb, tree, offset, ep_strdup_printf("RouteSet for col 0x%X", i));
        if (offset == -1){
          return;
        }
      }
    }
  }
}

/* Router Message */

static void
decode_msg_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree){
  gint offset = 0;  
  guint8 version_num;
  gboolean has_destination = FALSE;
  guint32 address = tvb_get_ntohl(tvb, offset+1);
  version_num = tvb_get_guint8(tvb, offset);
  if (tree){
    offset = decode_message_version(tvb,tree, offset, ROUTER);
    proto_tree_add_item(tree, hf_freepastry_router_sub_address, tvb, offset, 4, FALSE);
    offset += 4;
    if (tvb_reported_length_remaining(tvb, offset) < 20){
      proto_tree_add_text(tree, tvb, offset, -1, "Malformed message!");
      return;
    }
    switch(version_num) {
    case 0:
      proto_tree_add_string(tree, hf_freepastry_router_target, tvb, offset, 20, get_id_full(tvb, offset));
      offset += 20;
      break;
    case 1:
      if (tvb_get_guint8(tvb, offset) != 0){
        has_destination = TRUE;
      }
      proto_tree_add_boolean(tree, hf_freepastry_router_has_destination_handle, tvb, offset, 1, has_destination);
      offset++;
      if (has_destination) {
        offset = decode_nodehandle(tvb, tree, offset, "Destination Handle");
      } else {
	proto_tree_add_string(tree, hf_freepastry_router_target, tvb, offset, 20, get_id_full(tvb, offset));
	offset += 20;
      }
      if (offset == -1) {
        return;
      }
      break;
    }

    offset = decode_nodehandle(tvb, tree, offset, "Previous Hop");
  } else {    
    offset += 5; // version+auxAddress
    switch(version_num) {
    case 0:
      offset += 20;
      break;
    case 1:
      if (tvb_get_guint8(tvb, offset) != 0){
        has_destination = TRUE;
      }
      offset++; // hasDestHandle
      if (has_destination) {
        offset += get_node_handle_len(tvb, offset); // the destHandle
      } else {
	offset += 20;
      } 
    }    
    if (offset == -1) return; // error
    offset += get_node_handle_len(tvb, offset); // the previous Hop
  }

  if (offset != -1){
    decode_freepastry_tcp_msg_invariant(tvb, pinfo, tree, offset, address);
  }
}

/* Common API Message */

static void
decode_msg_pastry_endpoint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 address) {
  gint offset = 0;
  tvbuff_t *next_tvb;
  guint16 short_address = (guint16) ((address >> 16) & 0xffff);

  if (tvb_reported_length_remaining(tvb, offset+2) <= 0){
      return;	/* no more data */
  }

  if (tree){
    offset = decode_message_version(tvb,tree, offset, address);
    proto_tree_add_item(tree, hf_freepastry_msg_pastry_endpoint_priority, tvb, offset, 1, FALSE);
  }

  next_tvb = tvb_new_subset(tvb, offset + 1, -1, -1);
  dissector_try_port(subdissector_commonapi_table, short_address, next_tvb, pinfo, tree);
}

static void
handle_not_supported_msg(tvbuff_t *tvb, packet_info *pinfo  _U_, proto_tree *tree _U_) {
  if (check_col(pinfo->cinfo, COL_INFO)){
      col_append_str(pinfo->cinfo, COL_INFO, "Unsupported");
  }
  if (tree){
    proto_tree_add_text(tree, tvb, 1, -1, "Unsupported message for this version");
  }
}

/**
*   Heuristically dissect a tvbuff containing a FreePastry TCP Message
**/
static void 
dissect_freepastry_core_v0(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
{
  sub_message_info_t *message_info = (sub_message_info_t *)pinfo->private_data;
  switch (message_info->address){
    case DIRECT_ACCESS:
      proto_tree_add_item_hidden(tree, hf_freepastry_direct_v0, tvb, 0, -1, FALSE);
      switch (message_info->type){
        case LEAFSET_REQUEST_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_leafset_req, tvb, 0, -1, FALSE);
          decode_msg_leafset_request(tvb, pinfo, tree);
          break;
        case LEAFSET_RESPONSE_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_leafset_resp, tvb, 0, -1, FALSE);
          decode_msg_leafset_response(tvb, pinfo, tree);
          break;
        case NODE_ID_REQUEST_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_nodeid_req, tvb, 0, -1, FALSE);
          decode_msg_id_request(tvb, pinfo, tree);
          break;
        case NODE_ID_RESPONSE_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_nodeid_resp, tvb, 0, -1, FALSE);
          decode_msg_id_response(tvb, pinfo, tree);
          break;
        case ROUTE_ROW_REQUEST_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_routerow_req, tvb, 0, -1, FALSE);
          decode_msg_row_request(tvb, pinfo, tree);
          break;
        case ROUTE_ROW_RESPONSE_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_routerow_resp, tvb, 0, -1, FALSE);
          decode_msg_row_response(tvb, pinfo, tree);
          break;
        case SOURCEROUTE_REQUEST_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_sourceroute_req, tvb, 0, -1, FALSE);
          decode_msg_routes_request(tvb, pinfo, tree);
          break;
        case SOURCEROUTE_RESPONSE_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_sourceroute_resp, tvb, 0, -1, FALSE);
          decode_msg_routes_response(tvb, pinfo, tree);
          break;
       case SOURCEROUTE_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_direct_sourceroute, tvb, 0, -1, FALSE);
          decode_msg_source_route(tvb, pinfo, tree);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }
      break;
    case ROUTER:
      proto_tree_add_item_hidden(tree, hf_freepastry_router_v0, tvb, 0, -1, FALSE);
       switch (message_info->type){
        case ROUTE:
          proto_tree_add_item_hidden(tree, hf_freepastry_router_route, tvb, 0, -1, FALSE);
          decode_msg_route(tvb, pinfo, tree);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }
      break;
    case JOIN_PROTOCOL:
      proto_tree_add_item_hidden(tree, hf_freepastry_join_v0, tvb, 0, -1, FALSE);
      switch (message_info->type){
        case JOIN_REQUEST:
          proto_tree_add_item_hidden(tree, hf_freepastry_join_join_req, tvb, 0, -1, FALSE);
          decode_msg_join_request(tvb, pinfo, tree);
          break;
        case CONSISTENT_JOIN_MSG:
          proto_tree_add_item_hidden(tree, hf_freepastry_join_consistent_join, tvb, 0, -1, FALSE);
          decode_msg_consistent_join(tvb, pinfo, tree);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }
      break;
    case LEAFSET_PROTOCOL:
      proto_tree_add_item_hidden(tree, hf_freepastry_leafset_proto_v0, tvb, 0, -1, FALSE);
      switch (message_info->type){
        case REQUEST_LEAFSET:
          proto_tree_add_item_hidden(tree, hf_freepastry_leafset_proto_leafset_req, tvb, 0, -1, FALSE);
          decode_msg_request_leafset(tvb, pinfo, tree);
          break;
        case BROADCAST_LEAFSET:
          proto_tree_add_item_hidden(tree, hf_freepastry_leafset_proto_broadcast_leafset, tvb, 0, -1, FALSE);
          decode_msg_broadcast_leafset(tvb, pinfo, tree);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }
      break;
    case ROUTINGTABLE_PROTOCOL:
      proto_tree_add_item_hidden(tree, hf_freepastry_routingtable_v0, tvb, 0, -1, FALSE);
      switch (message_info->type){
        case REQUEST_ROUTE_ROW:
          proto_tree_add_item_hidden(tree, hf_freepastry_routingtable_routerow_req, tvb, 0, -1, FALSE);
          decode_msg_request_route_row(tvb, pinfo, tree);
          break;
        case BROADCAST_ROUTE_ROW:
          proto_tree_add_item_hidden(tree, hf_freepastry_routingtable_broadcast_routerow, tvb, 0, -1, FALSE);
          decode_msg_route_row(tvb, pinfo, tree);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }
      break;
    default:
      proto_tree_add_item_hidden(tree, hf_freepastry_commonapi_v0, tvb, 0, -1, FALSE);
      switch (message_info->type){
        case PASTRY_ENDPOINT_MESSAGE:
          proto_tree_add_item_hidden(tree, hf_freepastry_commonapi_pastry_endpoint, tvb, 0, -1, FALSE);
          decode_msg_pastry_endpoint(tvb, pinfo, tree, message_info->address);
          break;
        default:
          handle_not_supported_msg(tvb, pinfo, tree);
      }/*switch type*/
  }/*switch address*/
}


void
proto_register_freepastry_core_v0(void)
{
  static hf_register_info hf[] = {
    { &hf_freepastry_direct_v0,
	  { "Direct messages", "freepastry.direct.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only direct access traffic", HFILL }},
    { &hf_freepastry_router_v0,
	  { "Router messages", "freepastry.router.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only router traffic", HFILL }},
    { &hf_freepastry_commonapi_v0,
	  { "Common API messages", "freepastry.commonapi.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only common API traffic", HFILL }},
    { &hf_freepastry_join_v0,
	  { "Join protocol", "freepastry.join.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only join protocol traffic", HFILL }},
    { &hf_freepastry_routingtable_v0,
	  { "Routing table maintenance protocol", "freepastry.routingtable.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only routing table maintenance protocol traffic", HFILL }},
    { &hf_freepastry_leafset_proto_v0,
	  { "Leafset maintenance protocol", "freepastry.leafset_proto.v0", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only leafset maintenance protocol traffic", HFILL }},
    { &hf_freepastry_direct_leafset_req,
	  { "Leafset request", "freepastry.direct.v0.leafset_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only leafset request messages", HFILL }},
    { &hf_freepastry_direct_leafset_resp,
	  { "Leafset response", "freepastry.direct.v0.leafset_resp", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only leafset response messages", HFILL }},
    { &hf_freepastry_direct_nodeid_req,
	  { "Node ID request", "freepastry.direct.v0.nodeid_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only node ID request messages", HFILL }},
    { &hf_freepastry_direct_nodeid_resp,
	  { "Node ID response", "freepastry.direct.v0.nodeid_resp", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only node ID response messages", HFILL }},
    { &hf_freepastry_direct_routerow_req,
	  { "RouteRow request", "freepastry.direct.v0.routerow_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only RouteRow request messages", HFILL }},
    { &hf_freepastry_direct_routerow_resp,
	  { "RouteRow response", "freepastry.direct.v0.routerow_resp", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only RouteRow response messages", HFILL }},
    { &hf_freepastry_direct_sourceroute_req,
	  { "SourceRoute request", "freepastry.direct.v0.sourceroute_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only SourceRoute request messages", HFILL }},
    { &hf_freepastry_direct_sourceroute_resp,
	  { "SourceRoute response", "freepastry.direct.v0.sourceroute_resp", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only SourceRoute response messages", HFILL }},
    { &hf_freepastry_direct_sourceroute,
	  { "SourceRoute", "freepastry.direct.v0.sourceroute", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only SourceRoute messages", HFILL }},
    { &hf_freepastry_router_route,
	  { "Route", "freepastry.router.v0.route", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only Route messages", HFILL }},
    { &hf_freepastry_commonapi_pastry_endpoint,
	  { "RouteRow response", "freepastry.commonapi.v0.pastry_endpoint", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only Pastry endpoint messages", HFILL }},
    { &hf_freepastry_join_join_req,
	  { "Join request", "freepastry.join.v0.join_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only Join request messages", HFILL }},
    { &hf_freepastry_join_consistent_join,
	  { "ConsistentJoin", "freepastry.join.v0.consistent_join", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only ConsistentJoin messages", HFILL }},
    { &hf_freepastry_routingtable_routerow_req,
	  { "RouteRow request", "freepastry.routingtable.v0.routerow_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only RouteRow request messages", HFILL }},
    { &hf_freepastry_routingtable_broadcast_routerow,
	  { "BroadcastRouteRow", "freepastry.routingtable.v0.broadcast_routerow", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only BroadcastRouteRow messages", HFILL }},
    { &hf_freepastry_leafset_proto_leafset_req,
	  { "Leafset request", "freepastry.leafset_proto.v0.leafset_req", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only Leafset request messages", HFILL }},
    { &hf_freepastry_leafset_proto_broadcast_leafset,
	  { "BroadcastLeafset", "freepastry.leafset_proto.v0.broadcast_leafset", 
    FT_NONE, BASE_NONE, NULL, 0x0, 
    "Show only BroadcastLeafset messages", HFILL }},
    { &hf_freepastry_router_sub_address,
    { "Sub message address",	"freepastry.router.route.v0.sub",
    FT_UINT32, BASE_HEX, VALS(freepastry_address), 0x0,
    "Address for messages encapsulated in a route message", HFILL }},
    { &hf_freepastry_router_target,
    { "Target", "freepastry.router.route.v0.target",
    FT_STRING, BASE_NONE, NULL, 0x0,
    "target for a route message", HFILL }},
    { &hf_freepastry_router_has_destination_handle,
    { "Has destination handle",	"freepastry.router.route.v0.has_destination_handle",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if Route message has a destination handle", HFILL }},
    { &hf_freepastry_direct_nodeid_resp_id_value,
    { "ID value", "freepastry.direct.nodeid_resp.v0.id",
    FT_STRING, BASE_NONE, NULL, 0x0,
    "ID value", HFILL }},
    { &hf_freepastry_direct_nodeid_resp_epoch,
    { "Epoch",	"freepastry.direct.nodeid_resp.v0.epoch",
    FT_UINT64, BASE_DEC, NULL, 0x0,
    "Epoch", HFILL }},
    { &hf_freepastry_direct_routerow_row,
    { "Requested row",	"freepastry.direct.route_row.v0.row",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "Index of the requested row", HFILL }},
    { &hf_freepastry_direct_routerow_numroutesets,
    { "Number of RouteSets",	"freepastry.direct.route_row.v0.num_route_sets",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "Number of RouteSets", HFILL }},
    { &hf_freepastry_direct_routerow_notnull,
    { "RouteSet is not null",	"freepastry.direct.route_row.v0.not_null_routeset",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if the Routeset is not null", HFILL }},
    { &hf_freepastry_direct_sourceroute_numhops,
    { "Number of hops",	"freepastry.direct.source_route.v0.num_hops",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "Number of hops in the SourceRoute", HFILL }},
    { &hf_freepastry_direct_sourceroute_resp_numsourceroutes,
    { "Number of SourceRoutes",	"freepastry.direct.source_route.v0.num_source_routes",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "Number of SourceRoutes", HFILL }},
    { &hf_freepastry_join_consistent_join_is_request,
    { "Is request",	"freepastry.direct.join_proto.consistent_join.v0.is_request",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if the ConsistentJoin message is a request", HFILL }},
    { &hf_freepastry_join_consistent_join_num_failed,
    { "Number of failed set",	"freepastry.join_proto.consistent_join.v0.num_failed_sets",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "Number of failed set", HFILL }},
    { &hf_freepastry_join_join_req_rtbasebitlength,
    { "RT base bit length",	"freepastry.join_proto.join.v0.rtbasebitlength",
    FT_UINT32, BASE_DEC, NULL, 0x0,
    "RT base bit length", HFILL }},
    { &hf_freepastry_join_join_req_has_join_handle,
    { "Has join handle",	"freepastry.join_proto.join.v0.has_join_handle",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if Join message has a join handle", HFILL }},
    { &hf_freepastry_join_join_req_last_row,
    { "Number of rows left",	"freepastry.join_proto.join.v0.last_row",
    FT_UINT16, BASE_DEC, NULL, 0x0,
    "The number of rows left to determine", HFILL }},
    { &hf_freepastry_join_join_req_has_row,
    { "Has row",	"freepastry.join_proto.join.v0.has_row",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if there is a row for the current column", HFILL }},
    { &hf_freepastry_join_join_req_has_col,
    { "Has column",	"freepastry.join_proto.join.v0.has_col",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if there is an entry", HFILL }},
    { &hf_freepastry_join_join_req_has_leafset,
    { "Has leafset",	"freepastry.join_proto.join.v0.has_leafset",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True is the response contains a leafset", HFILL }},
    { &hf_freepastry_leafset_request_leafset_timestamp,
    { "Timestamp",	"freepastry.leafset_proto.request_leafset.v0.timestamp",
    FT_UINT64, BASE_DEC, NULL, 0x0,
    "Timestamp for LeafsetRequest message", HFILL }},
    { &hf_freepastry_leafset_broadcast_leafset_type,
    { "Type",	"freepastry.leafset_proto.broadcast_leafset.v0.type",
    FT_UINT8, BASE_DEC, NULL, 0x0,
    "Type", HFILL }},
    { &hf_freepastry_leafset_broadcast_leafset_timestamp,
    { "Timestamp",	"freepastry.leafset_proto.broadcast_leafset.v0.timestamp",
    FT_UINT64, BASE_DEC, NULL, 0x0,
    "Timestamp for LeafsetResponse message", HFILL }},
    { &hf_freepastry_routingtable_routerow_req_row,
    { "Row",	"freepastry.route_proto.request_routerow.row",
    FT_UINT8, BASE_DEC, NULL, 0x0,
    "Index of the requested row", HFILL }},
    { &hf_freepastry_routingtable_broadcast_routerow_num_row,
    { "Number of RouteSets",	"freepastry.route_proto.broadcast_routerow.v0.num_routesets",
    FT_UINT8, BASE_DEC, NULL, 0x0,
    "Number of RouteSets", HFILL }},
    { &hf_freepastry_routingtable_broadcast_routerow_notnull,
    { "RouteSet is not null",	"freepastry.route_proto.broadcast_routerow.v0.not_null_routeset",
    FT_BOOLEAN, 8, NULL, 0x0,
    "True if the row has a RouteSet at the current index", HFILL }},
    { &hf_freepastry_msg_pastry_endpoint_priority,
    { "Priority",	"freepastry.commonapi.pastry_endpoint.v0.priority",
    FT_INT8, BASE_DEC, VALS(freepastry_priority), 0x0,
    "Priority", HFILL }}
  };
  
  /* Setup protocol subtree array */
  static gint *ett[] = {
    &ett_freepastry_core_v0,
    &ett_freepastry_core_v0_sr
  };

  module_t *freepastry_core_v0_module;	

  if (proto_freepastry_core_v0 == -1) {
    proto_freepastry_core_v0 = proto_register_protocol (
      "FreePastry Binary Protocol Version 0",
      "FreePastry V0",
      "freepastry_v0");
  }

  freepastry_core_v0_module	= prefs_register_protocol(proto_freepastry_core_v0, NULL);
  proto_register_field_array(proto_freepastry_core_v0, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));

  subdissector_commonapi_table = register_dissector_table("commonapi.app", "Common API Application",
    FT_UINT16, BASE_HEX);
}


void
proto_reg_handoff_freepastry_core_v0(void)
{
  static int Initialized=FALSE;

  if (!Initialized) {
    freepastry_core_v0_handle = create_dissector_handle(dissect_freepastry_core_v0, 
      proto_freepastry_core_v0);
    dissector_add("freepastry.msg", 0, freepastry_core_v0_handle);
  }
}
