/* packet-uftp.c
 * Routines for UFTP packet dissection
 * Copyright Dennis Bush <bush@tcnj.edu>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

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

#define UFTP_VER_NUM 0x31
#define UFTP_3_0_VER 0x30

#define ANNOUNCE    1
#define REGISTER    2
#define CLIENT_KEY  3
#define REG_CONF    4
#define FILEINFO    5
#define KEYINFO     6
#define INFO_ACK    7
#define FILESEG     8
#define DONE        9
#define STATUS     10
#define PRSTATUS   11
#define COMPLETE   12
#define DONE_CONF  13
#define HB_REQ     14
#define HB_RESP    15
#define KEY_REQ    16
#define PROXY_KEY  17
#define ENCRYPTED  80
#define ABORT      99

#define MAXDEST 10000
#define MAXFILENAME 100
#define MAXDIRNAME 200
#define MAXPATHNAME 300
#define MAXPROXYDEST 1000

#define DESTNAME_LEN 100
#define MAXFILES 500
#define IFNAME_LEN 25
#define MAX_INTERFACES 20
#define IPSTR_LEN 16

#define FTYPE_REG 0
#define FTYPE_DIR 1
#define FTYPE_LINK 2

#define KEY_NONE 0x00
#define KEY_DES 0x01
#define KEY_DES_EDE3 0x02
#define KEY_AES128 0x03
#define KEY_AES256 0x04

#define HASH_NONE 0x00
#define HASH_MD5 0x01
#define HASH_SHA1 0x02
#define HASH_SHA256 0x03

#define SIG_NONE 0x00
#define SIG_HMAC 0x01
#define SIG_RSA 0x02

#define FLAG_RESTART 0x01
#define FLAG_SYNC_MODE 0x02
#define FLAG_SYNC_PREVIEW 0x04
#define FLAG_ANNOUNCE_RESERVED 0xF8

#define FLAG_PARTIAL 0x01
#define FLAG_INFOACK_RESERVED 0xFE

#define FLAG_CURRENT_FILE 0x01
#define FLAG_ABORT_RESERVED 0xFE

#define COMP_STAT_NORMAL 0
#define COMP_STAT_SKIPPED 1
#define COMP_STAT_OVERWRITE 2
#define COMP_STAT_REJECTED 3

#define HB_AUTH_FAILED 0
#define HB_AUTH_OK 1
#define HB_AUTH_CHALLENGE 2

#define PUBKEY_LEN 256  /* big enough for RSA-2048 */
#define RAND_LEN 32     /* rfc 5246 */
#define HMAC_LEN 32     /* big enough for SHA-256 */
#define VERIFY_LEN 12   /* rfc 5246 */
#define MASTER_LEN 48   /* rfc 5246 */
#define MAXIV 16        /* big enough for AES256 */
#define MAXKEY 32       /* big enough for AES256 */
#define KEYBLSIZE 16    /* Maximum symetric key blocksize */
#define DEF_RSA_LEN 512 /* Default length of generated RSA keys */
#define RSA_EXP 65537   /* Public key exponent of generated RSA keys */

#define UFTP_LEN 16
#define ANNOUNCE_LEN 64
#define REGISTER_LEN 40
#define CLIENT_KEY_LEN 12
#define REG_CONF_LEN 4
#define FILEINFO_LEN 324
#define FILEINFO_30_LEN 320
#define KEYINFO_LEN 12
#define DESTKEY_LEN 52
#define INFO_ACK_LEN 20
#define FILESEG_LEN 12
#define DONE_LEN 8
#define STATUS_LEN 12
#define PRSTATUS_LEN 12
#define COMPLETE_LEN 8
#define DONE_CONF_LEN 8
#define HB_REQ_LEN 16
#define HB_RESP_LEN 8
#define KEY_REQ_LEN 4
#define PROXY_KEY_LEN 16
#define ENCRYPTED_LEN 12
#define ABORT_LEN 308

void proto_register_uftp(void);
void proto_reg_handoff_uftp(void);

static dissector_handle_t uftp_handle;

static int proto_uftp;
#define UFTP_PORT   1044 /* Not IANA registered */

/* main header and common fields */
static int hf_uftp_version;
static int hf_uftp_func;
static int hf_uftp_blsize;
static int hf_uftp_group_id;
static int hf_uftp_srcaddr;
static int hf_uftp_destaddr;

static int hf_uftp_destlist;
static int hf_uftp_dest;

/* ANNOUNCE fields */
static int hf_uftp_announce;
static int hf_uftp_announce_func;
static int hf_uftp_announce_flags;
static int hf_uftp_announce_flags_restart;
static int hf_uftp_announce_flags_sync;
static int hf_uftp_announce_flags_syncpreview;
static int hf_uftp_announce_flags_reserved;
static int hf_uftp_announce_destcount;
static int hf_uftp_announce_announce_int;
static int hf_uftp_announce_status_int;
static int hf_uftp_announce_register_int;
static int hf_uftp_announce_done_int;
static int hf_uftp_announce_announce_time;
static int hf_uftp_announce_status_time;
static int hf_uftp_announce_mtu;
static int hf_uftp_announce_privatemcast;
static int hf_uftp_announce_client_auth;
static int hf_uftp_announce_sigtype;
static int hf_uftp_announce_hashtype;
static int hf_uftp_announce_keytype;
static int hf_uftp_announce_keylen;
static int hf_uftp_announce_reserved;
static int hf_uftp_announce_keyexp;
static int hf_uftp_announce_rand1;
static int hf_uftp_announce_keymod;

/* REGISTER fields */
static int hf_uftp_register;
static int hf_uftp_register_func;
static int hf_uftp_register_reserved;
static int hf_uftp_register_destcount;
static int hf_uftp_register_premaster_len;
static int hf_uftp_register_rand2;
static int hf_uftp_register_premaster;

/* CLIENT_KEY fields */
static int hf_uftp_clientkey;
static int hf_uftp_clientkey_func;
static int hf_uftp_clientkey_reserved;
static int hf_uftp_clientkey_keylen;
static int hf_uftp_clientkey_verifylen;
static int hf_uftp_clientkey_keyexp;
static int hf_uftp_clientkey_keymod;
static int hf_uftp_clientkey_verify;

/* REG_CONF fields */
static int hf_uftp_regconf;
static int hf_uftp_regconf_func;
static int hf_uftp_regconf_reserved;
static int hf_uftp_regconf_destcount;

/* FILEINFO fields */
static int hf_uftp_fileinfo;
static int hf_uftp_fileinfo_func;
static int hf_uftp_fileinfo_ftype;
static int hf_uftp_fileinfo_file_id;
static int hf_uftp_fileinfo_block_total;
static int hf_uftp_fileinfo_section_total;
static int hf_uftp_fileinfo_destcount;
static int hf_uftp_fileinfo_fsize;
static int hf_uftp_fileinfo_ftstamp;
static int hf_uftp_fileinfo_name;

/* KEYINFO fields */
static int hf_uftp_keyinfo;
static int hf_uftp_keyinfo_func;
static int hf_uftp_keyinfo_reserved;
static int hf_uftp_keyinfo_destcount;
static int hf_uftp_keyinfo_groupmaster_len;
static int hf_uftp_keyinfo_tstamp;
static int hf_uftp_keyinfo_destkey;
static int hf_uftp_keyinfo_destaddr;
static int hf_uftp_keyinfo_groupmaster;

