Logo Search packages:      
Sourcecode: wireshark version File versions

packet-atalk.c

/* packet-atalk.c
 * Routines for AppleTalk packet disassembly: LLAP, DDP, NBP, ATP, ASP,
 * RTMP, PAP.
 *
 * $Id: packet-atalk.c 23646 2007-11-28 15:55:50Z gerald $
 *
 * Simon Wilkinson <sxw@dcs.ed.ac.uk>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998
 *
 * 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 <glib.h>
#include <epan/packet.h>
#include <string.h>

#include <epan/etypes.h>
#include <epan/ppptypes.h>
#include <epan/aftypes.h>
#include <epan/arcnet_pids.h>
#include <epan/atalk-utils.h>
#include <epan/conversation.h>

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

#include "packet-atalk.h"
#include "packet-afp.h"

/* Tables for reassembly of fragments. */
static GHashTable *atp_fragment_table = NULL;
static GHashTable *atp_reassembled_table = NULL;

/* desegmentation of ATP */
static gboolean atp_defragment = TRUE;

static dissector_handle_t afp_handle;

static int proto_llap = -1;
static int hf_llap_dst = -1;
static int hf_llap_src = -1;
static int hf_llap_type = -1;

static int proto_ddp = -1;
static int hf_ddp_hopcount = -1;
static int hf_ddp_len = -1;
static int hf_ddp_checksum = -1;
static int hf_ddp_dst = -1;
static int hf_ddp_dst_net = -1;
static int hf_ddp_src = -1;
static int hf_ddp_src_net = -1;
static int hf_ddp_dst_node = -1;
static int hf_ddp_src_node = -1;
static int hf_ddp_dst_socket = -1;
static int hf_ddp_src_socket = -1;
static int hf_ddp_type = -1;

static dissector_handle_t ddp_handle;

/* --------------------------------------
 * ATP protocol parameters
 * from netatalk/include/atalk/atp.h
 */
#define ATP_MAXDATA     (578+4)         /* maximum ATP data size */
#define ATP_BUFSIZ      587             /* maximum packet size */
#define ATP_HDRSIZE     5               /* includes DDP type field */

#define ATP_TRELMASK    0x07            /* mask all but TREL */
#define ATP_RELTIME     30              /* base release timer (in secs) */

#define ATP_TREL30      0x0             /* release time codes */
#define ATP_TREL1M      0x1             /* these are passed in flags of */
#define ATP_TREL2M      0x2             /* atp_sreq call, and set in the */
#define ATP_TREL4M      0x3             /* packet control info. */
#define ATP_TREL8M      0x4

/* flags for ATP options (and control byte)
*/
#define ATP_XO          0x20 /* (1<<5)          eXactly Once mode */
#define ATP_EOM         0x10 /* (1<<4)          End Of Message */
#define ATP_STS         0x08 /* (1<<3)          Transaction Status */

/* function codes
*/
#define ATP_FUNCMASK    (3<<6)          /* mask all but function */

#define ATP_TREQ        1 /* (1<<6)        Trans. REQuest */
#define ATP_TRESP       2 /* (2<<6)        Trans. RESPonse */
#define ATP_TREL        3 /* (3<<6)        Trans. RELease */

/* ------------------------- */
static dissector_handle_t asp_handle;
static dissector_handle_t pap_handle;

static int proto_atp = -1;
static int hf_atp_ctrlinfo  = -1; /* guint8_t    control information */
static int hf_atp_function  = -1; /* bits 7,6    function */
static int hf_atp_xo        = -1; /* bit 5       exactly-once */
static int hf_atp_eom       = -1; /* bit 4       end-of-message */
static int hf_atp_sts       = -1; /* bit 3       send transaction status */
static int hf_atp_treltimer = -1; /* bits 2,1,0  TRel timeout indicator */

static int hf_atp_bitmap = -1;   /* guint8_t  bitmap or sequence number */
static int hf_atp_tid = -1;      /* guint16_t transaction id. */
static int hf_atp_user_bytes = -1;

static int hf_atp_segments = -1;
static int hf_atp_segment = -1;
static int hf_atp_segment_overlap = -1;
static int hf_atp_segment_overlap_conflict = -1;
static int hf_atp_segment_multiple_tails = -1;
static int hf_atp_segment_too_long_segment = -1;
static int hf_atp_segment_error = -1;
static int hf_atp_reassembled_in = -1;

/* ------------------------- */
static int proto_zip = -1;
static dissector_handle_t zip_atp_handle;

static int hf_zip_function = -1;
static int hf_zip_atp_function = -1;
static int hf_zip_start_index = -1;
static int hf_zip_count = -1;
static int hf_zip_zero_value  = -1;

static int hf_zip_network_count = -1;
static int hf_zip_network = -1;
static int hf_zip_network_start = -1;
static int hf_zip_network_end = -1;

static int hf_zip_flags = -1;

static int hf_zip_flags_zone_invalid  = -1;
static int hf_zip_flags_use_broadcast = -1;
static int hf_zip_flags_only_one_zone = -1;

static int hf_zip_last_flag = -1;

static int hf_zip_zone_name    = -1;
static int hf_zip_default_zone = -1;

static int hf_zip_multicast_length  = -1;
static int hf_zip_multicast_address = -1;

static const value_string zip_function_vals[] = {
  {1, "Query"},
  {2, "Reply"},
  {5, "GetNetInfo request"},
  {6, "GetNetInfo reply"},
  {7, "notify"},
  {8, "Extended reply"},
  {0, NULL}
};

static const value_string zip_atp_function_vals[] = {
  {7, "GetMyZone"},
  {8, "GetZoneList"},
  {9, "GetLocalZones"},
  {0, NULL}
};

static gint ett_zip              = -1;
static gint ett_zip_flags        = -1;
static gint ett_zip_zones_list   = -1;
static gint ett_zip_network_list = -1;

/* --------------------------------
 * from netatalk/include/atalk/ats.h
 */

#define ASPFUNC_CLOSE   1
#define ASPFUNC_CMD     2
#define ASPFUNC_STAT    3
#define ASPFUNC_OPEN    4
#define ASPFUNC_TICKLE  5
#define ASPFUNC_WRITE   6
#define ASPFUNC_WRTCONT 7
#define ASPFUNC_ATTN    8

#define ASP_HDRSIZ      4
#define ASPERR_OK       0x0000
#define ASPERR_BADVERS  0xfbd6
#define ASPERR_BUFSMALL 0xfbd5
#define ASPERR_NOSESS   0xfbd4
#define ASPERR_NOSERV   0xfbd3
#define ASPERR_PARM     0xfbd2
#define ASPERR_SERVBUSY 0xfbd1
#define ASPERR_SESSCLOS 0xfbd0
#define ASPERR_SIZERR   0xfbcf
#define ASPERR_TOOMANY  0xfbce
#define ASPERR_NOACK    0xfbcd

static int proto_asp = -1;
static int hf_asp_func = -1;
static int hf_asp_error = -1;
static int hf_asp_socket      = -1;
static int hf_asp_version     = -1;
static int hf_asp_session_id  = -1;
static int hf_asp_zero_value  = -1;
static int hf_asp_init_error  = -1;
static int hf_asp_attn_code   = -1;
static int hf_asp_seq         = -1;
static int hf_asp_size        = -1;

/* status stuff same for asp and afp */
static int hf_asp_server_name = -1;
static int hf_asp_server_type = -1;
static int hf_asp_server_vers = -1;
static int hf_asp_server_uams = -1;
static int hf_asp_server_icon = -1;
static int hf_asp_server_directory = -1;

static int hf_asp_server_flag = -1;
static int hf_asp_server_flag_copyfile = -1;
static int hf_asp_server_flag_passwd   = -1;
static int hf_asp_server_flag_no_save_passwd = -1;
static int hf_asp_server_flag_srv_msg     = -1;
static int hf_asp_server_flag_srv_sig     = -1;
static int hf_asp_server_flag_tcpip = -1;
static int hf_asp_server_flag_notify      = -1;
static int hf_asp_server_flag_reconnect   = -1;
static int hf_asp_server_flag_directory   = -1;
static int hf_asp_server_flag_utf8_name = -1;
static int hf_asp_server_flag_fast_copy = -1;
static int hf_asp_server_signature  = -1;
static int hf_asp_server_utf8_name_len  = -1;
static int hf_asp_server_utf8_name      = -1;

static int hf_asp_server_addr_len   = -1;
static int hf_asp_server_addr_type  = -1;
static int hf_asp_server_addr_value = -1;

static gint ett_asp_status = -1;
static gint ett_asp_uams   = -1;
static gint ett_asp_vers   = -1;
static gint ett_asp_addr   = -1;
static gint ett_asp_addr_line = -1;
static gint ett_asp_directory = -1;
static gint ett_asp_utf8_name = -1;
static gint ett_asp_status_server_flag = -1;

typedef struct {
      guint32 conversation;
      guint8  src[4];
      guint16     seq;
} asp_request_key;

typedef struct {
      guint8      value;      /* command for asp, bitmap for atp */
} asp_request_val;

static GHashTable *asp_request_hash = NULL;

/* Hash Functions */
static gint  asp_equal (gconstpointer v, gconstpointer v2)
{
      const asp_request_key *val1 = (const asp_request_key*)v;
      const asp_request_key *val2 = (const asp_request_key*)v2;

      if (val1->conversation == val2->conversation &&
                  val1->seq == val2->seq &&
                  !memcmp(val1->src, val2->src, 4)) {
            return 1;
      }
      return 0;
}

static guint asp_hash  (gconstpointer v)
{
        const asp_request_key *asp_key = (const asp_request_key*)v;
        return asp_key->seq;
}

/* ------------------------------------ */
static GHashTable *atp_request_hash = NULL;


/* ------------------------------------ */
static int proto_nbp = -1;
static int hf_nbp_op = -1;
static int hf_nbp_info = -1;
static int hf_nbp_count = -1;
static int hf_nbp_tid = -1;

static int hf_nbp_node_net = -1;
static int hf_nbp_node_port = -1;
static int hf_nbp_node_node = -1;
static int hf_nbp_node_enum = -1;
static int hf_nbp_node_object = -1;
static int hf_nbp_node_type = -1;
static int hf_nbp_node_zone = -1;

static int proto_rtmp = -1;
static int hf_rtmp_net = -1;
static int hf_rtmp_node_len = -1;
static int hf_rtmp_node = -1;
static int hf_rtmp_tuple_net = -1;
static int hf_rtmp_tuple_range_start = -1;
static int hf_rtmp_tuple_range_end = -1;
static int hf_rtmp_tuple_dist = -1;
static int hf_rtmp_function = -1;

static gint ett_atp = -1;

static gint ett_atp_segments = -1;
static gint ett_atp_segment = -1;
static gint ett_atp_info = -1;
static gint ett_asp = -1;
static gint ett_pap = -1;

