/* packet-iso8583.c
 * Routines for ISO-8583 Protocol dissection
 * Copyright 2015, Paulo Roberto Brandao <brandao@ubiqua.inf.br>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
 * ISO 8583 Financial transaction card originated messages - Interchange
 * message specifications is the International Organization for Standardization
 * standard for systems that exchange electronic transactions made by
 * cardholders using payment cards.
 */

#include <config.h>
#include <stdlib.h>
#include <string.h>

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/wmem_scopes.h>
#include "packet-tcp.h"

#include <wsutil/strtoi.h>

/* bitmap length */
#define BM_LEN 8

/* Endianness */
#define BIGEND 1
#define LITEND 2

/* conversion types */
enum bin2hex_enum {
  TYPE_BCD, /* nibble */
  TYPE_BIN  /* raw data */
};


/* ISO bits content types */
typedef enum {
  ISO_TNONE,
  ISO_TA,
  ISO_TN,
  ISO_TXN,
  ISO_TS,
  ISO_TAS,
  ISO_TAN,
  ISO_TANS,
  ISO_TNS,
  ISO_TB,
  ISO_TZ
} iso_srt_types;

struct iso_type {
  guint32 type;
  guint32 maxsize;
  guint32 varlen;
};

/* ISO 8583-1 version 1987 Bit type specification */
static struct iso_type iso_1987[128] = {
  { ISO_TB, 0, 0 }, /*Bit 1*/
  { ISO_TN, 19, 2 }, /*Bit 2*/
  { ISO_TN, 6, 0 }, /*Bit 3*/
  { ISO_TN, 12, 0 }, /*Bit 4*/
  { ISO_TN, 12, 0 }, /*Bit 5*/
  { ISO_TN, 12, 0 }, /*Bit 6*/
  { ISO_TN, 10, 0 }, /*Bit 7*/
  { ISO_TN, 8, 0 }, /*Bit 8*/
  { ISO_TN, 8, 0 }, /*Bit 9*/
  { ISO_TN, 8, 0 }, /*Bit 10*/
  { ISO_TN, 6, 0 }, /*Bit 11*/
  { ISO_TN, 6, 0 }, /*Bit 12*/
  { ISO_TN, 4, 0 }, /*Bit 13*/
  { ISO_TN, 4, 0 }, /*Bit 14*/
  { ISO_TN, 4, 0 }, /*Bit 15*/
  { ISO_TN, 4, 0 }, /*Bit 16*/
  { ISO_TN, 4, 0 }, /*Bit 17*/
  { ISO_TN, 4, 0 }, /*Bit 18*/
  { ISO_TN, 3, 0 }, /*Bit 19*/
  { ISO_TN, 3, 0 }, /*Bit 20*/
  { ISO_TN, 3, 0 }, /*Bit 21*/
  { ISO_TN, 3, 0 }, /*Bit 22*/
  { ISO_TN, 3, 0 }, /*Bit 23*/
  { ISO_TN, 3, 0 }, /*Bit 24*/
  { ISO_TN, 2, 0 }, /*Bit 25*/
  { ISO_TN, 2, 0 }, /*Bit 26*/
  { ISO_TN, 1, 0 }, /*Bit 27*/
  { ISO_TXN, 9, 0 }, /*Bit 28*/
  { ISO_TXN, 9, 0 }, /*Bit 29*/
  { ISO_TXN, 9, 0 }, /*Bit 30*/
  { ISO_TXN, 9, 0 }, /*Bit 31*/
  { ISO_TN, 11, 2 }, /*Bit 32*/
  { ISO_TN, 11, 2 }, /*Bit 33*/
  { ISO_TNS, 28, 2 }, /*Bit 34*/
  { ISO_TZ, 37, 2 }, /*Bit 35*/
  { ISO_TAN, 104, 3 }, /*Bit 36*/
  { ISO_TAN, 12, 0 }, /*Bit 37*/
  { ISO_TAN, 6, 0 }, /*Bit 38*/
  { ISO_TAN, 2, 0 }, /*Bit 39*/
  { ISO_TANS, 3, 0 }, /*Bit 40*/
  { ISO_TANS, 8, 0 }, /*Bit 41*/
  { ISO_TANS, 15, 0 }, /*Bit 42*/
  { ISO_TANS, 40, 0 }, /*Bit 43*/
  { ISO_TANS, 25, 2 }, /*Bit 44*/
  { ISO_TANS, 76, 2 }, /*Bit 45*/
  { ISO_TANS, 999, 3 }, /*Bit 46*/
  { ISO_TANS, 999, 3 }, /*Bit 47*/
  { ISO_TANS, 999, 3 }, /*Bit 48*/
  { ISO_TANS, 3, 0 }, /*Bit 49*/
  { ISO_TAN, 3, 0 }, /*Bit 50*/
  { ISO_TAN, 3, 0 }, /*Bit 51*/
  /*{ ISO_TB, 64, 0 },*/ /*Bit 52*/
  { ISO_TB, 8, 0 }, /*Bit 52*/
  { ISO_TN, 8, 0 }, /*Bit 53*/
  { ISO_TAN, 120, 3 }, /*Bit 54*/
  { ISO_TANS, 999, 3 }, /*Bit 55*/
  { ISO_TANS, 999, 3 }, /*Bit 56*/
  { ISO_TANS, 999, 3 }, /*Bit 57*/
  { ISO_TANS, 999, 3 }, /*Bit 58*/
  { ISO_TANS, 999, 3 }, /*Bit 59*/
  { ISO_TANS, 999, 3 }, /*Bit 60*/
  { ISO_TANS, 999, 3 }, /*Bit 61*/
  { ISO_TANS, 999, 3 }, /*Bit 62*/
  { ISO_TANS, 999, 3 }, /*Bit 63*/
  { ISO_TB, 8, 0 }, /*Bit 64*/
  { ISO_TB, 0, 0 }, /*Bit 65*/
  { ISO_TN, 1, 0 }, /*Bit 66*/
  { ISO_TN, 2, 0 }, /*Bit 67*/
  { ISO_TN, 3, 0 }, /*Bit 68*/
  { ISO_TN, 3, 0 }, /*Bit 69*/
  { ISO_TN, 3, 0 }, /*Bit 70*/
  { ISO_TN, 4, 0 }, /*Bit 71*/
  { ISO_TN, 4, 0 }, /*Bit 72*/
  { ISO_TN, 6, 0 }, /*Bit 73*/
  { ISO_TN, 10, 0 }, /*Bit 74*/
  { ISO_TN, 10, 0 }, /*Bit 75*/
  { ISO_TN, 10, 0 }, /*Bit 76*/
  { ISO_TN, 10, 0 }, /*Bit 77*/
  { ISO_TN, 10, 0 }, /*Bit 78*/
  { ISO_TN, 10, 0 }, /*Bit 79*/
  { ISO_TN, 10, 0 }, /*Bit 80*/
  { ISO_TN, 10, 0 }, /*Bit 81*/
  { ISO_TN, 12, 0 }, /*Bit 82*/
  { ISO_TN, 12, 0 }, /*Bit 83*/
  { ISO_TN, 12, 0 }, /*Bit 84*/
  { ISO_TN, 12, 0 }, /*Bit 85*/
  { ISO_TN, 15, 0 }, /*Bit 86*/
  { ISO_TN, 15, 0 }, /*Bit 87*/
  { ISO_TN, 15, 0 }, /*Bit 88*/
  { ISO_TN, 15, 0 }, /*Bit 89*/
  { ISO_TN, 42, 0 }, /*Bit 90*/
  { ISO_TANS, 1, 0 }, /*Bit 91*/
  { ISO_TN, 2, 0 }, /*Bit 92*/
  { ISO_TN, 5, 0 }, /*Bit 93*/
  { ISO_TANS, 7, 0 }, /*Bit 94*/
  { ISO_TANS, 42, 0 }, /*Bit 95*/
  { ISO_TB, 8, 0 }, /*Bit 96*/
  { ISO_TXN, 17, 0 }, /*Bit 97*/
  { ISO_TANS, 25, 0 }, /*Bit 98*/
  { ISO_TN, 11, 2 }, /*Bit 99*/
  { ISO_TN, 11, 2 }, /*Bit 100*/
  { ISO_TANS, 17, 2 }, /*Bit 101*/
  { ISO_TANS, 28, 2 }, /*Bit 102*/
  { ISO_TANS, 28, 2 }, /*Bit 103*/
  { ISO_TANS, 100, 3 }, /*Bit 104*/
  { ISO_TANS, 999, 3 }, /*Bit 105*/
  { ISO_TANS, 999, 3 }, /*Bit 106*/
  { ISO_TANS, 999, 3 }, /*Bit 107*/
  { ISO_TANS, 999, 3 }, /*Bit 108*/
  { ISO_TANS, 999, 3 }, /*Bit 109*/
  { ISO_TANS, 999, 3 }, /*Bit 110*/
  { ISO_TANS, 999, 3 }, /*Bit 111*/
  { ISO_TANS, 999, 3 }, /*Bit 112*/
  { ISO_TANS, 999, 3 }, /*Bit 113*/
  { ISO_TANS, 999, 3 }, /*Bit 114*/
  { ISO_TANS, 999, 3 }, /*Bit 115*/
  { ISO_TANS, 999, 3 }, /*Bit 116*/
  { ISO_TANS, 999, 3 }, /*Bit 117*/
  { ISO_TANS, 999, 3 }, /*Bit 118*/
  { ISO_TANS, 999, 3 }, /*Bit 119*/
  { ISO_TANS, 999, 3 }, /*Bit 120*/
  { ISO_TANS, 999, 3 }, /*Bit 121*/
  { ISO_TANS, 999, 3 }, /*Bit 122*/
  { ISO_TANS, 999, 3 }, /*Bit 123*/
  { ISO_TANS, 999, 3 }, /*Bit 124*/
  { ISO_TANS, 999, 3 }, /*Bit 125*/
  { ISO_TANS, 999, 3 }, /*Bit 126*/
  { ISO_TANS, 999, 3 }, /*Bit 127*/
  { ISO_TB, 8, 0 } /*Bit 128*/
};