/* INFO_ACK fields */
static int hf_uftp_infoack;
static int hf_uftp_infoack_func;
static int hf_uftp_infoack_flags;
static int hf_uftp_infoack_flags_partial;
static int hf_uftp_infoack_flags_reserved;
static int hf_uftp_infoack_file_id;
static int hf_uftp_infoack_destcount;
static int hf_uftp_infoack_reserved;
static int hf_uftp_infoack_verify_data;

/* FILESEG fields */
static int hf_uftp_fileseg;
static int hf_uftp_fileseg_func;
static int hf_uftp_fileseg_reserved1;
static int hf_uftp_fileseg_file_id;
static int hf_uftp_fileseg_pass;
static int hf_uftp_fileseg_reserved2;
static int hf_uftp_fileseg_section;
static int hf_uftp_fileseg_seq_num;
static int hf_uftp_fileseg_data;

/* DONE fields */
static int hf_uftp_done;
static int hf_uftp_done_func;
static int hf_uftp_done_pass;
static int hf_uftp_done_section;
static int hf_uftp_done_file_id;
static int hf_uftp_done_destcount;

/* STATUS fields */
static int hf_uftp_status;
static int hf_uftp_status_func;
static int hf_uftp_status_reserved;
static int hf_uftp_status_file_id;
static int hf_uftp_status_pass;
static int hf_uftp_status_seq;
static int hf_uftp_status_section;
static int hf_uftp_status_nak_count;
static int hf_uftp_status_naks;

/* PRSTATUS fields */
static int hf_uftp_prstatus;
static int hf_uftp_prstatus_func;
static int hf_uftp_prstatus_reserved1;
static int hf_uftp_prstatus_file_id;
static int hf_uftp_prstatus_pass;
static int hf_uftp_prstatus_seq;
static int hf_uftp_prstatus_section;
static int hf_uftp_prstatus_destcount;
static int hf_uftp_prstatus_reserved2;

/* COMPLETE fields */
static int hf_uftp_complete;
static int hf_uftp_complete_func;
static int hf_uftp_complete_status;
static int hf_uftp_complete_file_id;
static int hf_uftp_complete_destcount;
static int hf_uftp_complete_reserved2;

/* DONE_CONF fields */
static int hf_uftp_doneconf;
static int hf_uftp_doneconf_func;
static int hf_uftp_doneconf_reserved1;
static int hf_uftp_doneconf_file_id;
static int hf_uftp_doneconf_destcount;
static int hf_uftp_doneconf_reserved2;

/* HB_REQ fields */
static int hf_uftp_hbreq;
static int hf_uftp_hbreq_func;
static int hf_uftp_hbreq_reserved;
static int hf_uftp_hbreq_nonce;
static int hf_uftp_hbreq_keylen;
static int hf_uftp_hbreq_siglen;
static int hf_uftp_hbreq_keyexp;
static int hf_uftp_hbreq_keymod;
static int hf_uftp_hbreq_verify;

/* HB_RESP fields */
static int hf_uftp_hbresp;
static int hf_uftp_hbresp_func;
static int hf_uftp_hbresp_authenticated;
static int hf_uftp_hbresp_reserved;
static int hf_uftp_hbresp_nonce;

/* KEY_REQ fields */
static int hf_uftp_keyreq;
static int hf_uftp_keyreq_func;
static int hf_uftp_keyreq_reserved;

/* PROXY_KEY fields */
static int hf_uftp_proxykey;
static int hf_uftp_proxykey_func;
static int hf_uftp_proxykey_reserved;
static int hf_uftp_proxykey_nonce;
static int hf_uftp_proxykey_keylen;
static int hf_uftp_proxykey_siglen;
static int hf_uftp_proxykey_keyexp;
static int hf_uftp_proxykey_keymod;
static int hf_uftp_proxykey_verify;

/* ENCRYPTED fields */
static int hf_uftp_encrypted;
static int hf_uftp_encrypted_tstamp;
static int hf_uftp_encrypted_sig_len;
static int hf_uftp_encrypted_payload_len;
static int hf_uftp_encrypted_signature;
static int hf_uftp_encrypted_payload;

/* ABORT fields */
static int hf_uftp_abort;
static int hf_uftp_abort_func;
static int hf_uftp_abort_flags;
static int hf_uftp_abort_flags_curfile;
static int hf_uftp_abort_flags_reserved;
static int hf_uftp_abort_reserved;
static int hf_uftp_abort_host;
static int hf_uftp_abort_message;

static int ett_uftp;
static int ett_uftp_announce;
static int ett_uftp_register;
static int ett_uftp_clientkey;
static int ett_uftp_regconf;
static int ett_uftp_fileinfo;
static int ett_uftp_keyinfo;
static int ett_uftp_infoack;
static int ett_uftp_fileseg;
static int ett_uftp_done;
static int ett_uftp_status;
static int ett_uftp_prstatus;
static int ett_uftp_complete;
static int ett_uftp_doneconf;
static int ett_uftp_hbreq;
static int ett_uftp_hbresp;
static int ett_uftp_keyreq;
static int ett_uftp_proxykey;
static int ett_uftp_encrypted;
static int ett_uftp_abort;

static int ett_uftp_announce_flags;
static int ett_uftp_keyinfo_destkey;
static int ett_uftp_infoack_flags;
static int ett_uftp_abort_flags;

static int ett_uftp_destlist;

static expert_field ei_uftp_length_invalid;
static expert_field ei_uftp_func_unknown;

static dissector_handle_t uftp4_handle;
static dissector_handle_t uftp5_handle;

static const value_string messages[] = {
    { ANNOUNCE,   "ANNOUNCE" },
    { REGISTER,   "REGISTER" },
    { CLIENT_KEY, "CLIENT_KEY" },
    { REG_CONF,   "REG_CONF" },
    { FILEINFO,   "FILEINFO" },
    { KEYINFO,    "KEYINFO" },
    { INFO_ACK,   "INFO_ACK" },
    { FILESEG,    "FILESEG" },
    { DONE,       "DONE" },
    { STATUS,     "STATUS" },
    { PRSTATUS,   "PRSTATUS" },
    { COMPLETE,   "COMPLETE" },
    { DONE_CONF,  "DONE_CONF" },
    { HB_REQ,     "HB_REQ" },
    { HB_RESP,    "HB_RESP" },
    { KEY_REQ,    "KEY_REQ" },
    { PROXY_KEY,  "PROXY_KEY" },
    { ENCRYPTED,  "ENCRYPTED" },
    { ABORT,      "ABORT" },
    { 0, NULL }
};

static const value_string signature_types[] = {
    { SIG_NONE, "NONE" },
    { SIG_HMAC, "HMAC" },
    { SIG_RSA,  "RSA" },
    { 0, NULL }
};

static const value_string hash_types[] = {
    { HASH_NONE,   "NONE" },
    { HASH_MD5,    "MD5" },
    { HASH_SHA1,   "SHA-1" },
    { HASH_SHA256, "SHA-256" },
    { 0, NULL }
};