static gint ett_nbp = -1;
static gint ett_nbp_info = -1;
static gint ett_nbp_node = -1;
static gint ett_rtmp = -1;
static gint ett_rtmp_tuple = -1;
static gint ett_ddp = -1;
static gint ett_llap = -1;
static gint ett_pstring = -1;

static const fragment_items atp_frag_items = {
      &ett_atp_segment,
      &ett_atp_segments,
      &hf_atp_segments,
      &hf_atp_segment,
      &hf_atp_segment_overlap,
      &hf_atp_segment_overlap_conflict,
      &hf_atp_segment_multiple_tails,
      &hf_atp_segment_too_long_segment,
      &hf_atp_segment_error,
      &hf_atp_reassembled_in,
      "segments"
};

/* -------------------------------- */

#define PAPOpenConn       1
#define PAPOpenConnReply  2
#define PAPSendData       3
#define PAPData           4
#define PAPTickle         5
#define PAPCloseConn      6
#define PAPCloseConnReply 7
#define PAPSendStatus     8
#define PAPStatus         9

static int proto_pap = -1;

static int hf_pap_connid   = -1;
static int hf_pap_function = -1;
static int hf_pap_socket   = -1;
static int hf_pap_quantum  = -1;
static int hf_pap_waittime = -1;
static int hf_pap_result   = -1;
static int hf_pap_status   = -1;
static int hf_pap_seq      = -1;
static int hf_pap_eof      = -1;

static int hf_pap_pad = -1;

static const value_string pap_function_vals[] = {
  {PAPOpenConn       , "Open Connection Query"},
  {PAPOpenConnReply  , "Open Connection Reply"},
  {PAPSendData       , "Send Data"},
  {PAPData           , "Data"},
  {PAPTickle         , "Tickle"},
  {PAPCloseConn      , "Close Connection Query"},
  {PAPCloseConnReply , "Close Connection reply"},
  {PAPSendStatus     , "Send Status"},
  {PAPStatus         , "Status"},

  {0, NULL}
};

/* -------------------------------- */

static dissector_table_t ddp_dissector_table;

static dissector_handle_t data_handle;

#define DDP_SHORT_HEADER_SIZE 5

/*
 * P = Padding, H = Hops, L = Len
 *
 * PPHHHHLL LLLLLLLL
 *
 * Assumes the argument is in host byte order.
 */
#define ddp_hops(x)     ( ( x >> 10) & 0x3C )
#define ddp_len(x)            ( x & 0x03ff )
typedef struct _e_ddp {
  guint16   hops_len; /* combines pad, hops, and len */
  guint16   sum,dnet,snet;
  guint8    dnode,snode;
  guint8    dport,sport;
  guint8    type;
} e_ddp;

#define DDP_HEADER_SIZE 13


static const value_string op_vals[] = {
  {DDP_RTMPDATA, "AppleTalk Routing Table response or data" },
  {DDP_NBP, "AppleTalk Name Binding Protocol packet"},
  {DDP_ATP, "AppleTalk Transaction Protocol packet"},
  {DDP_AEP, "AppleTalk Echo Protocol packet"},
  {DDP_RTMPREQ, "AppleTalk Routing Table request"},
  {DDP_ZIP, "AppleTalk Zone Information Protocol packet"},
  {DDP_ADSP, "AppleTalk Data Stream Protocol"},
  {DDP_EIGRP, "Cisco EIGRP for AppleTalk"},
  {0, NULL}
};

static const value_string rtmp_function_vals[] = {
  {1, "Request"},
  {2, "Route Data Request (split horizon processed)"},
  {3, "Route Data Request (no split horizon processing)"},
  {0, NULL}
};

#define NBP_BROADCAST 1
#define NBP_LOOKUP 2
#define NBP_FORWARD 4
#define NBP_REPLY 3

static const value_string nbp_op_vals[] = {
  {NBP_BROADCAST, "broadcast request"},
  {NBP_LOOKUP, "lookup"},
  {NBP_FORWARD, "forward request"},
  {NBP_REPLY, "reply"},
  {0, NULL}
};

static const value_string atp_function_vals[] = {
  {ATP_TREQ        ,"REQuest"},
  {ATP_TRESP       ,"RESPonse"},
  {ATP_TREL        ,"RELease"},
  {0, NULL}
};

static const value_string atp_trel_timer_vals[] = {
  {0, "30 seconds"},
  {1, "1 minute"},
  {2, "2 minutes"},
  {3, "4 minutes"},
  {4, "8 minutes"},
  {0, NULL}
};

/*
*/
static const value_string asp_func_vals[] = {
  {ASPFUNC_CLOSE, "CloseSession" },
  {ASPFUNC_CMD,         "Command" },
  {ASPFUNC_STAT,  "GetStatus" },
  {ASPFUNC_OPEN,  "OpenSession" },
  {ASPFUNC_TICKLE,      "Tickle" },
  {ASPFUNC_WRITE, "Write" },
  {ASPFUNC_WRTCONT,     "Write Cont" },
  {ASPFUNC_ATTN,  "Attention" },
  {0,             NULL } };

const value_string asp_error_vals[] = {
  {AFP_OK         , "success"},
  {AFPERR_ACCESS  , "permission denied" },
  {AFPERR_AUTHCONT      , "logincont" },
  {AFPERR_BADUAM  , "uam doesn't exist" },
  {AFPERR_BADVERS , "bad afp version number" },
  {AFPERR_BITMAP  , "invalid bitmap" },
  {AFPERR_CANTMOVE      , "can't move file" },
  {AFPERR_DENYCONF      , "file synchronization locks conflict" },
  {AFPERR_DIRNEMPT      , "directory not empty" },
  {AFPERR_DFULL         , "disk full" },
  {AFPERR_EOF           , "end of file" },
  {AFPERR_BUSY          , "FileBusy" },
  {AFPERR_FLATVOL       , "volume doesn't support directories" },
  {AFPERR_NOITEM  , "ItemNotFound" },
  {AFPERR_LOCK          , "LockErr" },
  {AFPERR_MISC          , "misc. err" },
  {AFPERR_NLOCK         , "no more locks" },
  {AFPERR_NOSRVR  , "no response by server at that address" },
  {AFPERR_EXIST         , "object already exists" },
  {AFPERR_NOOBJ         , "object not found" },
  {AFPERR_PARAM         , "parameter error" },
  {AFPERR_NORANGE       , "no range lock" },
  {AFPERR_RANGEOVR      , "range overlap" },
  {AFPERR_SESSCLOS      , "session closed" },
  {AFPERR_NOTAUTH , "user not authenticated" },
  {AFPERR_NOOP          , "command not supported" },
  {AFPERR_BADTYPE , "object is the wrong type" },
  {AFPERR_NFILE         , "too many files open" },
  {AFPERR_SHUTDOWN      , "server is going down" },
  {AFPERR_NORENAME      , "can't rename" },
  {AFPERR_NODIR         , "couldn't find directory" },
  {AFPERR_ITYPE         , "wrong icon type" },
  {AFPERR_VLOCK         , "volume locked" },
  {AFPERR_OLOCK         , "object locked" },
  {AFPERR_CTNSHRD       , "share point contains a share point" },
  {AFPERR_NOID          , "file thread not found" },
  {AFPERR_EXISTID       , "file already has an id" },
  {AFPERR_DIFFVOL       , "different volume" },
  {AFPERR_CATCHNG       , "catalog has changed" },
  {AFPERR_SAMEOBJ       , "source file == destination file" },
  {AFPERR_BADID         , "non-existent file id" },
  {AFPERR_PWDSAME       , "same password/can't change password" },
  {AFPERR_PWDSHORT      , "password too short" },
  {AFPERR_PWDEXPR       , "password expired" },
  {AFPERR_INSHRD        , "folder being shared is inside a shared folder." },
  {AFPERR_INTRASH   , "shared folder in trash." },
  {AFPERR_PWDCHNG   , "password needs to be changed" },
  {AFPERR_PWDPOLCY  , "password fails policy check" },
  {AFPERR_USRLOGIN  , "user already logged on" },
  {0,             NULL } };

/*
 * XXX - do this with an FT_UINT_STRING?
 * Unfortunately, you can't extract from an FT_UINT_STRING the string,
 * which we'd want to do in order to put it into the "Data:" portion.
 *
 * Are these always in the Mac extended character set?
 */
static int dissect_pascal_string(tvbuff_t *tvb, int offset, proto_tree *tree,
      int hf_index)
{
      int len;
      char *tmp;

      len = tvb_get_guint8(tvb, offset);
      offset++;

      if ( tree )
      {
            proto_tree *item;
            proto_tree *subtree;

            /*
             * XXX - if we could do this inside the protocol tree
             * code, we could perhaps avoid allocating and freeing
             * this string buffer.
             */
            tmp = (gchar*)tvb_get_ephemeral_string(tvb, offset, len);
            item = proto_tree_add_string(tree, hf_index, tvb, offset-1, len+1, tmp);

            subtree = proto_item_add_subtree(item, ett_pstring);
            proto_tree_add_text(subtree, tvb, offset-1, 1, "Length: %d", len);
            proto_tree_add_text(subtree, tvb, offset, len, "Data: %s", tmp);

      }
      offset += len;

      return offset;
}

static void
dissect_rtmp_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  proto_tree *rtmp_tree;
  proto_item *ti;
  guint8 function;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  function = tvb_get_guint8(tvb, 0);

  if (check_col(pinfo->cinfo, COL_INFO))
    col_add_str(pinfo->cinfo, COL_INFO,
      val_to_str(function, rtmp_function_vals, "Unknown function (%02x)"));

  if (tree) {
    ti = proto_tree_add_item(tree, proto_rtmp, tvb, 0, 1, FALSE);
    rtmp_tree = proto_item_add_subtree(ti, ett_rtmp);

    proto_tree_add_uint(rtmp_tree, hf_rtmp_function, tvb, 0, 1, function);
  }
}