/* ISO 8583-1 version 1993 Bit type specification */
static struct iso_type  iso_1993[128] = {
  { ISO_TB, 0, 0 }, /*Bit 1*/
  { ISO_TN, 19, 2 }, /*Bit 2*/
  { ISO_TN, 6, 0 }, /*Bit 3*/
  { ISO_TN, 12, 0 }, /*Bit 4*/
  { ISO_TN, 12, 0 }, /*Bit 5*/
  { ISO_TN, 12, 0 }, /*Bit 6*/
  { ISO_TN, 10, 0 }, /*Bit 7*/
  { ISO_TN, 8, 0 }, /*Bit 8*/
  { ISO_TN, 8, 0 }, /*Bit 9*/
  { ISO_TN, 8, 0 }, /*Bit 10*/
  { ISO_TN, 6, 0 }, /*Bit 11*/
  { ISO_TN, 12, 0 }, /*Bit 12*/
  { ISO_TN, 4, 0 }, /*Bit 13*/
  { ISO_TN, 4, 0 }, /*Bit 14*/
  { ISO_TN, 4, 0 }, /*Bit 15*/
  { ISO_TN, 4, 0 }, /*Bit 16*/
  { ISO_TN, 4, 0 }, /*Bit 17*/
  { ISO_TN, 4, 0 }, /*Bit 18*/
  { ISO_TN, 3, 0 }, /*Bit 19*/
  { ISO_TN, 3, 0 }, /*Bit 20*/
  { ISO_TN, 3, 0 }, /*Bit 21*/
  { ISO_TAN, 12, 0 }, /*Bit 22*/
  { ISO_TN, 3, 0 }, /*Bit 23*/
  { ISO_TN, 3, 0 }, /*Bit 24*/
  { ISO_TN, 4, 0 }, /*Bit 25*/
  { ISO_TN, 4, 0 }, /*Bit 26*/
  { ISO_TN, 1, 0 }, /*Bit 27*/
  { ISO_TN, 6, 0 }, /*Bit 28*/
  { ISO_TN, 3, 0 }, /*Bit 29*/
  { ISO_TN, 24, 0 }, /*Bit 30*/
  { ISO_TANS, 99, 2 }, /*Bit 31*/
  { ISO_TN, 11, 2 }, /*Bit 32*/
  { ISO_TN, 11, 2 }, /*Bit 33*/
  { ISO_TNS, 28, 2 }, /*Bit 34*/
  { ISO_TZ, 37, 2 }, /*Bit 35*/
  { ISO_TZ, 104, 3 }, /*Bit 36*/
  { ISO_TAN, 12, 0 }, /*Bit 37*/
  { ISO_TAN, 6, 0 }, /*Bit 38*/
  { ISO_TN, 3, 0 }, /*Bit 39*/
  { ISO_TN, 3, 0 }, /*Bit 40*/
  { ISO_TANS, 8, 0 }, /*Bit 41*/
  { ISO_TANS, 15, 0 }, /*Bit 42*/
  { ISO_TANS, 99, 2 }, /*Bit 43*/
  { ISO_TANS, 99, 2 }, /*Bit 44*/
  { ISO_TANS, 76, 2 }, /*Bit 45*/
  { ISO_TANS, 204, 3 }, /*Bit 46*/
  { ISO_TANS, 999, 3 }, /*Bit 47*/
  { ISO_TANS, 999, 3 }, /*Bit 48*/
  { ISO_TAN, 3, 0 }, /*Bit 49*/
  { ISO_TAN, 3, 0 }, /*Bit 50*/
  { ISO_TAN, 3, 0 }, /*Bit 51*/
  /*{ ISO_TB, 64, 0 },*/ /*Bit 52*/
  { ISO_TB, 8, 0 }, /*Bit 52*/
  { ISO_TB, 48, 0 }, /*Bit 53*/
  { ISO_TANS, 120, 3 }, /*Bit 54*/
  { ISO_TB, 255, 3 }, /*Bit 55*/
  { ISO_TN, 35, 2 }, /*Bit 56*/
  { ISO_TN, 3, 0 }, /*Bit 57*/
  { ISO_TN, 11, 2 }, /*Bit 58*/
  { ISO_TANS, 999, 3 }, /*Bit 59*/
  { ISO_TANS, 999, 3 }, /*Bit 60*/
  { ISO_TANS, 999, 3 }, /*Bit 61*/
  { ISO_TANS, 999, 3 }, /*Bit 62*/
  { ISO_TANS, 999, 3 }, /*Bit 63*/
  { ISO_TB, 8, 0 }, /*Bit 64*/
  { ISO_TB, 0, 0 }, /*Bit 65*/
  { ISO_TANS, 204, 3 }, /*Bit 66*/
  { ISO_TN, 2, 0 }, /*Bit 67*/
  { ISO_TN, 3, 0 }, /*Bit 68*/
  { ISO_TN, 3, 0 }, /*Bit 69*/
  { ISO_TN, 3, 0 }, /*Bit 70*/
  { ISO_TN, 6, 0 }, /*Bit 71*/
  { ISO_TANS, 999, 3 }, /*Bit 72*/
  { ISO_TN, 6, 0 }, /*Bit 73*/
  { ISO_TN, 10, 0 }, /*Bit 74*/
  { ISO_TN, 10, 0 }, /*Bit 75*/
  { ISO_TN, 10, 0 }, /*Bit 76*/
  { ISO_TN, 10, 0 }, /*Bit 77*/
  { ISO_TN, 10, 0 }, /*Bit 78*/
  { ISO_TN, 10, 0 }, /*Bit 79*/
  { ISO_TN, 10, 0 }, /*Bit 80*/
  { ISO_TN, 10, 0 }, /*Bit 81*/
  { ISO_TN, 10, 0 }, /*Bit 82*/
  { ISO_TN, 10, 0 }, /*Bit 83*/
  { ISO_TN, 10, 0 }, /*Bit 84*/
  { ISO_TN, 10, 0 }, /*Bit 85*/
  { ISO_TN, 16, 0 }, /*Bit 86*/
  { ISO_TN, 16, 0 }, /*Bit 87*/
  { ISO_TN, 16, 0 }, /*Bit 88*/
  { ISO_TN, 16, 0 }, /*Bit 89*/
  { ISO_TN, 10, 0 }, /*Bit 90*/
  { ISO_TN, 3, 0 }, /*Bit 91*/
  { ISO_TN, 3, 0 }, /*Bit 92*/
  { ISO_TN, 11, 2 }, /*Bit 93*/
  { ISO_TN, 11, 2 }, /*Bit 94*/
  { ISO_TANS, 99, 2 }, /*Bit 95*/
  { ISO_TB, 999, 3 }, /*Bit 96*/
  { ISO_TXN, 17, 0 }, /*Bit 97*/
  { ISO_TANS, 25, 0 }, /*Bit 98*/
  { ISO_TAN, 11, 2 }, /*Bit 99*/
  { ISO_TN, 11, 2 }, /*Bit 100*/
  { ISO_TANS, 17, 2 }, /*Bit 101*/
  { ISO_TANS, 28, 2 }, /*Bit 102*/
  { ISO_TANS, 28, 2 }, /*Bit 103*/
  { ISO_TANS, 100, 3 }, /*Bit 104*/
  { ISO_TN, 16, 0 }, /*Bit 105*/
  { ISO_TN, 16, 0 }, /*Bit 106*/
  { ISO_TN, 10, 0 }, /*Bit 107*/
  { ISO_TN, 10, 0 }, /*Bit 108*/
  { ISO_TANS, 84, 2 }, /*Bit 109*/
  { ISO_TANS, 84, 2 }, /*Bit 110*/
  { ISO_TANS, 999, 3 }, /*Bit 111*/
  { ISO_TANS, 999, 3 }, /*Bit 112*/
  { ISO_TANS, 999, 3 }, /*Bit 113*/
  { ISO_TANS, 999, 3 }, /*Bit 114*/
  { ISO_TANS, 999, 3 }, /*Bit 115*/
  { ISO_TANS, 999, 3 }, /*Bit 116*/
  { ISO_TANS, 999, 3 }, /*Bit 117*/
  { ISO_TANS, 999, 3 }, /*Bit 118*/
  { ISO_TANS, 999, 3 }, /*Bit 119*/
  { ISO_TANS, 999, 3 }, /*Bit 120*/
  { ISO_TANS, 999, 3 }, /*Bit 121*/
  { ISO_TANS, 999, 3 }, /*Bit 122*/
  { ISO_TANS, 999, 3 }, /*Bit 123*/
  { ISO_TANS, 999, 3 }, /*Bit 124*/
  { ISO_TANS, 999, 3 }, /*Bit 125*/
  { ISO_TANS, 999, 3 }, /*Bit 126*/
  { ISO_TANS, 999, 3 }, /*Bit 127*/
  { ISO_TB, 8, 0 } /*Bit 128*/
};

