diff options
Diffstat (limited to 'epan/dissectors/packet-at.c')
-rw-r--r-- | epan/dissectors/packet-at.c | 3379 |
1 files changed, 3379 insertions, 0 deletions
diff --git a/epan/dissectors/packet-at.c b/epan/dissectors/packet-at.c new file mode 100644 index 00000000..948f2a11 --- /dev/null +++ b/epan/dissectors/packet-at.c @@ -0,0 +1,3379 @@ +/* packet-at.c + * Dissector for AT Commands + * + * Copyright 2011, Tyson Key <tyson.key@gmail.com> + * + * 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 <stdio.h> /* for sscanf() */ + +#include <epan/packet.h> +#include <epan/conversation.h> +#include <epan/expert.h> +#include <epan/proto_data.h> +#include <epan/strutil.h> + +#include "packet-e212.h" + +void proto_register_at_command(void); +void proto_reg_handoff_at_command(void); + +static int proto_at = -1; + +static dissector_handle_t gsm_sim_handle; +static dissector_handle_t gsm_sms_handle; + +static int hf_command = -1; +static int hf_data_part = -1; +static int hf_parameters = -1; +static int hf_role = -1; +static int hf_at_cmd = -1; +static int hf_at_cmd_type = -1; +static int hf_at_command_line_prefix = -1; +static int hf_at_ignored = -1; +static int hf_parameter = -1; +static int hf_unknown_parameter = -1; +static int hf_data = -1; +static int hf_chld_mode = -1; +static int hf_chld_mode_1x = -1; +static int hf_chld_mode_2x = -1; +static int hf_chld_supported_modes = -1; +static int hf_cimi_imsi = -1; +static int hf_cmer_mode = -1; +static int hf_cmer_keyp = -1; +static int hf_cmer_disp = -1; +static int hf_cmer_ind = -1; +static int hf_cmer_bfr = -1; +static int hf_cmee = -1; +static int hf_cme_error = -1; +static int hf_cme_error_verbose = -1; +static int hf_cmgl_req_status = -1; +static int hf_cmgl_msg_index = -1; +static int hf_cmgl_msg_status = -1; +static int hf_cmgl_msg_originator_name = -1; +static int hf_cmgl_msg_length = -1; +static int hf_cmgl_msg_pdu = -1; +static int hf_cmgr_address = -1; +static int hf_cmgr_mode = -1; +static int hf_cmgr_msg_index = -1; +static int hf_cmgr_msg_length = -1; +static int hf_cmgr_msg_pdu = -1; +static int hf_cmgr_stat = -1; +static int hf_cmux_k = -1; +static int hf_cmux_n1 = -1; +static int hf_cmux_n2 = -1; +static int hf_cmux_port_speed = -1; +static int hf_cmux_subset = -1; +static int hf_cmux_t1 = -1; +static int hf_cmux_t2 = -1; +static int hf_cmux_t3 = -1; +static int hf_cmux_transparency = -1; +static int hf_cnum_speed = -1; +static int hf_cnum_service = -1; +static int hf_cnum_itc = -1; +static int hf_ciev_indicator_index = -1; +static int hf_vts_dtmf = -1; +static int hf_vts_duration = -1; +static int hf_cops_mode = -1; +static int hf_cops_format = -1; +static int hf_cops_operator = -1; +static int hf_cops_act = -1; +static int hf_cpin_code = -1; +static int hf_cpin_newpin = -1; +static int hf_cpin_pin = -1; +static int hf_cpms_mem1 = -1; +static int hf_cpms_mem2 = -1; +static int hf_cpms_mem3 = -1; +static int hf_cpms_used1 = -1; +static int hf_cpms_used2 = -1; +static int hf_cpms_used3 = -1; +static int hf_cpms_total1 = -1; +static int hf_cpms_total2 = -1; +static int hf_cpms_total3 = -1; +static int hf_cscs_chset = -1; +static int hf_csim_command = -1; +static int hf_csim_length = -1; +static int hf_csim_response = -1; +static int hf_csq_ber = -1; +static int hf_csq_rssi = -1; +static int hf_at_number = -1; +static int hf_at_type = -1; +static int hf_at_subaddress = -1; +static int hf_at_subaddress_type = -1; +static int hf_at_alpha = -1; +static int hf_at_priority = -1; +static int hf_at_cli_validity = -1; +static int hf_clip_mode = -1; +static int hf_clip_status = -1; +static int hf_clcc_id = -1; +static int hf_clcc_dir = -1; +static int hf_clcc_stat = -1; +static int hf_clcc_mode = -1; +static int hf_clcc_mpty = -1; +static int hf_ccwa_show_result_code = -1; +static int hf_ccwa_mode = -1; +static int hf_ccwa_class = -1; +static int hf_cfun_fun = -1; +static int hf_cfun_rst = -1; +static int hf_cgdcont_cid = -1; +static int hf_cgdcont_pdp_type = -1; +static int hf_cgdcont_apn = -1; +static int hf_cgdcont_pdp_addr = -1; +static int hf_cgdcont_d_comp = -1; +static int hf_cgdcont_h_comp = -1; +static int hf_cgmi_manufacturer_id = -1; +static int hf_cgmm_model_id = -1; +static int hf_cgmr_revision_id = -1; +static int hf_gmi_manufacturer_id = -1; +static int hf_gmm_model_id = -1; +static int hf_gmr_revision_id = -1; +static int hf_zpas_network = -1; +static int hf_zpas_srv_domain = -1; +static int hf_zusim_usim_card = -1; +static int hf_indicator[20] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static expert_field ei_unknown_command = EI_INIT; +static expert_field ei_invalid_usage = EI_INIT; +static expert_field ei_unknown_parameter = EI_INIT; +static expert_field ei_cmer_mode = EI_INIT; +static expert_field ei_cmer_keyp = EI_INIT; +static expert_field ei_cmer_disp = EI_INIT; +static expert_field ei_cmer_ind = EI_INIT; +static expert_field ei_cmer_bfr = EI_INIT; +static expert_field ei_chld_mode = EI_INIT; +static expert_field ei_ciev_indicator = EI_INIT; +static expert_field ei_cfun_res_fun = EI_INIT; +static expert_field ei_cfun_range_fun = EI_INIT; +static expert_field ei_cfun_rst = EI_INIT; +static expert_field ei_vts_dtmf = EI_INIT; +static expert_field ei_at_type = EI_INIT; +static expert_field ei_cnum_service = EI_INIT; +static expert_field ei_cnum_itc = EI_INIT; +static expert_field ei_empty_hex = EI_INIT; +static expert_field ei_invalid_hex = EI_INIT; +static expert_field ei_odd_len = EI_INIT; +static expert_field ei_csq_ber = EI_INIT; +static expert_field ei_csq_rssi = EI_INIT; + + +/* Subtree handles: set by register_subtree_array */ +static gint ett_at = -1; +static gint ett_at_command = -1; +static gint ett_at_data_part = -1; +static gint ett_at_parameters = -1; + +#define ROLE_UNKNOWN 0 +#define ROLE_DCE 1 +#define ROLE_DTE 2 + +#define TYPE_UNKNOWN 0x0000 +#define TYPE_RESPONSE_ACK 0x0d0a +#define TYPE_RESPONSE 0x003a +#define TYPE_ACTION 0x003d +#define TYPE_ACTION_SIMPLY 0x000d +#define TYPE_READ 0x003f +#define TYPE_TEST 0x3d3f + +#define STORE_COMMAND_MAX_LEN 20 + +static gint at_role = ROLE_UNKNOWN; + +static const value_string role_vals[] = { + { ROLE_UNKNOWN, "Unknown" }, + { ROLE_DCE, "DCE - Data Circuit terminating Equipment (Modem)" }, + { ROLE_DTE, "DTE - Data Terminal Equipment (PC)" }, + { 0, NULL } +}; + +static const enum_val_t pref_at_role[] = { + { "off", "Off", ROLE_UNKNOWN }, + { "dte", "Sent is DTE, Rcvd is DCE", ROLE_DTE }, + { "dce", "Sent is DCE, Rcvd is DTE", ROLE_DCE }, + { NULL, NULL, 0 } +}; + +static const value_string at_cmd_type_vals[] = { + { 0x0d, "Action Command" }, + { 0x3a, "Response" }, + { 0x3d, "Action Command" }, + { 0x3f, "Read Command" }, + { 0x0d0a, "Response" }, + { 0x3d3f, "Test Command" }, + { 0, NULL } +}; + +static const value_string cfun_fun_vals[] = { + { 0, "Minimum functionality" }, + { 1, "Full functionality" }, + { 2, "Disable phone transmit RF circuits only" }, + { 3, "Disable phone receive RF circuits only" }, + { 4, "Disable phone both transmit and receive RF circuits" }, + { 0, NULL } +}; + +static const value_string cfun_rst_vals[] = { + { 0, "Do not reset the MT before setting it to the requested power level" }, + { 1, "Reset the MT before setting it to the requested power level" }, + { 0, NULL } +}; + +static const value_string cme_error_vals[] = { + { 0, "Phone/AG failure" }, + { 1, "No Connection to Phone" }, + { 2, "Phone-adaptor Link Reserved" }, + { 3, "Operation not Allowed" }, + { 4, "Operation not Supported" }, + { 5, "PH-SIM PIN required" }, + { 6, "PH-FSIM PIN Required" }, + { 7, "PH-FSIM PUK Required" }, + { 10, "SIM not Inserted" }, + { 11, "SIM PIN Required" }, + { 12, "SIM PUK Required" }, + { 13, "SIM Failure" }, + { 14, "SIM Busy" }, + { 15, "SIM Wrong" }, + { 16, "Incorrect Password" }, + { 17, "SIM PIN2 Required" }, + { 18, "SIM PUK2 Required" }, + { 20, "Memory Full" }, + { 21, "Invalid Index" }, + { 22, "Not Found" }, + { 23, "Memory Failure" }, + { 24, "Text String too Long" }, + { 25, "Invalid Characters in Text String" }, + { 26, "Dial String too Long" }, + { 27, "Invalid Characters in Dial String" }, + { 30, "No Network Service" }, + { 31, "Network Timeout" }, + { 32, "Network not Allowed - Emergency Calls Only" }, + { 40, "Network Personalization PIN Required" }, + { 41, "Network Personalization PUK Required" }, + { 42, "Network Subset Personalization PIN Required" }, + { 43, "Network Subset Personalization PUK Required" }, + { 44, "Service Provider Personalization PIN Required" }, + { 45, "Service Provider Personalization PUK Required" }, + { 46, "Corporate Personalization PIN Required" }, + { 47, "Corporate Personalization PUK Required" }, + { 48, "Hidden Key Required" }, + { 49, "EAP Method not Supported" }, + { 50, "Incorrect Parameters" }, + { 100, "Unknown" }, + { 0, NULL } +}; + +static const value_string cmee_vals[] = { + { 0, "Disabled" }, + { 1, "Enabled" }, + { 2, "Verbose" }, + { 0, NULL } +}; + +static const value_string cmux_port_speed_vals[] = { + { 1, "9,600 bit/s" }, + { 2, "19,200 bit/s" }, + { 3, "38,400 bit/s" }, + { 4, "57,600 bit/s" }, + { 5, "115,200 bit/s" }, + { 6, "230,400 bit/s" }, + { 0, NULL } +}; + +static const value_string cmux_subset_vals[] = { + { 0, "UIH frames used only" }, + { 1, "UI frames used only" }, + { 2, "I frames used only" }, + { 0, NULL } +}; + +static const value_string cmux_transparency_vals[] = { + { 0, "Basic option" }, + { 1, "Advanced option" }, + { 0, NULL } +}; + +static const value_string chld_vals[] = { + { 0, "Releases all held calls or sets User Determined User Busy (UDUB) for a waiting call" }, + { 1, "Releases all active calls (if any exist) and accepts the other (held or waiting) call" }, + { 2, "Places all active calls (if any exist) on hold and accepts the other (held or waiting) call" }, + { 3, "Adds a held call to the conversation" }, + { 4, "Connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer)" }, + { 0, NULL } +}; + +static const value_string cops_mode_vals[] = { + { 0, "Automatic" }, + { 1, "Manual" }, + { 2, "Deregister from Network" }, + { 3, "Set Only Format" }, + { 4, "Manual/Automatic" }, + { 0, NULL } +}; + +static const value_string cops_format_vals[] = { + { 0, "Long Format Alphanumeric" }, + { 1, "Short Format Alphanumeric" }, + { 2, "Numeric" }, + { 0, NULL } +}; + +static const value_string cops_act_vals[] = { + { 0, "GSM" }, + { 1, "GSM Compact" }, + { 2, "UTRAN" }, + { 3, "GSM with EGPRS" }, + { 4, "UTRAN with HSDPA" }, + { 5, "UTRAN with HSUPA" }, + { 6, "UTRAN with HSDPA and HSUPA" }, + { 7, "E-UTRAN" }, + { 8, "EC-GSM-IoT (A/Gb mode)" }, + { 9, "E-UTRAN (NB-S1 mode)" }, + { 10, "E-UTRA connected to a 5GCN" }, + { 11, "NR connected to a 5GCCN" }, + { 12, "NR connected to an EPS core" }, + { 13, "NG-RAN" }, + { 14, "E-UTRA-NR dual connectivity" }, + { 0, NULL } +}; + +static const range_string at_type_vals[] = { + { 128, 143, "The phone number format may be a national or international format, and may contain prefix and/or escape digits. No changes on the number presentation are required." }, + { 144, 159, "The phone number format is an international number, including the country code prefix. If the plus sign (\"+\") is not included as part of the number and shall be added by the AG as needed." }, + { 160, 175, "National number. No prefix nor escape digits included." }, + { 0, 0, NULL } +}; + +static const value_string cli_validity_vals[] = { + { 0, "CLI Valid" }, + { 1, "CLI has been withheld by the originator" }, + { 2, "CLI is not available due to interworking problems or limitations of originating network" }, + { 0, NULL } +}; + +static const value_string cnum_service_vals[] = { + { 0, "Asynchronous Modem" }, + { 1, "Synchronous Modem" }, + { 2, "PAD Access" }, + { 3, "Packet Access" }, + { 4, "Voice" }, + { 5, "Fax" }, + { 0, NULL } +}; + +static const value_string cnum_itc_vals[] = { + { 0, "3.1 kHz" }, + { 1, "UDI" }, + { 0, NULL } +}; + +static const value_string clip_mode_vals[] = { + { 0, "Disabled" }, + { 1, "Enabled" }, + { 0, NULL } +}; + +static const value_string clip_status_vals[] = { + { 0, "CLIP not Provisioned" }, + { 1, "CLIP Provisioned" }, + { 2, "Unknown" }, + { 0, NULL } +}; + +static const value_string clcc_dir_vals[] = { + { 0, "Mobile Originated" }, + { 1, "Mobile Terminated" }, + { 0, NULL } +}; + +static const value_string clcc_stat_vals[] = { + { 0, "Active" }, + { 1, "Held" }, + { 2, "Dialing" }, + { 3, "Alerting" }, + { 4, "Incoming" }, + { 5, "Waiting" }, + { 0, NULL } +}; + +static const value_string clcc_mode_vals[] = { + { 0, "Voice" }, + { 1, "Data" }, + { 2, "Fax" }, + { 3, "Voice Followed by Data, Voice Mode" }, + { 4, "Alternating Voice/Data, Voice Mode" }, + { 5, "Alternating Voice/Fax, Voice Mode" }, + { 6, "Voice Followed by Data, Data Mode" }, + { 7, "Alternating Voice/Data, Data Mode" }, + { 8, "Alternating Voice/Fax, Fax Mode" }, + { 9, "Unknown" }, + { 0, NULL } +}; + +static const value_string clcc_mpty_vals[] = { + { 0, "Call is not one of multiparty (conference) call parties" }, + { 1, "Call is one of multiparty (conference) call parties" }, + { 0, NULL } +}; + +static const value_string cmgr_mode_vals[] = { + { 0, "Normal (Change unread to read)" }, + { 1, "Do not change unread to read" }, + { 0, NULL } +}; + +static const value_string cmgr_stat_vals[] = { + { 0, "Received unread (i.e. new message)" }, + { 1, "Received read" }, + { 2, "Stored unsent" }, + { 3, "Stored sent" }, + { 4, "All" }, + { 0, NULL } +}; + +static const value_string ccwa_show_result_code_vals[] = { + { 0, "Disabled" }, + { 1, "Enabled" }, + { 0, NULL } +}; + +static const value_string ccwa_mode_vals[] = { + { 0, "Disabled" }, + { 1, "Enabled" }, + { 2, "Query Status" }, + { 0, NULL } +}; + +static const value_string ccwa_class_vals[] = { + { 1, "Voice" }, + { 2, "Data" }, + { 4, "Fax" }, + { 8, "Short Message Service" }, + { 16, "Data Circuit Sync" }, + { 32, "Data Circuit Async" }, + { 64, "Dedicated Packet Access" }, + { 128, "Dedicated PAD Access" }, + { 0, NULL } +}; + +static const value_string csq_ber_vals[] = { + { 0, "Less than 0.2 %" }, + { 1, "Between 0.2 % and 0.4 %" }, + { 2, "Between 0.4 % and 0.8 %" }, + { 3, "Between 0.8 % and 1.6 %" }, + { 4, "Between 1.6 % and 3.2 %" }, + { 5, "Between 3.2 % and 6.4 %" }, + { 6, "Between 6.4 % and 12.8 %" }, + { 7, "Greater than 12.8 %" }, + { 99, "Not known or not detectable" }, + { 0, NULL } +}; + +static const value_string csq_rssi_vals[] = { + { 0, "-113 dBm or less" }, + { 1, "-111 dBm" }, + { 2, "-109 dBm" }, + { 3, "-107 dBm" }, + { 4, "-105 dBm" }, + { 5, "-103 dBm" }, + { 6, "-101 dBm" }, + { 7, "-99 dBm" }, + { 8, "-97 dBm" }, + { 9, "-95 dBm" }, + { 10, "-93 dBm" }, + { 11, "-91 dBm" }, + { 12, "-89 dBm" }, + { 13, "-87 dBm" }, + { 14, "-85 dBm" }, + { 15, "-83 dBm" }, + { 16, "-81 dBm" }, + { 17, "-79 dBm" }, + { 18, "-77 dBm" }, + { 19, "-75 dBm" }, + { 20, "-73 dBm" }, + { 21, "-71 dBm" }, + { 22, "-69 dBm" }, + { 23, "-67 dBm" }, + { 24, "-65 dBm" }, + { 25, "-63 dBm" }, + { 26, "-61 dBm" }, + { 27, "-59 dBm" }, + { 28, "-57 dBm" }, + { 29, "-55 dBm" }, + { 30, "-53 dBm" }, + { 31, "-51 dBm or greater" }, + { 99, "Not known or not detectable" }, + { 0, NULL } +}; + +static const value_string zusim_usim_card_vals[] = { + { 0, "SIM" }, + { 1, "USIM" }, + { 0, NULL } +}; + +extern value_string_ext csd_data_rate_vals_ext; + +struct _at_packet_info_t; + +/* A command that either finished or is currently being processed */ +typedef struct _at_processed_cmd_t { + gchar name[STORE_COMMAND_MAX_LEN]; + guint16 type; + /* Indicates how many more textual data lines are we expecting */ + guint32 expected_data_parts; + /* Indicates how many textual data lines were already processed */ + guint32 consumed_data_parts; + /* Index of the command in within the original AT packet */ + guint32 cmd_indx; + /* Handler for textual data lines */ + gboolean (*dissect_data)(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, gint offset, gint role, guint16 type, + guint8 *data_part_stream, guint data_part_number, + gint data_part_length, struct _at_packet_info_t *at_info); +} at_processed_cmd_t; + +typedef struct _at_conv_info_t { + at_processed_cmd_t dte_command; + at_processed_cmd_t dce_command; +} at_conv_info_t; + +typedef struct _at_packet_info_t { + at_processed_cmd_t initial_dte_command; + at_processed_cmd_t initial_dce_command; + at_processed_cmd_t current_dte_command; + at_processed_cmd_t current_dce_command; +} at_packet_info_t; + +typedef struct _at_cmd_t { + const gchar *name; + const gchar *long_name; + + gboolean (*check_command)(gint role, guint16 type); + gboolean (*dissect_parameter)(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, gint offset, gint role, guint16 type, + guint8 *parameter_stream, guint parameter_number, + gint parameter_length, at_packet_info_t *at_info, void **data); +} at_cmd_t; + +static at_conv_info_t * +get_at_conv_info(conversation_t *conversation) +{ + if (!conversation) + return NULL; + at_conv_info_t *at_conv_info; + /* do we have conversation specific data ? */ + at_conv_info = (at_conv_info_t *)conversation_get_proto_data(conversation, proto_at); + if (!at_conv_info) { + /* no not yet so create some */ + at_conv_info = wmem_new0(wmem_file_scope(), at_conv_info_t); + conversation_add_proto_data(conversation, proto_at, at_conv_info); + } + return at_conv_info; +} + +static at_packet_info_t * +get_at_packet_info(packet_info *pinfo, at_conv_info_t *at_conv) +{ + at_packet_info_t *at_info; + at_info = (at_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_at, 0); + if (!at_info) { + at_info = wmem_new0(wmem_file_scope(), at_packet_info_t); + p_add_proto_data(wmem_file_scope(), pinfo, proto_at, 0, at_info); + if(at_conv) { + at_info->initial_dce_command = at_conv->dce_command; + at_info->initial_dte_command = at_conv->dte_command; + } + } + at_info->current_dce_command = at_info->initial_dce_command; + at_info->current_dte_command = at_info->initial_dte_command; + return at_info; +} + +static void +set_at_packet_info(packet_info *pinfo, at_conv_info_t *at_conv, at_packet_info_t *at_info) +{ + if(at_conv && !PINFO_FD_VISITED(pinfo)) + { + at_conv->dce_command = at_info->current_dce_command; + at_conv->dte_command = at_info->current_dte_command; + } +} + +static at_processed_cmd_t *get_current_role_last_command(at_packet_info_t *at_info, guint32 role) +{ + if(!at_info) return NULL; + return role == ROLE_DCE ? &at_info->current_dce_command : &at_info->current_dte_command; +} + +static guint32 get_uint_parameter(wmem_allocator_t *pool, guint8 *parameter_stream, gint parameter_length) +{ + guint32 value; + gchar *val; + + val = (gchar*) wmem_alloc(pool, parameter_length + 1); + memcpy(val, parameter_stream, parameter_length); + val[parameter_length] = '\0'; + value = (guint32) g_ascii_strtoull(val, NULL, 10); + + return value; +} + +static gboolean check_only_dce_role(gint role, guint16 type) { + if (role == ROLE_DCE && type == TYPE_RESPONSE_ACK) return TRUE; + + return FALSE; +} + +static gboolean check_only_dte_role(gint role, guint16 type) { + if (role == ROLE_DTE && type == TYPE_ACTION_SIMPLY) return TRUE; + + return FALSE; +} + +static gboolean check_ccwa(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cfun(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cgdcont(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_ACTION_SIMPLY || + type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cgmi(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cgmm(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cgmr(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cgsn(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + + return FALSE; +} + +static gboolean check_chld(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_chup(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + + return FALSE; +} + +static gboolean check_ciev(gint role, guint16 type) { + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cimi(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cind(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_clac(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + + return FALSE; +} + +static gboolean check_clcc(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_clip(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cme(gint role, guint16 type) { + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cmee(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_ACTION_SIMPLY || + type == TYPE_TEST || type == TYPE_READ)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cmer(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cmgl(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cmgr(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cmux(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cnum(gint role, guint16 type) { + if (role == ROLE_DTE && type == TYPE_ACTION_SIMPLY) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cops(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cpin(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cpms(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_cscs(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_csim(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_csq(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_csupi(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + + return FALSE; +} + +static gboolean check_gmi(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_gmm(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_gmr(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_gsn(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION_SIMPLY || type == TYPE_TEST)) return TRUE; + + return FALSE; +} + +static gboolean check_vts(gint role, guint16 type) { + if (role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_TEST)) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_zpas(gint role, guint16 type) { + if (role == ROLE_DTE && type == TYPE_READ) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean check_zusim(gint role, guint16 type) { + if (role == ROLE_DTE && type == TYPE_TEST) return TRUE; + if (role == ROLE_DCE && type == TYPE_RESPONSE) return TRUE; + + return FALSE; +} + +static gboolean +dissect_ccwa_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!check_ccwa(role, type)) return FALSE; + + if (role == ROLE_DTE && parameter_number > 2) return FALSE; + if (role == ROLE_DCE && parameter_number > 7) return FALSE; + + if (role == ROLE_DTE) switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_ccwa_show_result_code, tvb, offset, parameter_length, value); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_ccwa_mode, tvb, offset, parameter_length, value); + break; + case 2: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_ccwa_class, tvb, offset, parameter_length, value); + break; + } + + /* If AT+CCWA = 1 */ + if (role == ROLE_DCE) switch (parameter_number) { + case 0: + proto_tree_add_item(tree, hf_at_number, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_at_type, tvb, offset, parameter_length, value); + if (value < 128 || value > 175) + expert_add_info(pinfo, pitem, &ei_at_type); + break; + case 2: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_ccwa_class, tvb, offset, parameter_length, value); + break; + case 3: + proto_tree_add_item(tree, hf_at_alpha, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 4: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_cli_validity, tvb, offset, parameter_length, value); + break; + case 5: + proto_tree_add_item(tree, hf_at_subaddress, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 6: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_subaddress_type, tvb, offset, parameter_length, value); + break; + case 7: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_priority, tvb, offset, parameter_length, value); + break; + } + + return TRUE; +} + +static gboolean +dissect_cfun_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!check_cfun(role, type)) return FALSE; + + if (parameter_number > 1) return FALSE; + + if (role == ROLE_DTE) switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cfun_fun, tvb, offset, parameter_length, value); + if (value > 4 && value < 128) + expert_add_info(pinfo, pitem, &ei_cfun_res_fun); + else if (value >= 128) + expert_add_info(pinfo, pitem, &ei_cfun_range_fun); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cfun_rst, tvb, offset, parameter_length, value); + if (value > 1) + expert_add_info(pinfo, pitem, &ei_cfun_rst); + break; + } + + /* TODO: Currently assuming response is for READ command, add support for + * TEST commands response */ + if (role == ROLE_DCE) switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cfun_fun, tvb, offset, parameter_length, value); + if (value > 4 && value < 128) + expert_add_info(pinfo, pitem, &ei_cfun_res_fun); + else if (value >= 128) + expert_add_info(pinfo, pitem, &ei_cfun_range_fun); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cfun_rst, tvb, offset, parameter_length, value); + if (value > 1) + expert_add_info(pinfo, pitem, &ei_cfun_rst); + break; + } + + return TRUE; +} + +static gboolean +dissect_cgdcont_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, + at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + + if (!check_cgdcont(role, type)) return FALSE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cgdcont_cid, tvb, offset, parameter_length, value); + break; + case 1: + proto_tree_add_item(tree, hf_cgdcont_pdp_type, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 2: + proto_tree_add_item(tree, hf_cgdcont_apn, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 3: + proto_tree_add_item(tree, hf_cgdcont_pdp_addr, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 4: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cgdcont_d_comp, tvb, offset, parameter_length, value); + break; + case 5: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cgdcont_h_comp, tvb, offset, parameter_length, value); + break; + default: + proto_tree_add_item(tree, hf_parameter, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + } + + return TRUE; +} + +static gboolean +dissect_cgmi_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_cgmi_manufacturer_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_cgmm_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_cgmm_model_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_cgmr_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_cgmr_revision_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_chld_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + + if (!check_chld(role, type)) return FALSE; + + if (role == ROLE_DTE && type == TYPE_ACTION && parameter_number == 0) { + value = get_uint_parameter(pinfo->pool, parameter_stream, 1); + + if (parameter_length >= 2) { + if (tvb_get_guint8(tvb, offset + 1) == 'x') { + if (value == 1) + proto_tree_add_item(tree, hf_chld_mode_1x, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + else if (value == 2) + proto_tree_add_item(tree, hf_chld_mode_2x, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + } + + if (tvb_get_guint8(tvb, offset + 1) != 'x' || value > 4) { + proto_tree_add_expert(tree, pinfo, &ei_chld_mode, tvb, offset, parameter_length); + } + } + + proto_tree_add_uint(tree, hf_chld_mode, tvb, offset, parameter_length, value); + return TRUE; + } + + /* Type == Test */ + proto_tree_add_item(tree, hf_chld_supported_modes, tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_ciev_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data) +{ + guint32 value; + guint indicator_index; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) return TRUE; + if (parameter_number > 1) return FALSE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_ciev_indicator_index, tvb, offset, parameter_length, value); + *data = wmem_alloc(pinfo->pool, sizeof(guint)); + *((guint *) *data) = value; + break; + case 1: + indicator_index = *((guint *) *data) - 1; + if (indicator_index > 19) { + proto_tree_add_expert(tree, pinfo, &ei_ciev_indicator, tvb, offset, parameter_length); + } else { + proto_tree_add_item(tree, hf_indicator[indicator_index], tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + } + break; + } + + return TRUE; +} + +static gboolean +dissect_cimi_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + + if (!check_cimi(role, type)) return FALSE; + + if (role == ROLE_DTE) return FALSE; + if (parameter_number > 0) return FALSE; + + /* Only parameter is found in the response from DCE - the IMSI */ + pitem = proto_tree_add_item(tree, hf_cimi_imsi, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + /* Hiding the AT IMSI item because we are showing the detailed E.212 item */ + proto_item_set_hidden(pitem); + dissect_e212_utf8_imsi(tvb, pinfo, tree, offset, parameter_length); + + return TRUE; +} + +static gboolean +dissect_cind_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!check_cind(role, type)) return FALSE; + if (parameter_number > 19) return FALSE; + + proto_tree_add_item(tree, hf_indicator[parameter_number], tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_clcc_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!((role == ROLE_DTE && type == TYPE_ACTION_SIMPLY) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (parameter_number > 8) return FALSE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clcc_id, tvb, offset, parameter_length, value); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clcc_dir, tvb, offset, parameter_length, value); + break; + case 2: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clcc_stat, tvb, offset, parameter_length, value); + break; + case 3: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clcc_mode, tvb, offset, parameter_length, value); + break; + case 4: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clcc_mpty, tvb, offset, parameter_length, value); + break; + case 5: + proto_tree_add_item(tree, hf_at_number, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 6: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_at_type, tvb, offset, parameter_length, value); + if (value < 128 || value > 175) + expert_add_info(pinfo, pitem, &ei_at_type); + break; + case 7: + proto_tree_add_item(tree, hf_at_alpha, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 8: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_priority, tvb, offset, parameter_length, value); + break; + } + + return TRUE; +} + +static gboolean +dissect_clip_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!check_clip(role, type)) + return FALSE; + + if (role == ROLE_DTE && type == TYPE_ACTION && parameter_number > 1) + return FALSE; + else if (role == ROLE_DCE && parameter_number > 5) + return FALSE; + + if (role == ROLE_DTE && type == TYPE_ACTION) switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clip_mode, tvb, offset, parameter_length, value); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_clip_status, tvb, offset, parameter_length, value); + break; + } else { + switch (parameter_number) { + case 0: + proto_tree_add_item(tree, hf_at_number, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_at_type, tvb, offset, parameter_length, value); + if (value < 128 || value > 175) + expert_add_info(pinfo, pitem, &ei_at_type); + break; + case 2: + proto_tree_add_item(tree, hf_at_subaddress, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 3: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_subaddress_type, tvb, offset, parameter_length, value); + break; + case 4: + proto_tree_add_item(tree, hf_at_alpha, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 5: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_at_cli_validity, tvb, offset, parameter_length, value); + break; + } + } + + return TRUE; +} + +static gboolean +dissect_cme_error_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + gint i; + char curr_char; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 0) return FALSE; + + /* CME Error might work in 2 modes: Numeric error codes or Verbose error messages */ + /* if the parameter stream contains anything but digits and whitespaces, assume verbose */ + for (i = 0; i < parameter_length; i++) { + curr_char = parameter_stream[i]; + if (!g_ascii_isdigit(curr_char) && curr_char != ' ') { + proto_tree_add_item(tree, hf_cme_error_verbose, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + return TRUE; + } + } + /* Assume numeric error code*/ + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cme_error, tvb, offset, parameter_length, value); + + return TRUE; +} + +static gboolean +dissect_cmee_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + + if (!(role == ROLE_DTE && type == TYPE_ACTION) && + !(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 0) return FALSE; + + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmee, tvb, offset, parameter_length, value); + + return TRUE; +} + +static gboolean +dissect_cmer_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!((role == ROLE_DTE && type == TYPE_ACTION))) { + return FALSE; + } + + if (parameter_number > 4) return FALSE; + + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + + switch (parameter_number) { + case 0: + pitem = proto_tree_add_uint(tree, hf_cmer_mode, tvb, offset, parameter_length, value); + if (value > 3) + expert_add_info(pinfo, pitem, &ei_cmer_mode); + break; + case 1: + pitem = proto_tree_add_uint(tree, hf_cmer_keyp, tvb, offset, parameter_length, value); + if (value > 2) + expert_add_info(pinfo, pitem, &ei_cmer_keyp); + break; + case 2: + pitem = proto_tree_add_uint(tree, hf_cmer_disp, tvb, offset, parameter_length, value); + if (value > 2) + expert_add_info(pinfo, pitem, &ei_cmer_disp); + break; + case 3: + pitem = proto_tree_add_uint(tree, hf_cmer_ind, tvb, offset, parameter_length, value); + if (value > 2) + expert_add_info(pinfo, pitem, &ei_cmer_ind); + break; + case 4: + pitem = proto_tree_add_uint(tree, hf_cmer_bfr, tvb, offset, parameter_length, value); + if (value > 1) + expert_add_info(pinfo, pitem, &ei_cmer_bfr); + break; + } + + return TRUE; +} + +static gboolean +dissect_cmgl_data_part(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, gint offset, gint role, guint16 type, + guint8 *data_part_stream _U_, guint data_part_number _U_, + gint data_part_length, at_packet_info_t *at_info _U_) +{ + proto_item *pitem; + gint hex_length; + gint bytes_count; + gint i; + guint8 *final_arr; + tvbuff_t *final_tvb = NULL; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + pitem = proto_tree_add_item(tree, hf_cmgl_msg_pdu, tvb, offset, data_part_length, ENC_NA | ENC_ASCII); + + hex_length = data_part_length; + if (hex_length % 2 == 1) { + expert_add_info(pinfo, pitem, &ei_odd_len); + return TRUE; + } + if (hex_length < 1) { + expert_add_info(pinfo, pitem, &ei_empty_hex); + return TRUE; + } + bytes_count = hex_length / 2; + final_arr = wmem_alloc0_array(pinfo->pool, guint8, bytes_count + 1); + /* Try to parse the hex string into a byte array */ + guint8 *pos = data_part_stream; + pos += 16; + for (i = 8; i < bytes_count; i++) { + if (!g_ascii_isxdigit(*pos) || !g_ascii_isxdigit(*(pos + 1))) { + /* Either current or next char isn't a hex character */ + expert_add_info(pinfo, pitem, &ei_invalid_hex); + return TRUE; + } + sscanf((char *)pos, "%2hhx", &(final_arr[i-8])); + pos += 2; + } + final_tvb = tvb_new_child_real_data(tvb, final_arr, bytes_count, bytes_count); + add_new_data_source(pinfo, final_tvb, "GSM SMS payload"); + + /* Adjusting P2P direction as it is read by the SMS dissector */ + int at_dir = pinfo->p2p_dir; + pinfo->p2p_dir = P2P_DIR_SENT; + + /* Call GSM SMS dissector*/ + call_dissector_only(gsm_sms_handle, final_tvb, pinfo, tree, NULL); + + /* Restoring P2P direction */ + pinfo->p2p_dir = at_dir; + return TRUE; +} + +static gboolean +dissect_cmgl_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info, void **data _U_) +{ + guint32 value = 0; + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (role == ROLE_DTE && type == TYPE_ACTION && parameter_number > 0) + return FALSE; + else if (role == ROLE_DCE && parameter_number > 3) + return FALSE; + + if (role == ROLE_DTE && type == TYPE_ACTION) { + proto_tree_add_item(tree, hf_cmgl_req_status, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + } else { + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgl_msg_index, tvb, offset, parameter_length, value); + break; + case 1: + proto_tree_add_item(tree, hf_cmgl_msg_status, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 2: + proto_tree_add_item(tree, hf_cmgl_msg_originator_name, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 3: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgl_msg_length, tvb, offset, parameter_length, value); + // If we reached the length parameter we are + // expecting the next line to be our encoded data + at_processed_cmd_t * at_cmd = get_current_role_last_command(at_info, role); + if (!at_cmd) + break; + at_cmd->type = type; + at_cmd->expected_data_parts = 1; + at_cmd->consumed_data_parts = 0; + at_cmd->dissect_data = dissect_cmgl_data_part; + break; + } + } + + return TRUE; +} + +static gboolean +dissect_cmgr_data_part(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, gint offset, gint role, guint16 type, + guint8 *data_part_stream _U_, guint data_part_number _U_, + gint data_part_length, at_packet_info_t *at_info _U_) +{ + proto_item *pitem; + gint hex_length; + gint bytes_count; + gint i; + guint8 *final_arr; + tvbuff_t *final_tvb = NULL; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + pitem = proto_tree_add_item(tree, hf_cmgr_msg_pdu, tvb, offset, data_part_length, ENC_NA | ENC_ASCII); + + hex_length = data_part_length; + if (hex_length % 2 == 1) { + expert_add_info(pinfo, pitem, &ei_odd_len); + return TRUE; + } + if (hex_length < 1) { + expert_add_info(pinfo, pitem, &ei_empty_hex); + return TRUE; + } + bytes_count = hex_length / 2; + final_arr = wmem_alloc0_array(pinfo->pool, guint8, bytes_count + 1); + /* Try to parse the hex string into a byte array */ + guint8 *pos = data_part_stream; + pos += 16; + for (i = 8; i < bytes_count; i++) { + if (!g_ascii_isxdigit(*pos) || !g_ascii_isxdigit(*(pos + 1))) { + /* Either current or next char isn't a hex character */ + expert_add_info(pinfo, pitem, &ei_invalid_hex); + return TRUE; + } + sscanf((char *)pos, "%2hhx", &(final_arr[i-8])); + pos += 2; + } + final_tvb = tvb_new_child_real_data(tvb, final_arr, bytes_count, bytes_count); + add_new_data_source(pinfo, final_tvb, "GSM SMS payload"); + + /* Adjusting P2P direction as it is read by the SMS dissector */ + int at_dir = pinfo->p2p_dir; + pinfo->p2p_dir = P2P_DIR_SENT; + + /* Call GSM SMS dissector*/ + call_dissector_only(gsm_sms_handle, final_tvb, pinfo, tree, NULL); + + /* Restoring P2P direction */ + pinfo->p2p_dir = at_dir; + return TRUE; +} + +static gboolean +dissect_cmgr_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info, void **data _U_) +{ + guint32 value = 0; + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (role == ROLE_DTE && parameter_number > 1) + return FALSE; + else if (role == ROLE_DCE && parameter_number > 3) + return FALSE; + + if (role == ROLE_DTE) { + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgr_msg_index, tvb, offset, parameter_length, value); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgr_mode, tvb, offset, parameter_length, value); + break; + } + } else { + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgr_stat, tvb, offset, parameter_length, value); + break; + case 1: + proto_tree_add_item(tree, hf_cmgr_address, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 2: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cmgr_msg_length, tvb, offset, parameter_length, value); + // If we reached the length parameter we are + // expecting the next line to be our encoded data + at_processed_cmd_t * at_cmd = get_current_role_last_command(at_info, role); + if (!at_cmd) + break; + at_cmd->type = type; + at_cmd->expected_data_parts = 1; + at_cmd->consumed_data_parts = 0; + at_cmd->dissect_data = dissect_cmgr_data_part; + break; + } + } + + return TRUE; +} + +static gboolean +dissect_cmux_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value = 0; + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (parameter_number > 8) return FALSE; + + /* Parameters are the same for both ACTION and RESPONSE */ + if (parameter_length != 0) { + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + } + switch (parameter_number) { + case 0: + proto_tree_add_uint(tree, hf_cmux_transparency, tvb, offset, parameter_length, value); + break; + case 1: + /* In the RESPONSE, the subset parameter might be missing */ + if (type == TYPE_ACTION || parameter_length != 0) { + proto_tree_add_uint(tree, hf_cmux_subset, tvb, offset, parameter_length, value); + } + break; + case 2: + proto_tree_add_item(tree, hf_cmux_port_speed, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 3: + proto_tree_add_uint(tree, hf_cmux_n1, tvb, offset, parameter_length, value); + break; + case 4: + proto_tree_add_uint(tree, hf_cmux_t1, tvb, offset, parameter_length, value); + break; + case 5: + proto_tree_add_uint(tree, hf_cmux_n2, tvb, offset, parameter_length, value); + break; + case 6: + proto_tree_add_uint(tree, hf_cmux_t2, tvb, offset, parameter_length, value); + break; + case 7: + proto_tree_add_uint(tree, hf_cmux_t3, tvb, offset, parameter_length, value); + break; + case 8: + proto_tree_add_uint(tree, hf_cmux_k, tvb, offset, parameter_length, value); + break; + } + + return TRUE; +} + +static gboolean +dissect_cnum_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) return FALSE; + if (parameter_number > 5) return FALSE; + + switch (parameter_number) { + case 0: + proto_tree_add_item(tree, hf_at_alpha, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + proto_tree_add_item(tree, hf_at_number, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 2: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_at_type, tvb, offset, parameter_length, value); + if (value < 128 || value > 175) + expert_add_info(pinfo, pitem, &ei_at_type); + break; + case 3: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cnum_speed, tvb, offset, parameter_length, value); + break; + case 4: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cnum_service, tvb, offset, parameter_length, value); + if (value > 5) + expert_add_info(pinfo, pitem, &ei_cnum_service); + break; + case 5: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_cnum_itc, tvb, offset, parameter_length, value); + if (value > 1) + expert_add_info(pinfo, pitem, &ei_cnum_itc); + break; + } + + return TRUE; +} + +static gboolean +dissect_cops_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + + if (!((role == ROLE_DTE && (type == TYPE_ACTION || type == TYPE_READ)) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (parameter_number > 3) return FALSE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cops_mode, tvb, offset, parameter_length, value); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cops_format, tvb, offset, parameter_length, value); + break; + case 2: + proto_tree_add_item(tree, hf_cops_operator, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 3: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_cops_act, tvb, offset, parameter_length, value); + break; + } + + return TRUE; +} + +static gboolean +dissect_cpin_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + gboolean is_ready; + gchar *pin_type; + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (type == TYPE_ACTION) { + switch (parameter_number) { + case 0: + proto_tree_add_item(tree, hf_cpin_pin, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + proto_tree_add_item(tree, hf_cpin_newpin, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + default: + return FALSE; + } + return TRUE; + } + + /* type is TYPE_RESPONSE */ + if (parameter_number == 0) { + pitem = proto_tree_add_item(tree, hf_cpin_code, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + is_ready = g_ascii_strncasecmp("READY", (gchar*)parameter_stream, parameter_length) == 0; + if (is_ready) { + proto_item_append_text(pitem, " (MT is not pending for any password)"); + } + else { + pin_type = wmem_strndup(pinfo->pool, parameter_stream, parameter_length); + proto_item_append_text(pitem, " (MT is waiting %s to be given)", pin_type); + } + return TRUE; + } + return FALSE; +} + +static gboolean +dissect_cpms_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (type == TYPE_ACTION) { + switch (parameter_number) { + case 0: + proto_tree_add_item(tree, hf_cpms_mem1, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + proto_tree_add_item(tree, hf_cpms_mem2, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 2: + proto_tree_add_item(tree, hf_cpms_mem3, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + default: + return FALSE; + } + return TRUE; + } + else { + // TODO: Assuming response is for ACTION command, need to support + // responses for READ and QUERY + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + switch (parameter_number) { + case 0: + proto_tree_add_uint(tree, hf_cpms_used1, tvb, offset, parameter_length, value); + break; + case 1: + proto_tree_add_uint(tree, hf_cpms_total1, tvb, offset, parameter_length, value); + break; + case 2: + proto_tree_add_uint(tree, hf_cpms_used2, tvb, offset, parameter_length, value); + break; + case 3: + proto_tree_add_uint(tree, hf_cpms_total2, tvb, offset, parameter_length, value); + break; + case 4: + proto_tree_add_uint(tree, hf_cpms_used3, tvb, offset, parameter_length, value); + break; + case 5: + proto_tree_add_uint(tree, hf_cpms_total3, tvb, offset, parameter_length, value); + break; + default: + return FALSE; + } + return TRUE; + } +} + +static gboolean +dissect_cscs_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (parameter_number > 0) { + return FALSE; + } + + /* For both ACTION and RESPONSE the first + * and only parameter is the character set */ + proto_tree_add_item(tree, hf_cscs_chset, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + return TRUE; +} + +static gboolean +dissect_csim_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data) +{ + proto_item *pitem; + guint32 value; + gint hex_length; + gint bytes_count; + gint i; + guint8 *final_arr; + tvbuff_t *final_tvb=NULL; + + if (!((role == ROLE_DTE && type == TYPE_ACTION) || + (role == ROLE_DCE && type == TYPE_RESPONSE))) { + return FALSE; + } + + if (parameter_number > 1) return TRUE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_csim_length, tvb, offset, parameter_length, value); + break; + case 1: + if(role == ROLE_DTE) { + pitem = proto_tree_add_item(tree, hf_csim_command, tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + } + else { + pitem = proto_tree_add_item(tree, hf_csim_response, tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + } + hex_length = (parameter_length - 2); /* ignoring leading and trailing quotes */ + if (hex_length % 2 == 1) { + expert_add_info(pinfo, pitem, &ei_odd_len); + return TRUE; + } + if(hex_length < 1) { + expert_add_info(pinfo, pitem, &ei_empty_hex); + return TRUE; + } + bytes_count = hex_length / 2; + final_arr = wmem_alloc0_array(pinfo->pool,guint8,bytes_count); + /* Try to parse the hex string into a byte array */ + guint8 *pos = parameter_stream; + pos++; /* skipping first quotes */ + for (i = 0; i < bytes_count; i++) { + if (!g_ascii_isxdigit(*pos) || !g_ascii_isxdigit(*(pos + 1))) { + /* Either current or next char isn't a hex character */ + expert_add_info(pinfo, pitem, &ei_invalid_hex); + return TRUE; + } + sscanf((char *)pos, "%2hhx", &(final_arr[i])); + pos += 2; + } + final_tvb = tvb_new_child_real_data(tvb, final_arr, bytes_count, bytes_count); + add_new_data_source(pinfo, final_tvb, "GSM SIM payload"); + /* Call GSM SIM dissector*/ + call_dissector_with_data(gsm_sim_handle, final_tvb, pinfo, tree, data); + break; + } + + return TRUE; +} + +static gboolean +dissect_csq_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) return FALSE; + + if (parameter_number > 1) return FALSE; + + switch (parameter_number) { + case 0: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_csq_rssi, tvb, offset, parameter_length, value); + if (value > 31 && value != 99) + expert_add_info(pinfo, pitem, &ei_csq_rssi); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + pitem = proto_tree_add_uint(tree, hf_csq_ber, tvb, offset, parameter_length, value); + if (value > 7 && value != 99) + expert_add_info(pinfo, pitem, &ei_csq_ber); + break; + } + + return TRUE; +} + +static gboolean +dissect_gmi_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_gmi_manufacturer_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_gmm_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_gmm_model_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_gmr_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + proto_tree_add_item(tree, hf_gmr_revision_id, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + + return TRUE; +} + +static gboolean +dissect_vts_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + proto_item *pitem; + guint32 value; + + if (!(role == ROLE_DTE && type == TYPE_ACTION)) return FALSE; + if (parameter_number > 1) return FALSE; + + switch (parameter_number) { + case 0: + pitem = proto_tree_add_item(tree, hf_vts_dtmf, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + if (parameter_length != 1) + expert_add_info(pinfo, pitem, &ei_vts_dtmf); + break; + case 1: + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_vts_duration, tvb, offset, parameter_length, value); + break; + } + + return TRUE; +} + +static gboolean +dissect_zpas_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream _U_, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 1) return FALSE; + + switch(parameter_number) + { + case 0: + proto_tree_add_item(tree, hf_zpas_network, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + case 1: + proto_tree_add_item(tree, hf_zpas_srv_domain, tvb, offset, parameter_length, ENC_NA | ENC_ASCII); + break; + } + + return TRUE; +} + +static gboolean +dissect_zusim_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + gint offset, gint role, guint16 type, guint8 *parameter_stream, + guint parameter_number, gint parameter_length, at_packet_info_t *at_info _U_, void **data _U_) +{ + guint32 value; + + if (!(role == ROLE_DCE && type == TYPE_RESPONSE)) { + return FALSE; + } + + if (parameter_number > 0) return FALSE; + + value = get_uint_parameter(pinfo->pool, parameter_stream, parameter_length); + proto_tree_add_uint(tree, hf_zusim_usim_card, tvb, offset, parameter_length, value); + + return TRUE; +} + +static gboolean +dissect_no_parameter(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, + gint offset _U_, gint role _U_, guint16 type _U_, guint8 *parameter_stream _U_, + guint parameter_number _U_, gint parameter_length _U_, at_packet_info_t *at_info _U_, void **data _U_) +{ + return FALSE; +} + +/* TODO: Some commands need to save request command type (request with TYPE_READ vs TYPE_TEST, etc.) + to properly dissect response parameters. + Some commands can use TYPE_TEST respose to properly dissect parameters, + for example: AT+CIND=?, AT+CIND? */ +static const at_cmd_t at_cmds[] = { + { "+CCWA", "Call Waiting Notification", check_ccwa, dissect_ccwa_parameter }, + { "+CFUN", "Set Phone Functionality", check_cfun, dissect_cfun_parameter }, + { "+CGDCONT", "PDP context define", check_cgdcont, dissect_cgdcont_parameter }, + { "+CGMI", "Request manufacturer identification", check_cgmi, dissect_cgmi_parameter }, + { "+CGMM", "Request model identification", check_cgmm, dissect_cgmm_parameter }, + { "+CGMR", "Request revision identification", check_cgmr, dissect_cgmr_parameter }, + { "+CGSN", "Request Product Serial Number Identification (ESN/IMEI)", check_cgsn, dissect_no_parameter }, + { "+CHLD", "Call Hold and Multiparty Handling", check_chld, dissect_chld_parameter }, + { "+CHUP", "Call Hang-up", check_chup, dissect_no_parameter }, + { "+CIEV", "Indicator Events Reporting", check_ciev, dissect_ciev_parameter }, + { "+CIMI", "Request International Mobile Subscriber Identity (IMSI)", check_cimi, dissect_cimi_parameter }, + { "^CIMI", "Request International Mobile Subscriber Identity (IMSI)", check_cimi, dissect_cimi_parameter }, + { "+CIND", "Phone Indicators", check_cind, dissect_cind_parameter }, + { "+CLAC", "List All Available AT Commands", check_clac, dissect_no_parameter }, + { "+CLCC", "Current Calls", check_clcc, dissect_clcc_parameter }, + { "+CLIP", "Calling Line Identification Notification", check_clip, dissect_clip_parameter }, + { "+CME ERROR", "Mobile Termination Error Result Code", check_cme, dissect_cme_error_parameter }, + { "+CMEE", "Mobile Equipment Error", check_cmee, dissect_cmee_parameter }, + { "+CMER", "Event Reporting Activation/Deactivation", check_cmer, dissect_cmer_parameter }, + { "+CMGL", "List SMS messages", check_cmgl, dissect_cmgl_parameter }, + { "+CMGR", "Read SMS message", check_cmgr, dissect_cmgr_parameter }, + { "+CMUX", "Multiplexing mode", check_cmux, dissect_cmux_parameter }, + { "+CNUM", "Subscriber Number Information", check_cnum, dissect_cnum_parameter }, + { "+COPS", "Reading Network Operator", check_cops, dissect_cops_parameter }, + { "+CPIN", "Enter SIM PIN", check_cpin, dissect_cpin_parameter }, + { "+CPMS", "Preferred Message Storage", check_cpms, dissect_cpms_parameter }, + { "+CSCS", "Select TE Character Set", check_cscs, dissect_cscs_parameter }, + { "+CSIM", "Generic SIM access", check_csim, dissect_csim_parameter }, + { "+CSQ", "Signal Quality", check_csq, dissect_csq_parameter }, + { "+CSUPI", "Request 5G subscription permanent identifier", check_csupi, dissect_no_parameter }, + { "+GMI", "Request manufacturer identification", check_gmi, dissect_gmi_parameter }, + { "+GMM", "Request model identification", check_gmm, dissect_gmm_parameter }, + { "+GMR", "Request revision identification", check_gmr, dissect_gmr_parameter }, + { "+GSN", "Request Product Serial Number Identification (ESN/IMEI)", check_gsn, dissect_no_parameter }, + { "+VTS", "DTMF and tone generation", check_vts, dissect_vts_parameter }, + { "+ZPAS", "Check Card Status", check_zpas, dissect_zpas_parameter }, + { "+ZUSIM", "Check USIM Card Type", check_zusim, dissect_zusim_parameter }, + { "ERROR", "ERROR", check_only_dce_role, dissect_no_parameter }, + { "RING", "Incoming Call Indication", check_only_dce_role, dissect_no_parameter }, + { "OK", "OK", check_only_dce_role, dissect_no_parameter }, + { "D", "Dial", check_only_dte_role, NULL }, + { "A", "Call Answer", check_only_dte_role, dissect_no_parameter }, + { "E0", "Disable Echo", check_only_dte_role, dissect_no_parameter }, + { "E1", "Enable Echo", check_only_dte_role, dissect_no_parameter }, + { "I", "Product Identification Information", check_only_dte_role, dissect_no_parameter }, + { NULL, NULL, NULL, NULL } +}; + +static gint +dissect_at_command(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, guint32 role, gint command_number, at_packet_info_t *at_info) +{ + proto_item *pitem; + proto_tree *command_item = NULL; + proto_item *command_tree = NULL; + proto_tree *parameters_item = NULL; + proto_item *parameters_tree = NULL; + gchar *at_stream; + gchar *at_command = NULL; + char *name; + gint i_char = 0; + guint i_char_fix = 0; + gint length; + gint leftover_length; + const at_cmd_t *i_at_cmd; + gint parameter_length; + guint parameter_number = 0; + gint first_parameter_offset = offset; + gint last_parameter_offset = offset; + guint16 type = TYPE_UNKNOWN; + guint32 brackets; + gboolean quotation; + gboolean next; + void *data; + at_processed_cmd_t *last_command; + + length = tvb_reported_length_remaining(tvb, offset); + if (length <= 0) + return tvb_reported_length(tvb); + + if (!command_number) { + proto_tree_add_item(tree, hf_data, tvb, offset, length, ENC_NA | ENC_ASCII); + } + + at_stream = (guint8 *) wmem_alloc(pinfo->pool, length + 1); + tvb_memcpy(tvb, at_stream, offset, length); + at_stream[length] = '\0'; + + while (at_stream[i_char]) { + at_stream[i_char] = g_ascii_toupper(at_stream[i_char]); + i_char += 1; + } + + if (role == ROLE_DTE) { + if (command_number) { + at_command = at_stream; + i_char = 0; + } else { + at_command = g_strstr_len(at_stream, length, "AT"); + if (at_command) { + command_item = proto_tree_add_none_format(tree, hf_command, tvb, + offset, 0, "Command %u", command_number); + command_tree = proto_item_add_subtree(command_item, ett_at_command); + + i_char = (guint) (at_command - at_stream); + if (i_char) { + proto_tree_add_item(command_tree, hf_at_ignored, tvb, offset, + i_char, ENC_NA | ENC_ASCII); + offset += i_char; + } + + proto_tree_add_item(command_tree, hf_at_command_line_prefix, + tvb, offset, 2, ENC_NA | ENC_ASCII); + offset += 2; + i_char += 2; + at_command = at_stream; + at_command += i_char; + length -= i_char; + i_char_fix += i_char; + i_char = 0; + } + } + } else { + command_item = proto_tree_add_none_format(tree, hf_command, tvb, + offset, 0, "Command %u", command_number); + command_tree = proto_item_add_subtree(command_item, ett_at_command); + + at_command = at_stream; + i_char = 0; + while (i_char <= length && + (at_command[i_char] == '\r' || at_command[i_char] == '\n' || + at_command[i_char] == ' ' || at_command[i_char] == '\t')) { + /* ignore white characters */ + i_char += 1; + } + + offset += i_char; + at_command += i_char; + length -= i_char; + i_char_fix += i_char; + i_char = 0; + } + + if (at_command) { + + while (i_char < length && + (at_command[i_char] != '\r' && at_command[i_char] != '=' && + at_command[i_char] != ';' && at_command[i_char] != '?' && + at_command[i_char] != ':')) { + i_char += 1; + } + + i_at_cmd = at_cmds; + if (at_command[0] == '\r') { + pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset - 2, + 2, ENC_NA | ENC_ASCII); + i_at_cmd = NULL; + } else { + pitem = NULL; + while (i_at_cmd->name) { + if (g_str_has_prefix(&at_command[0], i_at_cmd->name)) { + pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset, + (gint) strlen(i_at_cmd->name), ENC_NA | ENC_ASCII); + proto_item_append_text(pitem, " (%s)", i_at_cmd->long_name); + break; + } + i_at_cmd += 1; + } + + if (!pitem) { + pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset, + i_char, ENC_NA | ENC_ASCII); + } + } + + name = format_text(pinfo->pool, at_command, i_char + 1); + + if (i_at_cmd && i_at_cmd->name == NULL) { + proto_item_append_text(command_item, ": %s (Unknown)", name); + proto_item_append_text(pitem, " (Unknown)"); + expert_add_info(pinfo, pitem, &ei_unknown_command); + } else if (i_at_cmd == NULL) { + proto_item_append_text(command_item, ": AT"); + } else { + proto_item_append_text(command_item, ": %s", i_at_cmd->name); + } + + offset += i_char; + + leftover_length = length - i_char; + if (i_at_cmd && g_strcmp0(i_at_cmd->name, "D")) { + if (leftover_length >= 2 && at_command[i_char] == '=' && at_command[i_char + 1] == '?') { + type = at_command[i_char] << 8 | at_command[i_char + 1]; + proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 2, type); + offset += 2; + i_char += 2; + } else if (role == ROLE_DCE && leftover_length >= 2 && at_command[i_char] == '\r' && at_command[i_char + 1] == '\n') { + type = at_command[i_char] << 8 | at_command[i_char + 1]; + proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 2, type); + offset += 2; + i_char += 2; + } else if (leftover_length >= 1 && (at_command[i_char] == '=' || + at_command[i_char] == '\r' || + at_command[i_char] == ':' || + at_command[i_char] == '?')) { + type = at_command[i_char]; + proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 1, type); + offset += 1; + i_char += 1; + } + else if (leftover_length == 0) { + /* No suffix, assume line break (which translates to 'ACTION_SIMPLY') */ + type = TYPE_ACTION_SIMPLY; + pitem = proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 0, type); + proto_item_set_generated(pitem); + } + } + + /* Setting new command's info in the Last Command field */ + last_command = get_current_role_last_command(at_info, role); + if (last_command) { + g_strlcpy(last_command->name, name, STORE_COMMAND_MAX_LEN); + last_command->type = type; + last_command->expected_data_parts = 0; + last_command->consumed_data_parts = 0; + } + + if (i_at_cmd && i_at_cmd->check_command && !i_at_cmd->check_command(role, type)) { + expert_add_info(pinfo, command_item, &ei_invalid_usage); + } + + parameters_item = proto_tree_add_none_format(command_tree, hf_parameters, tvb, + offset, 0, "Parameters"); + parameters_tree = proto_item_add_subtree(parameters_item, ett_at_parameters); + first_parameter_offset = offset; + + data = NULL; + + while (i_char < length) { + + while (at_command[i_char] == ' ' || at_command[i_char] == '\t') { + offset += 1; + i_char += 1; + } + + parameter_length = 0; + brackets = 0; + quotation = FALSE; + next = FALSE; + + if (at_command[i_char + parameter_length] != '\r') { + while (i_char + parameter_length < length && + at_command[i_char + parameter_length] != '\r') { + + if (at_command[i_char + parameter_length] == ';') { + next = TRUE; + break; + } + + if (at_command[i_char + parameter_length] == '"') { + quotation = quotation ? FALSE : TRUE; + } + + if (quotation == TRUE) { + parameter_length += 1; + continue; + } + + if (at_command[i_char + parameter_length] == '(') { + brackets += 1; + } + if (at_command[i_char + parameter_length] == ')') { + brackets -= 1; + } + + if (brackets == 0 && at_command[i_char + parameter_length] == ',') { + break; + } + + parameter_length += 1; + } + + if (type == TYPE_ACTION || type == TYPE_RESPONSE) { + if (i_at_cmd && (i_at_cmd->dissect_parameter != NULL && + !i_at_cmd->dissect_parameter(tvb, pinfo, parameters_tree, offset, role, + type, &at_command[i_char], parameter_number, parameter_length, at_info, &data) )) { + pitem = proto_tree_add_item(parameters_tree, + hf_unknown_parameter, tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + expert_add_info(pinfo, pitem, &ei_unknown_parameter); + } else if (i_at_cmd && i_at_cmd->dissect_parameter == NULL) { + proto_tree_add_item(parameters_tree, hf_parameter, tvb, offset, + parameter_length, ENC_NA | ENC_ASCII); + } + } + } + + if (type != TYPE_ACTION_SIMPLY && type != TYPE_RESPONSE_ACK && type != TYPE_TEST && type != TYPE_READ) + parameter_number += 1; + i_char += parameter_length; + offset += parameter_length; + last_parameter_offset = offset; + + if (role == ROLE_DCE && + i_char + 1 <= length && + at_command[i_char] == '\r' && + at_command[i_char + 1] == '\n') { + offset += 2; + i_char += 2; + break; + } else if (at_command[i_char] == ',' || + at_command[i_char] == '\r' || + at_command[i_char] == ';') { + i_char += 1; + offset += 1; + } + + if (next) break; + } + + i_char += i_char_fix; + proto_item_set_len(command_item, i_char); + } else { + length = tvb_reported_length_remaining(tvb, offset); + if (length < 0) + length = 0; + offset += length; + } + + if (parameter_number > 0 && last_parameter_offset - first_parameter_offset > 0) + proto_item_set_len(parameters_item, last_parameter_offset - first_parameter_offset); + else + proto_item_append_text(parameters_item, ": No"); + + return offset; +} + +static gint +dissect_at_command_continuation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + gint offset, guint32 role, gint command_number, at_packet_info_t *at_info) +{ + at_processed_cmd_t *cmd; + proto_item *data_part_item; + proto_item *data_part_tree; + proto_item *pitem; + gchar *data_stream; + gint data_part_index; + gint length; + gint data_part_length = 0; + + cmd = get_current_role_last_command(at_info, role); + if (!cmd) + return offset; + data_part_index = cmd->consumed_data_parts; + + length = tvb_reported_length_remaining(tvb, offset); + if (length <= 0) + return tvb_reported_length(tvb); + + data_stream = (guint8 *) wmem_alloc(pinfo->pool, length + 1); + tvb_memcpy(tvb, data_stream, offset, length); + data_stream[length] = '\0'; + + while (data_part_length < length && data_stream[data_part_length] != '\r') { + data_part_length += 1; + } + + data_part_item = proto_tree_add_none_format(tree, hf_data_part, tvb, + offset, data_part_length, "Command %u's Data Part %u", command_number, data_part_index); + data_part_tree = proto_item_add_subtree(data_part_item, ett_at_data_part); + + if (cmd && (cmd->dissect_data != NULL && + !cmd->dissect_data(tvb, pinfo, data_part_tree, offset, role, cmd->type, + data_stream, data_part_index, data_part_length, + at_info) )) { + pitem = proto_tree_add_item(data_part_tree, hf_unknown_parameter, tvb, offset, + data_part_length, ENC_NA | ENC_ASCII); + expert_add_info(pinfo, pitem, &ei_unknown_parameter); + } + offset += data_part_length; + return offset; +} + +/* The dissector itself */ +static int dissect_at(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_item *item; + proto_tree *at_tree; + gchar *string; + guint32 role = ROLE_UNKNOWN; + gint offset; + gint len; + guint32 cmd_indx; + conversation_t *conversation; + at_conv_info_t *at_conv; + at_packet_info_t *at_info; + at_processed_cmd_t *last_command; + + string = tvb_format_text_wsp(pinfo->pool, tvb, 0, tvb_captured_length(tvb)); + col_append_sep_str(pinfo->cinfo, COL_PROTOCOL, "/", "AT"); + switch (pinfo->p2p_dir) { + case P2P_DIR_SENT: + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Sent "); + break; + case P2P_DIR_RECV: + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Rcvd "); + break; + default: + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "UnknownDirection "); + break; + } + col_append_fstr(pinfo->cinfo, COL_INFO, "AT Command: %s", string); + + /* Check if user forces roles using preferences */ + if ((at_role == ROLE_DCE && pinfo->p2p_dir == P2P_DIR_SENT) || + (at_role == ROLE_DTE && pinfo->p2p_dir == P2P_DIR_RECV)) { + role = ROLE_DCE; + } else if (at_role != ROLE_UNKNOWN) { + role = ROLE_DTE; + } + + /* If no roles are forced, assume SENT from PC and RECV from device */ + if (role == ROLE_UNKNOWN) { + if (pinfo->p2p_dir == P2P_DIR_SENT) { + role = ROLE_DTE; + } else { + role = ROLE_DCE; + } + } + + /* Start with a top-level item to add everything else to */ + item = proto_tree_add_item(tree, proto_at, tvb, 0, -1, ENC_NA); + proto_item_append_text(item, ": %s", string); + at_tree = proto_item_add_subtree(item, ett_at); + + /* Show role in tree */ + item = proto_tree_add_uint(at_tree, hf_role, tvb, 0, 0, role); + proto_item_set_generated(item); + + + /* Dissect command(s) */ + len = tvb_captured_length(tvb); + offset = 0; + cmd_indx = 0; + + conversation = find_conversation(pinfo->num, + &pinfo->src, &pinfo->dst, + conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->srcport, pinfo->destport, 0); + at_conv = get_at_conv_info(conversation); + at_info = get_at_packet_info(pinfo, at_conv); + + while(offset < len) { + last_command = get_current_role_last_command(at_info, role); + if (last_command && last_command->expected_data_parts > last_command->consumed_data_parts) { + // Continuing a previous command + offset = dissect_at_command_continuation(tvb, pinfo, at_tree, offset, role, last_command->cmd_indx, at_info); + last_command->consumed_data_parts++; + } + else { + // New Command + offset = dissect_at_command(tvb, pinfo, at_tree, offset, role, cmd_indx, at_info); + /* Only if the command is expecting data parts save its index */ + last_command = get_current_role_last_command(at_info, role); + if (last_command && last_command->expected_data_parts > last_command->consumed_data_parts) { + last_command->cmd_indx = cmd_indx; + } + cmd_indx++; + } + } + set_at_packet_info(pinfo, at_conv, at_info); + return tvb_captured_length(tvb); +} + +static gint allowed_chars_len(tvbuff_t *tvb, gint captured_len) +{ + gint offset; + guint8 val; + + /* Get the amount of characters within the TVB which are ASCII, + * cartridge return or new line */ + for (offset = 0; offset < captured_len; offset++) { + val = tvb_get_guint8(tvb, offset); + if (!(g_ascii_isprint(val) || (val == 0x0a) || (val == 0x0d))) + return offset; + } + return captured_len; +} +static gboolean is_padded(tvbuff_t *tvb, gint captured_len, gint first_pad_offset) +{ + gint offset; + guint8 val; + + /* Check if the rest of the packet is 0x00 padding + * and no other values*/ + for (offset = first_pad_offset; offset < captured_len; offset++) { + val = tvb_get_guint8(tvb, offset); + if (val != 0x00) + return (FALSE); + } + return (TRUE); +} + +#define MIN_PADDED_ALLOWED_CHARS 4 +/* Experimental approach based upon the one used for PPP */ +static gboolean heur_dissect_at(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + const guint8 at_magic1[2] = {0x0d, 0x0a}; + const guint8 at_magic2[3] = {0x0d, 0x0d, 0x0a}; + const guint8 at_magic3[2] = {0x41, 0x54}; /* 'A' 'T' */ + gint len, allwd_chars_len; + tvbuff_t *tvb_no_padding; + + if ((tvb_memeql(tvb, 0, at_magic1, sizeof(at_magic1)) == 0) || + (tvb_memeql(tvb, 0, at_magic2, sizeof(at_magic2)) == 0) || + (tvb_memeql(tvb, 0, at_magic3, sizeof(at_magic3)) == 0)){ + len = tvb_captured_length(tvb); + allwd_chars_len = allowed_chars_len(tvb,len); + if(allwd_chars_len < len && allwd_chars_len > MIN_PADDED_ALLOWED_CHARS) { + /* Found some valid characters, check if rest is padding */ + if(is_padded(tvb,len,allwd_chars_len)) { + /* This is a padded AT Command */ + tvb_no_padding = tvb_new_subset_length(tvb, 0, allwd_chars_len); + dissect_at(tvb_no_padding, pinfo, tree, data); + return (TRUE); + } + } + else if(allwd_chars_len == len) { + /* This is an (unpadded) AT Command */ + dissect_at(tvb, pinfo, tree, data); + return (TRUE); + } + } + return (FALSE); +} + +void +proto_register_at_command(void) +{ + module_t *module; + expert_module_t *expert_at; + + static hf_register_info hf[] = { + { &hf_command, + { "Command", "at.command", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_data_part, + { "Data Part", "at.data_part", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_parameters, + { "Parameters", "at.parameters", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_data, + { "AT Stream", "at.data", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_ignored, + { "Ignored", "at.ignored", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_cmd, + { "Command", "at.cmd", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_cmd_type, + { "Type", "at.cmd.type", + FT_UINT16, BASE_HEX, VALS(at_cmd_type_vals), 0, + NULL, HFILL} + }, + { &hf_at_command_line_prefix, + { "Command Line Prefix", "at.command_line_prefix", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_parameter, + { "Parameter", "at.parameter", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_unknown_parameter, + { "Unknown Parameter", "at.unknown_parameter", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_role, + { "Role", "at.role", + FT_UINT8, BASE_DEC, VALS(role_vals), 0, + NULL, HFILL} + }, + { &hf_cmer_mode, + { "Mode", "at.cmer.mode", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmer_keyp, + { "Keypad", "at.cmer.keyp", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmer_disp, + { "Display", "at.cmer.disp", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmer_ind, + { "Indicator", "at.cmer.ind", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmer_bfr, + { "Buffer", "at.cmer.bfr", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cme_error, + { "CME Error (Numeric)", "at.cme_error", + FT_UINT8, BASE_DEC, VALS(cme_error_vals), 0, + NULL, HFILL} + }, + { &hf_cme_error_verbose, + { "CME Error (Verbose)", "at.cme_error_verbose", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cmee, + { "Mode", "at.cmee", + FT_UINT8, BASE_DEC, VALS(cmee_vals), 0, + NULL, HFILL} + }, + { &hf_cmgl_req_status, + { "Requested Status", "at.cmgl.req_status", + FT_STRING, BASE_NONE, NULL, 0, + "Status of the requested messages to list", + HFILL} + }, + { &hf_cmgl_msg_index, + { "Index", "at.cmgl.msg_index", + FT_UINT16, BASE_DEC, NULL, 0, + "Index of the message", + HFILL} + }, + { &hf_cmgl_msg_status, + { "Status", "at.cmgl.msg_status", + FT_STRING, BASE_NONE, NULL, 0, + "Status of the message", + HFILL} + }, + { &hf_cmgl_msg_originator_name, + { "Originator Name", "at.cmgl.originator_name", + FT_STRING, BASE_NONE, NULL, 0, + "Originator name as saved in the phonebook", + HFILL} + }, + { &hf_cmgl_msg_length, + { "Length", "at.cmgl.pdu_length", + FT_UINT16, BASE_DEC, NULL, 0, + "PDU Length", + HFILL} + }, + { &hf_cmgl_msg_pdu, + { "SMS PDU", "at.cmgl.pdu", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cmgr_address, + { "Address", "at.cmgr.address", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cmgr_mode, + { "Mode", "at.cmgr.mode", + FT_UINT16, BASE_DEC, VALS(cmgr_mode_vals), 0, + "Reading mode", + HFILL} + }, + { &hf_cmgr_msg_index, + { "Index", "at.cmgr.msg_index", + FT_UINT16, BASE_DEC, NULL, 0, + "Index of the message", + HFILL} + }, + { &hf_cmgr_msg_length, + { "Length", "at.cmgr.pdu_length", + FT_UINT16, BASE_DEC, NULL, 0, + "PDU Length", + HFILL} + }, + { &hf_cmgr_msg_pdu, + { "SMS PDU", "at.cmgr.pdu", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cmgr_stat, + { "Status", "at.cmgr.status", + FT_UINT32, BASE_DEC, VALS(cmgr_stat_vals), 0, + "Status of the returned message", + HFILL} + }, + { &hf_cmux_k, + { "Window Size", "at.k", + FT_UINT8, BASE_DEC, NULL, 0, + "Window Size for Advanced option with Error-Recovery Mode", + HFILL} + }, + { &hf_cmux_n1, + { "Maximum Frame Size", "at.n1", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmux_n2, + { "Maximum Number of Re-transmissions", "at.n2", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cmux_port_speed, + { "Transmission Rate", "at.port_speed", + FT_UINT8, BASE_DEC, VALS(cmux_port_speed_vals), 0, + NULL, + HFILL} + }, + { &hf_cmux_subset, + { "Subset", "at.subset", + FT_UINT8, BASE_DEC, VALS(cmux_subset_vals), 0, + NULL, + HFILL} + }, + { &hf_cmux_t1, + { "Acknowledgement Timer", "at.t1", + FT_UINT8, BASE_DEC, NULL, 0, + "Acknowledgement timer in units of ten milliseconds", + HFILL} + }, + { &hf_cmux_t2, + { "Response Timer", "at.t2", + FT_UINT8, BASE_DEC, NULL, 0, + "Response timer for the multiplexer control channel in units of ten milliseconds", + HFILL} + }, + { &hf_cmux_t3, + { "Wake Up Response Timer", "at.t3", + FT_UINT8, BASE_DEC, NULL, 0, + "Wake up response timer in seconds", + HFILL} + }, + { &hf_cmux_transparency, + { "Transparency Mechanism", "at.transparency", + FT_UINT8, BASE_DEC, VALS(cmux_transparency_vals), 0, + NULL, + HFILL} + }, + { &hf_chld_mode, + { "Mode", "at.chld.mode_value", + FT_UINT8, BASE_DEC, VALS(chld_vals), 0, + NULL, HFILL} + }, + { &hf_chld_mode_1x, + { "Mode: Releases specified active call only", "at.chld.mode", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_chld_mode_2x, + { "Mode: Request private consultation mode with specified call - place all calls on hold EXCEPT the call indicated by x", "at.chld.mode", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_chld_supported_modes, + { "Supported Modes", "at.chld.supported_modes", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cimi_imsi, + { "IMSI", "at.cimi.imsi", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_ciev_indicator_index, + { "Indicator Index", "at.ciev.indicator_index", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_vts_dtmf, + { "DTMF", "at.vts.dtmf", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_vts_duration, + { "Duration", "at.vts.duration", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cops_mode, + { "Mode", "at.cops.mode", + FT_UINT8, BASE_DEC, VALS(cops_mode_vals), 0, + NULL, HFILL} + }, + { &hf_cops_format, + { "Format", "at.cops.format", + FT_UINT8, BASE_DEC, VALS(cops_format_vals), 0, + NULL, HFILL} + }, + { &hf_cops_operator, + { "Operator", "at.cops.operator", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cops_act, + { "AcT", "at.cops.act", + FT_UINT8, BASE_DEC, VALS(cops_act_vals), 0, + NULL, HFILL} + }, + { &hf_cpin_code, + { "Code", "at.cpin.code", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cpin_pin, + { "PIN", "at.cpin.pin", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cpin_newpin, + { "New PIN", "at.cpin.newpin", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cpms_mem1, + { "Read Memory Storage", "at.cpms.mem1", + FT_STRING, BASE_NONE, NULL, 0, + "Memory from which SMS messages are read and deleted", + HFILL} + }, + { &hf_cpms_mem2, + { "Write Memory Storage", "at.cpms.mem2", + FT_STRING, BASE_NONE, NULL, 0, + "Memory to which writing and sending operations are made", + HFILL} + }, + { &hf_cpms_mem3, + { "Receive Memory Storage", "at.cpms.mem3", + FT_STRING, BASE_NONE, NULL, 0, + "Memory to which received SMS is preferred to be stored", + HFILL} + }, + { &hf_cpms_total1, + { "Read Storage Capacity", "at.cpms.total1", + FT_UINT32, BASE_DEC, NULL, 0, + "Total number of messages that the read/delete memory storage can contain", + HFILL} + }, + { &hf_cpms_total2, + { "Write Storage Capacity", "at.cpms.total2", + FT_UINT32, BASE_DEC, NULL, 0, + "Total number of messages that the write/send memory storage can contain", + HFILL} + }, + { &hf_cpms_total3, + { "Receive Storage Capacity", "at.cpms.total3", + FT_UINT32, BASE_DEC, NULL, 0, + "Total number of messages that the receive memory storage can contain", + HFILL} + }, + { &hf_cpms_used1, + { "Read Storage Messages Count", "at.cpms.used1", + FT_UINT32, BASE_DEC, NULL, 0, + "Amount of messages in the read/delete memory storage", + HFILL} + }, + { &hf_cpms_used2, + { "Write Storage Messages Count", "at.cpms.used2", + FT_UINT32, BASE_DEC, NULL, 0, + "Amount of messages in the write/send memory storage", + HFILL} + }, + { &hf_cpms_used3, + { "Receive Storage Messages Count", "at.cpms.used3", + FT_UINT32, BASE_DEC, NULL, 0, + "Amount of messages in the receive memory storage", + HFILL} + }, + { &hf_cscs_chset, + { "Character Set", "at.cscs.chset", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_csim_command, + { "Command", "at.csim.command", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_csim_length, + { "Length", "at.csim.length", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_csim_response, + { "Response", "at.csim.response", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_csq_ber, + { "BER", "at.csq.ber", + FT_UINT8, BASE_DEC, VALS(csq_ber_vals), 0, + "Bit Error Rate", + HFILL} + }, + { &hf_csq_rssi, + { "RSSI", "at.csq.rssi", + FT_UINT8, BASE_DEC, VALS(csq_rssi_vals), 0, + "Received Signal Strength Indication", + HFILL} + }, + { &hf_clip_mode, + { "Mode", "at.clip.mode", + FT_UINT8, BASE_DEC, VALS(clip_mode_vals), 0, + NULL, HFILL} + }, + { &hf_clip_status, + { "Status", "at.clip.status", + FT_UINT8, BASE_DEC, VALS(clip_status_vals), 0, + NULL, HFILL} + }, + { &hf_at_number, + { "Number", "at.number", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_type, + { "Type", "at.type", + FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(at_type_vals), 0, + NULL, HFILL} + }, + { &hf_at_subaddress, + { "Subaddress", "at.subaddress", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_subaddress_type, + { "Subaddress Type", "at.subaddress_type", + FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(at_type_vals), 0, + NULL, HFILL} + }, + { &hf_cnum_speed, + { "Speed", "at.cnum.speed", + FT_UINT8, BASE_DEC | BASE_EXT_STRING, &csd_data_rate_vals_ext, 0, + NULL, HFILL} + }, + { &hf_cnum_service, + { "Service", "at.cnum.service", + FT_UINT8, BASE_DEC, VALS(cnum_service_vals), 0, + NULL, HFILL} + }, + { &hf_cnum_itc, + { "Information Transfer Capability", "at.cnum.itc", + FT_UINT8, BASE_DEC, VALS(cnum_itc_vals), 0, + NULL, HFILL} + }, + { &hf_at_alpha, + { "Alpha", "at.alpha", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_at_cli_validity, + { "CLI Validity", "at.cli_validity", + FT_UINT8, BASE_DEC, VALS(cli_validity_vals), 0, + NULL, HFILL} + }, + { &hf_at_priority, + { "Priority", "at.priority", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_clcc_id, + { "ID", "at.clcc.id", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_clcc_dir, + { "Direction", "at.clcc.dir", + FT_UINT32, BASE_DEC, VALS(clcc_dir_vals), 0, + NULL, HFILL} + }, + { &hf_clcc_stat, + { "State", "at.clcc.stat", + FT_UINT32, BASE_DEC, VALS(clcc_stat_vals), 0, + NULL, HFILL} + }, + { &hf_clcc_mode, + { "Mode", "at.clcc.mode", + FT_UINT32, BASE_DEC, VALS(clcc_mode_vals), 0, + NULL, HFILL} + }, + { &hf_clcc_mpty, + { "Mpty", "at.clcc.mpty", + FT_UINT32, BASE_DEC, VALS(clcc_mpty_vals), 0, + NULL, HFILL} + }, + { &hf_ccwa_show_result_code, + { "Show Result Code Presentation Status", "at.ccwa.presentation_status", + FT_UINT32, BASE_DEC, VALS(ccwa_show_result_code_vals), 0, + NULL, HFILL} + }, + { &hf_ccwa_mode, + { "Mode", "at.ccwa.mode", + FT_UINT32, BASE_DEC, VALS(ccwa_mode_vals), 0, + NULL, HFILL} + }, + { &hf_ccwa_class, + { "Class", "at.ccwa.class", + FT_UINT32, BASE_DEC, VALS(ccwa_class_vals), 0, + NULL, HFILL} + }, + { &hf_cfun_fun, + { "Functionality", "at.cfun.fun", + FT_UINT8, BASE_DEC, VALS(cfun_fun_vals), 0, + NULL, HFILL} + }, + { &hf_cfun_rst, + { "Reset", "at.cfun.rst", + FT_UINT8, BASE_DEC, VALS(cfun_rst_vals), 0, + NULL, HFILL} + }, + { &hf_cgdcont_cid, + { "CID", "at.cgdcont.cid", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cgdcont_pdp_type, + { "PDP type", "at.cgdcont.pdp_type", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cgdcont_apn, + { "APN", "at.cgdcont.apn", + FT_STRING, BASE_NONE, NULL, 0, + "Access Point Name", HFILL} + }, + { &hf_cgdcont_pdp_addr, + { "PDP address", "at.cgdcont.pdp_addr", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cgdcont_d_comp, + { "Data compression", "at.cgdcont.d_comp", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cgdcont_h_comp, + { "Header compression", "at.cgdcont.h_comp", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL} + }, + { &hf_cgmi_manufacturer_id, + { "Manufacturer Identification", "at.cgmi.manufacturer_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cgmm_model_id, + { "Model Identification", "at.cgmm.model_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_cgmr_revision_id, + { "Revision Identification", "at.cgmr.revision_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_gmi_manufacturer_id, + { "Manufacturer Identification", "at.gmi.manufacturer_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_gmm_model_id, + { "Model Identification", "at.gmm.model_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_gmr_revision_id, + { "Revision Identification", "at.gmr.revision_id", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_zpas_network, + { "Network type", "at.zpas.network", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_zpas_srv_domain, + { "Service domain", "at.zpas.srv_domain", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_zusim_usim_card, + { "USIM card type", "at.zusim.usim_card", + FT_UINT8, BASE_DEC, VALS(zusim_usim_card_vals), 0, + "The type of the current (U)SIM card", + HFILL} + }, + { &hf_indicator[0], + { "Indicator 1", "at.indicator.1", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[1], + { "Indicator 2", "at.indicator.2", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[2], + { "Indicator 3", "at.indicator.3", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[3], + { "Indicator 4", "at.indicator.4", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[4], + { "Indicator 5", "at.indicator.5", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[5], + { "Indicator 6", "at.indicator.6", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[6], + { "Indicator 7", "at.indicator.7", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[7], + { "Indicator 8", "at.indicator.8", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[8], + { "Indicator 9", "at.indicator.9", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[9], + { "Indicator 10", "at.indicator.10", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[10], + { "Indicator 11", "at.indicator.11", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[11], + { "Indicator 12", "at.indicator.12", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[12], + { "Indicator 13", "at.indicator.13", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[13], + { "Indicator 14", "at.indicator.14", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[14], + { "Indicator 15", "at.indicator.15", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[15], + { "Indicator 16", "at.indicator.16", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[16], + { "Indicator 17", "at.indicator.17", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[17], + { "Indicator 18", "at.indicator.18", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[18], + { "Indicator 19", "at.indicator.19", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + { &hf_indicator[19], + { "Indicator 20", "at.indicator.20", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL} + }, + }; + + static ei_register_info ei[] = { + { &ei_unknown_command, { "at.expert.unknown_command", PI_PROTOCOL, PI_NOTE, "Unknown or Non-standard AT command", EXPFILL }}, + { &ei_invalid_usage, { "at.expert.invalid_usage", PI_PROTOCOL, PI_WARN, "Non mandatory type or command in this role", EXPFILL }}, + { &ei_unknown_parameter, { "at.expert.unknown_parameter", PI_PROTOCOL, PI_WARN, "Unknown parameter", EXPFILL }}, + { &ei_cmer_mode, { "at.expert.cmer.mode", PI_PROTOCOL, PI_WARN, "Only 0-3 are valid", EXPFILL }}, + { &ei_cmer_keyp, { "at.expert.cmer.keyp", PI_PROTOCOL, PI_WARN, "Only 0-2 are valid", EXPFILL }}, + { &ei_cmer_disp, { "at.expert.cmer.disp", PI_PROTOCOL, PI_WARN, "Only 0-2 are valid", EXPFILL }}, + { &ei_cmer_ind, { "at.expert.cmer.ind", PI_PROTOCOL, PI_WARN, "Only 0-2 are valid", EXPFILL }}, + { &ei_cmer_bfr, { "at.expert.cmer.bfr", PI_PROTOCOL, PI_WARN, "Only 0-1 are valid", EXPFILL }}, + { &ei_chld_mode, { "at.expert.chld.mode", PI_PROTOCOL, PI_WARN, "Invalid value", EXPFILL }}, + { &ei_ciev_indicator, { "at.expert.ciev.indicator", PI_PROTOCOL, PI_WARN, "Unknown indicator", EXPFILL }}, + { &ei_cfun_res_fun, { "at.expert.cfun.reserved_fun", PI_PROTOCOL, PI_NOTE, "Manufacturer specific value for an intermediate states between full and minimum functionality", EXPFILL }}, + { &ei_cfun_range_fun, { "at.expert.cfun.invalid_fun", PI_PROTOCOL, PI_WARN, "Only 0-127 are valid", EXPFILL }}, + { &ei_cfun_rst, { "at.expert.cfun.rst", PI_PROTOCOL, PI_WARN, "Only 0-1 are valid", EXPFILL }}, + { &ei_vts_dtmf, { "at.expert.vts.dtmf", PI_PROTOCOL, PI_WARN, "DTMF should be single character", EXPFILL }}, + { &ei_at_type, { "at.expert.at.type", PI_PROTOCOL, PI_WARN, "Unknown type value", EXPFILL }}, + { &ei_cnum_service, { "at.expert.cnum.service", PI_PROTOCOL, PI_WARN, "Only 0-5 are valid", EXPFILL }}, + { &ei_cnum_itc, { "at.expert.cnum.itc", PI_PROTOCOL, PI_WARN, "Only 0-1 are valid", EXPFILL }}, + { &ei_empty_hex, { "at.expert.csim.empty_hex", PI_PROTOCOL, PI_WARN, "Hex string is empty", EXPFILL }}, + { &ei_invalid_hex, { "at.expert.csim.invalid_hex", PI_PROTOCOL, PI_WARN, "Non hex character found in hex string", EXPFILL }}, + { &ei_odd_len, { "at.expert.csim.odd_len", PI_PROTOCOL, PI_WARN, "Odd hex string length", EXPFILL }}, + { &ei_csq_ber, { "at.expert.csq.ber", PI_PROTOCOL, PI_WARN, "Only 0-7 and 99 are valid", EXPFILL }}, + { &ei_csq_rssi, { "at.expert.csq.rssi", PI_PROTOCOL, PI_WARN, "Only 0-31 and 99 are valid", EXPFILL }}, + }; + + static gint *ett[] = { + &ett_at, + &ett_at_command, + &ett_at_data_part, + &ett_at_parameters, + }; + + proto_at = proto_register_protocol("AT Command", "AT", "at"); + proto_register_field_array(proto_at, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + expert_at = expert_register_protocol(proto_at); + expert_register_field_array(expert_at, ei, array_length(ei)); + + module = prefs_register_protocol(proto_at, NULL); + prefs_register_enum_preference(module, "role", + "Force treat packets as DTE (PC) or DCE (Modem) role", + "Force treat packets as DTE (PC) or DCE (Modem) role", + &at_role, pref_at_role, TRUE); + + register_dissector("at", dissect_at, proto_at); +} + +/* Handler registration */ +void +proto_reg_handoff_at_command(void) +{ + gsm_sim_handle = find_dissector_add_dependency("gsm_sim.part", proto_at); + gsm_sms_handle = find_dissector_add_dependency("gsm_sms", proto_at); + + heur_dissector_add("usb.bulk", heur_dissect_at, "AT Command USB bulk endpoint", "at_usb_bulk", proto_at, HEURISTIC_ENABLE); + heur_dissector_add("usb.control", heur_dissect_at, "AT Command USB control endpoint", "at_usb_control", proto_at, HEURISTIC_ENABLE); +} + +/* + * 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: + */ |