static void
dissect_rtmp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  proto_tree *rtmp_tree;
  proto_item *ti;
  int offset = 0;
  guint16 net;
  guint8 nodelen,nodelen_bits;
  guint16 node; /* might be more than 8 bits */
  int i;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  net = tvb_get_ntohs(tvb, offset);
  nodelen_bits = tvb_get_guint8(tvb, offset+2);
  if ( nodelen_bits <= 8 ) {
    node = tvb_get_guint8(tvb, offset)+1;
    nodelen = 1;
  } else {
    node = tvb_get_ntohs(tvb, offset);
    nodelen = 2;
  }

  if (check_col(pinfo->cinfo, COL_INFO))
    col_add_fstr(pinfo->cinfo, COL_INFO, "Net: %u  Node Len: %u  Node: %u",
            net, nodelen_bits, node);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_rtmp, tvb, offset, -1, FALSE);
    rtmp_tree = proto_item_add_subtree(ti, ett_rtmp);

    proto_tree_add_uint(rtmp_tree, hf_rtmp_net, tvb, offset, 2, net);
    proto_tree_add_uint(rtmp_tree, hf_rtmp_node_len, tvb, offset+2, 1,
                  nodelen_bits);
    proto_tree_add_uint(rtmp_tree, hf_rtmp_node, tvb, offset+3, nodelen,
                  node);
    offset += 3 + nodelen;

    i = 1;
    while (tvb_offset_exists(tvb, offset)) {
      proto_tree *tuple_item, *tuple_tree;
      guint16 tuple_net;
      guint8 tuple_dist;
      guint16 tuple_range_end;

      tuple_net = tvb_get_ntohs(tvb, offset);
      tuple_dist = tvb_get_guint8(tvb, offset+2);

      if (tuple_dist & 0x80) {
        tuple_range_end = tvb_get_ntohs(tvb, offset+3);
        tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 6,
                  "Tuple %d:  Range Start: %u  Dist: %u  Range End: %u",
                  i, tuple_net, tuple_dist&0x7F, tuple_range_end);
      } else {
        tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 3,
                  "Tuple %d:  Net: %u  Dist: %u",
                  i, tuple_net, tuple_dist);
      }
      tuple_tree = proto_item_add_subtree(tuple_item, ett_rtmp_tuple);

      if (tuple_dist & 0x80) {
        proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_range_start, tvb, offset, 2,
                  tuple_net);
      } else {
        proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_net, tvb, offset, 2,
                  tuple_net);
      }
      proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_dist, tvb, offset+2, 1,
                  tuple_dist & 0x7F);

      if (tuple_dist & 0x80) {
        /*
         * Extended network tuple.
         */
        proto_tree_add_item(tuple_tree, hf_rtmp_tuple_range_end, tvb, offset+3, 2,
                        FALSE);
      offset += 6;
      } else
        offset += 3;

      i++;
    }
  }
}

static void
dissect_nbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  proto_tree *nbp_tree;
  proto_tree *nbp_info_tree;
  proto_item *ti, *info_item;
  int offset = 0;
  guint8 info;
  guint op, count;
  guint i;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  info = tvb_get_guint8(tvb, offset);
  op = info >> 4;
  count = info & 0x0F;

  if (check_col(pinfo->cinfo, COL_INFO))
    col_add_fstr(pinfo->cinfo, COL_INFO, "Op: %s  Count: %u",
      val_to_str(op, nbp_op_vals, "Unknown (0x%01x)"), count);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_nbp, tvb, offset, -1, FALSE);
    nbp_tree = proto_item_add_subtree(ti, ett_nbp);

    info_item = proto_tree_add_uint_format(nbp_tree, hf_nbp_info, tvb, offset, 1,
            info,
            "Info: 0x%01X  Operation: %s  Count: %u", info,
            val_to_str(op, nbp_op_vals, "Unknown (0x%01X)"),
            count);
    nbp_info_tree = proto_item_add_subtree(info_item, ett_nbp_info);
    proto_tree_add_uint(nbp_info_tree, hf_nbp_op, tvb, offset, 1, info);
    proto_tree_add_uint(nbp_info_tree, hf_nbp_count, tvb, offset, 1, info);
    proto_tree_add_item(nbp_tree, hf_nbp_tid, tvb, offset+1, 1, FALSE);
    offset += 2;

    for (i=0; i<count; i++) {
      proto_tree *node_item,*node_tree;
      int soffset = offset;

      node_item = proto_tree_add_text(nbp_tree, tvb, offset, -1,
                  "Node %u", i+1);
      node_tree = proto_item_add_subtree(node_item, ett_nbp_node);

      proto_tree_add_item(node_tree, hf_nbp_node_net, tvb, offset, 2, FALSE);
      offset += 2;
      proto_tree_add_item(node_tree, hf_nbp_node_node, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(node_tree, hf_nbp_node_port, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(node_tree, hf_nbp_node_enum, tvb, offset, 1, FALSE);
      offset++;

      offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_object);
      offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_type);
      offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_zone);

      proto_item_set_len(node_item, offset-soffset);
    }
  }

  return;
}

/* -----------------------------
   ATP protocol cf. inside appletalk chap. 9
   desegmentation from packet-ieee80211.c
*/
static void
dissect_atp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
  proto_tree *atp_tree = NULL;
  proto_item *ti;
  proto_tree *atp_info_tree;
  proto_item *info_item;
  int offset = 0;
  guint8 ctrlinfo;
  guint8 frag_number = 0;
  guint op;
  guint16 tid;
  guint8 query;
  struct aspinfo aspinfo;
  tvbuff_t   *new_tvb = NULL;
  gboolean save_fragmented;
  gboolean more_fragment = FALSE;
  int len;
  guint8 bitmap;
  guint8 nbe = 0;
  guint8 t = 0;
  conversation_t  *conversation;
  asp_request_val *request_val = NULL;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATP");

  ctrlinfo = tvb_get_guint8(tvb, offset);
  bitmap   = tvb_get_guint8(tvb, offset +1);
  tid      = tvb_get_ntohs(tvb, offset +2);

  t = bitmap;
  while(t) {
      nbe++;
      t >>= 1;
  }

  op = ctrlinfo >> 6;

  aspinfo.reply   = (0x80 == (ctrlinfo & ATP_FUNCMASK))?1:0;
  aspinfo.release = (0xC0 == (ctrlinfo & ATP_FUNCMASK))?1:0;
  aspinfo.seq = tid;
  aspinfo.code = 0;
  query = (!aspinfo.reply && !aspinfo.release);

  conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
            pinfo->srcport, pinfo->destport, 0);

  if (conversation == NULL)
  {
      conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
                  pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
  }

  if (atp_defragment) {
      asp_request_key request_key;

      request_key.conversation = conversation->index;
      memcpy(request_key.src, (!aspinfo.reply)?pinfo->src.data:pinfo->dst.data, 4);
      request_key.seq = aspinfo.seq;

      request_val = (asp_request_val *) g_hash_table_lookup(atp_request_hash, &request_key);

      if (!request_val && query && nbe > 1)  {
            asp_request_key *new_request_key;

            /* only save nbe packets if more than 1 requested
               save some memory and help the defragmentation if tid wraparound, ie
               we have both a request for 1 packet and a request for n packets,
               hopefully most of the time ATP_EOM will be set in the last packet.
          */
            new_request_key = se_alloc(sizeof(asp_request_key));
            *new_request_key = request_key;

          request_val = se_alloc(sizeof(asp_request_val));
          request_val->value = nbe;

            g_hash_table_insert(atp_request_hash, new_request_key,request_val);
      }
  }

  /*
     ATP_EOM is not mandatory. Some implementations don't always set it
     if the query is only one packet.

     So it needs to keep the number of packets asked in request.
    */

  if (aspinfo.reply) {
     more_fragment = !(ATP_EOM & ctrlinfo) && request_val;
     frag_number = bitmap;
  }

  if (check_col(pinfo->cinfo, COL_INFO)) {
    col_clear(pinfo->cinfo, COL_INFO);
    col_add_fstr(pinfo->cinfo, COL_INFO, "%s transaction %u",
      val_to_str(op, atp_function_vals, "Unknown (0x%01x)"),tid);
    if (more_fragment)
      col_append_str(pinfo->cinfo, COL_INFO, " [fragment]");
  }

  if (tree) {
    ti = proto_tree_add_item(tree, proto_atp, tvb, offset, -1, FALSE);
    atp_tree = proto_item_add_subtree(ti, ett_atp);
    proto_item_set_len(atp_tree, aspinfo.release?8:ATP_HDRSIZE -1);

    info_item = proto_tree_add_item(atp_tree, hf_atp_ctrlinfo, tvb, offset, 1, FALSE);
    atp_info_tree = proto_item_add_subtree(info_item, ett_atp_info);

    proto_tree_add_item(atp_info_tree, hf_atp_function, tvb, offset, 1, FALSE);
    proto_tree_add_item(atp_info_tree, hf_atp_xo, tvb, offset, 1, FALSE);
    proto_tree_add_item(atp_info_tree, hf_atp_eom, tvb, offset, 1, FALSE);
    proto_tree_add_item(atp_info_tree, hf_atp_sts, tvb, offset, 1, FALSE);
    if ((ctrlinfo & (ATP_FUNCMASK|ATP_XO)) == (0x40|ATP_XO)) {
      /* TReq with XO set */
      proto_tree_add_item(atp_info_tree, hf_atp_treltimer, tvb, offset, 1, FALSE);
    }
    if (query) {
      proto_tree_add_text(atp_tree, tvb, offset +1, 1,
                    "Bitmap: 0x%02x  %u packet(s) max", bitmap, nbe);
    }
    else {
      proto_tree_add_item(atp_tree, hf_atp_bitmap, tvb, offset +1, 1, FALSE);
    }
    proto_tree_add_item(atp_tree, hf_atp_tid, tvb, offset +2, 2, FALSE);

    if (aspinfo.release)
      proto_tree_add_item(atp_tree, hf_atp_user_bytes, tvb, offset +4, 4, FALSE);

  }

  if (aspinfo.release)
      return;

  save_fragmented = pinfo->fragmented;

  /* FIXME
     asp doesn't fit very well here
     move asp back in atp?
  */
  if (atp_defragment && aspinfo.reply && (more_fragment || frag_number != 0)) {
     fragment_data *fd_head;
     int hdr;

     hdr = ATP_HDRSIZE -1;
     if (frag_number != 0)
      hdr += 4;   /* asp header */
     len = tvb_reported_length_remaining(tvb, hdr);
     fd_head = fragment_add_seq_check(tvb, hdr, pinfo, tid,
                             atp_fragment_table,
                             atp_reassembled_table,
                             frag_number,
                             len,
                             more_fragment);
     new_tvb = process_reassembled_data(tvb, ATP_HDRSIZE -1, pinfo,
                        "Reassembled ATP", fd_head, &atp_frag_items,
                        NULL, atp_tree);
  }
  else {
      /* full packet */
     new_tvb = tvb_new_subset(tvb, ATP_HDRSIZE -1, -1,- 1);
  }

  if (new_tvb) {
     pinfo->private_data = &aspinfo;
     /* if port == 6 it's not an ASP packet but a ZIP packet */
     if (pinfo->srcport == 6 || pinfo->destport == 6 )
      call_dissector(zip_atp_handle, new_tvb, pinfo, tree);
     else {
        /* XXX need a conversation_get_dissector function ? */
        if (!aspinfo.reply && !conversation->dissector_handle) {
            dissector_handle_t sub;

            /* if it's a known ASP function call ASP dissector
               else assume it's a PAP connection ID.
               the test is wrong because PAP conn IDs overlapped with ASP fn
               but I don't want to keep track of NBP msgs and open connection
               port allocation.
            */
            guint8 fn = tvb_get_guint8(new_tvb, 0);

            if (!fn || fn > ASPFUNC_ATTN) {
              sub = pap_handle;
            }
            else {
              sub = asp_handle;
          }
          call_dissector(sub, new_tvb, pinfo, tree);
          conversation_set_dissector(conversation, sub);
        }
        else if (!try_conversation_dissector(&pinfo->src, &pinfo->dst, pinfo->ptype,
                                          pinfo->srcport, pinfo->destport, new_tvb,pinfo, tree)) {
            call_dissector(data_handle, new_tvb, pinfo, tree);

        }
     }
  }
  else {
    /* Just show this as a fragment. */
    new_tvb = tvb_new_subset (tvb, ATP_HDRSIZE -1, -1, -1);
    call_dissector(data_handle, new_tvb, pinfo, tree);
  }
  pinfo->fragmented = save_fragmented;
  return;
}