void proto_reg_handoff_iso8583(void);
void proto_register_iso8583(void);

static dissector_handle_t iso8583_handle;

static int proto_iso8583 = -1;

static int hf_iso8583_len = -1;
static int hf_iso8583_mti = -1;
static int hf_iso8583_bitmap1 = -1;
static int hf_iso8583_bitmap2 = -1;

static int iso8583_data_bit[128] = {
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1
};

static gint ett_iso8583 = -1;

static expert_field ei_iso8583_MALFORMED = EI_INIT;

static struct iso_type *data_array = NULL;

/* Types definitions */
#define ASCII_CHARSET 1
#define NUM_NIBBLE_CHARSET 2
#define BIN_ASCII_ENC 1
#define BIN_BIN_ENC 2

/* Global preference */
static gint charset_pref = ASCII_CHARSET;
static gint bin_encode_pref = BIN_ASCII_ENC;

static gint len_byte_order = LITEND;

/*
 * Functions that check field type against specification.
 */

/*
 * Macro used by functions that check correctness of
 * the message type. Go through string checking the
 * condition passed as argument.
 */
#define char_cond( cond )\
  const char *c;\
const char *end= string + size;\
for(c=string; c< end && ( cond ) ; c++);\
return size && c==end

/* Hexa representation of Binary field */
static int ishex_str(const char* string, guint size)
{
  /*char_cond(g_ascii_isdigit(*c) || ( g_ascii_toupper(*c)>= 'A' && g_ascii_toupper(*c)<= 'F'));*/
  char_cond(g_ascii_isxdigit(*c));
}

