2020-08-03 22:44:14 +00:00
#!/usr/bin/env python3
# Wireshark - Network traffic analyzer
# By Gerald Combs <gerald@wireshark.org>
# Copyright 1998 Gerald Combs
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import re
import argparse
import signal
import subprocess
2020-12-20 20:04:00 +00:00
# This utility scans the dissector code for proto_tree_add_...() calls that constrain the type
# or length of the item added, and checks that the used item is acceptable.
2020-08-03 22:44:14 +00:00
#
2020-12-20 20:04:00 +00:00
# Note that this can only work where the hf_item variable or length is passed in directly - where it
# is assigned to a different variable or a macro is used, it isn't tracked.
2020-08-03 22:44:14 +00:00
# TODO:
# Attempt to check for allowed encoding types (most likely will be literal values |'d)?
# Try to exit soon after Ctrl-C is pressed.
should_exit = False
def signal_handler ( sig , frame ) :
global should_exit
should_exit = True
print ( ' You pressed Ctrl+C - exiting ' )
signal . signal ( signal . SIGINT , signal_handler )
2021-07-31 15:06:32 +00:00
warnings_found = 0
errors_found = 0
2020-08-03 22:44:14 +00:00
2022-12-27 12:10:03 +00:00
def name_has_one_of ( name , substring_list ) :
for word in substring_list :
2023-01-18 10:37:53 +00:00
if name . lower ( ) . find ( word ) != - 1 :
2022-12-27 12:10:03 +00:00
return True
return False
2020-08-03 22:44:14 +00:00
# A call is an individual call to an API we are interested in.
# Internal to APICheck below.
class Call :
2023-01-03 07:28:48 +00:00
def __init__ ( self , hf_name , line_number = None , length = None , fields = None ) :
self . hf_name = hf_name
self . line_number = line_number
self . fields = fields
self . length = None
if length :
try :
self . length = int ( length )
except :
pass
2020-08-03 22:44:14 +00:00
2022-01-20 10:40:46 +00:00
# These are variable names that have been seen to be used in calls..
common_hf_var_names = { ' hf_index ' , ' hf_item ' , ' hf_idx ' , ' hf_x ' , ' hf_id ' , ' hf_cookie ' , ' hf_flag ' ,
' hf_dos_time ' , ' hf_dos_date ' , ' hf_value ' , ' hf_num ' ,
' hf_cause_value ' , ' hf_uuid ' ,
' hf_endian ' , ' hf_ip ' , ' hf_port ' , ' hf_suff ' , ' hf_string ' , ' hf_uint ' ,
' hf_tag ' , ' hf_type ' , ' hf_hdr ' , ' hf_field ' , ' hf_opcode ' , ' hf_size ' ,
2023-01-21 11:16:20 +00:00
' hf_entry ' , ' field ' }
2022-01-20 10:40:46 +00:00
2020-08-03 22:44:14 +00:00
# A check for a particular API function.
class APICheck :
2022-01-19 17:14:59 +00:00
def __init__ ( self , fun_name , allowed_types , positive_length = False ) :
2020-08-03 22:44:14 +00:00
self . fun_name = fun_name
self . allowed_types = allowed_types
2022-01-19 17:14:59 +00:00
self . positive_length = positive_length
2020-08-03 22:44:14 +00:00
self . calls = [ ]
2021-08-08 16:47:42 +00:00
if fun_name . startswith ( ' ptvcursor ' ) :
# RE captures function name + 1st 2 args (always ptvc + hfindex)
2023-01-24 12:23:43 +00:00
self . p = re . compile ( ' [^ \n ]* ' + self . fun_name + ' \ s* \ (([a-zA-Z0-9_]+), \ s*([a-zA-Z0-9_]+) ' )
2021-08-08 16:47:42 +00:00
elif fun_name . find ( ' add_bitmask ' ) == - 1 :
2022-01-19 17:14:59 +00:00
# Normal case.
# RE captures function name + 1st 2 args (always tree + hfindex + length)
2023-01-24 12:23:43 +00:00
self . p = re . compile ( ' [^ \n ]* ' + self . fun_name + ' \ s* \ (([a-zA-Z0-9_]+), \ s*([a-zA-Z0-9_]+), \ s*[a-zA-Z0-9_]+, \ s*[a-zA-Z0-9_]+, \ s*([a-zA-Z0-9_]+) ' )
2021-07-25 21:45:45 +00:00
else :
2022-01-19 17:14:59 +00:00
# _add_bitmask functions.
2021-07-25 21:45:45 +00:00
# RE captures function name + 1st + 4th args (always tree + hfindex)
2023-01-03 07:28:48 +00:00
# 6th arg is 'fields'
2023-01-24 12:23:43 +00:00
self . p = re . compile ( ' [^ \n ]* ' + self . fun_name + ' \ s* \ (([a-zA-Z0-9_]+), \ s*[a-zA-Z0-9_]+, \ s*[a-zA-Z0-9_]+, \ s*([a-zA-Z0-9_]+) \ s*, \ s*[a-zA-Z0-9_]+ \ s*, \ s*([a-zA-Z0-9_]+) \ s*, ' )
2021-07-25 21:45:45 +00:00
2020-08-03 22:44:14 +00:00
self . file = None
2022-12-27 12:10:03 +00:00
self . mask_allowed = True
if fun_name . find ( ' proto_tree_add_bits_ ' ) != - 1 :
self . mask_allowed = False
2020-08-03 22:44:14 +00:00
2022-01-19 17:14:59 +00:00
2020-08-03 22:44:14 +00:00
def find_calls ( self , file ) :
self . file = file
self . calls = [ ]
2022-01-19 17:14:59 +00:00
2020-08-03 22:44:14 +00:00
with open ( file , ' r ' ) as f :
2022-01-19 17:14:59 +00:00
contents = f . read ( )
lines = contents . splitlines ( )
total_lines = len ( lines )
2022-04-17 22:47:09 +00:00
for line_number , line in enumerate ( lines ) :
2022-01-19 17:14:59 +00:00
# Want to check this, and next few lines
2022-01-20 10:40:46 +00:00
to_check = lines [ line_number - 1 ] + ' \n '
2022-01-19 17:14:59 +00:00
# Nothing to check if function name isn't in it
if to_check . find ( self . fun_name ) != - 1 :
# Ok, add the next file lines before trying RE
for i in range ( 1 , 4 ) :
2022-08-12 16:20:35 +00:00
if to_check . find ( ' ; ' ) != - 1 :
break
elif line_number + i < total_lines :
2022-01-20 10:40:46 +00:00
to_check + = ( lines [ line_number - 1 + i ] + ' \n ' )
m = self . p . search ( to_check )
2022-01-19 17:14:59 +00:00
if m :
2023-01-03 07:28:48 +00:00
fields = None
length = None
if self . fun_name . find ( ' add_bitmask ' ) != - 1 :
fields = m . group ( 3 )
else :
if self . p . groups == 3 :
length = m . group ( 3 )
2022-01-19 17:14:59 +00:00
# Add call. We have length if re had 3 groups.
num_groups = self . p . groups
self . calls . append ( Call ( m . group ( 2 ) ,
line_number = line_number ,
2023-01-03 07:28:48 +00:00
length = length ,
fields = fields ) )
2022-01-19 17:14:59 +00:00
2020-08-03 22:44:14 +00:00
2022-01-25 11:43:32 +00:00
def check_against_items ( self , items_defined , items_declared , items_declared_extern , check_missing_items = False ) :
2022-01-19 17:14:59 +00:00
global errors_found
2022-01-20 10:40:46 +00:00
global warnings_found
2020-08-03 22:44:14 +00:00
for call in self . calls :
2022-01-19 17:14:59 +00:00
if self . positive_length and call . length != None :
if call . length != - 1 and call . length < = 0 :
print ( ' Error: ' + self . fun_name + ' (.., ' + call . hf_name + ' , ...) called at ' +
self . file + ' : ' + str ( call . line_number ) +
' with length ' + str ( call . length ) + ' - must be > 0 or -1 ' )
# Inc global count of issues found.
errors_found + = 1
2022-01-25 11:43:32 +00:00
if call . hf_name in items_defined :
if not items_defined [ call . hf_name ] . item_type in self . allowed_types :
2020-08-03 22:44:14 +00:00
# Report this issue.
print ( ' Error: ' + self . fun_name + ' (.., ' + call . hf_name + ' , ...) called at ' +
self . file + ' : ' + str ( call . line_number ) +
2022-01-25 11:43:32 +00:00
' with type ' + items_defined [ call . hf_name ] . item_type )
2020-08-03 22:44:14 +00:00
print ( ' (allowed types are ' , self . allowed_types , ' ) \n ' )
# Inc global count of issues found.
2021-07-31 15:06:32 +00:00
errors_found + = 1
2022-12-27 12:10:03 +00:00
if not self . mask_allowed and items_defined [ call . hf_name ] . mask_value != 0 :
# Report this issue.
print ( ' Error: ' + self . fun_name + ' (.., ' + call . hf_name + ' , ...) called at ' +
self . file + ' : ' + str ( call . line_number ) +
' with mask ' + items_defined [ call . hf_name ] . mask + ' (must be zero!) \n ' )
# Inc global count of issues found.
errors_found + = 1
2022-01-20 10:40:46 +00:00
elif check_missing_items :
2022-01-25 11:43:32 +00:00
if call . hf_name in items_declared and not call . hf_name in items_declared_extern :
#not in common_hf_var_names:
2022-01-20 10:40:46 +00:00
print ( ' Warning: ' , self . file + ' : ' + str ( call . line_number ) ,
self . fun_name + ' called for " ' + call . hf_name + ' " ' , ' - but no item found ' )
warnings_found + = 1
2020-08-03 22:44:14 +00:00
2020-10-17 13:23:26 +00:00
class ProtoTreeAddItemCheck ( APICheck ) :
2021-08-08 16:47:42 +00:00
def __init__ ( self , ptv = None ) :
2020-10-17 13:23:26 +00:00
# RE will capture whole call. N.B. only looking at calls with literal numerical length field.
2021-08-08 16:47:42 +00:00
if not ptv :
# proto_item *
# proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
# const gint start, gint length, const guint encoding)
self . fun_name = ' proto_tree_add_item '
2023-01-24 12:23:43 +00:00
self . p = re . compile ( ' [^ \n ]* ' + self . fun_name + ' \ s* \ ( \ s*[a-zA-Z0-9_]+, \ s*([a-zA-Z0-9_]+), \ s*[a-zA-Z0-9_]+, \ s*[a-zA-Z0-9_]+, \ s*([0-9]+), \ s*([a-zA-Z0-9_]+) ' )
2021-08-08 16:47:42 +00:00
else :
# proto_item *
# ptvcursor_add(ptvcursor_t *ptvc, int hfindex, gint length,
# const guint encoding)
self . fun_name = ' ptvcursor_add '
2023-01-24 12:23:43 +00:00
self . p = re . compile ( ' [^ \n ]* ' + self . fun_name + ' \ s* \ ([a-zA-Z0-9_]+, \ s*([a-zA-Z0-9_]+), \ s*([a-zA-Z_0-9]+), \ s*([a-zA-Z0-9_ \ - \ >]+) ' )
2021-08-08 16:47:42 +00:00
2020-10-17 13:23:26 +00:00
self . lengths = { }
self . lengths [ ' FT_CHAR ' ] = 1
self . lengths [ ' FT_UINT8 ' ] = 1
self . lengths [ ' FT_INT8 ' ] = 1
self . lengths [ ' FT_UINT16 ' ] = 2
self . lengths [ ' FT_INT16 ' ] = 2
self . lengths [ ' FT_UINT24 ' ] = 3
self . lengths [ ' FT_INT24 ' ] = 3
self . lengths [ ' FT_UINT32 ' ] = 4
self . lengths [ ' FT_INT32 ' ] = 4
self . lengths [ ' FT_UINT40 ' ] = 5
self . lengths [ ' FT_INT40 ' ] = 5
self . lengths [ ' FT_UINT48 ' ] = 6
self . lengths [ ' FT_INT48 ' ] = 6
self . lengths [ ' FT_UINT56 ' ] = 7
self . lengths [ ' FT_INT56 ' ] = 7
self . lengths [ ' FT_UINT64 ' ] = 8
self . lengths [ ' FT_INT64 ' ] = 8
self . lengths [ ' FT_ETHER ' ] = 6
# TODO: other types...
def find_calls ( self , file ) :
self . file = file
self . calls = [ ]
with open ( file , ' r ' ) as f :
2022-01-19 22:32:04 +00:00
contents = f . read ( )
lines = contents . splitlines ( )
total_lines = len ( lines )
2022-04-17 22:47:09 +00:00
for line_number , line in enumerate ( lines ) :
2022-01-19 22:32:04 +00:00
# Want to check this, and next few lines
2022-01-20 10:40:46 +00:00
to_check = lines [ line_number - 1 ] + ' \n '
2022-08-12 16:20:35 +00:00
# Nothing to check if function name isn't in it
2022-01-19 22:32:04 +00:00
if to_check . find ( self . fun_name ) != - 1 :
# Ok, add the next file lines before trying RE
for i in range ( 1 , 5 ) :
2022-08-12 16:20:35 +00:00
if to_check . find ( ' ; ' ) != - 1 :
break
elif line_number + i < total_lines :
2022-01-20 10:40:46 +00:00
to_check + = ( lines [ line_number - 1 + i ] + ' \n ' )
m = self . p . search ( to_check )
2022-01-19 22:32:04 +00:00
if m :
2023-01-21 11:16:20 +00:00
enc = m . group ( 3 )
hf_name = m . group ( 1 )
if not enc . startswith ( ' ENC_ ' ) :
if not enc in { ' encoding ' , ' enc ' , ' client_is_le ' , ' cigi_byte_order ' , ' endian ' , ' endianess ' , ' machine_encoding ' , ' byte_order ' , ' bLittleEndian ' ,
' p_mq_parm ' , ' iEnc ' , ' strid_enc ' , ' iCod ' , ' nl_data ' , ' argp ' , ' gquic_info ' , ' writer_encoding ' ,
' tds_get_int2_encoding ' , ' tds_get_int4_encoding ' ,
' DREP_ENC_INTEGER ' } :
global warnings_found
print ( ' Warning: ' , self . file + ' : ' + str ( line_number ) ,
self . fun_name + ' called for " ' + hf_name + ' " ' , ' check last/enc param: ' , enc , ' ? ' )
warnings_found + = 1
self . calls . append ( Call ( hf_name , line_number = line_number , length = m . group ( 2 ) ) )
2020-10-17 13:23:26 +00:00
2022-01-25 11:43:32 +00:00
def check_against_items ( self , items_defined , items_declared , items_declared_extern , check_missing_items = False ) :
2020-10-17 13:23:26 +00:00
# For now, only complaining if length if call is longer than the item type implies.
#
# Could also be bugs where the length is always less than the type allows.
# Would involve keeping track (in the item) of whether any call had used the full length.
2022-01-20 10:40:46 +00:00
global warnings_found
2020-10-17 13:23:26 +00:00
for call in self . calls :
2022-01-25 11:43:32 +00:00
if call . hf_name in items_defined :
if call . length and items_defined [ call . hf_name ] . item_type in self . lengths :
if self . lengths [ items_defined [ call . hf_name ] . item_type ] < call . length :
2021-07-11 16:15:44 +00:00
print ( ' Warning: ' , self . file + ' : ' + str ( call . line_number ) ,
2021-08-08 16:47:42 +00:00
self . fun_name + ' called for ' , call . hf_name , ' - ' ,
2022-01-25 11:43:32 +00:00
' item type is ' , items_defined [ call . hf_name ] . item_type , ' but call has len ' , call . length )
2021-07-31 15:06:32 +00:00
warnings_found + = 1
2022-01-20 10:40:46 +00:00
elif check_missing_items :
2022-01-25 11:43:32 +00:00
if call . hf_name in items_declared and not call . hf_name in items_declared_extern :
#not in common_hf_var_names:
2022-01-20 10:40:46 +00:00
print ( ' Warning: ' , self . file + ' : ' + str ( call . line_number ) ,
self . fun_name + ' called for " ' + call . hf_name + ' " ' , ' - but no item found ' )
warnings_found + = 1
2020-10-17 13:23:26 +00:00
2020-08-09 21:33:58 +00:00
##################################################################################################
# This is a set of items (by filter name) where we know that the bitmask is non-contiguous,
# but is still believed to be correct.
known_non_contiguous_fields = { ' wlan.fixed.capabilities.cfpoll.sta ' ,
' wlan.wfa.ie.wme.qos_info.sta.reserved ' ,
' btrfcomm.frame_type ' , # https://os.itec.kit.edu/downloads/sa_2006_roehricht-martin_flow-control-in-bluez.pdf
' capwap.control.message_element.ac_descriptor.dtls_policy.r ' , # RFC 5415
' couchbase.extras.subdoc.flags.reserved ' ,
' wlan.fixed.capabilities.cfpoll.ap ' , # These are 3 separate bits...
' wlan.wfa.ie.wme.tspec.ts_info.reserved ' , # matches other fields in same sequence
' zbee_zcl_se.pp.attr.payment_control_configuration.reserved ' , # matches other fields in same sequence
2020-09-19 12:34:02 +00:00
' zbee_zcl_se.pp.snapshot_payload_cause.reserved ' , # matches other fields in same sequence
2020-12-15 11:16:45 +00:00
' ebhscr.eth.rsv ' , # matches other fields in same sequence
2021-07-11 12:19:10 +00:00
' v120.lli ' , # non-contiguous field (http://www.acacia-net.com/wwwcla/protocol/v120_l2.htm)
' stun.type.class ' ,
2022-12-15 13:28:05 +00:00
' bssgp.csg_id ' , ' tiff.t6.unused ' , ' artnet.ip_prog_reply.unused ' ,
2023-01-16 15:35:04 +00:00
' telnet.auth.mod.enc ' , ' osc.message.midi.bender ' , ' btle.data_header.rfu ' ,
2023-01-18 10:37:53 +00:00
' stun.type.method ' , # figure 3 in rfc 5389
2023-01-30 10:28:24 +00:00
' tds.done.status ' , # covers all bits in bitset
' hf_iax2_video_csub ' # RFC 5456, table 8.7
2020-08-09 21:33:58 +00:00
}
##################################################################################################
field_widths = {
2022-01-19 22:32:04 +00:00
' FT_BOOLEAN ' : 64 , # TODO: Width depends upon 'display' field
2021-07-29 09:40:15 +00:00
' FT_CHAR ' : 8 ,
2020-08-09 21:33:58 +00:00
' FT_UINT8 ' : 8 ,
' FT_INT8 ' : 8 ,
' FT_UINT16 ' : 16 ,
' FT_INT16 ' : 16 ,
' FT_UINT24 ' : 24 ,
' FT_INT24 ' : 24 ,
' FT_UINT32 ' : 32 ,
' FT_INT32 ' : 32 ,
' FT_UINT40 ' : 40 ,
' FT_INT40 ' : 40 ,
2020-12-15 11:16:45 +00:00
' FT_UINT48 ' : 48 ,
' FT_INT48 ' : 48 ,
' FT_UINT56 ' : 56 ,
' FT_INT56 ' : 56 ,
2020-08-09 21:33:58 +00:00
' FT_UINT64 ' : 64 ,
' FT_INT64 ' : 64
}
2023-01-29 18:39:20 +00:00
def is_ignored_consecutive_filter ( filter ) :
ignore_patterns = [
re . compile ( r ' ^elf.sh_type ' ) ,
re . compile ( r ' ^elf.p_type ' ) ,
re . compile ( r ' ^btavrcp.pdu_id ' ) ,
re . compile ( r ' ^nstrace.trcdbg.val( \ d+) ' ) ,
re . compile ( r ' ^netlogon.dummy_string ' ) ,
re . compile ( r ' ^opa.reserved ' ) ,
re . compile ( r ' ^mpls_pm.timestamp \ d \ ..* ' ) ,
re . compile ( r ' ^wassp.data.mu_mac ' ) ,
re . compile ( r ' ^thrift.type ' ) ,
re . compile ( r ' ^quake2.game.client.command.move.angles ' ) ,
re . compile ( r ' ^ipp.enum_value ' ) ,
re . compile ( r ' ^idrp.error.subcode ' ) ,
re . compile ( r ' ^ftdi-ft.lValue ' ) ,
re . compile ( r ' ^6lowpan.src ' ) ,
re . compile ( r ' ^couchbase.flex_frame.frame.id ' ) ,
re . compile ( r ' ^rtps.param.id ' ) ,
re . compile ( r ' ^rtps.locator.port ' ) ,
re . compile ( r ' ^sigcomp.udvm.value ' ) ,
re . compile ( r ' ^opa.mad.attributemodifier.n ' ) ,
re . compile ( r ' ^smb.cmd ' ) ,
re . compile ( r ' ^sctp.checksum ' ) ,
re . compile ( r ' ^dhcp.option.end ' ) ,
re . compile ( r ' ^nfapi.num.bf.vector.bf.value ' ) ,
re . compile ( r ' ^dnp3.al.range.abs ' ) ,
re . compile ( r ' ^dnp3.al.range.quantity ' ) ,
re . compile ( r ' ^dnp3.al.index ' ) ,
re . compile ( r ' ^dnp3.al.size ' ) ,
re . compile ( r ' ^ftdi-ft.hValue ' ) ,
re . compile ( r ' ^homeplug_av.op_attr_cnf.data.sw_sub ' ) ,
re . compile ( r ' ^radiotap.he_mu.preamble_puncturing ' ) ,
re . compile ( r ' ^ndmp.file ' ) ,
re . compile ( r ' ^ocfs2.dlm.lvb ' ) ,
re . compile ( r ' ^oran_fh_cus.reserved ' ) ,
re . compile ( r ' ^qnet6.kif.msgsend.msg.read.xtypes0-7 ' ) ,
2023-01-30 10:28:24 +00:00
re . compile ( r ' ^mih.sig_strength ' ) ,
re . compile ( r ' ^couchbase.flex_frame.frame.len ' ) ,
re . compile ( r ' ^nvme-rdma.read_to_host_req ' ) ,
re . compile ( r ' ^rpcap.dummy ' ) ,
re . compile ( r ' ^sflow.flow_sample.output_interface ' ) ,
re . compile ( r ' ^socks.results ' ) ,
re . compile ( r ' ^opa.mad.attributemodifier.p ' ) ,
re . compile ( r ' ^v5ua.efa ' ) ,
re . compile ( r ' ^zbncp.data.tx_power ' ) ,
2023-01-31 13:34:04 +00:00
re . compile ( r ' ^zbncp.data.nwk_addr ' ) ,
re . compile ( r ' ^zbee_zcl_hvac.pump_config_control.attr.ctrl_mode ' ) ,
re . compile ( r ' ^nat-pmp.external_port ' ) ,
re . compile ( r ' ^zbee_zcl.attr.float ' ) ,
re . compile ( r ' ^wpan-tap.phr.fsk_ms.mode ' ) ,
re . compile ( r ' ^mysql.exec_flags ' ) ,
re . compile ( r ' ^pim.metric_pref ' ) ,
re . compile ( r ' ^modbus.regval_float ' ) ,
re . compile ( r ' ^alcap.cau.value ' ) ,
re . compile ( r ' ^bpv7.crc_field ' ) ,
re . compile ( r ' ^at.chld.mode ' ) ,
re . compile ( r ' ^btl2cap.psm ' ) ,
2023-02-06 09:24:26 +00:00
re . compile ( r ' ^srvloc.srvtypereq.nameauthlistlen ' ) ,
re . compile ( r ' ^a11.ext.code ' ) ,
re . compile ( r ' ^adwin_config.port ' ) ,
re . compile ( r ' ^afp.unknown ' ) ,
re . compile ( r ' ^ansi_a_bsmap.mid.digit_1 ' ) ,
re . compile ( r ' ^ber.unknown.OCTETSTRING ' ) ,
re . compile ( r ' ^btatt.handle ' ) ,
re . compile ( r ' ^btl2cap.option_flushto ' ) ,
re . compile ( r ' ^cip.network_segment.prod_inhibit ' ) ,
re . compile ( r ' ^cql.result.rows.table_name ' ) ,
re . compile ( r ' ^dcom.sa.vartype ' ) ,
re . compile ( r ' ^f5ethtrailer.slot ' ) ,
re . compile ( r ' ^ipdr.cm_ipv6_addr ' ) ,
re . compile ( r ' ^mojito.kuid ' ) ,
re . compile ( r ' ^mtp3.priority ' ) ,
re . compile ( r ' ^pw.cw.length ' ) ,
re . compile ( r ' ^rlc.ciphered_data ' ) ,
re . compile ( r ' ^vp8.pld.pictureid ' ) ,
re . compile ( r ' ^gryphon.sched.channel ' ) ,
re . compile ( r ' ^pn_io.ioxs ' ) ,
re . compile ( r ' ^pn_dcp.block_qualifier_reset ' ) ,
re . compile ( r ' ^pn_dcp.suboption_device_instance ' )
2023-01-29 18:39:20 +00:00
]
for patt in ignore_patterns :
if patt . match ( filter ) :
return True
return False
2020-08-09 21:33:58 +00:00
2020-08-03 22:44:14 +00:00
# The relevant parts of an hf item. Used as value in dict where hf variable name is key.
class Item :
2020-09-26 21:33:21 +00:00
previousItem = None
2022-12-27 12:10:03 +00:00
def __init__ ( self , filename , hf , filter , label , item_type , type_modifier , mask = None ,
2022-12-15 13:28:05 +00:00
check_mask = False , mask_exact_width = False , check_label = False , check_consecutive = False ) :
2020-08-09 21:33:58 +00:00
self . filename = filename
2022-12-27 12:10:03 +00:00
self . hf = hf
2020-08-09 21:33:58 +00:00
self . filter = filter
self . label = label
2020-11-16 00:06:19 +00:00
self . mask = mask
2022-12-15 13:28:05 +00:00
self . mask_exact_width = mask_exact_width
2022-04-17 22:47:09 +00:00
global warnings_found
2022-12-29 17:35:18 +00:00
self . set_mask_value ( )
2020-11-16 00:06:19 +00:00
2020-09-26 21:33:21 +00:00
if check_consecutive :
if Item . previousItem and Item . previousItem . filter == filter :
if label != Item . previousItem . label :
2023-01-29 18:39:20 +00:00
if not is_ignored_consecutive_filter ( self . filter ) :
print ( ' Warning: ' , filename , hf , ' : - filter " ' + filter +
' " appears consecutively - labels are " ' + Item . previousItem . label + ' " and " ' + label + ' " ' )
warnings_found + = 1
2020-11-16 00:06:19 +00:00
2020-09-26 21:33:21 +00:00
Item . previousItem = self
2020-08-09 21:33:58 +00:00
# Optionally check label.
if check_label :
if label . startswith ( ' ' ) or label . endswith ( ' ' ) :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' + filename , hf , ' filter " ' + filter + ' " label ' + label + ' " begins or ends with a space ' )
2022-04-17 22:47:09 +00:00
warnings_found + = 1
2021-08-14 20:24:13 +00:00
if ( label . count ( ' ( ' ) != label . count ( ' ) ' ) or
label . count ( ' [ ' ) != label . count ( ' ] ' ) or
label . count ( ' { ' ) != label . count ( ' } ' ) ) :
2023-02-06 09:24:26 +00:00
# Ignore if includes quotes, as may be unbalanced.
if label . find ( " ' " ) == - 1 :
print ( ' Warning: ' + filename , hf , ' filter " ' + filter + ' " label ' , ' " ' + label + ' " ' , ' has unbalanced parens/braces/brackets ' )
warnings_found + = 1
2021-08-23 08:23:00 +00:00
if item_type != ' FT_NONE ' and label . endswith ( ' : ' ) :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' + filename , hf , ' filter " ' + filter + ' " label ' , ' " ' + label + ' " ' , ' ends with an unnecessary colon ' )
2022-04-17 22:47:09 +00:00
warnings_found + = 1
2020-08-09 21:33:58 +00:00
2020-08-03 22:44:14 +00:00
self . item_type = item_type
2021-07-11 12:19:10 +00:00
self . type_modifier = type_modifier
2020-08-03 22:44:14 +00:00
2020-08-09 21:33:58 +00:00
# Optionally check that mask bits are contiguous
if check_mask :
2022-05-21 19:54:13 +00:00
if self . mask_read and not mask in { ' NULL ' , ' 0x0 ' , ' 0 ' , ' 0x00 ' } :
2020-08-09 21:33:58 +00:00
self . check_contiguous_bits ( mask )
2022-02-08 08:56:17 +00:00
#self.check_mask_too_long(mask)
2021-07-08 08:54:34 +00:00
self . check_num_digits ( mask )
2021-07-23 21:33:34 +00:00
self . check_digits_all_zeros ( mask )
2021-07-07 09:00:45 +00:00
2020-08-09 21:33:58 +00:00
2022-12-15 13:28:05 +00:00
def __str__ ( self ) :
return ' Item ( {0} " {1} " {2} type= {3} : {4} mask= {5} ) ' . format ( self . filename , self . label , self . filter , self . item_type , self . type_modifier , self . mask )
2020-11-16 00:06:19 +00:00
def set_mask_value ( self ) :
try :
2022-05-21 19:54:13 +00:00
self . mask_read = True
if any ( not c in ' 0123456789abcdefABCDEFxX ' for c in self . mask ) :
self . mask_read = False
self . mask_value = 0
return
2020-11-16 00:06:19 +00:00
# Read according to the appropriate base.
if self . mask . startswith ( ' 0x ' ) :
self . mask_value = int ( self . mask , 16 )
elif self . mask . startswith ( ' 0 ' ) :
self . mask_value = int ( self . mask , 8 )
else :
self . mask_value = int ( self . mask , 10 )
except :
2022-05-21 19:54:13 +00:00
self . mask_read = False
2020-11-16 00:06:19 +00:00
self . mask_value = 0
2020-08-09 21:33:58 +00:00
# Return true if bit position n is set in value.
def check_bit ( self , value , n ) :
return ( value & ( 0x1 << n ) ) != 0
2021-12-20 02:40:23 +00:00
# Output a warning if non-contigous bits are found in the mask (guint64).
2020-08-09 21:33:58 +00:00
# Note that this legimately happens in several dissectors where multiple reserved/unassigned
# bits are conflated into one field.
# TODO: there is probably a cool/efficient way to check this?
def check_contiguous_bits ( self , mask ) :
2020-11-16 00:06:19 +00:00
if not self . mask_value :
return
2020-08-09 21:33:58 +00:00
2022-12-27 12:10:03 +00:00
# Do see non-contiguous bits often for these..
if name_has_one_of ( self . hf , [ ' reserved ' , ' unknown ' ] ) :
return
2023-01-18 10:37:53 +00:00
if name_has_one_of ( self . label , [ ' reserved ' , ' unknown ' ] ) :
return
2022-12-27 12:10:03 +00:00
2020-11-16 00:06:19 +00:00
# Walk past any l.s. 0 bits
n = 0
while not self . check_bit ( self . mask_value , n ) and n < = 63 :
2020-08-09 21:33:58 +00:00
n + = 1
2020-11-16 00:06:19 +00:00
if n == 63 :
return
2020-08-09 21:33:58 +00:00
2020-11-16 00:06:19 +00:00
mask_start = n
# Walk through any bits that are set
while self . check_bit ( self . mask_value , n ) and n < = 63 :
n + = 1
n + = 1
if n > = 63 :
return
# Look up the field width
field_width = 0
if not self . item_type in field_widths :
print ( ' unexpected item_type is ' , self . item_type )
field_width = 64
else :
2021-07-11 12:19:10 +00:00
field_width = self . get_field_width_in_bits ( )
2020-11-16 00:06:19 +00:00
# Its a problem is the mask_width is > field_width - some of the bits won't get looked at!?
mask_width = n - 1 - mask_start
if mask_width > field_width :
2020-12-15 11:16:45 +00:00
# N.B. No call, so no line number.
2022-12-27 12:10:03 +00:00
print ( self . filename + ' : ' , self . hf , ' filter= ' , self . filter , self . item_type , ' so field_width= ' , field_width ,
2020-11-16 00:06:19 +00:00
' but mask is ' , mask , ' which is ' , mask_width , ' bits wide! ' )
2021-07-31 15:06:32 +00:00
global warnings_found
warnings_found + = 1
2020-11-16 00:06:19 +00:00
# Now, any more zero set bits are an error!
2021-07-11 12:19:10 +00:00
if self . filter in known_non_contiguous_fields or self . filter . startswith ( ' rtpmidi ' ) :
2020-11-16 00:06:19 +00:00
# Don't report if we know this one is Ok.
return
while n < = 63 :
if self . check_bit ( self . mask_value , n ) :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' - mask with non-contiguous bits ' , mask )
2022-04-17 22:47:09 +00:00
warnings_found + = 1
2020-08-09 21:33:58 +00:00
return
2020-11-16 00:06:19 +00:00
n + = 1
2020-08-09 21:33:58 +00:00
2021-07-11 12:19:10 +00:00
def get_field_width_in_bits ( self ) :
if self . item_type == ' FT_BOOLEAN ' :
if self . type_modifier == ' NULL ' :
return 8 # i.e. 1 byte
elif self . type_modifier == ' BASE_NONE ' :
return 8
2022-12-15 13:28:05 +00:00
elif self . type_modifier == ' SEP_DOT ' : # from proto.h
2021-09-21 10:04:31 +00:00
return 64
2021-07-11 12:19:10 +00:00
else :
2022-12-15 13:28:05 +00:00
# For FT_BOOLEAN, modifier is just numerical number of bits. Round up to next nibble.
2021-10-04 08:11:49 +00:00
return int ( self . type_modifier ) + 3
2021-07-11 12:19:10 +00:00
else :
2022-12-15 13:28:05 +00:00
# Lookup fixed width for this type
2021-07-11 12:19:10 +00:00
return field_widths [ self . item_type ]
2022-12-15 13:28:05 +00:00
# N.B. Not currently used.
2021-07-07 09:00:45 +00:00
def check_mask_too_long ( self , mask ) :
if not self . mask_value :
return
if mask . startswith ( ' 0x00 ' ) or mask . endswith ( ' 00 ' ) :
2021-07-08 08:54:34 +00:00
# There may be good reasons for having a wider field/mask, e.g. if there are 32 related flags, showing them
# all lined up as part of the same word may make it clearer. But some cases have been found
2021-07-07 09:00:45 +00:00
# where the grouping does not seem to be natural..
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' - mask with leading or trailing 0 bytes suggests field ' , self . item_type , ' may be wider than necessary? ' , mask )
2021-07-31 15:06:32 +00:00
global warnings_found
warnings_found + = 1
2021-07-07 09:00:45 +00:00
2021-07-08 08:54:34 +00:00
def check_num_digits ( self , mask ) :
2021-07-11 12:19:10 +00:00
if mask . startswith ( ' 0x ' ) and len ( mask ) > 3 :
2021-07-31 15:06:32 +00:00
global warnings_found
2021-10-04 08:11:49 +00:00
global errors_found
2022-12-15 13:28:05 +00:00
# Warn if odd number of digits/ TODO: only if >= 5?
if len ( mask ) % 2 and self . item_type != ' FT_BOOLEAN ' :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' - mask has odd number of digits ' , mask ,
2021-10-04 08:11:49 +00:00
' expected max for ' , self . item_type , ' is ' , int ( ( self . get_field_width_in_bits ( ) ) / 4 ) )
2021-07-31 15:06:32 +00:00
warnings_found + = 1
2021-07-08 08:54:34 +00:00
if self . item_type in field_widths :
2022-12-15 13:28:05 +00:00
# Longer than it should be?
2021-07-11 12:19:10 +00:00
if len ( mask ) - 2 > self . get_field_width_in_bits ( ) / 4 :
2021-10-04 08:11:49 +00:00
extra_digits = mask [ 2 : 2 + ( len ( mask ) - 2 - int ( self . get_field_width_in_bits ( ) / 4 ) ) ]
2022-12-15 13:28:05 +00:00
# Its definitely an error if any of these are non-zero, as they won't have any effect!
2021-10-04 08:11:49 +00:00
if extra_digits != ' 0 ' * len ( extra_digits ) :
2022-12-27 12:10:03 +00:00
print ( ' Error: ' , self . filename , self . hf , ' filter= ' , self . filter , self . mask , " with len is " , len ( mask ) - 2 ,
2021-10-04 08:11:49 +00:00
" but type " , self . item_type , " indicates max of " , int ( self . get_field_width_in_bits ( ) / 4 ) ,
" and extra digits are non-zero ( " + extra_digits + " ) " )
errors_found + = 1
else :
2022-12-15 13:28:05 +00:00
# Has extra leading zeros, still confusing, so warn.
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , self . mask , " with len " , len ( mask ) - 2 ,
2021-10-04 08:11:49 +00:00
" but type " , self . item_type , " indicates max of " , int ( self . get_field_width_in_bits ( ) / 4 ) )
warnings_found + = 1
2022-12-15 13:28:05 +00:00
# Strict/fussy check - expecting mask length to match field width exactly!
# Currently only doing for FT_BOOLEAN
if self . mask_exact_width :
if self . item_type == ' FT_BOOLEAN ' and len ( mask ) - 2 != int ( self . get_field_width_in_bits ( ) / 4 ) :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' mask ' , self . mask , " with len " , len ( mask ) - 2 ,
2022-12-15 13:28:05 +00:00
" but type " , self . item_type , " | " , self . type_modifier , " indicates should be " , int ( self . get_field_width_in_bits ( ) / 4 ) )
warnings_found + = 1
2021-07-08 08:54:34 +00:00
else :
2022-12-15 13:28:05 +00:00
# This type shouldn't have a mask set at all.
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' - item has type ' , self . item_type , ' but mask set: ' , mask )
2021-07-31 15:06:32 +00:00
warnings_found + = 1
2021-07-08 08:54:34 +00:00
2021-07-23 21:33:34 +00:00
def check_digits_all_zeros ( self , mask ) :
if mask . startswith ( ' 0x ' ) and len ( mask ) > 3 :
if mask [ 2 : ] == ' 0 ' * ( len ( mask ) - 2 ) :
2022-12-27 12:10:03 +00:00
print ( ' Warning: ' , self . filename , self . hf , ' filter= ' , self . filter , ' - item has all zeros - this is confusing! : ' , mask )
2021-07-31 15:06:32 +00:00
global warnings_found
warnings_found + = 1
2021-07-23 21:33:34 +00:00
2021-07-07 09:00:45 +00:00
2022-08-12 16:20:35 +00:00
class CombinedCallsCheck :
def __init__ ( self , file , apiChecks ) :
self . file = file
self . apiChecks = apiChecks
self . get_all_calls ( )
def get_all_calls ( self ) :
self . all_calls = [ ]
# Combine calls into one list.
for check in self . apiChecks :
self . all_calls + = check . calls
# Sort by line number.
self . all_calls . sort ( key = lambda x : x . line_number )
def check_consecutive_item_calls ( self ) :
lines = open ( self . file , ' r ' ) . read ( ) . splitlines ( )
prev = None
for call in self . all_calls :
2022-12-27 12:10:03 +00:00
# These names commonly do appear together..
if name_has_one_of ( call . hf_name , [ ' unused ' , ' unknown ' , ' spare ' , ' reserved ' , ' default ' ] ) :
return
2022-08-12 16:20:35 +00:00
if prev and call . hf_name == prev . hf_name :
# More compelling if close together..
if call . line_number > prev . line_number and call . line_number - prev . line_number < = 4 :
scope_different = False
for l in range ( prev . line_number , call . line_number - 1 ) :
if lines [ l ] . find ( ' { ' ) != - 1 or lines [ l ] . find ( ' } ' ) != - 1 or lines [ l ] . find ( ' else ' ) != - 1 or lines [ l ] . find ( ' break; ' ) != - 1 or lines [ l ] . find ( ' if ' ) != - 1 :
scope_different = True
break
# Also more compelling if check for and scope changes { } in lines in-between?
if not scope_different :
print ( ' Warning: ' , f + ' : ' + str ( call . line_number ) ,
call . hf_name + ' called consecutively at line ' , call . line_number , ' - previous at ' , prev . line_number )
global warnings_found
warnings_found + = 1
prev = call
2020-08-03 22:44:14 +00:00
# These are APIs in proto.c that check a set of types at runtime and can print '.. is not of type ..' to the console
# if the type is not suitable.
apiChecks = [ ]
2022-01-19 17:14:59 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_uint ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' } , positive_length = True ) )
2020-08-03 22:44:14 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_int ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } ) )
2022-01-19 17:14:59 +00:00
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_uint ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' } , positive_length = True ) )
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_int ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } , positive_length = True ) )
Add FT_STRINGZTRUNC.
FT_STRINGZPAD is for null-*padded* strings, where the field is in an
area of specified length, and, if the string is shorter than that
length, all bytes past the end of the string are NULs.
FT_STRINGZTRUNC is for null-*truncated* strings, where the field is in
an area of specified length and, if the string is shorter than that
length, there's a null character (which might be more than one byte, for
UCS-2, UTF-16, or UTF-32), and anything after that is not guaranteed to
have any particular value.
Use IS_FT_STRING() in some places rather than enumerating all the string
types, so that those places get automatically changed if the set of
string types changes.
2020-09-12 21:16:12 +00:00
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_string ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' , ' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' } ) )
2022-01-19 17:14:59 +00:00
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_boolean ' , { ' FT_BOOLEAN ' } , positive_length = True ) )
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_uint64 ' , { ' FT_UINT40 ' , ' FT_UINT48 ' , ' FT_UINT56 ' , ' FT_UINT64 ' } , positive_length = True ) )
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_int64 ' , { ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' } , positive_length = True ) )
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_boolean ' , { ' FT_BOOLEAN ' } , positive_length = True ) )
Add FT_STRINGZTRUNC.
FT_STRINGZPAD is for null-*padded* strings, where the field is in an
area of specified length, and, if the string is shorter than that
length, all bytes past the end of the string are NULs.
FT_STRINGZTRUNC is for null-*truncated* strings, where the field is in
an area of specified length and, if the string is shorter than that
length, there's a null character (which might be more than one byte, for
UCS-2, UTF-16, or UTF-32), and anything after that is not guaranteed to
have any particular value.
Use IS_FT_STRING() in some places rather than enumerating all the string
types, so that those places get automatically changed if the set of
string types changes.
2020-09-12 21:16:12 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_string_and_length ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' , ' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' } ) )
2020-08-03 22:44:14 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_display_string_and_length ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' ,
Add FT_STRINGZTRUNC.
FT_STRINGZPAD is for null-*padded* strings, where the field is in an
area of specified length, and, if the string is shorter than that
length, all bytes past the end of the string are NULs.
FT_STRINGZTRUNC is for null-*truncated* strings, where the field is in
an area of specified length and, if the string is shorter than that
length, there's a null character (which might be more than one byte, for
UCS-2, UTF-16, or UTF-32), and anything after that is not guaranteed to
have any particular value.
Use IS_FT_STRING() in some places rather than enumerating all the string
types, so that those places get automatically changed if the set of
string types changes.
2020-09-12 21:16:12 +00:00
' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' , ' FT_BYTES ' , ' FT_UINT_BYTES ' } ) )
2020-08-03 22:44:14 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_time_string ' , { ' FT_ABSOLUTE_TIME ' , ' FT_RELATIVE_TIME ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_uint ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' , ' FT_FRAMENUM ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_uint_format_value ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' , ' FT_FRAMENUM ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_uint_format ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' , ' FT_FRAMENUM ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_uint64 ' , { ' FT_UINT40 ' , ' FT_UINT48 ' , ' FT_UINT56 ' , ' FT_UINT64 ' , ' FT_FRAMENUM ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int64 ' , { ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int64_format_value ' , { ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int64_format ' , { ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int_format_value ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_int_format ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_boolean ' , { ' FT_BOOLEAN ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_boolean64 ' , { ' FT_BOOLEAN ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_float ' , { ' FT_FLOAT ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_float_format ' , { ' FT_FLOAT ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_float_format_value ' , { ' FT_FLOAT ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_double ' , { ' FT_DOUBLE ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_double_format ' , { ' FT_DOUBLE ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_double_format_value ' , { ' FT_DOUBLE ' } ) )
2021-12-10 21:34:55 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_string ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' , ' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_string_format ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' , ' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_string_format_value ' , { ' FT_STRING ' , ' FT_STRINGZ ' , ' FT_UINT_STRING ' , ' FT_STRINGZPAD ' , ' FT_STRINGZTRUNC ' } ) )
2020-08-03 22:44:14 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_guid ' , { ' FT_GUID ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_oid ' , { ' FT_OID ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_none_format ' , { ' FT_NONE ' } ) )
2020-10-09 20:20:12 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_item_ret_varint ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' , ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' ,
' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' , ' FT_FRAMENUM ' ,
' FT_UINT40 ' , ' FT_UINT48 ' , ' FT_UINT56 ' , ' FT_UINT64 ' , } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_boolean_bits_format_value ' , { ' FT_BOOLEAN ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_boolean_bits_format_value64 ' , { ' FT_BOOLEAN ' } ) )
apiChecks . append ( APICheck ( ' proto_tree_add_ascii_7bits_item ' , { ' FT_STRING ' } ) )
2022-08-12 16:20:35 +00:00
# TODO: positions are different, and takes 2 hf_fields..
#apiChecks.append(APICheck('proto_tree_add_checksum', { 'FT_UINT8', 'FT_UINT16', 'FT_UINT24', 'FT_UINT32'}))
2020-10-09 20:20:12 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_int64_bits_format_value ' , { ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' } ) )
2020-08-03 22:44:14 +00:00
2022-01-19 17:14:59 +00:00
# TODO: add proto_tree_add_bytes_item, proto_tree_add_time_item ?
2021-07-25 21:45:45 +00:00
bitmask_types = { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' ,
' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' ,
' FT_UINT40 ' , ' FT_UINT48 ' , ' FT_UINT56 ' , ' FT_UINT64 ' ,
' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' ,
' FT_BOOLEAN ' }
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_tree ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_ret_uint64 ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_with_flags ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_with_flags_ret_uint64 ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_value ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_value_with_flags ' , bitmask_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bitmask_len ' , bitmask_types ) )
2022-12-27 12:10:03 +00:00
add_bits_types = { ' FT_CHAR ' , ' FT_BOOLEAN ' ,
' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' , ' FT_UINT40 ' , ' FT_UINT48 ' , ' FT_UINT56 ' , ' FT_UINT64 ' ,
' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' , ' FT_INT40 ' , ' FT_INT48 ' , ' FT_INT56 ' , ' FT_INT64 ' ,
' FT_BYTES ' }
2022-12-29 17:35:18 +00:00
apiChecks . append ( APICheck ( ' proto_tree_add_bits_item ' , add_bits_types ) )
apiChecks . append ( APICheck ( ' proto_tree_add_bits_ret_val ' , add_bits_types ) )
2022-12-27 12:10:03 +00:00
2022-08-12 16:20:35 +00:00
# TODO: doesn't even have an hf_item !
#apiChecks.append(APICheck('proto_tree_add_bitmask_text', bitmask_types))
2021-07-25 21:45:45 +00:00
2021-08-08 16:47:42 +00:00
# Check some ptvcuror calls too.
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_uint ' , { ' FT_CHAR ' , ' FT_UINT8 ' , ' FT_UINT16 ' , ' FT_UINT24 ' , ' FT_UINT32 ' } ) )
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_int ' , { ' FT_INT8 ' , ' FT_INT16 ' , ' FT_INT24 ' , ' FT_INT32 ' } ) )
apiChecks . append ( APICheck ( ' ptvcursor_add_ret_boolean ' , { ' FT_BOOLEAN ' } ) )
2021-07-25 21:45:45 +00:00
2020-12-20 20:04:00 +00:00
# Also try to check proto_tree_add_item() calls (for length)
2020-10-17 13:23:26 +00:00
apiChecks . append ( ProtoTreeAddItemCheck ( ) )
2021-08-08 16:47:42 +00:00
apiChecks . append ( ProtoTreeAddItemCheck ( True ) ) # for ptvcursor_add()
2020-10-17 13:23:26 +00:00
2020-08-03 22:44:14 +00:00
def removeComments ( code_string ) :
2020-09-26 21:33:21 +00:00
code_string = re . sub ( re . compile ( r " / \ *.*? \ */ " , re . DOTALL ) , " " , code_string ) # C-style comment
code_string = re . sub ( re . compile ( r " //.*? \ n " ) , " " , code_string ) # C++-style comment
2023-01-31 13:34:04 +00:00
code_string = re . sub ( re . compile ( r " #if 0.*?#endif " , re . DOTALL ) , " " , code_string ) # Ignored region
2020-08-03 22:44:14 +00:00
return code_string
2020-09-26 21:33:21 +00:00
# Test for whether the given file was automatically generated.
def isGeneratedFile ( filename ) :
# Open file
f_read = open ( os . path . join ( filename ) , ' r ' )
lines_tested = 0
for line in f_read :
# The comment to say that its generated is near the top, so give up once
# get a few lines down.
if lines_tested > 10 :
f_read . close ( )
return False
if ( line . find ( ' Generated automatically ' ) != - 1 or
2022-12-15 13:28:05 +00:00
line . find ( ' Generated Automatically ' ) != - 1 or
2020-09-26 21:33:21 +00:00
line . find ( ' Autogenerated from ' ) != - 1 or
line . find ( ' is autogenerated ' ) != - 1 or
line . find ( ' automatically generated by Pidl ' ) != - 1 or
line . find ( ' Created by: The Qt Meta Object Compiler ' ) != - 1 or
2020-12-20 20:04:00 +00:00
line . find ( ' This file was generated ' ) != - 1 or
2021-11-07 15:39:42 +00:00
line . find ( ' This filter was automatically generated ' ) != - 1 or
line . find ( ' This file is auto generated, do not edit! ' ) != - 1 ) :
2020-12-20 20:04:00 +00:00
2020-09-26 21:33:21 +00:00
f_read . close ( )
return True
lines_tested = lines_tested + 1
# OK, looks like a hand-written file!
f_read . close ( )
return False
2022-01-25 11:43:32 +00:00
# Look for hf items (i.e. full item to be registered) in a dissector file.
2022-12-15 13:28:05 +00:00
def find_items ( filename , check_mask = False , mask_exact_width = False , check_label = False , check_consecutive = False ) :
2020-09-26 21:33:21 +00:00
is_generated = isGeneratedFile ( filename )
2020-08-03 22:44:14 +00:00
items = { }
with open ( filename , ' r ' ) as f :
contents = f . read ( )
# Remove comments so as not to trip up RE.
contents = removeComments ( contents )
2023-01-21 11:16:20 +00:00
2022-12-15 13:28:05 +00:00
# N.B. re extends all the way to HFILL to avoid greedy matching
2023-01-31 13:34:04 +00:00
matches = re . finditer ( r ' .* \ { \ s* \ &(hf_[a-z_A-Z0-9]*) \ s*, \ s* { \ s* \ " (.*?) \ " \ s*, \ s* \ " (.*?) \ " \ s*, \ s*(.*?) \ s*, \ s*([0-9A-Z_ \ | \ s]*?) \ s*, \ s*(.*?) \ s*, \ s*(.*?) \ s*, \ s*([a-zA-Z0-9 \ W \ s_ \ u00f6 \ u00e4]*?) \ s*, \ s*HFILL ' , contents )
2020-08-03 22:44:14 +00:00
for m in matches :
# Store this item.
hf = m . group ( 1 )
2023-01-31 13:34:04 +00:00
#print(hf)
2022-12-27 12:10:03 +00:00
items [ hf ] = Item ( filename , hf , filter = m . group ( 3 ) , label = m . group ( 2 ) , item_type = m . group ( 4 ) , mask = m . group ( 7 ) ,
2021-07-11 12:19:10 +00:00
type_modifier = m . group ( 5 ) ,
2020-09-26 21:33:21 +00:00
check_mask = check_mask ,
check_label = check_label ,
2022-12-15 13:28:05 +00:00
mask_exact_width = mask_exact_width ,
2020-09-26 21:33:21 +00:00
check_consecutive = ( not is_generated and check_consecutive ) )
2020-08-03 22:44:14 +00:00
return items
2022-12-03 17:15:24 +00:00
# Looking for args to ..add_bitmask_..() calls that are not NULL-terminated or have repeated items.
# TODO: some dissectors have similar-looking hf arrays for other reasons, so need to cross-reference with
# the 6th arg of ..add_bitmask_..() calls...
# TODO: return items (rather than local checks) from here so can be checked against list of calls for given filename
2023-01-03 07:28:48 +00:00
def find_field_arrays ( filename , all_fields , all_hf ) :
2022-12-03 17:15:24 +00:00
global warnings_found
with open ( filename , ' r ' ) as f :
contents = f . read ( )
# Remove comments so as not to trip up RE.
contents = removeComments ( contents )
matches = re . finditer ( r ' static \ s*g?int \ s* \ * \ s*const \ s+([a-zA-Z0-9_]*) \ s* \ [ \ ] \ s* \ = \ s* \ { ([a-zA-Z0-9,_ \ & \ s]*) \ } ' , contents )
for m in matches :
name = m . group ( 1 )
2023-01-03 07:28:48 +00:00
# Ignore if not used in a call to an _add_bitmask_ API
if name not in all_fields :
continue
2022-12-03 17:15:24 +00:00
all_fields = m . group ( 2 )
all_fields = all_fields . replace ( ' & ' , ' ' )
all_fields = all_fields . replace ( ' , ' , ' ' )
fields = all_fields . split ( )
if fields [ 0 ] . startswith ( ' ett_ ' ) :
continue
if fields [ - 1 ] . find ( ' NULL ' ) == - 1 and fields [ - 1 ] != ' 0 ' :
print ( ' Warning: ' , filename , name , ' is not NULL-terminated - { ' , ' , ' . join ( fields ) , ' } ' )
warnings_found + = 1
# Do any hf items reappear?
seen_fields = set ( )
for f in fields :
if f in seen_fields :
print ( filename , name , f , ' already added! ' )
warnings_found + = 1
seen_fields . add ( f )
2023-01-03 07:28:48 +00:00
# Check for duplicated flags among entries..
combined_mask = 0x0
for f in fields [ 0 : - 1 ] :
if f in all_hf :
new_mask = all_hf [ f ] . mask_value
if new_mask & combined_mask :
print ( ' Warning: ' , filename , name , ' has overlapping mask - { ' , ' , ' . join ( fields ) , ' } combined currently ' , hex ( combined_mask ) , f , ' adds ' , hex ( new_mask ) )
warnings_found + = 1
combined_mask | = new_mask
2022-12-03 17:15:24 +00:00
return [ ]
2022-01-25 11:43:32 +00:00
def find_item_declarations ( filename ) :
items = set ( )
with open ( filename , ' r ' ) as f :
lines = f . read ( ) . splitlines ( )
p = re . compile ( r ' ^static int (hf_[a-zA-Z0-9_]*) \ s* \ = \ s*-1; ' )
for line in lines :
m = p . search ( line )
if m :
items . add ( m . group ( 1 ) )
return items
def find_item_extern_declarations ( filename ) :
items = set ( )
with open ( filename , ' r ' ) as f :
lines = f . read ( ) . splitlines ( )
p = re . compile ( r ' ^ \ s*(hf_[a-zA-Z0-9_]*) \ s* \ = \ s*proto_registrar_get_id_byname \ s* \ ( ' )
for line in lines :
m = p . search ( line )
if m :
items . add ( m . group ( 1 ) )
return items
2020-08-03 22:44:14 +00:00
Fix issues discovered by common python linters
Fix some issues discovered by common python linters including:
* switch `None` comparisons to use `is` rather than `==`. Identity !=
equality, and I've spent 40+ hours before tracking down a subtle bug
caused by exactly this issue. Note that this may introduce a problem if
one of the scripts is depending on this behavior, in which case the
comparison should be changed to `True`/`False` rather than `None`.
* Use `except Exception:` as bare `except:` statements have been
discouraged for years. Ideally for some of these we'd examine if there
were specific exceptions that should be caught, but for now I simply
caught all. Again, this could introduce very subtle behavioral changes
under Python 2, but IIUC, that was all fixed in Python 3, so safe to
move to `except Exception:`.
* Use more idiomatic `if not x in y`--> `if x not in y`
* Use more idiomatic 2 blank lines. I only did this at the beginning,
until I realized how overwhelming this was going to be to apply, then I
stopped.
* Add a TODO where an undefined function name is called, so will fail
whenever that code is run.
* Add more idiomatic spacing around `:`. This is also only partially
cleaned up, as I gave up when I saw how `asn2wrs.py` was clearly
infatuated with the construct.
* Various other small cleanups, removed some trailing whitespace and
improper indentation that wasn't a multiple of 4, etc.
There is still _much_ to do, but I haven't been heavily involved with
this project before, so thought this was a sufficient amount to put up
and see what the feedback is.
Linters that I have enabled which highlighted some of these issues
include:
* `pylint`
* `flake8`
* `pycodestyle`
2020-09-21 05:44:41 +00:00
def is_dissector_file ( filename ) :
2022-01-25 11:43:32 +00:00
p = re . compile ( r ' .*(packet|file)-.* \ .c$ ' )
2020-08-03 22:44:14 +00:00
return p . match ( filename )
2021-01-24 16:14:11 +00:00
def findDissectorFilesInFolder ( folder , dissector_files = None , recursive = False ) :
if dissector_files is None :
dissector_files = [ ]
2020-09-19 12:34:02 +00:00
if recursive :
for root , subfolders , files in os . walk ( folder ) :
for f in files :
if should_exit :
return
f = os . path . join ( root , f )
dissector_files . append ( f )
else :
for f in sorted ( os . listdir ( folder ) ) :
if should_exit :
return
2020-08-03 22:44:14 +00:00
filename = os . path . join ( folder , f )
2020-09-19 12:34:02 +00:00
dissector_files . append ( filename )
Fix issues discovered by common python linters
Fix some issues discovered by common python linters including:
* switch `None` comparisons to use `is` rather than `==`. Identity !=
equality, and I've spent 40+ hours before tracking down a subtle bug
caused by exactly this issue. Note that this may introduce a problem if
one of the scripts is depending on this behavior, in which case the
comparison should be changed to `True`/`False` rather than `None`.
* Use `except Exception:` as bare `except:` statements have been
discouraged for years. Ideally for some of these we'd examine if there
were specific exceptions that should be caught, but for now I simply
caught all. Again, this could introduce very subtle behavioral changes
under Python 2, but IIUC, that was all fixed in Python 3, so safe to
move to `except Exception:`.
* Use more idiomatic `if not x in y`--> `if x not in y`
* Use more idiomatic 2 blank lines. I only did this at the beginning,
until I realized how overwhelming this was going to be to apply, then I
stopped.
* Add a TODO where an undefined function name is called, so will fail
whenever that code is run.
* Add more idiomatic spacing around `:`. This is also only partially
cleaned up, as I gave up when I saw how `asn2wrs.py` was clearly
infatuated with the construct.
* Various other small cleanups, removed some trailing whitespace and
improper indentation that wasn't a multiple of 4, etc.
There is still _much_ to do, but I haven't been heavily involved with
this project before, so thought this was a sufficient amount to put up
and see what the feedback is.
Linters that I have enabled which highlighted some of these issues
include:
* `pylint`
* `flake8`
* `pycodestyle`
2020-09-21 05:44:41 +00:00
return [ x for x in filter ( is_dissector_file , dissector_files ) ]
2020-09-19 12:34:02 +00:00
2020-08-03 22:44:14 +00:00
2020-12-20 20:04:00 +00:00
# Run checks on the given dissector file.
2022-12-15 13:28:05 +00:00
def checkFile ( filename , check_mask = False , mask_exact_width = False , check_label = False , check_consecutive = False , check_missing_items = False , check_bitmask_fields = False ) :
2020-12-31 10:35:14 +00:00
# Check file exists - e.g. may have been deleted in a recent commit.
if not os . path . exists ( filename ) :
print ( filename , ' does not exist! ' )
return
2020-08-03 22:44:14 +00:00
# Find important parts of items.
2022-12-15 13:28:05 +00:00
items_defined = find_items ( filename , check_mask , mask_exact_width , check_label , check_consecutive )
2022-01-25 11:43:32 +00:00
items_extern_declared = { }
items_declared = { }
if check_missing_items :
items_declared = find_item_declarations ( filename )
items_extern_declared = find_item_extern_declarations ( filename )
2023-01-03 07:28:48 +00:00
fields = set ( )
2020-08-03 22:44:14 +00:00
# Check each API
for c in apiChecks :
c . find_calls ( filename )
2023-01-03 07:28:48 +00:00
for call in c . calls :
if call . fields :
fields . add ( call . fields )
2022-01-25 11:43:32 +00:00
c . check_against_items ( items_defined , items_declared , items_extern_declared , check_missing_items )
2020-08-03 22:44:14 +00:00
2022-12-03 17:15:24 +00:00
# Checking for lists of fields for add_bitmask calls
if check_bitmask_fields :
2023-01-03 07:28:48 +00:00
field_arrays = find_field_arrays ( filename , fields , items_defined )
2022-12-03 17:15:24 +00:00
2020-08-03 22:44:14 +00:00
2020-12-20 20:04:00 +00:00
2020-08-03 22:44:14 +00:00
#################################################################
# Main logic.
# command-line args. Controls which dissector files should be checked.
# If no args given, will just scan epan/dissectors folder.
parser = argparse . ArgumentParser ( description = ' Check calls in dissectors ' )
2022-02-20 23:12:10 +00:00
parser . add_argument ( ' --file ' , action = ' append ' ,
2020-08-03 22:44:14 +00:00
help = ' specify individual dissector file to test ' )
2021-09-21 10:04:31 +00:00
parser . add_argument ( ' --folder ' , action = ' store ' , default = ' ' ,
help = ' specify folder to test ' )
2020-08-03 22:44:14 +00:00
parser . add_argument ( ' --commits ' , action = ' store ' ,
help = ' last N commits to check ' )
parser . add_argument ( ' --open ' , action = ' store_true ' ,
help = ' check open files ' )
2020-08-09 21:33:58 +00:00
parser . add_argument ( ' --mask ' , action = ' store_true ' ,
help = ' when set, check mask field too ' )
2022-12-15 13:28:05 +00:00
parser . add_argument ( ' --mask-exact-width ' , action = ' store_true ' ,
help = ' when set, check width of mask against field width ' )
2020-08-09 21:33:58 +00:00
parser . add_argument ( ' --label ' , action = ' store_true ' ,
help = ' when set, check label field too ' )
2020-09-26 21:33:21 +00:00
parser . add_argument ( ' --consecutive ' , action = ' store_true ' ,
help = ' when set, copy copy/paste errors between consecutive items ' )
2022-01-20 10:40:46 +00:00
parser . add_argument ( ' --missing-items ' , action = ' store_true ' ,
help = ' when set, look for used items that were never registered ' )
2022-12-03 17:15:24 +00:00
parser . add_argument ( ' --check-bitmask-fields ' , action = ' store_true ' ,
help = ' when set, attempt to check arrays of hf items passed to add_bitmask() calls ' )
2022-01-20 10:40:46 +00:00
2020-09-26 21:33:21 +00:00
2020-08-09 21:33:58 +00:00
2020-08-03 22:44:14 +00:00
args = parser . parse_args ( )
# Get files from wherever command-line args indicate.
files = [ ]
if args . file :
2022-02-20 23:12:10 +00:00
# Add specified file(s)
for f in args . file :
if not f . startswith ( ' epan ' ) :
f = os . path . join ( ' epan ' , ' dissectors ' , f )
if not os . path . isfile ( f ) :
print ( ' Chosen file ' , f , ' does not exist. ' )
exit ( 1 )
else :
files . append ( f )
2021-09-21 10:04:31 +00:00
elif args . folder :
# Add all files from a given folder.
folder = args . folder
if not os . path . isdir ( folder ) :
print ( ' Folder ' , folder , ' not found! ' )
exit ( 1 )
# Find files from folder.
print ( ' Looking for files in ' , folder )
files = findDissectorFilesInFolder ( folder , recursive = True )
2020-08-03 22:44:14 +00:00
elif args . commits :
# Get files affected by specified number of commits.
2020-12-27 17:19:53 +00:00
command = [ ' git ' , ' diff ' , ' --name-only ' , ' --diff-filter=d ' , ' HEAD~ ' + args . commits ]
2020-08-03 22:44:14 +00:00
files = [ f . decode ( ' utf-8 ' )
for f in subprocess . check_output ( command ) . splitlines ( ) ]
# Will examine dissector files only
Fix issues discovered by common python linters
Fix some issues discovered by common python linters including:
* switch `None` comparisons to use `is` rather than `==`. Identity !=
equality, and I've spent 40+ hours before tracking down a subtle bug
caused by exactly this issue. Note that this may introduce a problem if
one of the scripts is depending on this behavior, in which case the
comparison should be changed to `True`/`False` rather than `None`.
* Use `except Exception:` as bare `except:` statements have been
discouraged for years. Ideally for some of these we'd examine if there
were specific exceptions that should be caught, but for now I simply
caught all. Again, this could introduce very subtle behavioral changes
under Python 2, but IIUC, that was all fixed in Python 3, so safe to
move to `except Exception:`.
* Use more idiomatic `if not x in y`--> `if x not in y`
* Use more idiomatic 2 blank lines. I only did this at the beginning,
until I realized how overwhelming this was going to be to apply, then I
stopped.
* Add a TODO where an undefined function name is called, so will fail
whenever that code is run.
* Add more idiomatic spacing around `:`. This is also only partially
cleaned up, as I gave up when I saw how `asn2wrs.py` was clearly
infatuated with the construct.
* Various other small cleanups, removed some trailing whitespace and
improper indentation that wasn't a multiple of 4, etc.
There is still _much_ to do, but I haven't been heavily involved with
this project before, so thought this was a sufficient amount to put up
and see what the feedback is.
Linters that I have enabled which highlighted some of these issues
include:
* `pylint`
* `flake8`
* `pycodestyle`
2020-09-21 05:44:41 +00:00
files = list ( filter ( lambda f : is_dissector_file ( f ) , files ) )
2020-08-03 22:44:14 +00:00
elif args . open :
# Unstaged changes.
2020-12-27 17:19:53 +00:00
command = [ ' git ' , ' diff ' , ' --name-only ' , ' --diff-filter=d ' ]
2020-08-03 22:44:14 +00:00
files = [ f . decode ( ' utf-8 ' )
for f in subprocess . check_output ( command ) . splitlines ( ) ]
# Only interested in dissector files.
Fix issues discovered by common python linters
Fix some issues discovered by common python linters including:
* switch `None` comparisons to use `is` rather than `==`. Identity !=
equality, and I've spent 40+ hours before tracking down a subtle bug
caused by exactly this issue. Note that this may introduce a problem if
one of the scripts is depending on this behavior, in which case the
comparison should be changed to `True`/`False` rather than `None`.
* Use `except Exception:` as bare `except:` statements have been
discouraged for years. Ideally for some of these we'd examine if there
were specific exceptions that should be caught, but for now I simply
caught all. Again, this could introduce very subtle behavioral changes
under Python 2, but IIUC, that was all fixed in Python 3, so safe to
move to `except Exception:`.
* Use more idiomatic `if not x in y`--> `if x not in y`
* Use more idiomatic 2 blank lines. I only did this at the beginning,
until I realized how overwhelming this was going to be to apply, then I
stopped.
* Add a TODO where an undefined function name is called, so will fail
whenever that code is run.
* Add more idiomatic spacing around `:`. This is also only partially
cleaned up, as I gave up when I saw how `asn2wrs.py` was clearly
infatuated with the construct.
* Various other small cleanups, removed some trailing whitespace and
improper indentation that wasn't a multiple of 4, etc.
There is still _much_ to do, but I haven't been heavily involved with
this project before, so thought this was a sufficient amount to put up
and see what the feedback is.
Linters that I have enabled which highlighted some of these issues
include:
* `pylint`
* `flake8`
* `pycodestyle`
2020-09-21 05:44:41 +00:00
files = list ( filter ( lambda f : is_dissector_file ( f ) , files ) )
2020-08-03 22:44:14 +00:00
# Staged changes.
2020-12-27 17:19:53 +00:00
command = [ ' git ' , ' diff ' , ' --staged ' , ' --name-only ' , ' --diff-filter=d ' ]
2020-08-03 22:44:14 +00:00
files_staged = [ f . decode ( ' utf-8 ' )
for f in subprocess . check_output ( command ) . splitlines ( ) ]
# Only interested in dissector files.
Fix issues discovered by common python linters
Fix some issues discovered by common python linters including:
* switch `None` comparisons to use `is` rather than `==`. Identity !=
equality, and I've spent 40+ hours before tracking down a subtle bug
caused by exactly this issue. Note that this may introduce a problem if
one of the scripts is depending on this behavior, in which case the
comparison should be changed to `True`/`False` rather than `None`.
* Use `except Exception:` as bare `except:` statements have been
discouraged for years. Ideally for some of these we'd examine if there
were specific exceptions that should be caught, but for now I simply
caught all. Again, this could introduce very subtle behavioral changes
under Python 2, but IIUC, that was all fixed in Python 3, so safe to
move to `except Exception:`.
* Use more idiomatic `if not x in y`--> `if x not in y`
* Use more idiomatic 2 blank lines. I only did this at the beginning,
until I realized how overwhelming this was going to be to apply, then I
stopped.
* Add a TODO where an undefined function name is called, so will fail
whenever that code is run.
* Add more idiomatic spacing around `:`. This is also only partially
cleaned up, as I gave up when I saw how `asn2wrs.py` was clearly
infatuated with the construct.
* Various other small cleanups, removed some trailing whitespace and
improper indentation that wasn't a multiple of 4, etc.
There is still _much_ to do, but I haven't been heavily involved with
this project before, so thought this was a sufficient amount to put up
and see what the feedback is.
Linters that I have enabled which highlighted some of these issues
include:
* `pylint`
* `flake8`
* `pycodestyle`
2020-09-21 05:44:41 +00:00
files_staged = list ( filter ( lambda f : is_dissector_file ( f ) , files_staged ) )
2020-08-03 22:44:14 +00:00
for f in files_staged :
if not f in files :
files . append ( f )
else :
2020-09-19 12:34:02 +00:00
# Find all dissector files.
2020-08-03 22:44:14 +00:00
files = findDissectorFilesInFolder ( os . path . join ( ' epan ' , ' dissectors ' ) )
2020-09-19 12:34:02 +00:00
files = findDissectorFilesInFolder ( os . path . join ( ' plugins ' , ' epan ' ) , recursive = True , dissector_files = files )
2020-08-03 22:44:14 +00:00
# If scanning a subset of files, list them here.
print ( ' Examining: ' )
if args . file or args . commits or args . open :
if files :
print ( ' ' . join ( files ) , ' \n ' )
else :
print ( ' No files to check. \n ' )
else :
print ( ' All dissector modules \n ' )
# Now check the files.
for f in files :
if should_exit :
exit ( 1 )
2022-12-15 13:28:05 +00:00
checkFile ( f , check_mask = args . mask , mask_exact_width = args . mask_exact_width , check_label = args . label ,
2022-12-03 17:15:24 +00:00
check_consecutive = args . consecutive , check_missing_items = args . missing_items ,
check_bitmask_fields = args . check_bitmask_fields )
2020-08-03 22:44:14 +00:00
2022-08-12 16:20:35 +00:00
# Do checks against all calls.
if args . consecutive :
combined_calls = CombinedCallsCheck ( f , apiChecks )
combined_calls . check_consecutive_item_calls ( )
2020-08-03 22:44:14 +00:00
# Show summary.
2021-07-31 15:06:32 +00:00
print ( warnings_found , ' warnings ' )
if errors_found :
print ( errors_found , ' errors ' )
exit ( 1 )