/*
      copy and paste from dsi
      XXX - is the format of this reply dependent on the type of server,
      with this format being the format for AFP servers?
*/
static gint
dissect_asp_reply_get_status(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, gint offset)
{
        proto_tree      *sub_tree;
      proto_item  *ti;

      guint16 ofs;
      guint16 flag;
      guint16 machine_ofs;
      guint16 sign_ofs = 0;
      guint16 adr_ofs = 0;
      guint16 dir_ofs = 0;
      guint16 utf_ofs = 0;
      guint8      nbe;
      guint   len;
      guint   i;

      proto_tree *adr_tree;
      char *tmp;
      const guint8 *ip;
      guint16 net;
      guint8  node;
      guint16 port;

      guint16 ulen;

      if (!tree)
            return offset;

      ti = proto_tree_add_text(tree, tvb, offset, -1, "Get Status");
      tree = proto_item_add_subtree(ti, ett_asp_status);

      machine_ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_MACHOFF);
      proto_tree_add_text(tree, tvb, offset +AFPSTATUS_MACHOFF, 2, "Machine offset: %u", machine_ofs);
      if (machine_ofs)
            machine_ofs += offset;

      ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF);
      proto_tree_add_text(tree, tvb, offset +AFPSTATUS_VERSOFF, 2, "Version offset: %u", ofs);

      ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF);
      proto_tree_add_text(tree, tvb, offset +AFPSTATUS_UAMSOFF, 2, "UAMS offset: %u", ofs);

      ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF);
      proto_tree_add_text(tree, tvb, offset +AFPSTATUS_ICONOFF, 2, "Icon offset: %u", ofs);

      ofs = offset +AFPSTATUS_FLAGOFF;
      ti = proto_tree_add_item(tree, hf_asp_server_flag, tvb, ofs, 2, FALSE);
      sub_tree = proto_item_add_subtree(ti, ett_asp_status_server_flag);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_copyfile      , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_passwd        , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_no_save_passwd, tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_msg       , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_sig       , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_tcpip         , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_notify        , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_reconnect     , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_directory     , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_utf8_name     , tvb, ofs, 2, FALSE);
      proto_tree_add_item(sub_tree, hf_asp_server_flag_fast_copy     , tvb, ofs, 2, FALSE);

      proto_tree_add_item(tree, hf_asp_server_name, tvb, offset +AFPSTATUS_PRELEN, 1, FALSE);

      flag = tvb_get_ntohs(tvb, ofs);
      if ((flag & AFPSRVRINFO_SRVSIGNATURE)) {
            ofs = offset +AFPSTATUS_PRELEN +tvb_get_guint8(tvb, offset +AFPSTATUS_PRELEN) +1;
            if ((ofs & 1))
                  ofs++;

            sign_ofs = tvb_get_ntohs(tvb, ofs);
            proto_tree_add_text(tree, tvb, ofs, 2, "Signature offset: %u", sign_ofs);
            sign_ofs += offset;
            ofs += 2;

            if ((flag & AFPSRVRINFO_TCPIP) && ofs < machine_ofs ) {
                  adr_ofs =  tvb_get_ntohs(tvb, ofs);
                  proto_tree_add_text(tree, tvb, ofs, 2, "Network address offset: %u", adr_ofs);
                  adr_ofs += offset;
                  ofs += 2;
            }

            if ((flag & AFPSRVRINFO_SRVDIRECTORY) && ofs < machine_ofs) {
                  dir_ofs =  tvb_get_ntohs(tvb, ofs);
                  proto_tree_add_text(tree, tvb, ofs, 2, "Directory services offset: %u", dir_ofs);
                  dir_ofs += offset;
                  ofs += 2;
            }

            if ((flag & AFPSRVRINFO_SRVUTF8) && ofs < machine_ofs) {
                  utf_ofs =  tvb_get_ntohs(tvb, ofs);
                  proto_tree_add_text(tree, tvb, ofs, 2, "UTF-8 Server name offset: %u", utf_ofs);
                  utf_ofs += offset;
            }
      }

      if (machine_ofs)
            proto_tree_add_item(tree, hf_asp_server_type, tvb, machine_ofs, 1, FALSE);

      ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF);
      if (ofs) {
            nbe = tvb_get_guint8(tvb, ofs);
            ti = proto_tree_add_text(tree, tvb, ofs, 1, "Version list: %u", nbe);
            ofs++;
            sub_tree = proto_item_add_subtree(ti, ett_asp_vers);
            for (i = 0; i < nbe; i++) {
                  len = tvb_get_guint8(tvb, ofs);
                  proto_tree_add_item(sub_tree, hf_asp_server_vers, tvb, ofs, 1, FALSE);
                  ofs += len + 1;
            }
      }

      ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF);
      if (ofs) {
            nbe = tvb_get_guint8(tvb, ofs);
            ti = proto_tree_add_text(tree, tvb, ofs, 1, "UAMS list: %u", nbe);
            ofs++;
            sub_tree = proto_item_add_subtree(ti, ett_asp_uams);
            for (i = 0; i < nbe; i++) {
                  len = tvb_get_guint8(tvb, ofs);
                  proto_tree_add_item(sub_tree, hf_asp_server_uams, tvb, ofs, 1, FALSE);
                  ofs += len + 1;
            }
      }

      ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF);
      if (ofs)
            proto_tree_add_item(tree, hf_asp_server_icon, tvb, ofs, 256, FALSE);

      if (sign_ofs) {
            proto_tree_add_item(tree, hf_asp_server_signature, tvb, sign_ofs, 16, FALSE);
      }

      if (adr_ofs) {

            ofs = adr_ofs;
            nbe = tvb_get_guint8(tvb, ofs);
            ti = proto_tree_add_text(tree, tvb, ofs, 1, "Address list: %u", nbe);
            ofs++;
            adr_tree = proto_item_add_subtree(ti, ett_asp_addr);
            for (i = 0; i < nbe; i++) {
                  guint8 type;

                  len = tvb_get_guint8(tvb, ofs);
                  type =  tvb_get_guint8(tvb, ofs +1);
                  switch (type) {
                  case 1:     /* IP */
                        ip = tvb_get_ptr(tvb, ofs+2, 4);
                        ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s", ip_to_str(ip));
                        break;
                  case 2: /* IP + port */
                        ip = tvb_get_ptr(tvb, ofs+2, 4);
                        port = tvb_get_ntohs(tvb, ofs+6);
                        ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s:%u",ip_to_str(ip),port);
                        break;
                  case 3: /* DDP, atalk_addr_to_str want host order not network */
                        net  = tvb_get_ntohs(tvb, ofs+2);
                        node = tvb_get_guint8(tvb, ofs +4);
                        port = tvb_get_guint8(tvb, ofs +5);
                        ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ddp %u.%u:%u",
                              net, node, port);
                        break;
                  case 5: /* IP + port ssh tunnel */
                        ip = tvb_get_ptr(tvb, ofs+2, 4);
                        port = tvb_get_ntohs(tvb, ofs+6);
                        ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip (ssh tunnel) %s:%u",ip_to_str(ip),port);
                        break;
                  case 4: /* DNS */
                        if (len > 2) {
                              tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs +2, len -2);
                              ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "dns %s", tmp);
                              break;
                        }
                        /* else fall to default malformed record */
                  default:
                        ti = proto_tree_add_text(adr_tree, tvb, ofs, len,"Unknow type : %u", type);
                        break;
                  }
                  len -= 2;
                  sub_tree = proto_item_add_subtree(ti,ett_asp_addr_line);
                  proto_tree_add_item(sub_tree, hf_asp_server_addr_len, tvb, ofs, 1, FALSE);
                  ofs++;
                  proto_tree_add_item(sub_tree, hf_asp_server_addr_type, tvb, ofs, 1, FALSE);
                  ofs++;
                  proto_tree_add_item(sub_tree, hf_asp_server_addr_value,tvb, ofs, len, FALSE);
                  ofs += len;
            }
      }

      if (dir_ofs) {
            ofs = dir_ofs;
            nbe = tvb_get_guint8(tvb, ofs);
            ti = proto_tree_add_text(tree, tvb, ofs, 1, "Directory services list: %u", nbe);
            ofs++;
            sub_tree = proto_item_add_subtree(ti, ett_asp_directory);
            for (i = 0; i < nbe; i++) {
                  len = tvb_get_guint8(tvb, ofs);
                  proto_tree_add_item(sub_tree, hf_asp_server_directory, tvb, ofs, 1, FALSE);
                  ofs += len + 1;
            }
      }
      if (utf_ofs) {

            ofs = utf_ofs;
            ulen = tvb_get_ntohs(tvb, ofs);
            tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs + 2, ulen);
            ti = proto_tree_add_text(tree, tvb, ofs, ulen +2, "UTF8 server name: %s", tmp);
            sub_tree = proto_item_add_subtree(ti, ett_asp_utf8_name);
            proto_tree_add_uint(sub_tree, hf_asp_server_utf8_name_len, tvb, ofs, 2, ulen);
            ofs += 2;
            proto_tree_add_string(sub_tree, hf_asp_server_utf8_name, tvb, ofs, ulen, tmp);
            ofs += ulen;
      }
      /* FIXME: offset is not updated */
      return offset;
}

/* -----------------------------
   PAP protocol cf. inside appletalk chap. 10
*/
#define PAD(x)      { proto_tree_add_item(pap_tree, hf_pap_pad, tvb, offset,  x, FALSE); offset += x; }