/* ISO_TN */
static int isnum_str(const char* string, unsigned int size)
{
  char_cond( g_ascii_isdigit(*c) );
}

/* ISO_TAS */
static int isalspec_str(const char* string, unsigned int size)
{
  char_cond(g_ascii_isalpha(*c) || g_ascii_isspace(*c) || g_ascii_ispunct(*c));
}

/* ISO_TA */
static int isalpha_str(const char* string, unsigned int size)
{
  char_cond( g_ascii_isalpha(*c));
}

/* ISO_TAN */
static int isalnum_str(const char* string, unsigned int size)
{
  char_cond( g_ascii_isalnum(*c));
}

/* ISO_ANS */
static int isalnumspec_str(const char* string, unsigned int size)
{
  char_cond(g_ascii_isalnum(*c) || g_ascii_isspace(*c) || g_ascii_ispunct(*c));
}

/* ISO_NS */
static int isnumspec_str(const char* string, unsigned int size)
{
  char_cond(g_ascii_isdigit(*c) || g_ascii_isspace(*c) || g_ascii_ispunct(*c));
}

/* ISO_S */
static int isspec_str(const char* string, unsigned int size)
{
  char_cond(g_ascii_isspace(*c) || g_ascii_ispunct(*c));
}

static gboolean isstrtype_ok( int type, const char* string, unsigned int size)
{
  switch(type)
  {
    case ISO_TA:
      return isalpha_str(string, size);
    case ISO_TN:
      return isnum_str(string, size);
    case ISO_TXN:
      return ishex_str(string, size);
    case ISO_TS:
      return isspec_str(string, size);
    case ISO_TAS:
      return isalspec_str(string, size);
    case ISO_TAN:
      return isalnum_str(string, size);
    case ISO_TANS:
      return isalnumspec_str(string, size);
    case ISO_TNS:
      return isnumspec_str(string, size);
    case ISO_TB:
      return ishex_str(string, size);
    case ISO_TZ:
      if(charset_pref == ASCII_CHARSET)
        return isalnumspec_str(string, size);
      else
        return ishex_str(string, size);
  }
  return 0;
}

/* Endianness */
static const enum_val_t enumendians[] = {
  { "bigendian", "Big endian", BIGEND},
  { "littleendian", "Little endian", LITEND},
  { NULL, NULL, 0}
};

/* Charset */
static const enum_val_t enum_charset[] = {
  {"ascii", "Digits represented as ASCII Characters", ASCII_CHARSET},
  {"bcd", "Digits represented in nibbles", NUM_NIBBLE_CHARSET},
  {NULL, NULL, 0}
};

/* Encoding */
static const enum_val_t enum_bin_encode[] = {
  {"ascii", "Bin data represented as Hex Ascii characters", BIN_ASCII_ENC},
  {"bin", "Bin data not encoded", BIN_BIN_ENC},
  {NULL, NULL, 0}
};


#define iso8583_MIN_LENGTH 22 /* 2 (len) + 4 (mti) + 16 (fst bitmap in hexa) */


/* ISO standard version */
static const value_string packetversionnames[] = {
  { 48, ":1987"}, /*48 == '0'*/
  { 49, ":1993"}, /*49 == '1'*/
  { 50, ":2003"}, /*50 == '2'*/
  { 0, NULL }
};

static const value_string packettypenames[] = {
  { 48, "Reserved by ISO"},         /*48 == '0'*/
  { 49, "Authorization Message"},   /*49 == '1'*/
  { 50, "Financial Messages"},      /*50 == '2'*/
  { 51, "File Actions Message"},    /*51 == '3'*/
  { 52, "Reversal and Chargeback"}, /*52 == '4'*/
  { 53, "Reconciliation Message"},  /*53 == '5'*/
  { 54, "Administrative Message"},  /*54 == '6'*/
  { 55, "Fee Collection Messages"}, /*54 == '7'*/
  { 56, "Network Management"},      /*56 == '8'*/
  { 57, "Reserved by ISO"},         /*57 == '9'*/
  { 0, NULL }
};
#define FRAME_HEADER_LEN 2

static guint get_iso8583_msg_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
  const guint enc = (len_byte_order == BIGEND)?ENC_BIG_ENDIAN:ENC_LITTLE_ENDIAN;

  return tvb_get_guint16(tvb, offset, enc) + 2;
}