static const value_string key_types[] = {
    { KEY_NONE,     "NONE" },
    { KEY_DES,      "DES" },
    { KEY_DES_EDE3, "3 Key Triple DES" },
    { KEY_AES128,   "AES-128" },
    { KEY_AES256,   "AES-256" },
    { 0, NULL }
};

static const value_string hb_auth_types[] = {
    { HB_AUTH_FAILED,    "Authorization Failed" },
    { HB_AUTH_OK,        "Authorization Succeeded" },
    { HB_AUTH_CHALLENGE, "Authorization Required" },
    { 0, NULL }
};

static const value_string file_types[] = {
    { FTYPE_REG,  "Regular file" },
    { FTYPE_DIR,  "Directory" },
    { FTYPE_LINK, "Symbolic link" },
    { 0, NULL }
};

static int * const announce_flags[] = {
    &hf_uftp_announce_flags_restart,
    &hf_uftp_announce_flags_sync,
    &hf_uftp_announce_flags_syncpreview,
    &hf_uftp_announce_flags_reserved,
    NULL
};

static int * const infoack_flags[] = {
    &hf_uftp_infoack_flags_partial,
    &hf_uftp_infoack_flags_reserved,
    NULL
};

static int * const abort_flags[] = {
    &hf_uftp_abort_flags_curfile,
    &hf_uftp_abort_flags_reserved,
    NULL
};

static const value_string comp_status[] = {
    { COMP_STAT_NORMAL,     "Normal" },
    { COMP_STAT_SKIPPED,    "Skipped" },
    { COMP_STAT_OVERWRITE,  "Overwrite" },
    { COMP_STAT_REJECTED,   "Rejected" },
    { 0, NULL }
};