static void
dissect_pap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  int offset = 0;
  guint8 fn;
  guint8 connID;
  proto_tree *pap_tree = NULL;
  proto_item *ti;
  int len;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "PAP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_pap, tvb, offset, -1, FALSE);
    pap_tree = proto_item_add_subtree(ti, ett_pap);
  }

  connID = tvb_get_guint8(tvb, offset);
  proto_tree_add_item(pap_tree, hf_pap_connid, tvb, offset, 1, FALSE);
  offset++;

  fn = tvb_get_guint8(tvb, offset);
  proto_tree_add_item(pap_tree, hf_pap_function, tvb, offset, 1, FALSE);
  offset++;

  if (check_col(pinfo->cinfo, COL_INFO)) {
       col_add_fstr(pinfo->cinfo, COL_INFO, "%s  ID: %d",
                              val_to_str(fn, pap_function_vals, "Unknown (0x%01x)"), connID);
  }
  switch(fn) {
  case PAPOpenConn:
      PAD(2);
      proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(pap_tree, hf_pap_waittime, tvb, offset, 2, FALSE);
      offset += 2;
      break;

  case PAPOpenConnReply:
      PAD(2);
      proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(pap_tree, hf_pap_result, tvb, offset, 2, FALSE);
      offset += 2;
    offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status);
      break;

  case PAPSendData:
      proto_tree_add_item(pap_tree, hf_pap_seq, tvb, offset, 2, FALSE);
      offset += 2;
      break;

  case PAPData:
      proto_tree_add_item(pap_tree, hf_pap_eof, tvb, offset, 1, FALSE);
      offset++;
      PAD(1);
      /* follow by data */
      len = tvb_reported_length_remaining(tvb,offset);
      call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree);
      break;

  case PAPTickle:
  case PAPCloseConn:
  case PAPCloseConnReply:
      PAD(2);
      break;

  case PAPSendStatus:
      PAD(2);
      break;

  case PAPStatus:
      PAD(2);
      PAD(4);
    offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status);
      break;

  default:  /* unknown */
    break;
  }
}

/* -----------------------------
   ASP protocol cf. inside appletalk chap. 11
*/
static struct aspinfo *
get_transaction(tvbuff_t *tvb, packet_info *pinfo)
{
  struct aspinfo *aspinfo = pinfo->private_data;
  conversation_t  *conversation;
  asp_request_key request_key, *new_request_key;
  asp_request_val *request_val;
  guint8 fn;

  conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
            pinfo->srcport, pinfo->destport, 0);

  if (conversation == NULL)
  {
      conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
                  pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
  }

  request_key.conversation = conversation->index;
  memcpy(request_key.src, (!aspinfo->reply)?pinfo->src.data:pinfo->dst.data, 4);
  request_key.seq = aspinfo->seq;

  request_val = (asp_request_val *) g_hash_table_lookup(
                                                asp_request_hash, &request_key);
  if (!request_val && !aspinfo->reply )  {
       fn = tvb_get_guint8(tvb, 0);
       new_request_key = se_alloc(sizeof(asp_request_key));
       *new_request_key = request_key;

       request_val = se_alloc(sizeof(asp_request_val));
       request_val->value = fn;

       g_hash_table_insert(asp_request_hash, new_request_key,
                                                request_val);
  }

  if (!request_val)
      return NULL;

  aspinfo->command = request_val->value;
  return aspinfo;
}


static void
dissect_asp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  struct aspinfo *aspinfo;
  int offset = 0;
  proto_tree *asp_tree = NULL;
  proto_item *ti;
  guint8 fn;
  int len;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ASP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  aspinfo = get_transaction(tvb, pinfo);
  if (!aspinfo)
     return;

  fn = (guint8) aspinfo->command;

  if (check_col(pinfo->cinfo, COL_INFO)) {
      if (aspinfo->reply)
            col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq);
      else
            col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s  tid %u",
                              val_to_str(fn, asp_func_vals, "Unknown (0x%01x)"), aspinfo->seq);
  }

  if (tree) {
    ti = proto_tree_add_item(tree, proto_asp, tvb, offset, -1, FALSE);
    asp_tree = proto_item_add_subtree(ti, ett_asp);
  }
  if (!aspinfo->reply) {
      tvbuff_t   *new_tvb;
      /* let the called deal with asp_tree == NULL */

            proto_tree_add_item(asp_tree, hf_asp_func, tvb, offset, 1, FALSE);
      offset++;
      switch(fn) {
      case ASPFUNC_OPEN:
            proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_version, tvb, offset, 2, FALSE);
            offset += 2;
            break;
      case ASPFUNC_TICKLE:
      case ASPFUNC_CLOSE:
            proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE);
            offset +=2;
            break;
      case ASPFUNC_STAT:
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE);
            offset += 2;
            break;
      case ASPFUNC_ATTN:
            proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_attn_code, tvb, offset, 2, FALSE);
            offset +=2;
            break;
      case ASPFUNC_CMD:
      case ASPFUNC_WRITE:
            proto_item_set_len(asp_tree, 4);
            proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE);
            offset += 2;
            len = tvb_reported_length_remaining(tvb,offset);
            new_tvb = tvb_new_subset(tvb, offset,-1,len);
            call_dissector(afp_handle, new_tvb, pinfo, tree);
            break;
      case ASPFUNC_WRTCONT:
            proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE);
            offset += 2;
            proto_tree_add_item(asp_tree, hf_asp_size, tvb, offset, 2, FALSE);
            offset += 2;
            break;
      default:
            proto_item_set_len(asp_tree, 4);
            offset += 3;
            len = tvb_reported_length_remaining(tvb,offset);
            call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree);
            break;
      }
  }
  else {
      tvbuff_t   *new_tvb;

      proto_tree_add_uint(asp_tree, hf_asp_func, tvb, 0, 0, fn);
      switch(fn) {
      case ASPFUNC_OPEN:
            proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_init_error, tvb, offset, 2, FALSE);
            offset += 2;
            break;
      case ASPFUNC_CLOSE:
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE);
            offset++;
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE);
            offset += 2;
            break;
      case ASPFUNC_STAT:
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE);
            offset += 4;
            dissect_asp_reply_get_status(tvb, pinfo, asp_tree, offset);
            break;
      case ASPFUNC_CMD:
      case ASPFUNC_WRITE:
            proto_item_set_len(asp_tree, 4);
            aspinfo->code = tvb_get_ntohl(tvb, offset);
            proto_tree_add_item(asp_tree, hf_asp_error, tvb, offset, 4, FALSE);
            offset += 4;
            len = tvb_reported_length_remaining(tvb,offset);
            new_tvb = tvb_new_subset(tvb, offset,-1,len);
            call_dissector(afp_handle, new_tvb, pinfo, tree);
            break;
      case ASPFUNC_TICKLE:
      case ASPFUNC_WRTCONT:
            proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE);
            /* fall */
      case ASPFUNC_ATTN:      /* FIXME capture and spec disagree */
      default:
            proto_item_set_len(asp_tree, 4);
            offset += 4;
            len = tvb_reported_length_remaining(tvb,offset);
            call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree);
            break;
      }
  }
}

/* -----------------------------
   ZIP protocol cf. inside appletalk chap. 8
*/
static void
dissect_atp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  struct aspinfo *aspinfo;
  int offset = 0;
  proto_tree *zip_tree;
  proto_tree *sub_tree;
  proto_item *ti;
  guint8 fn;
  guint16 count;
  guint8 len;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  aspinfo = get_transaction(tvb, pinfo);
  if (!aspinfo)
     return;

  fn = (guint8) aspinfo->command;

  if (check_col(pinfo->cinfo, COL_INFO)) {
      if (aspinfo->reply)
            col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq);
      else
            col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s  tid %u",
                              val_to_str(fn, zip_atp_function_vals, "Unknown (0x%01x)"), aspinfo->seq);
  }

  if (!tree)
     return;

  ti = proto_tree_add_item(tree, proto_zip, tvb, offset, -1, FALSE);
  zip_tree = proto_item_add_subtree(ti, ett_zip);

  if (!aspinfo->reply) {
     proto_tree_add_item(zip_tree, hf_zip_atp_function, tvb, offset, 1, FALSE);
     offset++;
     switch(fn) {
     case 7:      /* start_index = 0 */
     case 8:
     case 9:
         proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE);
         offset++;
         proto_tree_add_item(zip_tree, hf_zip_start_index, tvb, offset, 2, FALSE);
         break;
     }
  }
  else {
  guint i;

     proto_tree_add_uint(zip_tree, hf_zip_atp_function, tvb, 0, 0, fn);
     switch(fn) {
     case 7:
     case 8:
     case 9:
         proto_tree_add_item(zip_tree, hf_zip_last_flag, tvb, offset, 1, FALSE);
         offset++;

         proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE);
         offset++;
       count = tvb_get_ntohs(tvb, offset);
         ti = proto_tree_add_item(zip_tree, hf_zip_count, tvb, offset, 2, FALSE);
         offset += 2;
             sub_tree = proto_item_add_subtree(ti, ett_zip_zones_list);
       for (i= 0; i < count; i++) {
           len = tvb_get_guint8(tvb, offset);
             proto_tree_add_item(sub_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
           offset += len +1;
       }
         break;
     }
  }
}

static void
dissect_ddp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  proto_tree *zip_tree = NULL;
  proto_item *ti;
  guint8  fn;
  guint8  len;
  gint    offset = 0;
  proto_tree *flag_tree;
  proto_tree *sub_tree;
  proto_tree *net_tree;
  guint8 flag;
  guint16  net;
  guint i;
  guint count;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  fn = tvb_get_guint8(tvb, 0);
  if (check_col(pinfo->cinfo, COL_INFO)) {
    col_add_str(pinfo->cinfo, COL_INFO,
      val_to_str(fn, zip_function_vals, "Unknown ZIP function (%02x)"));
  }

  if (!tree)
     return;

  ti = proto_tree_add_item(tree, proto_zip, tvb, 0, -1, FALSE);
  zip_tree = proto_item_add_subtree(ti, ett_zip);

  proto_tree_add_item(zip_tree, hf_zip_function, tvb, offset, 1,FALSE);
  offset++;
  /* fn 1,7,2,8 are not tested */
  switch (fn) {
  case 1: /* Query */
      count = tvb_get_guint8(tvb, offset);
      ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE);
      offset++;
      sub_tree = proto_item_add_subtree(ti, ett_zip_network_list);
      for (i= 0; i < count; i++) {
          proto_tree_add_item(sub_tree, hf_zip_network, tvb, offset, 2, FALSE);
          offset += 2;
      }
      break;
  case 7: /* Notify */
      flag = tvb_get_guint8(tvb, offset);
      ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag);
      flag_tree = proto_item_add_subtree(ti, ett_zip_flags);
      proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE);
      proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE);
      proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE);
      offset++;

      proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE);
      offset += 4;

      len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
      offset += len +1;

      len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE);
      offset++;
      proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE);
      offset += len;

      proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
      break;

  case 2: /* Reply */
  case 8: /* Extended Reply */
      count = tvb_get_guint8(tvb, offset);
      ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE);
      offset++;
      sub_tree = proto_item_add_subtree(ti, ett_zip_network_list);
      for (i= 0; i < count; i++) {
          net = tvb_get_ntohs(tvb, offset);
          ti = proto_tree_add_text(zip_tree, tvb, offset , 2, "Zone for network : %u", net);
          net_tree = proto_item_add_subtree(ti, ett_zip_network_list);
          proto_tree_add_item(net_tree, hf_zip_network, tvb, offset, 2, FALSE);
          offset += 2;
          len = tvb_get_guint8(tvb, offset);
          proto_tree_add_item(net_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
          offset += len +1;
      }
      break;

  case 5 :  /* GetNetInfo request */
      proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE);
      offset++;
      proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE);
      offset += 4;
      proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
      break;

  case 6 :  /* GetNetInfo reply */
      flag = tvb_get_guint8(tvb, offset);
      ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag);
      flag_tree = proto_item_add_subtree(ti, ett_zip_flags);
      proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE);
      proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE);
      proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE);
      offset++;

      proto_tree_add_item(zip_tree, hf_zip_network_start, tvb, offset, 2, FALSE);
      offset += 2;

      proto_tree_add_item(zip_tree, hf_zip_network_end, tvb, offset, 2, FALSE);
      offset += 2;

      len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE);
      offset += len +1;

      len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE);
      offset++;
      proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE);
      offset += len;
      if ((flag & 0x80) != 0)
         proto_tree_add_item(zip_tree, hf_zip_default_zone, tvb, offset, 1,FALSE);
      break;

  default:
      break;
  }
}