#define NIBBLE_2_ASCHEX(nibble)\
  ( ((nibble)>9) ? (((nibble)-10))+'A' : ((nibble)+'0') )

/*
 * Convert a sequence of nibbles to a string of ASCII characters
 * corresponding to the hex digits in those nibbles.
 */
static gchar* bin2hex(wmem_allocator_t *pool, const guint8 *bin, enum bin2hex_enum type, guint32 len)
{
  gchar* ret;
  guint8 ch;
  const guint8* str = bin;
  guint32 size = len;
  gchar* buff;

  /* "size" characters, plus terminating NUL */
  ret = (gchar *)wmem_alloc(pool, size + 1);
  buff = ret;
  if(type == TYPE_BCD)
  {
    if(size % 2) /* odd */
    {
      ch = *str & 0x0f;
      *buff++ = NIBBLE_2_ASCHEX(ch);
      str++;
      size--;
    }
    size = (len/2);
  }

  while(size-- > 0)
  {
    ch = (*str >> 4) & 0x0f;
    *buff++ = NIBBLE_2_ASCHEX(ch);
    ch = *str & 0x0f;
    *buff++ = NIBBLE_2_ASCHEX(ch);
    str++;
  }
  *buff = '\0';
  return ret;
}

static guint64 hex2bin(const char* hexstr, int len)
{
  char nibble;
  int i;
  guint64 bin= 0;

  for(i=0; i< len && i<16; i++)
  {
    nibble = hexstr[i];
    bin <<= 4;
    if (g_ascii_isdigit(nibble))
      bin |=  nibble - 48;
    else
      bin |= g_ascii_toupper(nibble) - 55; /* nibble - ('A') + 10 ; */
  }

  return bin;
}

#define checksize(len)\
      if((offset -2 + len) > iso8583_len)\
        return NULL

static gchar *get_bit(guint ind, packet_info *pinfo, tvbuff_t *tvb, guint *off_set, proto_tree *tree, proto_item **exp, gint *length, guint32 iso8583_len)
{
  gchar aux[1024];
  gchar* ret=NULL;
  guint32 len;
  guint offset = *off_set;
  gboolean str_input = FALSE;

  /* Check if it is a fixed or variable length
   * data field */

  if(data_array[ind].varlen == 0)
    len = data_array[ind].maxsize; /* fixed len */
  else
  {
    /* var len*/
    len = data_array[ind].varlen;

    switch(charset_pref)
    {
      case ASCII_CHARSET:
      {
        guint8* sizestr;
        checksize(len);

        sizestr = tvb_get_string_enc(pinfo->pool, tvb, offset, len , ENC_ASCII);
        offset += len;
        if (!ws_strtou32(sizestr, NULL, &len))
          return NULL;
        break;
      }
      case NUM_NIBBLE_CHARSET:
      {
        gint sizestr =0;
        gchar* tmp;
        if(len%2)
          len++;

        tvb_memcpy(tvb, aux, offset, len);
        tmp = aux;

        checksize((len/2));

        offset+=len/2;
        while(len > 0)
        {
          sizestr = sizestr*100 + (((guint8)(*tmp)>>4) & 0x0f)*10 +
            (((guint8)(*tmp)) & 0x0f);
          len-=2;
          tmp++;
        }
        len = sizestr;
        break;
      }
    }
  }

  *off_set = offset;

  if(len > 0)
  {
    if((guint)len > data_array[ind].maxsize)
      return NULL;

    if(data_array[ind].type == ISO_TN || data_array[ind].type == ISO_TXN)
    {
      if(charset_pref == ASCII_CHARSET)
      {
        checksize(len);
        ret = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset,
          len , ENC_ASCII);
        *length = len;
      }
      else if(charset_pref == NUM_NIBBLE_CHARSET)
      {
        gint tlen = (len%2)? len/2 + 1 : len/2;
        checksize(tlen);
        tvb_memcpy(tvb, aux, offset, tlen);
        if((ret = bin2hex(pinfo->pool, (guint8 *)aux, TYPE_BCD, len)) == NULL)
          return NULL;
        *length = (gint)strlen(ret);
        len = tlen;
        str_input = TRUE;
      }
      /* else */
    }
    else if(data_array[ind].type == ISO_TB || data_array[ind].type == ISO_TZ)
    {
      if( bin_encode_pref == BIN_ASCII_ENC)
      {
        if(data_array[ind].type == ISO_TB)
          len*=2;
        *length = len;
        checksize(len);
        ret = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset,
          len, ENC_ASCII);
      }
      else
      {
        checksize(len);
        tvb_memcpy(tvb, aux, offset, len);
        if((ret = bin2hex(pinfo->pool, (guint8 *)aux, TYPE_BIN, len)) == NULL)
          return NULL;
        *length = (gint)strlen(ret);
        str_input = TRUE;
      }
      /* else */
    }
    else
    {
      checksize(len);
      ret = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset,
        len , ENC_ASCII);
      *length = len;
    }
    /* TODO: check type of ret content */
    if(str_input && tree != NULL)
        *exp = proto_tree_add_string(tree, iso8583_data_bit[ind], tvb, offset, len, ret);
    else if (tree != NULL)
        *exp = proto_tree_add_item(tree, iso8583_data_bit[ind], tvb,
              offset, len, ENC_ASCII);

    *off_set = offset + len;
  }
  else
  {
    *length = 0;
    ret = "";
  }

  return ret;
}


static int get_bitmap(packet_info *pinfo, tvbuff_t *tvb, guint64* bitmap, guint offset, gint* nbitmaps, guint32 iso8583_len)
{
  gchar* hexbit;
  gint i;
  gboolean isbreak = FALSE;

  *nbitmaps=0;

  for(i=0; i<2; i++)
  {
    if(bin_encode_pref == BIN_BIN_ENC)
    {
      if((offset -2 + 8) > iso8583_len)
        return -1;

      (*nbitmaps)++;
      bitmap[i] = tvb_get_bits64(tvb, offset*8, 64, ENC_BIG_ENDIAN);
      offset+= BM_LEN;
    }
    else
    {
      gint len = BM_LEN*2;
      if((offset -2 + len) > iso8583_len)
        return -1;
      (*nbitmaps)++;
      hexbit = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset, len , ENC_ASCII);
      offset+= len;

      if(!ishex_str(hexbit, len))
        return 0;

      bitmap[i] = hex2bin(hexbit, len);
    }

    if(! (bitmap[i] & (((guint64)1) << 63))) /*bit 1 is set; there is a second bitmap*/
    {
      isbreak = TRUE;
      break;
    }
  }
  if(!isbreak)
    (*nbitmaps)++;

  return 0;
}

