dcerpc-nt: add UNION_ALIGN_TO... helpers
[wireshark-sm.git] / tools / make-iana-ip.py
blobab6a0b414f0d29dd228ddebf8b87fe61a5ac2f24
1 #!/usr/bin/env python3
3 # Wireshark - Network traffic analyzer
4 # By Gerald Combs <gerald@wireshark.org>
5 # Copyright 1998 Gerald Combs
7 # SPDX-License-Identifier: GPL-2.0-or-later
8 '''Update the IANA IP registry file.
10 Make-iana-ip creates a file containing information about IPv4/IPv6 allocation blocks.
11 '''
13 import csv
14 import io
15 import ipaddress
16 import os
17 import re
18 import sys
19 import urllib.request, urllib.error, urllib.parse
21 def exit_msg(msg=None, status=1):
22 if msg is not None:
23 sys.stderr.write(msg + '\n\n')
24 sys.stderr.write(__doc__ + '\n')
25 sys.exit(status)
27 def open_url(url):
28 '''Open a URL.
29 Returns a tuple containing the body and response dict. The body is a
30 str in Python 3 and bytes in Python 2 in order to be compatible with
31 csv.reader.
32 '''
34 if len(sys.argv) > 1:
35 url_path = os.path.join(sys.argv[1], url[1])
36 url_fd = open(url_path)
37 body = url_fd.read()
38 url_fd.close()
39 else:
40 url_path = '/'.join(url)
42 req_headers = { 'User-Agent': 'Wireshark iana-ip' }
43 try:
44 req = urllib.request.Request(url_path, headers=req_headers)
45 response = urllib.request.urlopen(req)
46 body = response.read().decode('UTF-8', 'replace')
47 except Exception:
48 exit_msg('Error opening ' + url_path)
50 return body
52 class IPv4SpecialBlock(ipaddress.IPv4Network):
53 @staticmethod
54 def ip_get_subnet_mask(bits):
55 masks = (
56 0x00000000,
57 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
58 0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
59 0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
60 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
61 0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
62 0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
63 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
64 0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff)
65 if bits > 32:
66 ValueError("Expected bit mask less or equal to 32")
67 return masks[bits]
69 def __str__(self):
70 addr = self.network_address
71 mask = self.prefixlen
72 line = '{{ .ipv4 = {{ {:#x}, {:#010x} }} }}'.format(addr, self.ip_get_subnet_mask(mask))
73 return line
75 class IPv6SpecialBlock(ipaddress.IPv6Network):
76 @staticmethod
77 def addr_c_array(byte_array):
78 if len(byte_array) != 16:
79 raise ValueError("Expected byte array of length 16")
80 c_array = ", ".join(f"0x{byte:02x}" for byte in byte_array)
81 return f"{{ {c_array} }}"
83 def __str__(self):
84 addr = self.network_address.packed
85 mask = self.prefixlen
86 line = '{{ .ipv6 = {{ {}, {} }} }}'.format(self.addr_c_array(addr), mask)
87 return line
89 class IPRegistry(list):
90 @staticmethod
91 def true_or_false(val):
92 if val == 'True':
93 return '1'
94 elif val == 'False':
95 return '0'
96 else:
97 return '-1'
99 def append(self, row):
100 ip, name, _, _, termin_date, source, destination, forward, glob, reserved = row
101 if termin_date[0].isdigit():
102 # skip allocations that have expired
103 return
104 name = re.sub(r'\[.*\]', '', name)
105 name = '"' + name.replace('"', '\\"') + '"'
106 source = self.true_or_false(source)
107 destination = self.true_or_false(destination)
108 forward = self.true_or_false(forward)
109 glob = self.true_or_false(glob)
110 reserved = self.true_or_false(reserved)
111 super().append([ip, name, source, destination, forward, glob, reserved])
113 class IPv4Registry(IPRegistry):
114 @staticmethod
115 def ipv4_addr_and_mask(s):
116 ip = IPv4SpecialBlock(s)
117 return ip
119 def append(self, row):
120 # some lines contain multiple (comma separated) blocks
121 ip_list = row[0].split(',')
122 for s in ip_list:
123 # remove annotations like "1.1.1.1 [2]"
124 ip_str = s.split()[0]
125 row = [self.ipv4_addr_and_mask(ip_str)] + row[1:]
126 super().append(row)
128 def fill(self):
129 self.sort()
130 fill_data = '_U_ static const struct ws_iana_ip_special_block __ipv4_special_block[] = {\n'
131 for row in self:
132 line = ' {{ 4, {}, {}, {}, {}, {}, {}, {} }},\n'.format(*row)
133 fill_data += line
134 fill_data += '};\n'
135 return fill_data
137 class IPv6Registry(IPRegistry):
138 @staticmethod
139 def ipv6_addr_and_mask(s):
140 ip_str = s.split()[0]
141 ip = IPv6SpecialBlock(ip_str)
142 return ip
144 def append(self, row):
145 # remove annotations like "1.1.1.1 [2]"
146 ip_str = row[0].split()[0]
147 row = [self.ipv6_addr_and_mask(ip_str)] + row[1:]
148 super().append(row)
150 def fill(self):
151 self.sort()
152 fill_data = '// GCC bug?\n'
153 fill_data += 'DIAG_OFF(missing-braces)\n'
154 fill_data += '_U_ static const struct ws_iana_ip_special_block __ipv6_special_block[] = {\n'
155 for row in self:
156 line = \
157 ''' {{ 6, {},
158 {}, {}, {}, {}, {}, {} }},\n'''.format(*row)
159 fill_data += line
160 fill_data += '};\n'
161 fill_data += 'DIAG_ON(missing-braces)\n'
162 return fill_data
164 IANA_URLS = {
165 'IPv4': { 'url': ["https://www.iana.org/assignments/iana-ipv4-special-registry", "iana-ipv4-special-registry-1.csv"], 'min_entries': 2 },
166 'IPv6': { 'url': ["https://www.iana.org/assignments/iana-ipv6-special-registry", "iana-ipv6-special-registry-1.csv"], 'min_entries': 2 },
169 def dump_registry(db, reg):
170 db_url = IANA_URLS[db]['url']
171 print('Loading {} data from {}'.format(db, db_url))
172 body = open_url(db_url)
173 iana_csv = csv.reader(body.splitlines())
175 # Pop the title row.
176 next(iana_csv)
177 for iana_row in iana_csv:
178 # Address Block,Name,RFC,Allocation Date,Termination Date,Source,Destination,Forwardable,Globally Reachable,Reserved-by-Protocol
179 # ::1/128,Loopback Address,[RFC4291],2006-02,N/A,False,False,False,False,True
180 reg.append(iana_row)
182 if len(reg) < IANA_URLS[db]['min_entries']:
183 exit_msg("Too few {} entries. Got {}, wanted {}".format(db, len(reg), IANA_URLS[db]['min_entries']))
185 return reg.fill()
187 def main():
188 iana_path = os.path.join(os.path.dirname(__file__), '..', 'epan', 'iana-ip-data.c')
190 iana_data = '''\
192 * This file was generated by running ./tools/make-iana-ip.py.
194 * SPDX-License-Identifier: GPL-2.0-or-later
197 #include "iana-ip.h"
201 iana_data += dump_registry('IPv4', IPv4Registry())
202 iana_data += '\n'
203 iana_data += dump_registry('IPv6', IPv6Registry())
205 try:
206 with io.open(iana_path, 'w', encoding='UTF-8') as iana_f:
207 iana_f.write(iana_data)
208 except:
209 exit_msg("Couldn't open \"{}\" file for writing".format(iana_path))
211 if __name__ == '__main__':
212 main()