static void dissect_uftp_announce(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *announce_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t destcount, keylen, idx;

    if (tvb_reported_length(tvb) < ANNOUNCE_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 2);
    keylen = tvb_get_ntohs(tvb, 24);
    if ((int)tvb_reported_length(tvb) < ANNOUNCE_LEN + keylen + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, keylen = %d, count=%d",
                            tvb_reported_length(tvb), keylen, destcount);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_announce, tvb, offset, -1, ENC_NA);
    announce_tree = proto_item_add_subtree(ti, ett_uftp_announce);
    proto_tree_add_item(announce_tree, hf_uftp_announce_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_bitmask(announce_tree, tvb, offset, hf_uftp_announce_flags, ett_uftp_announce_flags, announce_flags, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_announce_int, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_status_int, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_register_int, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_done_int, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_announce_time, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_status_time, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_mtu, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_privatemcast, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(announce_tree, hf_uftp_announce_client_auth, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_sigtype, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_hashtype, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_keytype, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(announce_tree, hf_uftp_announce_keylen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(announce_tree, hf_uftp_announce_keyexp, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(announce_tree, hf_uftp_announce_rand1, tvb, offset, RAND_LEN, ENC_NA);
    offset += RAND_LEN;
    if (keylen > 0) {
        proto_tree_add_item(announce_tree, hf_uftp_announce_keymod, tvb, offset, keylen, ENC_NA);
        offset += keylen;
    }
    if (destcount > 0) {
        destlist = proto_tree_add_item(announce_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_register(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *register_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t destcount, keylen, idx;

    if (tvb_reported_length(tvb) < REGISTER_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 4);
    keylen = tvb_get_ntohs(tvb, 6);
    if ((int)tvb_reported_length(tvb) < REGISTER_LEN + keylen + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, keylen = %d, count=%d",
                            tvb_reported_length(tvb), keylen, destcount);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_register, tvb, offset, -1, ENC_NA);
    register_tree = proto_item_add_subtree(ti, ett_uftp_register);
    proto_tree_add_item(register_tree, hf_uftp_register_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(register_tree, hf_uftp_register_reserved, tvb, offset, 3, ENC_BIG_ENDIAN);
    offset += 3;
    proto_tree_add_item(register_tree, hf_uftp_register_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(register_tree, hf_uftp_register_premaster_len, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(register_tree, hf_uftp_register_rand2, tvb, offset, RAND_LEN, ENC_NA);
    offset += RAND_LEN;
    if (keylen > 0) {
        proto_tree_add_item(register_tree, hf_uftp_register_premaster, tvb, offset, keylen, ENC_NA);
        offset += keylen;
    }
    if (destcount > 0) {
        destlist = proto_tree_add_item(register_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_clientkey(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *clientkey_tree = NULL;
    unsigned offset = 0;
    uint16_t keylen, verifylen;

    if (tvb_reported_length(tvb) < CLIENT_KEY_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    keylen = tvb_get_ntohs(tvb, 4);
    verifylen = tvb_get_ntohs(tvb, 6);
    if ((int)tvb_reported_length(tvb) < CLIENT_KEY_LEN + keylen + verifylen) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, keylen=%d verifylen=%d",
                            tvb_reported_length(tvb), keylen, verifylen);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_clientkey, tvb, offset, -1, ENC_NA);
    clientkey_tree = proto_item_add_subtree(ti, ett_uftp_clientkey);
    proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_reserved, tvb, offset, 3, ENC_BIG_ENDIAN);
    offset += 3;
    proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_keylen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_verifylen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_keyexp, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    if (keylen > 0) {
        proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_keymod, tvb, offset, keylen, ENC_NA);
        offset += keylen;
    }
    if (verifylen > 0) {
        proto_tree_add_item(clientkey_tree, hf_uftp_clientkey_verify, tvb, offset, verifylen, ENC_NA);
    }
}

static void dissect_uftp_regconf(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *regconf_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t destcount, idx;

    if (tvb_reported_length(tvb) < REG_CONF_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 2);
    if ((int)tvb_reported_length(tvb) < REG_CONF_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_regconf, tvb, offset, -1, ENC_NA);
    regconf_tree = proto_item_add_subtree(ti, ett_uftp_regconf);
    proto_tree_add_item(regconf_tree, hf_uftp_regconf_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(regconf_tree, hf_uftp_regconf_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(regconf_tree, hf_uftp_regconf_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    if (destcount > 0) {
        destlist = proto_tree_add_item(regconf_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_fileinfo_30(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *fileinfo_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t file_id, destcount, idx;

    if (tvb_reported_length(tvb) < FILEINFO_30_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 10);
    if ((int)tvb_reported_length(tvb) < FILEINFO_30_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X", file_id);

    ti = proto_tree_add_item(tree, hf_uftp_fileinfo, tvb, offset, -1, ENC_NA);
    fileinfo_tree = proto_item_add_subtree(ti, ett_uftp_fileinfo);
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_ftype, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_block_total, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_section_total, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_fsize, tvb, offset, 8, ENC_BIG_ENDIAN);
    offset += 8;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_name, tvb, offset, MAXPATHNAME, ENC_ASCII);
    offset += MAXPATHNAME;
    if (destcount > 0) {
        destlist = proto_tree_add_item(fileinfo_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_fileinfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *fileinfo_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t file_id, destcount, idx;

    if (tvb_reported_length(tvb) < FILEINFO_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 10);
    if ((int)tvb_reported_length(tvb) < FILEINFO_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X", file_id);

    ti = proto_tree_add_item(tree, hf_uftp_fileinfo, tvb, offset, -1, ENC_NA);
    fileinfo_tree = proto_item_add_subtree(ti, ett_uftp_fileinfo);
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_ftype, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_block_total, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_section_total, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_fsize, tvb, offset, 8, ENC_BIG_ENDIAN);
    offset += 8;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_ftstamp, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(fileinfo_tree, hf_uftp_fileinfo_name, tvb, offset, MAXPATHNAME, ENC_ASCII);
    offset += MAXPATHNAME;
    if (destcount > 0) {
        destlist = proto_tree_add_item(fileinfo_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_keyinfo(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_item *destkey = NULL;
    proto_tree *keyinfo_tree = NULL;
    proto_tree *destlist_tree = NULL;
    proto_tree *destkey_tree = NULL;
    unsigned offset = 0;
    uint8_t destcount, idx;

    if (tvb_reported_length(tvb) < KEYINFO_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_uint8(tvb, 2);
    if ((int)tvb_reported_length(tvb) < KEYINFO_LEN + (destcount * DESTKEY_LEN)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_keyinfo, tvb, offset, -1, ENC_NA);
    keyinfo_tree = proto_item_add_subtree(ti, ett_uftp_keyinfo);
    proto_tree_add_item(keyinfo_tree, hf_uftp_keyinfo_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(keyinfo_tree, hf_uftp_keyinfo_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(keyinfo_tree, hf_uftp_keyinfo_destcount, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(keyinfo_tree, hf_uftp_keyinfo_groupmaster_len, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(keyinfo_tree, hf_uftp_keyinfo_tstamp, tvb, offset, 8, ENC_TIME_SECS_NSECS);
    offset += 8;
    if (destcount > 0) {
        destlist = proto_tree_add_item(keyinfo_tree, hf_uftp_destlist, tvb, offset, destcount * DESTKEY_LEN, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        destkey = proto_tree_add_item(destlist_tree, hf_uftp_keyinfo_destkey, tvb, offset, DESTKEY_LEN, ENC_NA);
        destkey_tree = proto_item_add_subtree(destkey, ett_uftp_keyinfo_destkey);
        proto_tree_add_item(destkey_tree, hf_uftp_keyinfo_destaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
        proto_tree_add_item(destkey_tree, hf_uftp_keyinfo_groupmaster, tvb, offset, 48, ENC_NA);
        offset += 48;
    }
}

static void dissect_uftp_infoack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *infoack_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t file_id, destcount, idx;

    if (tvb_reported_length(tvb) < INFO_ACK_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 4);
    if ((int)tvb_reported_length(tvb) < INFO_ACK_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    if (file_id > 0) {
        col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X", file_id);
    }

    ti = proto_tree_add_item(tree, hf_uftp_infoack, tvb, offset, -1, ENC_NA);
    infoack_tree = proto_item_add_subtree(ti, ett_uftp_infoack);
    proto_tree_add_item(infoack_tree, hf_uftp_infoack_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_bitmask(infoack_tree, tvb, offset, hf_uftp_infoack_flags, ett_uftp_infoack_flags, infoack_flags, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(infoack_tree, hf_uftp_infoack_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(infoack_tree, hf_uftp_infoack_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(infoack_tree, hf_uftp_infoack_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(infoack_tree, hf_uftp_infoack_verify_data, tvb, offset, VERIFY_LEN, ENC_NA);
    offset += VERIFY_LEN;
    if (destcount > 0) {
        destlist = proto_tree_add_item(infoack_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_fileseg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *fileseg_tree = NULL;
    unsigned offset = 0;
    uint8_t pass;
    uint16_t file_id;
    uint32_t seq_num;

    if (tvb_reported_length(tvb) < FILESEG_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    pass = tvb_get_uint8(tvb, 4);
    seq_num = tvb_get_ntohl(tvb, 8);
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X  Pass=%d  Seq=%d",
                    file_id, pass, seq_num);

    ti = proto_tree_add_item(tree, hf_uftp_fileseg, tvb, offset, -1, ENC_NA);
    fileseg_tree = proto_item_add_subtree(ti, ett_uftp_fileseg);
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_pass, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_reserved2, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_section, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_seq_num, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(fileseg_tree, hf_uftp_fileseg_data, tvb, offset, -1, ENC_NA);
}

static void dissect_uftp_done(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *done_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint8_t pass;
    uint16_t file_id, section, destcount, idx;

    if (tvb_reported_length(tvb) < DONE_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 6);
    if ((int)tvb_reported_length(tvb) < DONE_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    pass = tvb_get_uint8(tvb, 1);
    section = tvb_get_ntohs(tvb, 2);
    file_id = tvb_get_ntohs(tvb, 4);
    if (file_id > 0) {
        col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X  Pass=%d  Section=%d",
                        file_id, pass, section);
    }

    ti = proto_tree_add_item(tree, hf_uftp_done, tvb, offset, -1, ENC_NA);
    done_tree = proto_item_add_subtree(ti, ett_uftp_done);
    proto_tree_add_item(done_tree, hf_uftp_done_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(done_tree, hf_uftp_done_pass, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(done_tree, hf_uftp_done_section, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(done_tree, hf_uftp_done_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(done_tree, hf_uftp_done_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    if (destcount > 0) {
        destlist = proto_tree_add_item(done_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_status(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *status_tree = NULL;
    unsigned offset = 0;
    uint8_t pass, seq;
    uint16_t file_id, section;
    uint32_t nak_count;

    if (tvb_reported_length(tvb) < STATUS_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    section = tvb_get_ntohs(tvb, 2);
    pass = tvb_get_uint8(tvb, 4);
    seq = tvb_get_uint8(tvb, 5);
    nak_count = tvb_get_ntohl(tvb, 8);
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X  Pass=%d  Section=%d Seq=%d",
                    file_id, pass, section, seq);

    ti = proto_tree_add_item(tree, hf_uftp_status, tvb, offset, -1, ENC_NA);
    status_tree = proto_item_add_subtree(ti, ett_uftp_status);
    proto_tree_add_item(status_tree, hf_uftp_status_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(status_tree, hf_uftp_status_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(status_tree, hf_uftp_status_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(status_tree, hf_uftp_status_pass, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(status_tree, hf_uftp_status_seq, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(status_tree, hf_uftp_status_section, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(status_tree, hf_uftp_status_nak_count, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    if (nak_count > 0) {
        proto_tree_add_item(status_tree, hf_uftp_status_naks, tvb, offset, -1, ENC_NA);
    }
}

static void dissect_uftp_prstatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *prstatus_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint8_t pass, seq;
    uint16_t file_id, destcount, idx, section;
    uint32_t nak_count;

    if (tvb_reported_length(tvb) < PRSTATUS_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 8);
    if ((int)tvb_reported_length(tvb) < PRSTATUS_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    section = tvb_get_ntohs(tvb, 2);
    pass = tvb_get_uint8(tvb, 4);
    seq = tvb_get_uint8(tvb, 5);
    nak_count = tvb_get_ntohl(tvb, 8);
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X  Pass=%d Section=%d Seq=%d "
                    "NAKs=%d", file_id, pass, section, seq, nak_count);

    ti = proto_tree_add_item(tree, hf_uftp_prstatus, tvb, offset, -1, ENC_NA);
    prstatus_tree = proto_item_add_subtree(ti, ett_uftp_prstatus);
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_pass, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_seq, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_section, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(prstatus_tree, hf_uftp_prstatus_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    if (destcount > 0) {
        destlist = proto_tree_add_item(prstatus_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_complete(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *complete_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t file_id, destcount, idx;

    if (tvb_reported_length(tvb) < COMPLETE_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 4);
    if ((int)tvb_reported_length(tvb) < COMPLETE_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    if (file_id > 0) {
        col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X", file_id);
    }

    ti = proto_tree_add_item(tree, hf_uftp_complete, tvb, offset, -1, ENC_NA);
    complete_tree = proto_item_add_subtree(ti, ett_uftp_complete);
    proto_tree_add_item(complete_tree, hf_uftp_complete_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(complete_tree, hf_uftp_complete_status, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(complete_tree, hf_uftp_complete_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(complete_tree, hf_uftp_complete_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(complete_tree, hf_uftp_complete_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    if (destcount > 0) {
        destlist = proto_tree_add_item(complete_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_doneconf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_item *destlist = NULL;
    proto_tree *doneconf_tree = NULL;
    proto_tree *destlist_tree = NULL;
    unsigned offset = 0;
    uint16_t file_id, destcount, idx;

    if (tvb_reported_length(tvb) < DONE_CONF_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    destcount = tvb_get_ntohs(tvb, 4);
    if ((int)tvb_reported_length(tvb) < DONE_CONF_LEN + (destcount * 4)) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, count=%d",
                            tvb_reported_length(tvb), destcount);
        return;
    }

    file_id = tvb_get_ntohs(tvb, 2);
    if (file_id > 0) {
        col_append_fstr(pinfo->cinfo, COL_INFO, ":%04X", file_id);
    }

    ti = proto_tree_add_item(tree, hf_uftp_doneconf, tvb, offset, -1, ENC_NA);
    doneconf_tree = proto_item_add_subtree(ti, ett_uftp_doneconf);
    proto_tree_add_item(doneconf_tree, hf_uftp_doneconf_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(doneconf_tree, hf_uftp_doneconf_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(doneconf_tree, hf_uftp_doneconf_file_id, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(doneconf_tree, hf_uftp_doneconf_destcount, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(doneconf_tree, hf_uftp_doneconf_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    if (destcount > 0) {
        destlist = proto_tree_add_item(doneconf_tree, hf_uftp_destlist, tvb, offset, destcount * 4, ENC_NA);
        destlist_tree = proto_item_add_subtree(destlist, ett_uftp_destlist);
    }
    for (idx = 0; idx < destcount; idx++) {
        proto_tree_add_item(destlist_tree, hf_uftp_dest, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}

static void dissect_uftp_hbreq(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *hbreq_tree = NULL;
    unsigned offset = 0;
    uint16_t keylen, siglen;

    if (tvb_reported_length(tvb) < HB_REQ_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    keylen = tvb_get_ntohs(tvb, 8);
    siglen = tvb_get_ntohs(tvb, 10);
    if ((int)tvb_reported_length(tvb) < HB_REQ_LEN + keylen + siglen) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, keylen=%d siglen=%d",
                            tvb_reported_length(tvb), keylen, siglen);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_hbreq, tvb, offset, -1, ENC_NA);
    hbreq_tree = proto_item_add_subtree(ti, ett_uftp_hbreq);
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_reserved, tvb, offset, 3, ENC_BIG_ENDIAN);
    offset += 3;
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_nonce, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_keylen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_siglen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_keyexp, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    if (keylen > 0) {
        proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_keymod, tvb, offset, keylen, ENC_NA);
        offset += keylen;
    }
    if (siglen > 0) {
        proto_tree_add_item(hbreq_tree, hf_uftp_hbreq_verify, tvb, offset, siglen, ENC_NA);
    }
}

static void dissect_uftp_hbresp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *hbresp_tree = NULL;
    unsigned offset = 0;

    if (tvb_reported_length(tvb) < HB_RESP_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_hbresp, tvb, offset, -1, ENC_NA);
    hbresp_tree = proto_item_add_subtree(ti, ett_uftp_hbresp);
    proto_tree_add_item(hbresp_tree, hf_uftp_hbresp_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(hbresp_tree, hf_uftp_hbresp_authenticated, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(hbresp_tree, hf_uftp_hbresp_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(hbresp_tree, hf_uftp_hbresp_nonce, tvb, offset, 4, ENC_BIG_ENDIAN);
}

static void dissect_uftp_keyreq(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *keyreq_tree = NULL;
    unsigned offset = 0;

    if (tvb_reported_length(tvb) < KEY_REQ_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_keyreq, tvb, offset, -1, ENC_NA);
    keyreq_tree = proto_item_add_subtree(ti, ett_uftp_keyreq);
    proto_tree_add_item(keyreq_tree, hf_uftp_keyreq_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(keyreq_tree, hf_uftp_keyreq_reserved, tvb, offset, 3, ENC_BIG_ENDIAN);
}

static void dissect_uftp_proxykey(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *proxykey_tree = NULL;
    unsigned offset = 0;
    uint16_t keylen, siglen;

    if (tvb_reported_length(tvb) < PROXY_KEY_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    keylen = tvb_get_ntohs(tvb, 8);
    siglen = tvb_get_ntohs(tvb, 10);
    if ((int)tvb_reported_length(tvb) < PROXY_KEY_LEN + keylen + siglen) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, keylen=%d siglen=%d",
                            tvb_reported_length(tvb), keylen, siglen);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_proxykey, tvb, offset, -1, ENC_NA);
    proxykey_tree = proto_item_add_subtree(ti, ett_uftp_proxykey);
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_reserved, tvb, offset, 3, ENC_BIG_ENDIAN);
    offset += 3;
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_nonce, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_keylen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_siglen, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_keyexp, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    if (keylen > 0) {
        proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_keymod, tvb, offset, keylen, ENC_NA);
        offset += keylen;
    }
    if (siglen > 0) {
        proto_tree_add_item(proxykey_tree, hf_uftp_proxykey_verify, tvb, offset, siglen, ENC_NA);
    }
}

static void dissect_uftp_encrypted(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *encrypted_tree = NULL;
    unsigned offset = 0;
    uint16_t sig_len, payload_len;

    if (tvb_reported_length(tvb) < ENCRYPTED_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    sig_len = tvb_get_ntohs(tvb, 8);
    payload_len = tvb_get_ntohs(tvb, 10);
    if ((int)tvb_reported_length(tvb) < ENCRYPTED_LEN + sig_len + payload_len) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length, len = %d, sig=%d, payload=%d",
                            tvb_reported_length(tvb), sig_len, payload_len);
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_encrypted, tvb, offset, -1, ENC_NA);
    encrypted_tree = proto_item_add_subtree(ti, ett_uftp_encrypted);
    proto_tree_add_item(encrypted_tree, hf_uftp_encrypted_tstamp, tvb, offset, 8, ENC_TIME_SECS_NSECS);
    offset += 8;
    proto_tree_add_item(encrypted_tree, hf_uftp_encrypted_sig_len, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(encrypted_tree, hf_uftp_encrypted_payload_len, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(encrypted_tree, hf_uftp_encrypted_signature, tvb, offset, sig_len, ENC_NA);
    offset += sig_len;
    proto_tree_add_item(encrypted_tree, hf_uftp_encrypted_payload, tvb, offset, payload_len, ENC_NA);
}

static void dissect_uftp_abort(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_item *ti = NULL;
    proto_tree *abort_tree = NULL;
    unsigned offset = 0;

    if (tvb_reported_length(tvb) < ABORT_LEN) {
        proto_tree_add_expert_format_remaining(tree, pinfo, &ei_uftp_length_invalid, tvb, offset,
                            "Invalid length: %d", tvb_reported_length(tvb));
        return;
    }

    ti = proto_tree_add_item(tree, hf_uftp_abort, tvb, offset, -1, ENC_NA);
    abort_tree = proto_item_add_subtree(ti, ett_uftp_abort);
    proto_tree_add_item(abort_tree, hf_uftp_abort_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_bitmask(abort_tree, tvb, offset, hf_uftp_abort_flags, ett_uftp_abort_flags, abort_flags, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(abort_tree, hf_uftp_abort_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(abort_tree, hf_uftp_abort_host, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(abort_tree, hf_uftp_abort_message, tvb, offset, -1, ENC_ASCII);
}

static int dissect_uftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    uint8_t version;
    uint8_t mes_type;
    uint32_t group_id;
    uint16_t blsize;
    tvbuff_t *next_tvb;
    proto_item *ti = NULL;
    proto_tree *uftp_tree = NULL;
    unsigned offset = 0;

    version = tvb_get_uint8(tvb, 0);
    if (version == 0x50) {
        return call_dissector(uftp5_handle, tvb, pinfo, tree);
    } else if (version == 0x40) {
        return call_dissector(uftp4_handle, tvb, pinfo, tree);
    } else if (version != UFTP_VER_NUM && version != UFTP_3_0_VER) {
        return 0;
    }

    if (tvb_reported_length(tvb) < UFTP_LEN) {
        return 0;
    }

    mes_type = tvb_get_uint8(tvb, 1);
    blsize = tvb_get_ntohs(tvb, 2);
    group_id = tvb_get_ntohl(tvb, 4);

    if (tvb_reported_length(tvb) != (unsigned)UFTP_LEN + blsize) {
        return 0;
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "UFTP");
    /* Clear out stuff in the info column */
    col_clear(pinfo->cinfo,COL_INFO);
    col_add_fstr(pinfo->cinfo, COL_INFO, "%-10s",
                 val_to_str(pinfo->pool, mes_type, messages, "Unknown (%d)"));
    if ((mes_type != HB_REQ) && (mes_type != HB_RESP)) {
        col_append_fstr(pinfo->cinfo, COL_INFO, " ID=%08X", group_id);
    }

    ti = proto_tree_add_item(tree, proto_uftp, tvb, 0, -1, ENC_NA);
    uftp_tree = proto_item_add_subtree(ti, ett_uftp);
    proto_tree_add_item(uftp_tree, hf_uftp_version, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(uftp_tree, hf_uftp_func, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(uftp_tree, hf_uftp_blsize, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(uftp_tree, hf_uftp_group_id, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(uftp_tree, hf_uftp_srcaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;
    proto_tree_add_item(uftp_tree, hf_uftp_destaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;

    next_tvb = tvb_new_subset_length(tvb, offset, blsize);

    switch (mes_type) {
        case ANNOUNCE:
            dissect_uftp_announce(next_tvb, pinfo, uftp_tree);
            break;
        case REGISTER:
            dissect_uftp_register(next_tvb, pinfo, uftp_tree);
            break;
        case CLIENT_KEY:
            dissect_uftp_clientkey(next_tvb, pinfo, uftp_tree);
            break;
        case REG_CONF:
            dissect_uftp_regconf(next_tvb, pinfo, uftp_tree);
            break;
        case FILEINFO:
            if (version == UFTP_3_0_VER) {
                dissect_uftp_fileinfo_30(next_tvb, pinfo, uftp_tree);
            } else {
                dissect_uftp_fileinfo(next_tvb, pinfo, uftp_tree);
            }
            break;
        case KEYINFO:
            dissect_uftp_keyinfo(next_tvb, pinfo, uftp_tree);
            break;
        case INFO_ACK:
            dissect_uftp_infoack(next_tvb, pinfo, uftp_tree);
            break;
        case FILESEG:
            dissect_uftp_fileseg(next_tvb, pinfo, uftp_tree);
            break;
        case DONE:
            dissect_uftp_done(next_tvb, pinfo, uftp_tree);
            break;
        case STATUS:
            dissect_uftp_status(next_tvb, pinfo, uftp_tree);
            break;
        case PRSTATUS:
            dissect_uftp_prstatus(next_tvb, pinfo, uftp_tree);
            break;
        case COMPLETE:
            dissect_uftp_complete(next_tvb, pinfo, uftp_tree);
            break;
        case DONE_CONF:
            dissect_uftp_doneconf(next_tvb, pinfo, uftp_tree);
            break;
        case HB_REQ:
            dissect_uftp_hbreq(next_tvb, pinfo, uftp_tree);
            break;
        case HB_RESP:
            dissect_uftp_hbresp(next_tvb, pinfo, uftp_tree);
            break;
        case KEY_REQ:
            dissect_uftp_keyreq(next_tvb, pinfo, uftp_tree);
            break;
        case PROXY_KEY:
            dissect_uftp_proxykey(next_tvb, pinfo, uftp_tree);
            break;
        case ENCRYPTED:
            dissect_uftp_encrypted(next_tvb, pinfo, uftp_tree);
            break;
        case ABORT:
            dissect_uftp_abort(next_tvb, pinfo, uftp_tree);
            break;
        default:
            proto_tree_add_expert_format(tree, pinfo, &ei_uftp_func_unknown, tvb, offset, -1,
                        "Function unknown: %d", mes_type);
            break;
    }

    return tvb_reported_length(tvb);
}

void proto_register_uftp(void)
{
    static hf_register_info hf[] = {
        { &hf_uftp_version,
            { "Protocol Version", "uftp.version",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_func,
            { "Type", "uftp.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_blsize,
            { "Block Size", "uftp.blsize",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_group_id,
            { "Group ID", "uftp.group_id",
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_srcaddr,
            { "Source Address", "uftp.srcaddr",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_destaddr,
            { "Destination Address", "uftp.destaddr",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_destlist,
            { "Destination List", "uftp.destlist",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_dest,
            { "Destination", "uftp.dest",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce,
            { "ANNOUNCE", "uftp.announce",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_func,
            { "Type", "uftp.announce.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_flags,
            { "Flags", "uftp.announce.flags",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_flags_restart,
            { "Restart", "uftp.announce.flags.restart",
            FT_BOOLEAN, 8, NULL, FLAG_RESTART, NULL, HFILL }
        },
        { &hf_uftp_announce_flags_sync,
            { "Sync mode", "uftp.announce.flags.sync",
            FT_BOOLEAN, 8, NULL, FLAG_SYNC_MODE, NULL, HFILL }
        },
        { &hf_uftp_announce_flags_syncpreview,
            { "Sync preview mode", "uftp.announce.flags.syncpreview",
            FT_BOOLEAN, 8, NULL, FLAG_SYNC_PREVIEW, NULL, HFILL }
        },
        { &hf_uftp_announce_flags_reserved,
            { "Reserved", "uftp.announce.flags.reserved",
            FT_BOOLEAN, 8, NULL, FLAG_ANNOUNCE_RESERVED, NULL, HFILL }
        },
        { &hf_uftp_announce_destcount,
            { "Destination Count", "uftp.announce.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_announce_int,
            { "Announce Interval", "uftp.announce.announce_int",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_status_int,
            { "Status Interval", "uftp.announce.status_int",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_register_int,
            { "Register Interval", "uftp.announce.register_int",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_done_int,
            { "Done Interval", "uftp.announce.done_int",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_announce_time,
            { "Announce Time", "uftp.announce.announce_time",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_status_time,
            { "Status Time", "uftp.announce.status_time",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_mtu,
            { "MTU", "uftp.announce.mtu",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_privatemcast,
            { "Private Multicast Address", "uftp.announce.privatemcast",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_client_auth,
            { "Client Auth", "uftp.announce.client_auth",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_sigtype,
            { "Signature Type", "uftp.announce.sigtype",
            FT_UINT8, BASE_DEC, VALS(signature_types), 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_hashtype,
            { "Hash Type", "uftp.announce.hashtype",
            FT_UINT8, BASE_DEC, VALS(hash_types), 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_keytype,
            { "Key Type", "uftp.announce.keytype",
            FT_UINT8, BASE_DEC, VALS(key_types), 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_keylen,
            { "Public Key Length", "uftp.announce.keylen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_reserved,
            { "Reserved", "uftp.announce.reserved",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_keyexp,
            { "Public Key Exponent", "uftp.announce.keyexp",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_rand1,
            { "Server Random Number", "uftp.announce.rand1",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_announce_keymod,
            { "Public Key Modulus", "uftp.announce.keymod",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register,
            { "REGISTER", "uftp.register",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_func,
            { "Type", "uftp.register.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_reserved,
            { "Reserved", "uftp.register.reserved",
            FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_destcount,
            { "Destination Count", "uftp.register.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_premaster_len,
            { "Premaster Secret Length", "uftp.register.premaster_len",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_rand2,
            { "Client Random Number", "uftp.register.rand2",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_register_premaster,
            { "Encrypted Premaster Secret", "uftp.register.premaster",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey,
            { "CLIENT_KEY", "uftp.clientkey",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_func,
            { "Type", "uftp.clientkey.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_reserved,
            { "Reserved", "uftp.clientkey.reserved",
            FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_keylen,
            { "Key Length", "uftp.clientkey.keylen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_verifylen,
            { "Signature Length", "uftp.clientkey.verifylen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_keyexp,
            { "Public Key Exponent", "uftp.clientkey.keyexp",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_keymod,
            { "Public Key Modulus", "uftp.clientkey.keymod",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_clientkey_verify,
            { "Signature", "uftp.clientkey.verify",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_regconf,
            { "REG_CONF", "uftp.regconf",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_regconf_func,
            { "Type", "uftp.regconf.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_regconf_reserved,
            { "Reserved", "uftp.regconf.reserved",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_regconf_destcount,
            { "Destination Count", "uftp.regconf.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo,
            { "FILEINFO", "uftp.fileinfo",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_func,
            { "Type", "uftp.fileinfo.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_ftype,
            { "File Type", "uftp.fileinfo.ftype",
            FT_UINT8, BASE_DEC, VALS(file_types), 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_file_id,
            { "File ID", "uftp.fileinfo.file_id",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_block_total,
            { "Total Blocks", "uftp.fileinfo.block_total",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_section_total,
            { "Total Sections", "uftp.fileinfo.section_total",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_destcount,
            { "Destination Count", "uftp.fileinfo.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_fsize,
            { "File Size", "uftp.fileinfo.fsize",
            FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_ftstamp,
            { "File Timestamp", "uftp.fileinfo.tstamp",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileinfo_name,
            { "File Name", "uftp.fileinfo.name",
            FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo,
            { "KEYINFO", "uftp.keyinfo",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_func,
            { "Type", "uftp.keyinfo.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_reserved,
            { "Reserved", "uftp.keyinfo.reserved",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_destcount,
            { "Destination Count", "uftp.keyinfo.destcount",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_groupmaster_len,
            { "Group Master Length", "uftp.keyinfo.groupmaster_len",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_tstamp,
            { "Timestamp", "uftp.keyinfo.tstamp",
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_destkey,
            { "Destination Key", "uftp.keyinfo.destkey",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_destaddr,
            { "Destination Address", "uftp.keyinfo.destaddr",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyinfo_groupmaster,
            { "Encrypted Group Master", "uftp.keyinfo.groupmaster",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack,
            { "INFO_ACK", "uftp.infoack",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_func,
            { "Type", "uftp.infoack.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_flags,
            { "Flags", "uftp.infoack.flags",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_flags_partial,
            { "Partial", "uftp.infoack.flags.partial",
            FT_BOOLEAN, 8, NULL, FLAG_PARTIAL, NULL, HFILL }
        },
        { &hf_uftp_infoack_flags_reserved,
            { "Reserved", "uftp.infoack.flags.reserved",
            FT_BOOLEAN, 8, NULL, FLAG_INFOACK_RESERVED, NULL, HFILL }
        },
        { &hf_uftp_infoack_file_id,
            { "File ID", "uftp.infoack.file_id",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_destcount,
            { "Destination Count", "uftp.infoack.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_reserved,
            { "Reserved", "uftp.infoack.reserved",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_infoack_verify_data,
            { "Verify Data", "uftp.infoack.verify_data",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg,
            { "FILESEG", "uftp.fileseg",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_func,
            { "Type", "uftp.fileseg.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_reserved1,
            { "Reserved", "uftp.fileseg.reserved1",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_file_id,
            { "File ID", "uftp.fileseg.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_pass,
            { "Pass", "uftp.fileseg.pass",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_reserved2,
            { "Reserved", "uftp.fileseg.reserved2",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_section,
            { "Section", "uftp.fileseg.section",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_seq_num,
            { "Sequence Number", "uftp.fileseg.seq_num",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_fileseg_data,
            { "Data", "uftp.fileseg.data",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_done,
            { "DONE", "uftp.done",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_done_func,
            { "Type", "uftp.done.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_done_pass,
            { "Pass", "uftp.done.pass",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_done_section,
            { "Section", "uftp.done.section",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_done_file_id,
            { "File ID", "uftp.done.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_done_destcount,
            { "Destination Count", "uftp.done.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status,
            { "STATUS", "uftp.status",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_func,
            { "Type", "uftp.status.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_reserved,
            { "Reserved", "uftp.status.reserved",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_file_id,
            { "File ID", "uftp.status.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_pass,
            { "Pass", "uftp.status.pass",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_seq,
            { "Sequence", "uftp.status.seq",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_section,
            { "Section", "uftp.status.section",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_nak_count,
            { "NAK Count", "uftp.status.nak_count",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_status_naks,
            { "NAKs", "uftp.status.naks",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus,
            { "PRSTATUS", "uftp.prstatus",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_func,
            { "Type", "uftp.prstatus.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_reserved1,
            { "Reserved", "uftp.prstatus.reserved1",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_file_id,
            { "File ID", "uftp.prstatus.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_pass,
            { "Pass", "uftp.prstatus.pass",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_seq,
            { "Sequence", "uftp.prstatus.seq",
            FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_section,
            { "Section", "uftp.prstatus.section",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_destcount,
            { "Destination Count", "uftp.prstatus.destcount",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_prstatus_reserved2,
            { "Reserved", "uftp.prstatus.reserved2",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete,
            { "COMPLETE", "uftp.complete",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete_func,
            { "Type", "uftp.complete.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete_status,
            { "Completion status", "uftp.complete.status",
            FT_UINT8, BASE_DEC, VALS(comp_status), 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete_file_id,
            { "File ID", "uftp.complete.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete_destcount,
            { "Destination Count", "uftp.complete.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_complete_reserved2,
            { "Reserved", "uftp.complete.reserved2",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf,
            { "DONE_CONF", "uftp.doneconf",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf_func,
            { "Type", "uftp.doneconf.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf_reserved1,
            { "Reserved", "uftp.doneconf.reserved1",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf_file_id,
            { "File ID", "uftp.doneconf.file_id",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf_destcount,
            { "Destination Count", "uftp.doneconf.destcount",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_doneconf_reserved2,
            { "Reserved", "uftp.doneconf.reserved2",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq,
            { "HB_REQ", "uftp.hbreq",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_func,
            { "Type", "uftp.hbreq.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_reserved,
            { "Reserved", "uftp.hbreq.reserved",
            FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_nonce,
            { "Nonce", "uftp.hbreq.nonce",
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_keylen,
            { "Key Length", "uftp.hbreq.keylen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_siglen,
            { "Signature Length", "uftp.hbreq.siglen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_keyexp,
            { "Public Key Exponent", "uftp.hbreq.keyexp",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_keymod,
            { "Public Key Modulus", "uftp.hbreq.keymod",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbreq_verify,
            { "Signature", "uftp.hbreq.verify",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbresp,
            { "HB_RESP", "uftp.hbresp",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbresp_func,
            { "Type", "uftp.hbresp.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbresp_authenticated,
            { "Authenticated", "uftp.hbresp.authenticated",
            FT_UINT8, BASE_DEC, VALS(hb_auth_types), 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbresp_reserved,
            { "Reserved", "uftp.hbresp.reserved",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_hbresp_nonce,
            { "Nonce", "uftp.hbresp.nonce",
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyreq,
            { "KEY_REQ", "uftp.keyreq",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyreq_func,
            { "Type", "uftp.keyreq.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_keyreq_reserved,
            { "Reserved", "uftp.keyreq.reserved",
            FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey,
            { "PROXY_KEY", "uftp.proxykey",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_func,
            { "Type", "uftp.proxykey.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_reserved,
            { "Reserved", "uftp.proxykey.reserved",
            FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_nonce,
            { "Nonce", "uftp.proxykey.nonce",
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_keylen,
            { "Key Length", "uftp.proxykey.keylen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_siglen,
            { "Signature Length", "uftp.proxykey.siglen",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_keyexp,
            { "Public Key Exponent", "uftp.proxykey.keyexp",
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_keymod,
            { "Public Key Modulus", "uftp.proxykey.keymod",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_proxykey_verify,
            { "Signature", "uftp.proxykey.verify",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted,
            { "ENCRYPTED", "uftp.encrypted",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted_tstamp,
            { "Timestamp", "uftp.encrypted.tstamp",
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted_sig_len,
            { "Signature Length", "uftp.encrypted.sig_len",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted_payload_len,
            { "Payload Length", "uftp.encrypted.payload_len",
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted_signature,
            { "Signature", "uftp.encrypted.signature",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_encrypted_payload,
            { "Encrypted Payload", "uftp.encrypted.payload",
            FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort,
            { "ABORT", "uftp.abort",
            FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort_func,
            { "Type", "uftp.abort.func",
            FT_UINT8, BASE_DEC, VALS(messages), 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort_flags,
            { "Flags", "uftp.abort.flags",
            FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort_flags_curfile,
            { "Current file", "uftp.abort.flags.curfile",
            FT_BOOLEAN, 8, NULL, FLAG_CURRENT_FILE, NULL, HFILL }
        },
        { &hf_uftp_abort_flags_reserved,
            { "Reserved", "uftp.abort.flags.reserved",
            FT_BOOLEAN, 8, NULL, FLAG_ABORT_RESERVED, NULL, HFILL }
        },
        { &hf_uftp_abort_reserved,
            { "Reserved", "uftp.abort.reserved",
            FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort_host,
            { "Host", "uftp.abort.host",
            FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
        },
        { &hf_uftp_abort_message,
            { "Message", "uftp.abort.message",
            FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
        }
    };

    /* Setup protocol subtree array */
    static int *ett[] = {
        &ett_uftp,
        &ett_uftp_announce,
        &ett_uftp_register,
        &ett_uftp_clientkey,
        &ett_uftp_regconf,
        &ett_uftp_fileinfo,
        &ett_uftp_keyinfo,
        &ett_uftp_keyinfo_destkey,
        &ett_uftp_infoack,
        &ett_uftp_fileseg,
        &ett_uftp_done,
        &ett_uftp_status,
        &ett_uftp_prstatus,
        &ett_uftp_complete,
        &ett_uftp_doneconf,
        &ett_uftp_hbreq,
        &ett_uftp_hbresp,
        &ett_uftp_keyreq,
        &ett_uftp_proxykey,
        &ett_uftp_encrypted,
        &ett_uftp_abort,
        &ett_uftp_announce_flags,
        &ett_uftp_infoack_flags,
        &ett_uftp_abort_flags,
        &ett_uftp_destlist
    };

    static ei_register_info ei[] = {
        { &ei_uftp_length_invalid, { "uftp.length.invalid", PI_MALFORMED, PI_ERROR, "Length is invalid", EXPFILL }},
        { &ei_uftp_func_unknown, { "uftp.func.invalid", PI_MALFORMED, PI_ERROR, "Unknown function", EXPFILL }}
    };

    expert_module_t* expert_uftp;

    proto_uftp = proto_register_protocol("UDP based FTP w/ multicast", "UFTP", "uftp");

    proto_register_field_array(proto_uftp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    expert_uftp = expert_register_protocol(proto_uftp);
    expert_register_field_array(expert_uftp, ei, array_length(ei));
    uftp_handle = register_dissector("uftp", dissect_uftp, proto_uftp);
}

void proto_reg_handoff_uftp(void)
{
    uftp4_handle = find_dissector("uftp4");
    uftp5_handle = find_dissector("uftp5");
    dissector_add_uint_with_preference("udp.port", UFTP_PORT, uftp_handle);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