static void
dissect_ddp_short(tvbuff_t *tvb, packet_info *pinfo, guint8 dnode,
              guint8 snode, proto_tree *tree)
{
  guint16 len;
  guint8  dport;
  guint8  sport;
  guint8  type;
  proto_tree *ddp_tree = NULL;
  proto_item *ti;
  static struct atalk_ddp_addr src, dst;
  tvbuff_t   *new_tvb;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_SHORT_HEADER_SIZE,
                       FALSE);
    ddp_tree = proto_item_add_subtree(ti, ett_ddp);
  }
  len = tvb_get_ntohs(tvb, 0);
  if (tree)
      proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, len);
  dport = tvb_get_guint8(tvb, 2);
  if (tree)
    proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 2, 1, dport);
  sport = tvb_get_guint8(tvb, 3);
  if (tree)
    proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 3, 1, sport);
  type = tvb_get_guint8(tvb, 4);

  src.net = 0;
  src.node = snode;
  dst.net = 0;
  dst.node = dnode;
  SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src);
  SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src);
  SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
  SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst);

  pinfo->ptype = PT_DDP;
  pinfo->destport = dport;
  pinfo->srcport = sport;

  if (check_col(pinfo->cinfo, COL_INFO)) {
    col_add_str(pinfo->cinfo, COL_INFO,
      val_to_str(type, op_vals, "Unknown DDP protocol (%02x)"));
  }
  if (tree) {
    proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb,
                         4, 3, atalk_addr_to_str(&src));
    proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb,
                         6, 3, atalk_addr_to_str(&dst));

    proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 4, 1, type);
  }
  new_tvb = tvb_new_subset(tvb, DDP_SHORT_HEADER_SIZE, -1, -1);

  if (!dissector_try_port(ddp_dissector_table, type, new_tvb, pinfo, tree))
    call_dissector(data_handle,new_tvb, pinfo, tree);
}

static void
dissect_ddp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  e_ddp       ddp;
  proto_tree *ddp_tree;
  proto_item *ti;
  static struct atalk_ddp_addr src, dst;
  tvbuff_t   *new_tvb;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  tvb_memcpy(tvb, (guint8 *)&ddp, 0, sizeof(e_ddp));
  ddp.dnet=g_ntohs(ddp.dnet);
  ddp.snet=g_ntohs(ddp.snet);
  ddp.sum=g_ntohs(ddp.sum);
  ddp.hops_len=g_ntohs(ddp.hops_len);

  src.net = ddp.snet;
  src.node = ddp.snode;
  dst.net = ddp.dnet;
  dst.node = ddp.dnode;
  SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src);
  SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src);
  SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
  SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst);

  pinfo->ptype = PT_DDP;
  pinfo->destport = ddp.dport;
  pinfo->srcport = ddp.sport;

  if (check_col(pinfo->cinfo, COL_INFO))
    col_add_str(pinfo->cinfo, COL_INFO,
      val_to_str(ddp.type, op_vals, "Unknown DDP protocol (%02x)"));

  if (tree) {
    ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_HEADER_SIZE,
                       FALSE);
    ddp_tree = proto_item_add_subtree(ti, ett_ddp);

    proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb,
                         4, 3, atalk_addr_to_str(&src));
    proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb,
                         6, 3, atalk_addr_to_str(&dst));

    proto_tree_add_uint(ddp_tree, hf_ddp_hopcount,   tvb, 0, 1,
                  ddp_hops(ddp.hops_len));
    proto_tree_add_uint(ddp_tree, hf_ddp_len,        tvb, 0, 2,
                  ddp_len(ddp.hops_len));
    proto_tree_add_uint(ddp_tree, hf_ddp_checksum,   tvb, 2,  2,
                  ddp.sum);
    proto_tree_add_uint(ddp_tree, hf_ddp_dst_net,    tvb, 4,  2,
                  ddp.dnet);
    proto_tree_add_uint(ddp_tree, hf_ddp_src_net,    tvb, 6,  2,
                  ddp.snet);
    proto_tree_add_uint(ddp_tree, hf_ddp_dst_node,   tvb, 8,  1,
                  ddp.dnode);
    proto_tree_add_uint(ddp_tree, hf_ddp_src_node,   tvb, 9,  1,
                  ddp.snode);
    proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 10, 1,
                  ddp.dport);
    proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 11, 1,
                  ddp.sport);
    proto_tree_add_uint(ddp_tree, hf_ddp_type,       tvb, 12, 1,
                  ddp.type);
  }

  new_tvb = tvb_new_subset(tvb, DDP_HEADER_SIZE, -1, -1);

  if (!dissector_try_port(ddp_dissector_table, ddp.type, new_tvb, pinfo, tree))
    call_dissector(data_handle,new_tvb, pinfo, tree);
}

static const value_string llap_type_vals[] = {
  {0x01, "Short DDP"},
  {0x02, "DDP" },
  {0x81, "Enquiry"},
  {0x82, "Acknowledgement"},
  {0x84, "RTS"},
  {0x85, "CTS"},
  {0, NULL}
};

void
capture_llap(packet_counts *ld)
{
  ld->other++;
}

static void
dissect_llap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  guint8 dnode;
  guint8 snode;
  guint8 type;
  proto_tree *llap_tree = NULL;
  proto_item *ti;
  tvbuff_t   *new_tvb;

  if (check_col(pinfo->cinfo, COL_PROTOCOL))
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLAP");
  if (check_col(pinfo->cinfo, COL_INFO))
    col_clear(pinfo->cinfo, COL_INFO);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_llap, tvb, 0, 3, FALSE);
    llap_tree = proto_item_add_subtree(ti, ett_llap);
  }

  dnode = tvb_get_guint8(tvb, 0);
  if (tree)
    proto_tree_add_uint(llap_tree, hf_llap_dst, tvb, 0, 1, dnode);
  snode = tvb_get_guint8(tvb, 1);
  if (tree)
    proto_tree_add_uint(llap_tree, hf_llap_src, tvb, 1, 1, snode);
  type = tvb_get_guint8(tvb, 2);
  if (check_col(pinfo->cinfo, COL_INFO)) {
    col_add_str(pinfo->cinfo, COL_INFO,
      val_to_str(type, llap_type_vals, "Unknown LLAP type (%02x)"));
  }
  if (tree)
    proto_tree_add_uint(llap_tree, hf_llap_type, tvb, 2, 1, type);

  new_tvb = tvb_new_subset(tvb, 3, -1, -1);

  switch (type) {

  case 0x01:
    if (proto_is_protocol_enabled(find_protocol_by_id(proto_ddp))) {
      pinfo->current_proto = "DDP";
      dissect_ddp_short(new_tvb, pinfo, dnode, snode, tree);
      return;
    }

  case 0x02:
    if (call_dissector(ddp_handle, new_tvb, pinfo, tree))
      return;
  }
  call_dissector(data_handle,new_tvb, pinfo, tree);
}

static void
atp_init(void)
{
      /* fragment */
      fragment_table_init(&atp_fragment_table);
      reassembled_table_init(&atp_reassembled_table);
      /* bitmap */
      if (atp_request_hash)
            g_hash_table_destroy(atp_request_hash);

      atp_request_hash = g_hash_table_new(asp_hash, asp_equal);

}

static void
asp_reinit( void)
{

      if (asp_request_hash)
            g_hash_table_destroy(asp_request_hash);

      asp_request_hash = g_hash_table_new(asp_hash, asp_equal);

}

