diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
commit | 9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9 (patch) | |
tree | 2784370cda9bbf2da9114d70f05399c0b229d28c /tools/make-bluetooth.py | |
parent | Adding debian version 4.2.6-1. (diff) | |
download | wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.tar.xz wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.zip |
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/make-bluetooth.py')
-rwxr-xr-x | tools/make-bluetooth.py | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/tools/make-bluetooth.py b/tools/make-bluetooth.py new file mode 100755 index 00000000..71942b91 --- /dev/null +++ b/tools/make-bluetooth.py @@ -0,0 +1,368 @@ +#!/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 +# +''' +make-bluetooth - Generate value_strings containing bluetooth uuids and company identifiers. +It makes use of the databases from +The Bluetooth SIG Repository: https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/ +and processes the YAML into human-readable strings to go into packet-bluetooth.c. +''' + +import sys +import urllib.request, urllib.error, urllib.parse +import yaml + +base_url = "https://bitbucket.org/bluetooth-SIG/public/raw/HEAD/assigned_numbers/" + +MIN_UUIDS = 1400 # 1424 as of 31-12-2023 +MIN_COMPANY_IDS = 3400 # 3405 as of 31-12-2023 + +## +## UUIDs +## + +''' +List of all YAML files to retrieve, the lists of UUIDs to put into the value_string +and other information. +Unfortunately the encoding of the names among the YAML files is inconsistent, +to say the least. This will need post-processing. +Also the previous value_string contained additional uuids, which are not currently +present in the databases. Prepare the lists with these uuids so they are not lost. +When they do appear in the databases they must be removed here. +''' + +uuids_sources = [ +{ # 0x0001 + "yaml": "protocol_identifiers.yaml", + "description": "Protocol Identifiers", + "unCamelCase": True, + "unlist": [], + "list": [ + { "uuid": 0x001D, "name": "UDI C-Plane" }, + ] +}, +{ # 0x1000 + "yaml": "service_class.yaml", + "description": "Service Class", + "unCamelCase": True, + "unlist": [], + "list": [ + # Then we have this weird one stuck in between "Service Class" + # from browse_group_identifiers.yaml + { "uuid": 0x1002, "name": "Public Browse Group" }, + # And some from other sources + { "uuid": 0x1129, "name": "Video Conferencing GW" }, + { "uuid": 0x112A, "name": "UDI MT" }, + { "uuid": 0x112B, "name": "UDI TA" }, + { "uuid": 0x112C, "name": "Audio/Video" }, + ] +}, +{ # 0x1600 + "yaml": "mesh_profile_uuids.yaml", + "description": "Mesh Profile", + "unCamelCase": False, + "unlist": [], + "list": [] +}, +{ # 0x1800 + "yaml": "service_uuids.yaml", + "description": "Service", + "unCamelCase": False, + "unlist": [], + "list": [] +}, +{ # 0x2700 + "yaml": "units.yaml", + "description": "Units", + "unCamelCase": False, + "unlist": [], + "list": [] +}, +{ # 0x2800 + "yaml": "declarations.yaml", + "description": "Declarations", + "unCamelCase": False, + "unlist": [], + "list": [] +}, +{ # 0x2900 + "yaml": "descriptors.yaml", + "description": "Descriptors", + "unCamelCase": False, + "unlist": [], + "list": [] +}, +{ # 0x2a00 + "yaml": "characteristic_uuids.yaml", + "description": "Characteristics", + "unCamelCase": False, + "unlist": [], + "list": [ + # Then we have these weird ones stuck in between "Characteristics" + # from object_types.yaml + { "uuid": 0x2ACA, "name": "Unspecified" }, + { "uuid": 0x2ACB, "name": "Directory Listing" }, + # And some from other sources + { "uuid": 0x2A0B, "name": "Exact Time 100" }, + { "uuid": 0x2A10, "name": "Secondary Time Zone" }, + { "uuid": 0x2A15, "name": "Time Broadcast" }, + { "uuid": 0x2A1A, "name": "Battery Power State" }, + { "uuid": 0x2A1B, "name": "Battery Level State" }, + { "uuid": 0x2A1F, "name": "Temperature Celsius" }, + { "uuid": 0x2A20, "name": "Temperature Fahrenheit" }, + { "uuid": 0x2A2F, "name": "Position 2D" }, + { "uuid": 0x2A30, "name": "Position 3D" }, + { "uuid": 0x2A3A, "name": "Removable" }, + { "uuid": 0x2A3B, "name": "Service Required" }, + { "uuid": 0x2A3C, "name": "Scientific Temperature Celsius" }, + { "uuid": 0x2A3D, "name": "String" }, + { "uuid": 0x2A3E, "name": "Network Availability" }, + { "uuid": 0x2A56, "name": "Digital" }, + { "uuid": 0x2A57, "name": "Digital Output" }, + { "uuid": 0x2A58, "name": "Analog" }, + { "uuid": 0x2A59, "name": "Analog Output" }, + { "uuid": 0x2A62, "name": "Pulse Oximetry Control Point" }, + # These have somehow disappeared. We keep them for if they were used. + { "uuid": 0x2BA9, "name": "Media Player Icon Object Type" }, + { "uuid": 0x2BAA, "name": "Track Segments Object Type" }, + { "uuid": 0x2BAB, "name": "Track Object Type" }, + { "uuid": 0x2BAC, "name": "Group Object Type" }, + ] +}, +{ # 0xfxxx + "yaml": "member_uuids.yaml", + "description": "Members", + "unCamelCase": False, + "unlist": [], + "list": [ + # This they really screwed up. The UUID was moved to sdo_uuids, + # thereby breaking the range and ordering completely. + { "uuid": 0xFCCC, "name": "Wi-Fi Easy Connect Specification" }, + ] +}, +{ # 0xffef (and 0xfccc) + "yaml": "sdo_uuids.yaml", + "description": "SDO", + "unCamelCase": False, + "unlist": [ 0xFCCC, + ], + "list": [] +}] + +''' +Retrieve the YAML files defining the UUIDs and add them to the lists +''' +for uuids in uuids_sources: + req_headers = { 'User-Agent': 'Wireshark make-bluetooth' } + try: + req = urllib.request.Request(base_url + 'uuids/' + uuids["yaml"], headers=req_headers) + response = urllib.request.urlopen(req) + lines = response.read().decode('UTF-8', 'replace') + except Exception as e: + print("Failed to get UUIDs at {url}, because of: {e}".format(url=base_url + 'uuids/' + uuids["yaml"], e=e), file=sys.stderr) + sys.exit(1) + + uuids_dir = yaml.safe_load(lines) + for uuid in uuids_dir["uuids"]: + if uuid["uuid"] not in uuids["unlist"]: + uuids["list"].append(uuid) + +''' +Go through the lists and perform general and specific transforms. +Several exceptional cases are addressed directly by their UUID, because of the inconsistent nature +by which their name is constructed. +When they appear more sensibly in the databases they must be removed here. +When new inconsistent entries appear in the databases their transforms can be added here, +but also add their UUID below. +''' +for uuids in uuids_sources: + for uuid in uuids["list"]: + # Handle a few exceptional cases + if uuid["uuid"] == 0x001E: + uuid["name"] = "MCAP Control Channel" + elif uuid["uuid"] == 0x001F: + uuid["name"] = "MCAP Data Channel" + elif uuid["uuid"] == 0x1102: + uuid["name"] = "LAN Access Using PPP" + elif uuid["uuid"] == 0x1104: + uuid["name"] = "IrMC Sync" + elif uuid["uuid"] == 0x1105: + uuid["name"] = "OBEX Object Push" + elif uuid["uuid"] == 0x1106: + uuid["name"] = "OBEX File Transfer" + elif uuid["uuid"] == 0x1107: + uuid["name"] = "IrMC Sync Command" + elif uuid["uuid"] == 0x1200: + uuid["name"] = "PnP Information" + elif uuid["uuid"] == 0x2B8C: + uuid["name"] = "CO\u2082 Concentration" + else: + # And these in general + uuid["name"] = uuid["name"].replace("_", " ") + uuid["name"] = uuid["name"].replace('"', '\\"') + +''' +Go through the lists and, for those lists flagged as such, perform the unCamelCase transform +on all the names in that list. +Several exceptional cases were addressed directly by their UUID and must be excluded from this +transform. +When additional characters indicating a break in words appear in database entries they can be +added to break_chars. +''' +for uuids in uuids_sources: + if uuids["unCamelCase"]: + for uuid in uuids["list"]: + # if not a few exceptional cases (see above) + if uuid["uuid"] not in [0x001E, 0x001F, 0x1102, 0x1104, 0x1105, 0x1106, 0x1107, 0x1200, 0x2B8C]: + # Parse through the names and look for capital letters; when + # not preceded by another capital letter or one of break_chars, insert a space + break_chars = [" ", "-", "+", "/", "(", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + was_break = True # fake space at beginning of string + was_upper = False + name = "" + for character in uuid["name"]: + is_upper = True if character.isupper() else False + if is_upper and not was_break and not was_upper: + name += " " + name += character + was_break = True if character in break_chars else False + was_upper = is_upper + uuid["name"] = name + +''' +To be able to generate a value_string_ext array the entries need to be sorted. +''' +for uuids in uuids_sources: + uuids_sorted = sorted(uuids["list"], key=lambda uuid: uuid["uuid"]) + uuids["list"] = uuids_sorted + +''' +Do a check on duplicate entries. +While at it, do a count of the number of UUIDs retrieved. +''' +prev_uuid = 0 +uuid_count = 0 +for uuids in uuids_sources: + for uuid in uuids["list"]: + if uuid["uuid"] > prev_uuid: + prev_uuid = uuid["uuid"] + else: + print("Duplicate UUID detected: 0x{uuid:04X}".format(uuid=uuid["uuid"]), file=sys.stderr) + sys.exit(1) + uuid_count += len(uuids["list"]) + +''' +Sanity check to see if enough entries were retrieved +''' +if (uuid_count < MIN_UUIDS): + print("There are fewer UUIDs than expected: got {count} but was expecting {minimum}".format(count=uuid_count, minimum=MIN_UUIDS), file=sys.stderr) + sys.exit(1) + +''' +Finally output the annotated source code for the value_string +''' +print("const value_string bluetooth_uuid_vals[] = {") + +for uuids in uuids_sources: + print(" /* {description} - {base_url}uuids/{yaml} */".format(description=uuids["description"], base_url=base_url, yaml=uuids["yaml"])) + for uuid in uuids["list"]: + print(" {{ 0x{uuid:04X}, \"{name}\" }},".format(uuid=uuid["uuid"], name=uuid["name"])) + +print(" { 0, NULL }") +print("};") +print("value_string_ext bluetooth_uuid_vals_ext = VALUE_STRING_EXT_INIT(bluetooth_uuid_vals);") +print("") + +## +## Company Identifiers +## + +''' +List of the YAML files to retrieve and the lists of values to put into the value_string. +Also the previous value_string contained additional company IDs, which are not currently +present in the databases. Prepare the lists with these company IDs so they are not lost. +When they do appear in the databases they must be removed here. +''' + +company_ids_sources = [ +{ + "yaml": "company_identifiers.yaml", + "list": [ + # Some from other sources + { "value": 0x0418, "name": "Alpine Electronics Inc." }, + { "value": 0x0943, "name": "Inovonics Corp." }, + { "value": 0xFFFF, "name": "For use in internal and interoperability tests" }, + ] +}] + +''' +Retrieve the YAML files defining the company IDs and add them to the lists +''' +for company_ids in company_ids_sources: + req_headers = { 'User-Agent': 'Wireshark make-bluetooth' } + try: + req = urllib.request.Request(base_url + 'company_identifiers/' + company_ids["yaml"], headers=req_headers) + response = urllib.request.urlopen(req) + lines = response.read().decode('UTF-8', 'replace') + except Exception as e: + print("Failed to get company IDs at {url}, because of: {e}".format(url=base_url + 'company_identifiers/' + company_ids["yaml"], e=e), file=sys.stderr) + sys.exit(-1) + + company_ids_dir = yaml.safe_load(lines) + company_ids["list"].extend(company_ids_dir["company_identifiers"]) + +''' +Go through the lists and perform general transforms. +''' +for company_ids in company_ids_sources: + for company_id in company_ids["list"]: + company_id["name"] = company_id["name"].replace('"', '\\"') + +''' +To be able to generate a value_string_ext array the entries need to be sorted. +''' +for company_ids in company_ids_sources: + company_ids_sorted = sorted(company_ids["list"], key=lambda company_id: company_id['value']) + company_ids["list"] = company_ids_sorted + +''' +Do a check on duplicate entries. +While at it, do a count of the number of company IDs retrieved. +''' +prev_company_id = -1 +company_id_count = 0 +for company_ids in company_ids_sources: + for company_id in company_ids["list"]: + if company_id["value"] > prev_company_id: + prev_company_id = company_id["value"] + else: + print("Duplicate company ID detected: 0x{company_id:04X}".format(company_id=company_id["value"]), file=sys.stderr) + sys.exit(1) + company_id_count += len(company_ids["list"]) + +''' +Sanity check to see if enough entries were retrieved +''' +if company_id_count < MIN_COMPANY_IDS: + print("There are fewer company IDs than expected: got {count} but was expecting {minimum}".format(count=company_id_count, minimum=MIN_COMPANY_IDS), file=sys.stderr) + sys.exit(1) + +''' +Finally output the source code for the value_string +''' +print("/* Taken from {base_url}company_identifiers/{yaml} */".format(base_url=base_url, yaml=company_ids_sources[0]["yaml"])) +print("static const value_string bluetooth_company_id_vals[] = {") + +for company_ids in company_ids_sources: + for company_id in company_ids["list"]: + print(" {{ 0x{company_id:04X}, \"{name}\" }},".format(company_id=company_id["value"], name=company_id["name"])) + +print(" { 0, NULL }") +print("};") +print("value_string_ext bluetooth_company_id_vals_ext = VALUE_STRING_EXT_INIT(bluetooth_company_id_vals);") + |