static int dissect_databits(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    int offset, int nofbitmaps, guint64 *bitmap, guint32 iso8583_len)
{
  proto_item *exp;
  gint nofbits = nofbitmaps*64, i;
  guint64 bit;
  gchar* cod;
  gint len;

  if(!pinfo)
    return 0;

  for(i=0; i < nofbits; i++)
  {
    exp = NULL;
    bit = i%64;
    if( !bit)
      continue;

    if(bitmap[i/64] & (((guint64)1)<< (63 -bit)))
    {
      cod = get_bit(i, pinfo, tvb, &offset, tree, &exp, &len, iso8583_len);
      if(cod == NULL || ! isstrtype_ok(data_array[i].type, cod, len ))
      {
        if(!exp)
          exp = proto_tree_add_string(tree, iso8583_data_bit[i], tvb, offset, 0, "");
        expert_add_info(pinfo, exp, &ei_iso8583_MALFORMED);
        return offset;
      }

      if( i == 2 || i == 69) /*Processing code or Net. info code*/
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " %s. cod: %s", ((i==2)?"Proc":"Net"), cod);
        continue;
      }

      /*test if exp is of type expected*/
      if(exp)
      {
      }
    }
  }
  return tvb_captured_length(tvb);
}

static int dissect_iso8583_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    void *data _U_)
{
  proto_item *ti, *exp;
  proto_tree *iso8583_tree;
  guint       offset = 0;
  int         len    = 0;
  gchar *msg_type, *msg_bitmap;
  gchar aux[24];
  guint64 bitmap[3]= {0,0,0};
  int nofbitmaps=0;
  guint ret;
  guint32 iso8583_len;


  /* Check that the packet is long enough for it to belong to ISO 8583-1. */
  if (tvb_reported_length(tvb) < iso8583_MIN_LENGTH)
  {
    return 0;
  }

  /* Heuristic: 4 bytes MTI - all digits */
  if(charset_pref == ASCII_CHARSET) /* ASCII NUMBER REPRESENTATION */
  {
    len = 4;
    msg_type = (gchar*) tvb_get_string_enc(pinfo->pool, tvb, 2, len, ENC_ASCII);
  }
  else /* NUMBERS REPRESENTED IN NIBBLES */
  {
    len = 2;
    tvb_memcpy(tvb, aux, 2, len);
    if((msg_type = bin2hex(pinfo->pool, (guint8 *)aux, TYPE_BCD, len*2)) == NULL)
      return 0;
  }

  if(strlen(msg_type) == 4 && !isnum_str(msg_type,4)) /*MTI is composed of 4 digits*/
  {
    return 0;
  }

  /* Heuristic: 16 bytes Bitmap1 - all HEX digits */

  if(bin_encode_pref == BIN_BIN_ENC) /* ASCII NUMBER REPRESENTATION */
    msg_bitmap = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, 6, BM_LEN*2 , ENC_ASCII);
  else
  {
    tvb_memcpy(tvb, aux, 6, BM_LEN);
    if((msg_bitmap = bin2hex(pinfo->pool, (guint8 *)aux, TYPE_BCD, BM_LEN)) == NULL)
      return 0;
  }

  if(strlen(msg_bitmap) == 16 && !ishex_str(msg_bitmap, BM_LEN*2)) /*MTI is composed of 4 digits*/
  {
    return 0;
  }

  /* check for message type format */
  if(msg_type[0] == '0')
    data_array = iso_1987;
  else if (msg_type[0] == '1')
    data_array = iso_1993;
  else
  {
    return 0;
  }


  /* Set the Protocol column */
  col_clear(pinfo->cinfo, COL_PROTOCOL);
  col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "ISO 8583-1%s",
      val_to_str_const((guint)msg_type[0], packetversionnames, " Unknown VERSION"));
  col_clear(pinfo->cinfo, COL_INFO);
  /* print version of the packet*/
  col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s - %s", msg_type,
      val_to_str_const((guint)msg_type[1], packettypenames, "Unknown type"));

  /*** PROTOCOL TREE ***/

  /* create display subtree for the protocol */
  ti = proto_tree_add_item(tree, proto_iso8583, tvb, 0, -1, ENC_NA);
  proto_item_append_text(ti, ":  Type %s - %s", msg_type,
      val_to_str_const((guint)msg_type[1], packettypenames, "Unknown type"));

  iso8583_tree = proto_item_add_subtree(ti, ett_iso8583);

  /*Length of the package*/
  len=2;
  proto_tree_add_item_ret_uint(iso8583_tree, hf_iso8583_len, tvb,
      offset, len, (len_byte_order == BIGEND)?ENC_BIG_ENDIAN:ENC_LITTLE_ENDIAN,
      &iso8583_len);
  offset += len;

  iso8583_len += 2;

  /*MTI*/
  /* TODO: check BCD or ASCII */
  if(charset_pref == ASCII_CHARSET) /* ASCII NUMBER REPRESENTATION */
  {
    len=4;
    proto_tree_add_item(iso8583_tree, hf_iso8583_mti, tvb,
        offset, len, ENC_ASCII | ENC_NA);
  }
  else
  {
    len=2;
    proto_tree_add_string(iso8583_tree, hf_iso8583_mti, tvb, offset, len, msg_type);
  }

  /*BITMAPS*/
  offset+=len;

  get_bitmap(pinfo, tvb, bitmap, offset, &nofbitmaps, iso8583_len);

  if(nofbitmaps == 0)
  {
      expert_add_info(pinfo, ti, &ei_iso8583_MALFORMED);
      return offset;
  }

  /*BITMAP 1*/
  if(bin_encode_pref == BIN_ASCII_ENC)
  {
    len = BM_LEN*2;
    exp = proto_tree_add_item(iso8583_tree, hf_iso8583_bitmap1, tvb,
        offset, len, ENC_ASCII);
    if(!ishex_str((gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset, len , ENC_ASCII), len))
    {
      expert_add_info(pinfo, exp, &ei_iso8583_MALFORMED);
      return offset + len;
    }
  }
  else
  {
    gchar* hexstr;
    len = BM_LEN;
    hexstr = tvb_bytes_to_str(pinfo->pool, tvb, offset, len);
    exp = proto_tree_add_string(iso8583_tree, hf_iso8583_bitmap1, tvb, offset, len, hexstr);
  }
  offset+=len;

  /*BITMAP 2*/
  if(nofbitmaps > 1)
  {
    if(bin_encode_pref == BIN_ASCII_ENC)
    {
      exp = proto_tree_add_item(iso8583_tree, hf_iso8583_bitmap2, tvb,
          offset, len, ENC_ASCII);
      if(!ishex_str((gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset, len , ENC_ASCII), len))
      {
        expert_add_info(pinfo, exp, &ei_iso8583_MALFORMED);
        return offset + len;
      }
    }
    else
    {
      gchar* hexstr = tvb_bytes_to_str(pinfo->pool, tvb, offset, len);
      exp = proto_tree_add_string(iso8583_tree, hf_iso8583_bitmap2, tvb, offset, len, hexstr);
    }
    offset+=len;
  }

  /*BITMAP 3*/
  if(nofbitmaps > 2)
  {
    expert_add_info(pinfo, exp, &ei_iso8583_MALFORMED);
    return offset;
  }

  /*DISSECT BITS*/
  ret = dissect_databits(tvb, pinfo, iso8583_tree, offset, nofbitmaps, bitmap,
    iso8583_len);

  return ret;
}

