3 # Parses the CSV version of the IANA Service Name and Transport Protocol Port Number Registry
4 # and generates a services(5) file.
6 # Wireshark - Network traffic analyzer
7 # By Gerald Combs <gerald@wireshark.org>
8 # Copyright 2013 Gerald Combs
10 # SPDX-License-Identifier: GPL-2.0-or-later
17 import urllib
.request
, urllib
.error
, urllib
.parse
20 iana_svc_url
= 'https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv'
23 Usage: make-services.py [url]
30 services_file
= 'epan/services-data.c'
38 min_source_lines
= 14000 # Size was ~ 14800 on 2017-07-20
40 def parse_port(port_str
):
42 p
= port_str
.split('-')
45 return tuple([int(p
[0])])
47 return tuple(range(int(p
[0]), int(p
[1]) + 1))
52 def port_to_str(port
):
54 return str(port
[0]) + '-' + str(port
[1])
57 def parse_rows(svc_fd
):
58 port_reader
= csv
.reader(svc_fd
)
61 # Header positions as of 2013-08-06
62 headers
= next(port_reader
)
65 sn_pos
= headers
.index('Service Name')
69 pn_pos
= headers
.index('Port Number')
73 tp_pos
= headers
.index('Transport Protocol')
77 desc_pos
= headers
.index('Description')
83 for row
in port_reader
:
85 port
= parse_port(row
[pn_pos
])
87 description
= row
[desc_pos
]
90 if len(service
) < 1 or not port
or len(proto
) < 1:
93 if re
.search('|'.join(exclude_services
), service
):
97 service
= service
[:15].rstrip()
99 # replace blanks (for some non-standard long names)
100 service
= service
.replace(" ", "-")
102 description
= description
.replace("\n", "")
103 description
= re
.sub("IANA assigned this well-formed service .+$", "", description
)
104 description
= re
.sub(" +", " ", description
)
105 description
= description
.strip()
106 if description
== service
or description
== service
.replace("-", " "):
109 if port
not in services_map
:
110 services_map
[port
] = collections
.OrderedDict()
112 # Remove some duplicates (first entry wins)
114 for k
in services_map
[port
].keys():
115 if proto
in services_map
[port
][k
]:
121 if service
not in services_map
[port
]:
122 services_map
[port
][service
] = [description
]
123 services_map
[port
][service
].append(proto
)
125 if count
< min_source_lines
:
126 exit_msg('Not enough parsed data')
131 keys
= list(d
.keys())
136 for serv
in d
[port
].keys():
137 line
= [port
, d
[port
][serv
][1:], serv
]
138 description
= d
[port
][serv
][0]
140 line
.append(description
)
145 def add_entry(table
, port
, service_name
, description
):
146 table
.append([int(port
), service_name
, description
])
149 # body = [(port-range,), [proto-list], service-name, optional-description]
150 # table = [port-number, service-name, optional-description]
151 def compile_tables(body
):
162 port_range
, proto_list
, service_name
, description
= entry
164 port_range
, proto_list
, service_name
= entry
167 for port
in port_range
:
168 if 'tcp' in proto_list
and 'udp' in proto_list
:
169 add_entry(tcp_udp_table
, port
, service_name
, description
)
171 if 'tcp' in proto_list
:
172 add_entry(tcp_table
, port
, service_name
, description
)
173 if 'udp' in proto_list
:
174 add_entry(udp_table
, port
, service_name
, description
)
175 if 'sctp' in proto_list
:
176 add_entry(sctp_table
, port
, service_name
, description
)
177 if 'dccp' in proto_list
:
178 add_entry(dccp_table
, port
, service_name
, description
)
180 return tcp_udp_table
, tcp_table
, udp_table
, sctp_table
, dccp_table
183 def exit_msg(msg
=None, status
=1):
185 sys
.stderr
.write(msg
+ '\n\n')
186 sys
.stderr
.write(__doc__
+ '\n')
190 if sys
.version_info
[0] < 3:
191 print("This requires Python 3")
195 opts
, _
= getopt
.getopt(argv
, "h", ["help"])
196 except getopt
.GetoptError
:
199 if opt
in ("-h", "--help"):
205 svc_url
= iana_svc_url
208 if not svc_url
.startswith('http'):
209 svc_fd
= open(svc_url
)
211 req
= urllib
.request
.urlopen(svc_url
)
212 svc_fd
= codecs
.getreader('utf8')(req
)
214 exit_msg('Error opening ' + svc_url
)
216 body
= parse_rows(svc_fd
)
218 out
= open(services_file
, 'w')
221 * Wireshark - Network traffic analyzer
222 * By Gerald Combs <gerald@wireshark.org>
223 * Copyright 1998 Gerald Combs
225 * SPDX-License-Identifier: GPL-2.0-or-later
227 * This is a local copy of the IANA port-numbers file.
229 * Wireshark uses it to resolve port numbers into human readable
230 * service names, e.g. TCP port 80 -> http.
232 * It is subject to copyright and being used with IANA's permission:
233 * https://lists.wireshark.org/archives/wireshark-dev/200708/msg00160.html
235 * The original file can be found at:
238 * Generated by tools/make-services.py
241 ''' % (iana_svc_url
))
243 body
= compile_body(body
)
244 # body = [(port-range,), [proto-list], service-name, optional-description]
248 tcp_udp
, tcp
, udp
, sctp
, dccp
= compile_tables(body
)
250 def write_entry(f
, e
, max_port
):
251 line
= " {{ {}, \"{}\", ".format(*e
)
252 sep_len
= 32 - len(line
)
255 line
+= ' ' * sep_len
256 if len(e
) == 3 and e
[2]:
257 line
+= "\"{}\" }},\n".format(e
[2].replace('"', '\\"'))
261 if int(e
[0]) > int(max_port
):
265 out
.write("static const ws_services_entry_t global_tcp_udp_services_table[] = {\n")
267 max_port
= write_entry(out
, e
, max_port
)
270 out
.write("static const ws_services_entry_t global_tcp_services_table[] = {\n")
272 max_port
= write_entry(out
, e
, max_port
)
275 out
.write("static const ws_services_entry_t global_udp_services_table[] = {\n")
277 max_port
= write_entry(out
, e
, max_port
)
280 out
.write("static const ws_services_entry_t global_sctp_services_table[] = {\n")
282 max_port
= write_entry(out
, e
, max_port
)
285 out
.write("static const ws_services_entry_t global_dccp_services_table[] = {\n")
287 max_port
= write_entry(out
, e
, max_port
)
290 out
.write("static const uint16_t _services_max_port = {};\n".format(max_port
))
294 if __name__
== "__main__":
295 sys
.exit(main(sys
.argv
[1:]))