1#!/usr/bin/env python3
2#
3# Copyright 2019, Dario Lombardo <lomato@gmail.com>
4#
5# Wireshark - Network traffic analyzer
6# By Gerald Combs <gerald@wireshark.org>
7# Copyright 1998 Gerald Combs
8#
9# SPDX-License-Identifier: GPL-2.0-or-later
10#
11# This script generates a Wireshark skeleton dissector, based on the example in the doc/ directory.
12#
13# Example usage:
14#
15# generate-dissector.py --name "My Self" --email "myself@example.com" --protoname "The dumb protocol"
16#   --protoshortname DUMB --protoabbrev dumb --license GPL-2.0-or-later --years "2019-2020"
17#
18
19import argparse
20from datetime import datetime
21import os
22
23
24parser = argparse.ArgumentParser(description='The Wireshark Dissector Generator')
25parser.add_argument("--name", help="The author of the dissector", required=True)
26parser.add_argument("--email", help="The email address of the author", required=True)
27parser.add_argument("--protoname", help="The name of the protocol", required=True)
28parser.add_argument("--protoshortname", help="The protocol short name", required=True)
29parser.add_argument("--protoabbrev", help="The protocol abbreviation", required=True)
30parser.add_argument("--license", help="The license for this dissector (please use a SPDX-License-Identifier). If omitted, GPL-2.0-or-later will be used")
31parser.add_argument("--years", help="Years of validity for the license. If omitted, the current year will be used")
32parser.add_argument("-f", "--force", action='store_true', help="Force overwriting the dissector file if it already exists")
33
34
35def wsdir():
36    return os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
37
38
39def output_file(args):
40    return os.path.join(wsdir(), "epan/dissectors/packet-" + args.protoabbrev + ".c")
41
42
43def read_skeleton():
44    skeletonfile = os.path.join(wsdir(), "doc/packet-PROTOABBREV.c")
45    print("Reading skeleton file: " + skeletonfile)
46    return open(skeletonfile).read()
47
48
49def replace_fields(buffer, args):
50    print("Replacing fields in skeleton")
51    output = buffer\
52        .replace("YOUR_NAME", args.name)\
53        .replace("YOUR_EMAIL_ADDRESS", args.email)\
54        .replace("PROTONAME", args.protoname)\
55        .replace("PROTOSHORTNAME", args.protoshortname)\
56        .replace("PROTOABBREV", args.protoabbrev)\
57        .replace("FIELDNAME", "Sample Field")\
58        .replace("FIELDABBREV", "sample_field")\
59        .replace("FT_FIELDTYPE", "FT_STRING")\
60        .replace("FIELDDISPLAY", "BASE_NONE")\
61        .replace("FIELDCONVERT", "NULL")\
62        .replace("BITMASK", "0x0")\
63        .replace("FIELDDESCR", "NULL")\
64        .replace("MAX_NEEDED_FOR_HEURISTICS", "1")\
65        .replace("TEST_HEURISTICS_FAIL", "0")\
66        .replace("ENC_xxx", "ENC_NA")\
67        .replace("EXPERTABBREV", "expert")\
68        .replace("PI_GROUP", "PI_PROTOCOL")\
69        .replace("PI_SEVERITY", "PI_ERROR")\
70        .replace("TEST_EXPERT_condition", "0")\
71        .replace("const char *subtree", "\"\"")
72
73    if args.license:
74        output = output.replace("LICENSE", args.license)
75    else:
76        output = output.replace("LICENSE", "GPL-2.0-or-later")
77
78    if args.years:
79        output = output.replace("YEARS", args.years)
80    else:
81        output = output.replace("YEARS", str(datetime.now().year))
82
83    return output
84
85
86def write_dissector(buffer, args):
87    ofile = output_file(args)
88    if os.path.isfile(ofile) and not args.force:
89        raise Exception("The file " + ofile + " already exists. You're likely overwriting an existing dissector.")
90    print("Writing output file: " + ofile)
91    return open(ofile, "w").write(buffer)
92
93
94def patch_makefile(args):
95    cmakefile = os.path.join(wsdir(), "epan/dissectors/CMakeLists.txt")
96    print("Patching makefile: " + cmakefile)
97    output = ""
98    patchline = "${CMAKE_CURRENT_SOURCE_DIR}/packet-" + args.protoabbrev + ".c"
99    in_group = False
100    patched = False
101    for line in open(cmakefile):
102        line_strip = line.strip()
103        if in_group and line_strip == ")":
104            in_group = False
105        if in_group and not patched and line_strip > patchline:
106            output += "\t" + patchline + "\n"
107            patched = True
108        if line_strip == "set(DISSECTOR_SRC":
109            in_group = True
110        if line_strip != patchline:
111            output += line
112    open(cmakefile, "w").write(output)
113
114
115def print_header():
116    print("")
117    print("**************************************************")
118    print("*   Wireshark skeleton dissector generator       *")
119    print("*                                                *")
120    print("*   Generate a new dissector for your protocol   *")
121    print("*   starting from the skeleton provided in the   *")
122    print("*   doc directory.                               *")
123    print("*                                                *")
124    print("*   Copyright 2019 Dario Lombardo                *")
125    print("**************************************************")
126    print("")
127
128
129def print_trailer(args):
130    print("")
131    print("The skeleton for the dissector of the " + args.protoshortname + " protocol has been generated.")
132    print("Please review/extend it to match your specific criterias.")
133    print("")
134
135
136if __name__ == '__main__':
137    print_header()
138    args = parser.parse_args()
139    buffer = replace_fields(read_skeleton(), args)
140    write_dissector(buffer, args)
141    patch_makefile(args)
142    print_trailer(args)
143