static int dissect_iso8583(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    void *data _U_)
{
  tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN, get_iso8583_msg_len, dissect_iso8583_msg, data);

  return tvb_captured_length(tvb);
}



void
proto_register_iso8583(void)
{
  module_t        *iso8583_module;
  expert_module_t *expert_iso8583;
  int              i;

  static hf_register_info hf[] = {
    { &hf_iso8583_len,
      { "Message length", "iso8583.len",
        FT_UINT16, BASE_DEC, NULL, 0x0,
        "Message length field", HFILL }
    },
    { &hf_iso8583_mti,
      { "MTI", "iso8583.mti",
        FT_STRING, BASE_NONE, NULL , 0,
        "Message Type Indicator (MTI)", HFILL }
    },
    { &hf_iso8583_bitmap1,
      { "Bitmap 1", "iso8583.map1",
        FT_STRING, BASE_NONE, NULL , 0,
        "First Bitmap (hex representation)", HFILL }
    },
    { &hf_iso8583_bitmap2,
      { "Bitmap 2", "iso8583.map2",
        FT_STRING, BASE_NONE, NULL , 0,
        "Second Bitmap (hex representation)", HFILL }
    }
  };

  static hf_register_info hf_data[128];

  static const char *hf_data_blurb[128] = {
    /* Bit 1 */
    "Second Bit map present",
    /* Bit 2 */
    "Primary account number (PAN)",
    /* Bit 3 */
    "Processing code",
    /* Bit 4 */
    "Amount, transaction",
    /* Bit 5 */
    "Amount, settlement",
    /* Bit 6 */
    "Amount, cardholder billing",
    /* Bit 7 */
    "Transmission date &amp",
    /* Bit 8 */
    "Amount, cardholder billing fee",
    /* Bit 9 */
    "Conversion rate, settlement",
    /* Bit 10 */
    "Conversion rate, cardholder billing",
    /* Bit 11 */
    "System trace audit number",
    /* Bit 12 */
    "Time, local transaction (hhmmss)",
    /* Bit 13 */
    "Date, local transaction (MMDD)",
    /* Bit 14 */
    "Date, expiration",
    /* Bit 15 */
    "Date, settlement",
    /* Bit 16 */
    "Date, conversion",
    /* Bit 17 */
    "Date, capture",
    /* Bit 18 */
    "Merchant type",
    /* Bit 19 */
    "Acquiring institution country code",
    /* Bit 20 */
    "PAN extended, country code",
    /* Bit 21 */
    "Forwarding institution. country code",
    /* Bit 22 */
    "Point of service entry mode",
    /* Bit 23 */
    "Application PAN sequence number",
    /* Bit 24 */
    "Function code (ISO 8583:1993)/Network International identifier (NII)",
    /* Bit 25 */
    "Point of service condition code",
    /* Bit 26 */
    "Point of service capture code",
    /* Bit 27 */
    "Authorizing identification response length",
    /* Bit 28 */
    "Amount, transaction fee",
    /* Bit 29 */
    "Amount, settlement fee",
    /* Bit 30 */
    "Amount, transaction processing fee",
    /* Bit 31 */
    "Amount, settlement processing fee",
    /* Bit 32 */
    "Acquiring institution identification code",
    /* Bit 33 */
    "Forwarding institution identification code",
    /* Bit 34 */
    "Primary account number, extended",
    /* Bit 35 */
    "Track 2 data",
    /* Bit 36 */
    "Track 3 data",
    /* Bit 37 */
    "Retrieval reference number",
    /* Bit 38 */
    "Authorization identification response",
    /* Bit 39 */
    "Response code",
    /* Bit 40 */
    "Service restriction code",
    /* Bit 41 */
    "Card acceptor terminal identification",
    /* Bit 42 */
    "Card acceptor identification code",
    /* Bit 43 */
    "Card acceptor name/location (1-23 address 24-36 city 37-38 state 39-40 country)",
    /* Bit 44 */
    "Additional response data",
    /* Bit 45 */
    "Track 1 data",
    /* Bit 46 */
    "Additional data - ISO",
    /* Bit 47 */
    "Additional data - national",
    /* Bit 48 */
    "Additional data - private",
    /* Bit 49 */
    "Currency code, transaction",
    /* Bit 50 */
    "Currency code, settlement",
    /* Bit 51 */
    "Currency code, cardholder billing",
    /* Bit 52 */
    "Personal identification number data",
    /* Bit 53 */
    "Security related control information",
    /* Bit 54 */
    "Additional amounts",
    /* Bit 55 */
    "Reserved ISO",
    /* Bit 56 */
    "Reserved ISO",
    /* Bit 57 */
    "Reserved national",
    /* Bit 58 */
    "Reserved national",
    /* Bit 59 */
    "Reserved national",
    /* Bit 60 */
    "Reserved national",
    /* Bit 61 */
    "Reserved private",
    /* Bit 62 */
    "Reserved private",
    /* Bit 63 */
    "Reserved private",
    /* Bit 64 */
    "Message authentication code (MAC)",
    /* Bit 65 */
    "Third Bitmap, extended",
    /* Bit 66 */
    "Settlement code",
    /* Bit 67 */
    "Extended payment code",
    /* Bit 68 */
    "Receiving institution country code",
    /* Bit 69 */
    "Settlement institution country code",
    /* Bit 70 */
    "Network management information code",
    /* Bit 71 */
    "Message number",
    /* Bit 72 */
    "Message number, last",
    /* Bit 73 */
    "Date, action (YYMMDD)",
    /* Bit 74 */
    "Credits, number",
    /* Bit 75 */
    "Credits, reversal number",
    /* Bit 76 */
    "Debits, number",
    /* Bit 77 */
    "Debits, reversal number",
    /* Bit 78 */
    "Transfer number",
    /* Bit 79 */
    "Transfer, reversal number",
    /* Bit 80 */
    "Inquiries number",
    /* Bit 81 */
    "Authorizations, number",
    /* Bit 82 */
    "Credits, processing fee amount",
    /* Bit 83 */
    "Credits, transaction fee amount",
    /* Bit 84 */
    "Debits, processing fee amount",
    /* Bit 85 */
    "Debits, transaction fee amount",
    /* Bit 86 */
    "Credits, amount",
    /* Bit 87 */
    "Credits, reversal amount",
    /* Bit 88 */
    "Debits, amount",
    /* Bit 89 */
    "Debits, reversal amount",
    /* Bit 90 */
    "Original data elements",
    /* Bit 91 */
    "File update code",
    /* Bit 92 */
    "File security code",
    /* Bit 93 */
    "Response indicator",
    /* Bit 94 */
    "Service indicator",
    /* Bit 95 */
    "Replacement amounts",
    /* Bit 96 */
    "Message security code",
    /* Bit 97 */
    "Amount, net settlement",
    /* Bit 98 */
    "Payee",
    /* Bit 99 */
    "Settlement institution identification code",
    /* Bit 100 */
    "Receiving institution identification code",
    /* Bit 101 */
    "File name",
    /* Bit 102 */
    "Account identification 1",
    /* Bit 103 */
    "Account identification 2",
    /* Bit 104 */
    "Transaction description",
    /* Bit 105 */
    "Reserved for ISO use",
    /* Bit 106 */
    "Reserved for ISO use",
    /* Bit 107 */
    "Reserved for ISO use",
    /* Bit 108 */
    "Reserved for ISO use",
    /* Bit 109 */
    "Reserved for ISO use",
    /* Bit 110 */
    "Reserved for ISO use",
    /* Bit 111 */
    "Reserved for ISO use",
    /* Bit 112 */
    "Reserved for national use",
    /* Bit 113 */
    "Reserved for national use",
    /* Bit 114 */
    "Reserved for national use",
    /* Bit 115 */
    "Reserved for national use",
    /* Bit 116 */
    "Reserved for national use",
    /* Bit 117 */
    "Reserved for national use",
    /* Bit 118 */
    "Reserved for national use",
    /* Bit 119 */
    "Reserved for national use",
    /* Bit 120 */
    "Reserved for private use",
    /* Bit 121 */
    "Reserved for private use",
    /* Bit 122 */
    "Reserved for private use",
    /* Bit 123 */
    "Reserved for private use",
    /* Bit 124 */
    "Reserved for private use",
    /* Bit 125 */
    "Reserved for private use",
    /* Bit 126 */
    "Reserved for private use",
    /* Bit 127 */
    "Reserved for private use",
    /* Bit 128 */
    "Message authentication code"
  };

  static gint *ett[] = {
    &ett_iso8583
  };

  static ei_register_info ei[] = {
    { &ei_iso8583_MALFORMED,
      { "iso8583.MALFORMED", PI_MALFORMED, PI_ERROR,
        "MALFORMED", EXPFILL }
    }
  };

  proto_iso8583 = proto_register_protocol("ISO 8583-1", "ISO 8583", "iso8583");

  /* Function calls to register the header fields and subtrees */
  proto_register_field_array(proto_iso8583, hf, array_length(hf));
  for (i = 0; i < 128; i++) {
    HFILL_INIT(hf_data[i]);
    hf_data[i].p_id = &iso8583_data_bit[i];
    hf_data[i].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "Bit %d", i + 1);
    hf_data[i].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "iso8583.bit%d", i + 1);
    if(! i%64 ) /* bit 1 and bit 65 */
    {
      hf_data[i].hfinfo.type = FT_BOOLEAN;
      hf_data[i].hfinfo.display = 8;
    }
    else
    {
      hf_data[i].hfinfo.type = FT_STRING;
      hf_data[i].hfinfo.display = BASE_NONE;
    }
    hf_data[i].hfinfo.strings = NULL;
    hf_data[i].hfinfo.bitmask = 0;
    hf_data[i].hfinfo.blurb = hf_data_blurb[i];
  }
  proto_register_field_array(proto_iso8583, hf_data, array_length(hf_data));
  proto_register_subtree_array(ett, array_length(ett));

  expert_iso8583 = expert_register_protocol(proto_iso8583);
  expert_register_field_array(expert_iso8583, ei, array_length(ei));

  /* Register dissector handle */
  iso8583_handle = register_dissector("iso8583", dissect_iso8583, proto_iso8583);

  /* Register preferences module */
  iso8583_module = prefs_register_protocol(proto_iso8583, NULL);

  prefs_register_enum_preference(iso8583_module, "len_endian",
      "Length field endian",
      "Endian of the length field. Big endian or Little endian",
      &len_byte_order,
      enumendians, TRUE);

  prefs_register_enum_preference(iso8583_module, "charset",
      "Charset for numbers",
      " charset for numbers",
      &charset_pref, enum_charset, TRUE);

  prefs_register_enum_preference(iso8583_module, "binencode",
      "Binary encode",
      " binary data representation",
      &bin_encode_pref, enum_bin_encode, TRUE);
}

void proto_reg_handoff_iso8583(void)
{
  dissector_add_for_decode_as_with_preference("tcp.port", iso8583_handle);
}

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