void
proto_register_atalk(void)
{
  static hf_register_info hf_llap[] = {
    { &hf_llap_dst,
      { "Destination Node",   "llap.dst", FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_llap_src,
      { "Source Node",        "llap.src", FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_llap_type,
      { "Type",               "llap.type",      FT_UINT8,  BASE_HEX, VALS(llap_type_vals), 0x0,
            "", HFILL }},
  };

  static hf_register_info hf_ddp[] = {
    { &hf_ddp_hopcount,
      { "Hop count",          "ddp.hopcount",   FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_len,
      { "Datagram length",    "ddp.len",  FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_checksum,
      { "Checksum",           "ddp.checksum",   FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_dst,
      { "Destination address",      "ddp.dst",  FT_STRING, BASE_NONE, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_dst_net,
      { "Destination Net",    "ddp.dst.net",    FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_src,
      { "Source address",     "ddp.src",  FT_STRING, BASE_NONE, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_src_net,
      { "Source Net",         "ddp.src.net",    FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_dst_node,
      { "Destination Node",   "ddp.dst.node",   FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_src_node,
      { "Source Node",        "ddp.src.node",   FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_dst_socket,
      { "Destination Socket", "ddp.dst_socket", FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_src_socket,
      { "Source Socket",            "ddp.src_socket", FT_UINT8,  BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_ddp_type,
      { "Protocol type",            "ddp.type", FT_UINT8,  BASE_DEC, VALS(op_vals), 0x0,
            "", HFILL }},
  };

  static hf_register_info hf_nbp[] = {
    { &hf_nbp_op,
      { "Operation",          "nbp.op",   FT_UINT8,  BASE_DEC,
            VALS(nbp_op_vals), 0xF0, "Operation", HFILL }},
    { &hf_nbp_info,
      { "Info",         "nbp.info", FT_UINT8,  BASE_HEX,
            NULL, 0x0, "Info", HFILL }},
    { &hf_nbp_count,
      { "Count",        "nbp.count",      FT_UINT8,  BASE_DEC,
            NULL, 0x0F, "Count", HFILL }},
    { &hf_nbp_node_net,
      { "Network",            "nbp.net",  FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Network", HFILL }},
    { &hf_nbp_node_node,
      { "Node",         "nbp.node", FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Node", HFILL }},
    { &hf_nbp_node_port,
      { "Port",         "nbp.port", FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Port", HFILL }},
    { &hf_nbp_node_enum,
      { "Enumerator",         "nbp.enum", FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Enumerator", HFILL }},
    { &hf_nbp_node_object,
      { "Object",       "nbp.object",     FT_STRING,  BASE_DEC,
            NULL, 0x0, "Object", HFILL }},
    { &hf_nbp_node_type,
      { "Type",         "nbp.type", FT_STRING,  BASE_DEC,
            NULL, 0x0, "Type", HFILL }},
    { &hf_nbp_node_zone,
      { "Zone",         "nbp.zone", FT_STRING,  BASE_DEC,
            NULL, 0x0, "Zone", HFILL }},
    { &hf_nbp_tid,
      { "Transaction ID",           "nbp.tid",  FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Transaction ID", HFILL }}
  };

  static hf_register_info hf_rtmp[] = {
    { &hf_rtmp_net,
      { "Net",          "rtmp.net", FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Net", HFILL }},
    { &hf_rtmp_node,
      { "Node",         "nbp.nodeid",     FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Node", HFILL }},
    { &hf_rtmp_node_len,
      { "Node Length",        "nbp.nodeid.length",    FT_UINT8,  BASE_DEC,
            NULL, 0x0, "Node Length", HFILL }},
    { &hf_rtmp_tuple_net,
      { "Net",          "rtmp.tuple.net", FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Net", HFILL }},
    { &hf_rtmp_tuple_range_start,
      { "Range Start",        "rtmp.tuple.range_start",     FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Range Start", HFILL }},
    { &hf_rtmp_tuple_range_end,
      { "Range End",          "rtmp.tuple.range_end", FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Range End", HFILL }},
    { &hf_rtmp_tuple_dist,
      { "Distance",           "rtmp.tuple.dist",      FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Distance", HFILL }},
    { &hf_rtmp_function,
      { "Function",           "rtmp.function",  FT_UINT8,  BASE_DEC,
            VALS(rtmp_function_vals), 0x0, "Request Function", HFILL }}
  };

  static hf_register_info hf_atp[] = {
    { &hf_atp_ctrlinfo,
      { "Control info",       "atp.ctrlinfo",   FT_UINT8,  BASE_HEX,
            NULL, 0, "control info", HFILL }},

    { &hf_atp_function,
      { "Function",           "atp.function",   FT_UINT8,  BASE_DEC,
            VALS(atp_function_vals), ATP_FUNCMASK, "function code", HFILL }},


    { &hf_atp_xo,
      { "XO",           "atp.xo",   FT_BOOLEAN,  8,
            NULL, ATP_XO, "Exactly-once flag", HFILL }},

    { &hf_atp_eom,
      { "EOM",          "atp.eom",  FT_BOOLEAN,  8,
            NULL, ATP_EOM, "End-of-message", HFILL }},

    { &hf_atp_sts,
      { "STS",          "atp.sts",  FT_BOOLEAN,  8,
            NULL, ATP_STS, "Send transaction status", HFILL }},

    { &hf_atp_treltimer,
      { "TRel timer",         "atp.treltimer",  FT_UINT8,  BASE_DEC,
            VALS(atp_trel_timer_vals), 0x07, "TRel timer", HFILL }},

    { &hf_atp_bitmap,
      { "Bitmap",       "atp.bitmap",     FT_UINT8,  BASE_HEX,
            NULL, 0x0, "Bitmap or sequence number", HFILL }},

    { &hf_atp_tid,
      { "TID",                "atp.tid",  FT_UINT16,  BASE_DEC,
            NULL, 0x0, "Transaction id", HFILL }},
    { &hf_atp_user_bytes,
      { "User bytes",               "atp.user_bytes", FT_UINT32,  BASE_HEX,
            NULL, 0x0, "User bytes", HFILL }},

    { &hf_atp_segment_overlap,
      { "Segment overlap",    "atp.segment.overlap", FT_BOOLEAN, BASE_NONE,
            NULL, 0x0, "Segment overlaps with other segments", HFILL }},

    { &hf_atp_segment_overlap_conflict,
      { "Conflicting data in segment overlap", "atp.segment.overlap.conflict",
      FT_BOOLEAN, BASE_NONE,
            NULL, 0x0, "Overlapping segments contained conflicting data", HFILL }},

    { &hf_atp_segment_multiple_tails,
      { "Multiple tail segments found", "atp.segment.multipletails",
      FT_BOOLEAN, BASE_NONE,
            NULL, 0x0, "Several tails were found when desegmenting the packet", HFILL }},

    { &hf_atp_segment_too_long_segment,
      { "Segment too long",   "atp.segment.toolongsegment", FT_BOOLEAN, BASE_NONE,
            NULL, 0x0, "Segment contained data past end of packet", HFILL }},

    { &hf_atp_segment_error,
      {"Desegmentation error",      "atp.segment.error", FT_FRAMENUM, BASE_NONE,
            NULL, 0x0, "Desegmentation error due to illegal segments", HFILL }},

    { &hf_atp_segment,
      { "ATP Fragment",       "atp.fragment", FT_FRAMENUM, BASE_NONE,
            NULL, 0x0, "ATP Fragment", HFILL }},

    { &hf_atp_segments,
      { "ATP Fragments",      "atp.fragments", FT_NONE, BASE_NONE,
            NULL, 0x0, "ATP Fragments", HFILL }},

    { &hf_atp_reassembled_in,
      { "Reassembled ATP in frame", "atp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
       "This ATP packet is reassembled in this frame", HFILL }}
  };

  static hf_register_info hf_asp[] = {
    { &hf_asp_func,
      { "asp function",       "asp.function",   FT_UINT8,  BASE_DEC,
            VALS(asp_func_vals), 0, "asp function", HFILL }},

    { &hf_asp_error,
      { "asp error",          "asp.error",      FT_INT32,  BASE_DEC,
            VALS(asp_error_vals), 0, "return error code", HFILL }},

    { &hf_asp_version,
      { "Version",            "asp.version",    FT_UINT16,  BASE_HEX,
            NULL, 0, "asp version", HFILL }},

    { &hf_asp_attn_code,
      { "Attn code",          "asp.attn_code",  FT_UINT16,  BASE_HEX,
            NULL, 0, "asp attention code", HFILL }},

    { &hf_asp_init_error,
      { "Error",        "asp.init_error", FT_UINT16,  BASE_DEC,
            NULL, 0, "asp init error", HFILL }},

    { &hf_asp_session_id,
      { "Session ID",         "asp.session_id", FT_UINT8,  BASE_DEC,
            NULL, 0, "asp session id", HFILL }},

    { &hf_asp_socket,
      { "Socket",       "asp.socket",     FT_UINT8,  BASE_DEC,
            NULL, 0, "asp socket", HFILL }},

    { &hf_asp_seq,
      { "Sequence",           "asp.seq",  FT_UINT16,  BASE_DEC,
            NULL, 0, "asp sequence number", HFILL }},

    { &hf_asp_size,
      { "size",         "asp.size", FT_UINT16,  BASE_DEC,
            NULL, 0, "asp available size for reply", HFILL }},

    { &hf_asp_zero_value,
      { "Pad (0)",         "asp.zero_value",
      FT_BYTES, BASE_HEX, NULL, 0x0,
            "Pad", HFILL }},

      /* asp ,dsi, afp */
    { &hf_asp_server_name,
      { "Server name",         "asp.server_name",
      FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "Server name", HFILL }},

    { &hf_asp_server_type,
      { "Server type",         "asp.server_type",
      FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "Server type", HFILL }},

    { &hf_asp_server_vers,
      { "AFP version",         "asp.server_vers",
      FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "AFP version", HFILL }},

    { &hf_asp_server_uams,
      { "UAM",         "asp.server_uams",
      FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "UAM", HFILL }},

    { &hf_asp_server_icon,
      { "Icon bitmap",         "asp.server_icon",
      FT_BYTES, BASE_HEX, NULL, 0x0,
            "Server icon bitmap", HFILL }},

    { &hf_asp_server_directory,
      { "Directory service",         "asp.server_directory",
      FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "Server directory service", HFILL }},

    { &hf_asp_server_signature,
      { "Server signature",         "asp.server_signature",
      FT_BYTES, BASE_HEX, NULL, 0x0,
            "Server signature", HFILL }},

    { &hf_asp_server_flag,
      { "Flag",         "asp.server_flag",
      FT_UINT16, BASE_HEX, NULL, 0x0,
            "Server capabilities flag", HFILL }},
    { &hf_asp_server_flag_copyfile,
      { "Support copyfile",      "asp.server_flag.copyfile",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_COPY,
            "Server support copyfile", HFILL }},
    { &hf_asp_server_flag_passwd,
      { "Support change password",      "asp.server_flag.passwd",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_PASSWD,
            "Server support change password", HFILL }},
    { &hf_asp_server_flag_no_save_passwd,
      { "Don't allow save password",      "asp.server_flag.no_save_passwd",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_NOSAVEPASSWD,
            "Don't allow save password", HFILL }},
    { &hf_asp_server_flag_srv_msg,
      { "Support server message",      "asp.server_flag.srv_msg",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVMSGS,
            "Support server message", HFILL }},
    { &hf_asp_server_flag_srv_sig,
      { "Support server signature",      "asp.server_flag.srv_sig",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVSIGNATURE,
            "Support server signature", HFILL }},
    { &hf_asp_server_flag_tcpip,
      { "Support TCP/IP",      "asp.server_flag.tcpip",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_TCPIP,
            "Server support TCP/IP", HFILL }},
    { &hf_asp_server_flag_notify,
      { "Support server notifications",      "asp.server_flag.notify",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVNOTIFY,
            "Server support notifications", HFILL }},
    { &hf_asp_server_flag_reconnect,
      { "Support server reconnect",      "asp.server_flag.reconnect",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVRECONNECT,
            "Server support reconnect", HFILL }},
    { &hf_asp_server_flag_directory,
      { "Support directory services",      "asp.server_flag.directory",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVDIRECTORY,
            "Server support directory services", HFILL }},
    { &hf_asp_server_flag_utf8_name,
      { "Support UTF8 server name",      "asp.server_flag.utf8_name",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVUTF8,
            "Server support UTF8 server name", HFILL }},
    { &hf_asp_server_flag_fast_copy,
      { "Support fast copy",      "asp.server_flag.fast_copy",
            FT_BOOLEAN, 16, NULL, AFPSRVRINFO_FASTBOZO,
            "Server support fast copy", HFILL }},

    { &hf_asp_server_addr_len,
      { "Length",          "asp.server_addr.len",
      FT_UINT8, BASE_DEC, NULL, 0x0,
            "Address length.", HFILL }},

    { &hf_asp_server_addr_type,
      { "Type",          "asp.server_addr.type",
      FT_UINT8, BASE_DEC, VALS(afp_server_addr_type_vals), 0x0,
            "Address type.", HFILL }},

    { &hf_asp_server_addr_value,
      { "Value",          "asp.server_addr.value",
      FT_BYTES, BASE_HEX, NULL, 0x0,
            "Address value", HFILL }},

    { &hf_asp_server_utf8_name_len,
      { "Server name length",         "asp.server_utf8_name_len",
      FT_UINT16, BASE_DEC, NULL, 0x0,
            "UTF8 server name length", HFILL }},

    { &hf_asp_server_utf8_name,
      { "Server name (UTF8)",         "asp.server_utf8_name",
      FT_STRING, BASE_NONE, NULL, 0x0,
            "Server name (UTF8)", HFILL }},
  };

  static hf_register_info hf_zip[] = {
    { &hf_zip_function,
      { "Function",     "zip.function",   FT_UINT8,  BASE_DEC, VALS(zip_function_vals), 0x0,
            "ZIP function", HFILL }},

    { &hf_zip_zero_value,
      { "Pad (0)",      "zip.zero_value",FT_BYTES, BASE_HEX, NULL, 0x0,
            "Pad", HFILL }},

    { &hf_zip_atp_function,
      { "Function",     "zip.atp_function", FT_UINT8,  BASE_DEC, VALS(zip_atp_function_vals), 0x0,
            "", HFILL }},

    { &hf_zip_start_index,
      { "Start index",  "zip.start_index", FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_zip_count,
      { "Count",  "zip.count", FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_zip_network_count,
      { "Count",  "zip.network_count", FT_UINT8, BASE_DEC, NULL, 0x0,
            "", HFILL }},
    { &hf_zip_network,
      { "Network","zip.network", FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},
    { &hf_zip_network_start,
      { "Network start","zip.network_start", FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},
    { &hf_zip_network_end,
      { "Network end",  "zip.network_end", FT_UINT16, BASE_DEC, NULL, 0x0,
            "", HFILL }},

    { &hf_zip_flags,
      { "Flags",  "zip.flags", FT_BOOLEAN, 8, NULL, 0xC0,
            "", HFILL }},

    { &hf_zip_last_flag,
      { "Last Flag",    "zip.last_flag", FT_BOOLEAN, 8, NULL, 0,
            "Non zero if contains last zone name in the zone list", HFILL }},

    { &hf_zip_flags_zone_invalid,
      { "Zone invalid", "zip.flags.zone_invalid", FT_BOOLEAN, 8, NULL, 0x80,
            "", HFILL }},

    { &hf_zip_flags_use_broadcast,
      { "Use broadcast","zip.flags.use_broadcast", FT_BOOLEAN, 8, NULL, 0x40,
            "", HFILL }},

    { &hf_zip_flags_only_one_zone,
      { "Only one zone","zip.flags.only_one_zone", FT_BOOLEAN, 8, NULL, 0x20,
            "", HFILL }},

    { &hf_zip_zone_name,
      { "Zone",         "zip.zone_name", FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "", HFILL }},

    { &hf_zip_default_zone,
      { "Default zone", "zip.default_zone",FT_UINT_STRING, BASE_NONE, NULL, 0x0,
            "", HFILL }},

    { &hf_zip_multicast_length,
      { "Multicast length",   "zip.multicast_length", FT_UINT8,  BASE_DEC, NULL, 0x0,
            "Multicast address length", HFILL }},

    { &hf_zip_multicast_address,
      { "Multicast address", "zip.multicast_address",FT_BYTES, BASE_HEX, NULL, 0x0,
            "Multicast address", HFILL }},

  };

  static hf_register_info hf_pap[] = {
    { &hf_pap_connid,
      { "ConnID", "pap.connid",     FT_UINT8,  BASE_DEC, NULL, 0x0,
            "PAP connection ID", HFILL }},

    { &hf_pap_function,
      { "Function",     "pap.function",   FT_UINT8,  BASE_DEC, VALS(pap_function_vals), 0x0,
            "PAP function", HFILL }},

    { &hf_pap_socket,
      { "Socket", "pap.socket",     FT_UINT8,  BASE_DEC, NULL, 0x0,
            "ATP responding socket number", HFILL }},

    { &hf_pap_quantum,
      { "Quantum",      "pap.quantum",    FT_UINT8,  BASE_DEC, NULL, 0x0,
            "Flow quantum", HFILL }},

    { &hf_pap_waittime,
      { "Wait time",    "pap.quantum",    FT_UINT16,  BASE_DEC, NULL, 0x0,
            "Wait time", HFILL }},

    { &hf_pap_result,
      { "Result", "pap.quantum",    FT_UINT16,  BASE_DEC, NULL, 0x0,
            "Result", HFILL }},

    { &hf_pap_seq,
      { "Sequence",     "pap.seq",  FT_UINT16,  BASE_DEC, NULL, 0x0,
            "Sequence number", HFILL }},

    { &hf_pap_status,
      { "Status", "pap.status",     FT_STRING,  BASE_DEC, NULL, 0x0,
            "Printer status", HFILL }},

    { &hf_pap_eof,
      { "EOF",    "pap.eof", FT_BOOLEAN, BASE_NONE,
            NULL, 0x0, "EOF", HFILL }},

    { &hf_pap_pad,
      { "Pad",          "pad.pad",        FT_NONE,   BASE_NONE, NULL, 0,
            "Pad Byte", HFILL }},

  };

  static gint *ett[] = {
      &ett_llap,
      &ett_ddp,
      &ett_atp,
      &ett_atp_info,
      &ett_atp_segments,
      &ett_atp_segment,
      &ett_asp,
      &ett_pap,

      /* asp dsi afp */
      &ett_asp_status,
      &ett_asp_status_server_flag,
      &ett_asp_vers,
      &ett_asp_uams,
      &ett_asp_addr,
      &ett_asp_addr_line,
      &ett_asp_directory,
      &ett_asp_utf8_name,

      &ett_nbp,
      &ett_nbp_info,
      &ett_nbp_node,
      &ett_pstring,
      &ett_rtmp,
      &ett_rtmp_tuple,

      &ett_zip,
      &ett_zip_flags,
        &ett_zip_zones_list,
        &ett_zip_network_list,
  };
  module_t *atp_module;

  proto_llap = proto_register_protocol("LocalTalk Link Access Protocol", "LLAP", "llap");
  proto_register_field_array(proto_llap, hf_llap, array_length(hf_llap));

  proto_ddp = proto_register_protocol("Datagram Delivery Protocol", "DDP", "ddp");
  proto_register_field_array(proto_ddp, hf_ddp, array_length(hf_ddp));

  proto_nbp = proto_register_protocol("Name Binding Protocol", "NBP", "nbp");
  proto_register_field_array(proto_nbp, hf_nbp, array_length(hf_nbp));

  proto_atp = proto_register_protocol("AppleTalk Transaction Protocol packet", "ATP", "atp");
  proto_register_field_array(proto_atp, hf_atp, array_length(hf_atp));

  proto_asp = proto_register_protocol("AppleTalk Session Protocol", "ASP", "asp");
  proto_register_field_array(proto_asp, hf_asp, array_length(hf_asp));

  proto_pap = proto_register_protocol("Printer Access Protocol", "PAP", "apap");
  proto_register_field_array(proto_pap, hf_pap, array_length(hf_pap));

  proto_zip = proto_register_protocol("Zone Information Protocol", "ZIP", "zip");
  proto_register_field_array(proto_zip, hf_zip, array_length(hf_zip));

  atp_module = prefs_register_protocol(proto_atp, NULL);
  prefs_register_bool_preference(atp_module, "desegment",
    "Reassemble ATP messages spanning multiple DDP packets",
    "Whether the ATP dissector should reassemble messages spanning multiple DDP packets",
    &atp_defragment);

  proto_rtmp = proto_register_protocol("Routing Table Maintenance Protocol",
                               "RTMP", "rtmp");
  proto_register_field_array(proto_rtmp, hf_rtmp, array_length(hf_rtmp));

  proto_register_subtree_array(ett, array_length(ett));

  /* subdissector code */
  ddp_dissector_table = register_dissector_table("ddp.type", "DDP packet type",
                                     FT_UINT8, BASE_HEX);
}

void
proto_reg_handoff_atalk(void)
{
  dissector_handle_t nbp_handle, rtmp_request_handle;
  dissector_handle_t atp_handle;
  dissector_handle_t zip_ddp_handle;
  dissector_handle_t rtmp_data_handle, llap_handle;

  ddp_handle = create_dissector_handle(dissect_ddp, proto_ddp);
  dissector_add("ethertype", ETHERTYPE_ATALK, ddp_handle);
  dissector_add("chdlctype", ETHERTYPE_ATALK, ddp_handle);
  dissector_add("ppp.protocol", PPP_AT, ddp_handle);
  dissector_add("null.type", BSD_AF_APPLETALK, ddp_handle);
  dissector_add("arcnet.protocol_id", ARCNET_PROTO_APPLETALK, ddp_handle);

  nbp_handle = create_dissector_handle(dissect_nbp, proto_nbp);
  dissector_add("ddp.type", DDP_NBP, nbp_handle);

  atp_handle = create_dissector_handle(dissect_atp, proto_atp);
  dissector_add("ddp.type", DDP_ATP, atp_handle);

  asp_handle = create_dissector_handle(dissect_asp, proto_asp);
  pap_handle = create_dissector_handle(dissect_pap, proto_pap);

  rtmp_request_handle = create_dissector_handle(dissect_rtmp_request, proto_rtmp);
  rtmp_data_handle = create_dissector_handle(dissect_rtmp_data, proto_rtmp);
  dissector_add("ddp.type", DDP_RTMPREQ, rtmp_request_handle);
  dissector_add("ddp.type", DDP_RTMPDATA, rtmp_data_handle);

  zip_ddp_handle = create_dissector_handle(dissect_ddp_zip, proto_zip);
  dissector_add("ddp.type", DDP_ZIP, zip_ddp_handle);

  zip_atp_handle = create_dissector_handle(dissect_atp_zip, proto_zip);

  llap_handle = create_dissector_handle(dissect_llap, proto_llap);
  dissector_add("wtap_encap", WTAP_ENCAP_LOCALTALK, llap_handle);

  register_init_routine( atp_init);
  register_init_routine( &asp_reinit);

  afp_handle = find_dissector("afp");
  data_handle = find_dissector("data");
}

Generated by  Doxygen 1.6.0   Back to index