├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── Makefile ├── README ├── TODO ├── block_finder ├── __init__.py ├── blockfinder.py └── test.py ├── blockfinder ├── blockfinder.bat ├── embedded_ipaddr ├── COPYING ├── MANIFEST.in ├── OWNERS ├── README ├── __init__.py ├── ipaddr.py ├── ipaddr_test.py ├── setup.py └── test-2to3.sh ├── requirements-py2.txt ├── setup.cfg ├── setup.py ├── test_lir_data.gz └── test_rir_data /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "2.6" 5 | - "2.7" 6 | - "3.3" 7 | - "3.4" 8 | - "3.5" 9 | before_script: 10 | - pip install pep8==1.6.2 11 | script: 12 | - python setup.py test 13 | - pep8 blockfinder block_finder/ setup.py 14 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Changes in version 3.1 - 2009-12-25 2 | o Initial public release 3 | - Published on github 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions 3 | are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | 2. Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 12 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 13 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 14 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 15 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 16 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 17 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 18 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 19 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 20 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXEC =blockfinder 2 | 3 | default: 4 | echo "This is a python program and doesn't compile!" 5 | 6 | install: 7 | test -d $(DESTDIR)/usr/bin/ || mkdir -p $(DESTDIR)/usr/bin/ 8 | cp $(EXEC) $(DESTDIR)/usr/bin/ 9 | 10 | uninstall: 11 | rm $(DESTDIR)/usr/bin/$(EXEC) 12 | 13 | deb-src: 14 | dpkg-buildpackage -S -rfakeroot -us -uc 15 | 16 | deb: 17 | dpkg-buildpackage -rfakeroot -us -uc 18 | 19 | deb-clean: 20 | -rm build 21 | debian/rules clean 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | blockfinder by Jacob Appelbaum 2 | 3 | "All that Blockfinder does is allow you to identify, contact and potentially 4 | hack into every computer network in the world." - Rolling Stone, August 2010 [0] 5 | 6 | Contrary to popular media claims, blockfinder is a simple text based console 7 | tool that returns a list of netblocks for a given country. It does this by 8 | fetching the following lists of allocations: 9 | 10 | https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest 11 | https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest 12 | https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest 13 | https://ftp.apnic.net/stats/apnic/delegated-apnic-latest 14 | https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest 15 | 16 | The list of ISO codes is ISO 3166-1 as found here: 17 | 18 | https://web.archive.org/web/20161122071627if_/http://www.iso.org/iso/home/standards/country_codes/country_names_and_code_elements_txt-temp.htm 19 | http://en.wikipedia.org/wiki/ISO_3166-1 20 | 21 | blockfinder has two methods of operation: 22 | 23 | Update the lists of network object allocations 24 | Returning a list of all netblocks (or asn, etc) for a given country 25 | 26 | To use blockfinder, first create or update the cache: 27 | 28 | blockfinder -i 29 | 30 | Once you have a proper cache, search for the desired resource in the country 31 | of your choice: 32 | 33 | ./blockfinder -v -t mm 34 | 35 | Additionally, to filter results for a specific protocol you can use a selector: 36 | 37 | ./blockfinder -v -t mm:ipv4 38 | 39 | 40 | On Windows (in cmd, PowerShell and 4nt) one may find blockfinder.bat useful: 41 | 42 | blockfinder.bat 43 | 44 | [0] http://www.rollingstone.com/culture/news/17389/192242 45 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | High Priority: 2 | 3 | Fix -m: 4 | Remove Maxmind GeoIP usage; it is broken 5 | Consider using ipfire's database and convert it to geoip format: 6 | https://location.ipfire.org/how-to-use 7 | Add https://location.ipfire.org/databases/1/ 8 | 9 | Add support for LIR blocks by parsing the ripe.db.inetnum.gz. 10 | This lists sub-allocations and assignments made by local LIR/ISPs: 11 | ftp://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz 12 | 13 | Add Custom GeoIP support 14 | If a user wants to supply their own DB; we should allow this 15 | 16 | Add GeoIP database inversion 17 | Optionally combine the results with delegation information to produce more 18 | accurate netblocks 19 | 20 | Add/Fix support for IPv6 21 | ./blockfinder -r 2001:200:dff:fff1:216:3eff:feb1:44d7 22 | 23 | Add lock file to ensure '-i' only runs once 24 | 25 | Add support for BGP related information 26 | Interface with routeviews.org for data 27 | show all ASNs announced by a country (as allocated) 28 | show all blocks announced by ASN 29 | show all blocks announced by a collection of ASNs (country) 30 | we need to parse MRT binary archives from http://archive.routeviews.org/ 31 | maybe with http://code.google.com/p/pybgpdump/ ? 32 | 33 | Add latency GeoIP confirmation test 34 | As an interface with routeviews.org 35 | pick n out of x netblocks total, randomly select an ip in the block, 36 | traceroute, repeat this process until n is reasonably near x, average 37 | the results to find a likely lowest latency into the country. 38 | Given the target ip and the above calculated latency map, compare the 39 | latency - shorest latency is likely the most honest - liars can only 40 | delay packets or drop them. 41 | 42 | Unit Tests! 43 | 44 | Lower Priority: 45 | Fix country code searching code 46 | It appears to be broken for "korea" 47 | Add country-code re-init code and progress meter 48 | When creating or refreshng the cache, we should display progress 49 | Add manpage 50 | Add support for proxies during updating/fetching 51 | Fix country xml download 52 | content-length verify and check for stale cache 53 | Add support to list all country codes with possible results 54 | Add an option to print the whois description(seems the most complete). 55 | Refactor the argument parsing to make it reasonable 56 | Required arguments should be postional, etc 57 | Refactor updating/fetching animation 58 | Add system wide cache_dir to ease blockfinder use on multi user systems 59 | Package for Debian 60 | -------------------------------------------------------------------------------- /block_finder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioerror/blockfinder/3e0f4f0971b63f115e740f57d9298deb5cec0cd8/block_finder/__init__.py -------------------------------------------------------------------------------- /block_finder/blockfinder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # For the people of Smubworld! 5 | import os 6 | import time 7 | import optparse 8 | import sys 9 | import sqlite3 10 | import hashlib 11 | import gzip 12 | import zipfile 13 | import re 14 | import bz2 15 | from math import log 16 | 17 | if sys.version_info[0] >= 3: 18 | from configparser import ConfigParser 19 | import ipaddress as ipaddr 20 | from urllib.request import (urlopen, Request) 21 | from urllib.error import URLError 22 | long = int 23 | else: 24 | from configparser import SafeConfigParser as ConfigParser 25 | from urllib2 import (urlopen, Request, URLError) 26 | try: 27 | from embedded_ipaddr import ipaddr 28 | ipaddr.ip_address = ipaddr.IPAddress 29 | except: 30 | import ipaddress as ipaddr 31 | 32 | is_win32 = (sys.platform == "win32") 33 | 34 | __program__ = 'blockfinder' 35 | __url__ = 'https://github.com/ioerror/blockfinder/' 36 | __author__ = 'Jacob Appelbaum , David ' 37 | __copyright__ = 'Copyright (c) 2010' 38 | __license__ = 'See LICENSE for licensing information' 39 | __version__ = '4.0.1' 40 | 41 | try: 42 | from future import antigravity 43 | except ImportError: 44 | antigravity = None 45 | 46 | 47 | class DatabaseCache(object): 48 | 49 | def __init__(self, cache_dir, verbose=False): 50 | self.cache_dir = cache_dir 51 | self.verbose = verbose 52 | self.cursor = None 53 | self.conn = None 54 | self.db_version = "0.0.4" 55 | self.db_path = os.path.join(self.cache_dir + "sqlitedb") 56 | 57 | def erase_database(self): 58 | """ Erase the database file. """ 59 | if os.path.exists(self.db_path): 60 | os.remove(self.db_path) 61 | 62 | def connect_to_database(self): 63 | """ Connect to the database cache, possibly after creating it if 64 | it doesn't exist yet, or after making sure an existing 65 | database cache has the correct version. Return True if a 66 | connection could be established, False otherwise. """ 67 | if not os.path.exists(self.cache_dir): 68 | if self.verbose: 69 | print("Initializing the cache directory...") 70 | os.mkdir(self.cache_dir) 71 | if os.path.exists(self.db_path): 72 | cache_version = self.get_db_version() 73 | if not cache_version: 74 | cache_version = "0.0.1" 75 | if cache_version != self.db_version: 76 | print(("The existing database cache uses version %s, " 77 | "not the expected %s." % (cache_version, 78 | self.db_version))) 79 | return False 80 | self.conn = sqlite3.connect(self.db_path) 81 | self.cursor = self.conn.cursor() 82 | self.create_assignments_table() 83 | self.create_asn_description_table() 84 | self.create_asn_assignments_table() 85 | return True 86 | 87 | def __get_default_config_file_obj(self): 88 | open_flags = 'r+' 89 | file_path = os.path.join(self.cache_dir, 'db.cfg') 90 | if not os.path.exists(file_path): 91 | open_flags = 'w+' 92 | return open(file_path, open_flags) 93 | 94 | def _get_db_config(self, file_obj=None): 95 | """ Return the database configuration object from the provided 96 | file_obj if provided, otherwise from the default database 97 | configuration file. """ 98 | if file_obj is None: 99 | file_obj = self.__get_default_config_file_obj() 100 | config = ConfigParser() 101 | if sys.version_info[0] >= 3: 102 | config.read_file(file_obj) 103 | else: 104 | config.readfp(file_obj) 105 | file_obj.close() 106 | return config 107 | 108 | def set_db_version(self, file_obj=None): 109 | """ Set the database version string in the config file. """ 110 | if file_obj is None: 111 | file_obj = self.__get_default_config_file_obj() 112 | config = self._get_db_config() 113 | if not config.has_section('db'): 114 | config.add_section('db') 115 | config.set('db', 'version', self.db_version) 116 | config.write(file_obj) 117 | file_obj.close() 118 | 119 | def get_db_version(self): 120 | """ Read and return the database version string from the config 121 | file. """ 122 | config = self._get_db_config() 123 | if not config.has_section('db'): 124 | return None 125 | return config.get('db', 'version') 126 | 127 | def commit_and_close_database(self): 128 | self.conn.commit() 129 | self.cursor.close() 130 | 131 | def create_assignments_table(self): 132 | """ Create the assignments table that stores all assignments from 133 | IPv4/IPv6/ASN to country code. Blocks are stored as first hex 134 | of and first hex after the assignment. Numbers are stored 135 | as hex strings, because SQLite's INTEGER type only holds up to 136 | 63 unsigned bits, which is not enough to store a /64 IPv6 137 | block. Hex strings have leading zeros, with IPv6 addresses 138 | being 33 hex characters long and IPv4 addresses and ASN being 139 | 9 hex characters long. The first number after an assignment 140 | range is stored instead of the last number in the range to 141 | facilitate comparisons with neighboring ranges. """ 142 | sql = ('CREATE TABLE IF NOT EXISTS assignments(start_hex TEXT, ' 143 | 'next_start_hex TEXT, num_type TEXT, country_code TEXT, ' 144 | 'source_type TEXT, source_name TEXT)') 145 | self.cursor.execute(sql) 146 | self.conn.commit() 147 | 148 | def create_asn_description_table(self): 149 | """ Create the assignments table that stores all the descriptions 150 | associated with ASNs. """ 151 | sql = ('CREATE TABLE IF NOT EXISTS asn_descriptions(as_num INT, ' 152 | 'source_name TEXT, description TEXT)') 153 | self.cursor.execute(sql) 154 | sql = ('CREATE INDEX IF NOT EXISTS DescriptionsByASN ON ' 155 | 'asn_descriptions ( as_num )') 156 | self.cursor.execute(sql) 157 | self.conn.commit() 158 | 159 | def create_asn_assignments_table(self): 160 | """ Create the assignments table that stores the assignments from 161 | IPv4 to ASN """ 162 | # XXX: IPv6 not yet supported. (Not available from routeviews?) 163 | sql = ('CREATE TABLE IF NOT EXISTS asn_assignments(start_hex TEXT, ' 164 | 'next_start_hex TEXT, num_type TEXT, as_num INT, ' 165 | 'source_type TEXT, source_name TEXT, PRIMARY KEY(start_hex, ' 166 | 'next_start_hex))') 167 | self.cursor.execute(sql) 168 | sql = ('CREATE INDEX IF NOT EXISTS ASNEntriesByStartHex on ' 169 | 'asn_assignments ( start_hex )') 170 | self.cursor.execute(sql) 171 | self.conn.commit() 172 | 173 | def delete_assignments(self, source_type): 174 | """ Delete all assignments from the database cache matching a 175 | given source type ("rir", "lir", etc.). """ 176 | sql = 'DELETE FROM assignments WHERE source_type = ?' 177 | self.cursor.execute(sql, (source_type, )) 178 | self.conn.commit() 179 | 180 | def delete_asn_descriptions(self): 181 | """ Delete all asn descriptions from the database cache. """ 182 | sql = 'DELETE FROM asn_descriptions' 183 | self.cursor.execute(sql) 184 | self.conn.commit() 185 | 186 | def delete_asn_assignments(self): 187 | """ Delete all the bgp netblock to as entries """ 188 | sql = 'DELETE FROM asn_assignments' 189 | self.cursor.execute(sql) 190 | self.conn.commit() 191 | 192 | def insert_assignment(self, start_num, end_num, num_type, 193 | country_code, source_type, source_name): 194 | """ Insert an assignment into the database cache, without 195 | committing after the insertion. """ 196 | sql = ('INSERT INTO assignments (start_hex, next_start_hex, ' 197 | 'num_type, country_code, source_type, source_name) ' 198 | 'VALUES (?, ?, ?, ?, ?, ?)') 199 | if num_type == 'ipv6': 200 | start_hex = '%033x' % start_num 201 | next_start_hex = '%033x' % (end_num + 1) 202 | else: 203 | start_hex = '%09x' % start_num 204 | next_start_hex = '%09x' % (end_num + 1) 205 | country_code = normalize_country_code(country_code) 206 | self.cursor.execute(sql, (start_hex, next_start_hex, num_type, 207 | country_code, source_type, source_name)) 208 | 209 | def insert_asn_description(self, asn, source_name, description): 210 | sql = ('INSERT INTO asn_descriptions ' 211 | '(as_num, source_name, description) ' 212 | 'VALUES (?, ?, ?)') 213 | self.cursor.execute(sql, (asn, source_name, str(description))) 214 | 215 | def insert_asn_assignment(self, start_num, end_num, num_type, asn, 216 | source_type, source_name): 217 | # XXX: This is sqlite specific syntax 218 | sql = ('INSERT OR IGNORE INTO asn_assignments (start_hex, ' 219 | 'next_start_hex, num_type, as_num, source_type, source_name) ' 220 | 'VALUES (?, ?, ?, ?, ?, ?)') 221 | if num_type == 'ipv6': 222 | start_hex = '%033x' % start_num 223 | next_start_hex = '%033x' % (end_num + 1) 224 | else: 225 | start_hex = '%09x' % start_num 226 | next_start_hex = '%09x' % (end_num + 1) 227 | self.cursor.execute(sql, (start_hex, next_start_hex, num_type, asn, 228 | source_type, source_name)) 229 | 230 | def commit_changes(self): 231 | """ Commit changes, e.g., after inserting assignments into the 232 | database cache. """ 233 | self.conn.commit() 234 | 235 | def fetch_assignments(self, num_type, country_code): 236 | """ Fetch all assignments from the database cache matching the 237 | given number type ("asn", "ipv4", or "ipv6") and country code. 238 | The result is a sorted list of tuples containing (start_num, 239 | end_num). """ 240 | sql = ('SELECT start_hex, next_start_hex FROM assignments ' 241 | 'WHERE num_type = ? AND country_code = ? ' 242 | 'ORDER BY start_hex') 243 | self.cursor.execute(sql, (num_type, country_code)) 244 | result = [] 245 | for row in self.cursor: 246 | result.append((int(row[0], 16), int(row[1], 16) - 1)) 247 | return result 248 | 249 | def fetch_country_code(self, num_type, source_type, lookup_num): 250 | """ Fetch the country code from the database cache that is 251 | assigned to the given number (e.g., IPv4 address in decimal 252 | notation), number type (e.g., "ipv4"), and source type (e.g., 253 | "rir"). """ 254 | sql = ('SELECT country_code FROM assignments WHERE num_type = ? ' 255 | 'AND source_type = ? AND start_hex <= ? ' 256 | 'AND next_start_hex > ?') 257 | if num_type == 'ipv6': 258 | lookup_hex = '%033x' % int(lookup_num) 259 | else: 260 | lookup_hex = '%09x' % int(lookup_num) 261 | self.cursor.execute(sql, (num_type, source_type, lookup_hex, 262 | lookup_hex)) 263 | row = self.cursor.fetchone() 264 | if row: 265 | return row[0] 266 | 267 | def fetch_country_blocks_in_other_sources(self, first_country_code): 268 | """ Fetch all assignments matching the given country code, then look 269 | up to which country code(s) the same number ranges are assigned in 270 | other source types. Return 8-tuples containing (1) first source 271 | type, (2) first and (3) last number of the assignment in the first 272 | source type, (4) second source type, (5) first and (6) last number 273 | of the assignment in the second source type, (7) country code in 274 | the second source type, and (8) number type. """ 275 | sql = ('SELECT first.source_type, first.start_hex, ' 276 | 'first.next_start_hex, second.source_type, ' 277 | 'second.start_hex, second.next_start_hex, ' 278 | 'second.country_code, first.num_type ' 279 | 'FROM assignments AS first ' 280 | 'JOIN assignments AS second ' 281 | 'WHERE first.country_code = ? ' 282 | 'AND first.start_hex <= second.next_start_hex ' 283 | 'AND first.next_start_hex >= second.start_hex ' 284 | 'AND first.num_type = second.num_type ' 285 | 'ORDER BY first.source_type, first.start_hex, ' 286 | 'second.source_type, second.start_hex') 287 | self.cursor.execute(sql, (first_country_code, )) 288 | result = [] 289 | for row in self.cursor: 290 | result.append((str(row[0]), int(row[1], 16), 291 | int(row[2], 16) - 1, str(row[3]), int(row[4], 16), 292 | int(row[5], 16) - 1, str(row[6]), str(row[7]))) 293 | return result 294 | 295 | def fetch_org_by_ip_address(self, lookup_str, num_type): 296 | if num_type == 'ipv4': 297 | lookup_hex = '%09x' % int(int(lookup_str)) 298 | else: 299 | lookup_hex = '%033x' % int(int(lookup_str)) 300 | sql = ('SELECT asn_descriptions.as_num, asn_descriptions.description, ' 301 | 'asn_assignments.start_hex, asn_assignments.next_start_hex ' 302 | 'FROM asn_descriptions JOIN asn_assignments ON ' 303 | 'asn_assignments.as_num = asn_descriptions.as_num ' 304 | 'WHERE num_type = ? AND start_hex <= ? AND next_start_hex > ?') 305 | self.cursor.execute(sql, (num_type, lookup_hex, lookup_hex)) 306 | row = self.cursor.fetchall() 307 | if row: 308 | return row 309 | 310 | def fetch_org_by_ip_range(self, lookup_start, lookup_end, num_type): 311 | if num_type == 'ipv4': 312 | lookup_start_hex = '%09x' % int(int(lookup_start)) 313 | lookup_end_hex = '%09x' % int(int(lookup_end)) 314 | else: 315 | lookup_start_hex = '%033x' % int(int(lookup_start)) 316 | lookup_end_hex = '%033x' % int(int(lookup_end)) 317 | 318 | sql = ('SELECT asn_descriptions.as_num, asn_descriptions.description, ' 319 | 'asn_assignments.start_hex, asn_assignments.next_start_hex ' 320 | 'FROM asn_descriptions JOIN asn_assignments ON ' 321 | 'asn_assignments.as_num = asn_descriptions.as_num ' 322 | 'WHERE num_type = ? AND start_hex >= ? AND next_start_hex <= ?') 323 | self.cursor.execute(sql, (num_type, lookup_start_hex, lookup_end_hex)) 324 | row = self.cursor.fetchall() 325 | if row: 326 | return row 327 | 328 | def _concatenate_and_write( 329 | self, records, write_function=None, record_filter=None, bits=32): 330 | netblocks = [] 331 | for row in records: 332 | try: 333 | start_hex, next_start_hex, record = \ 334 | int(row[0], 16), int(row[1], 16), str(row[2]) 335 | nb = bits - int(log(next_start_hex - start_hex, 2)) 336 | net = ipaddr.IPNetwork("%s/%d" % 337 | (ipaddr.IPAddress(start_hex), nb)) 338 | if callable(record_filter): 339 | record = record_filter(record) 340 | except ValueError: 341 | continue 342 | 343 | # Concatenate adjacent blocks of the same country 344 | if netblocks and netblocks[-1][1] == record: 345 | pn = netblocks[-1][0] 346 | nb = bits - int(log(int(net.network) + 347 | int(net.numhosts) - int(pn.network), 2)) 348 | netblocks[-1] = (ipaddr.IPNetwork("%s/%d" % 349 | (pn.network, nb)), record) 350 | 351 | # if the adjacent blocks aren't the same country, 352 | # write the last block out to csv and add the new block 353 | # to the list for possible concatenation 354 | elif netblocks: 355 | prev_n, prev_record = netblocks.pop() 356 | if write_function: 357 | write_function(prev_n, prev_record) 358 | netblocks.append((net, record)) 359 | 360 | # this is the base case 361 | else: 362 | netblocks.append((net, record)) 363 | 364 | def export_asn(self, filename, num_type): 365 | """ Export assignments to the CSV format used to build the 366 | geoip-database asn lookup 367 | """ 368 | sql = ('SELECT start_hex, next_start_hex, as_num ' 369 | 'FROM asn_assignments WHERE num_type = ? ORDER BY start_hex') 370 | self.cursor.execute(sql, (num_type,)) 371 | try: 372 | f = open(filename, 'w') 373 | except IOError: 374 | print(("Unable to open %s" % filename)) 375 | return 376 | 377 | def write_csv_line(network, asn): 378 | # XXX: wild guess 379 | f.write(""""%s","%s","%d","%d","%s"\n""" % (network.network, 380 | network.broadcast, 381 | int(network.network), 382 | int(network.broadcast), 383 | asn)) 384 | if num_type == 'ipv6': 385 | ip_bits = 128 386 | elif num_type == 'ipv4': 387 | ip_bits = 32 388 | else: 389 | return 390 | 391 | self._concatenate_and_write(self.cursor, write_function=write_csv_line, 392 | bits=ip_bits) 393 | f.close() 394 | 395 | def export_geoip(self, lookup, filename, num_type): 396 | """ Export assignments to the CSV format used to build the 397 | geoip-database package """ 398 | 399 | sql = ('SELECT start_hex, next_start_hex, country_code ' 400 | 'FROM assignments WHERE num_type = ? ORDER BY start_hex') 401 | self.cursor.execute(sql, (num_type,)) 402 | 403 | try: 404 | f = open(filename, 'w') 405 | except IOError: 406 | print(("Unable to open %s" % filename)) 407 | return 408 | 409 | def write_csv_line(network, country_code): 410 | country_name = lookup.get_name_from_country_code(country_code) 411 | if country_name: 412 | country_name = country_name.split( 413 | "#")[0].strip() # Drop comments 414 | f.write(""""%s","%s","%d","%d","%s","%s"\n""" % ( 415 | network.network, 416 | network.broadcast, 417 | int(network.network), 418 | int(network.broadcast), 419 | country_code, 420 | country_name)) 421 | 422 | if num_type == 'ipv6': 423 | ip_bits = 128 424 | elif num_type == 'ipv4': 425 | ip_bits = 32 426 | else: 427 | return 428 | 429 | self._concatenate_and_write(self.cursor, write_function=write_csv_line, 430 | record_filter=str.upper, bits=ip_bits) 431 | f.close() 432 | 433 | 434 | class DownloaderParser(object): 435 | 436 | def __init__(self, cache_dir, database_cache, user_agent, 437 | verbose=False): 438 | self.cache_dir = cache_dir 439 | self.database_cache = database_cache 440 | self.user_agent = user_agent 441 | self.verbose = verbose 442 | 443 | MAXMIND_URLS = """ 444 | http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip 445 | http://geolite.maxmind.com/download/geoip/database/GeoIPv6.csv.gz 446 | """ 447 | 448 | RIR_URLS = """ 449 | https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest 450 | https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest 451 | https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest 452 | https://ftp.apnic.net/stats/apnic/delegated-apnic-latest 453 | https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest 454 | """ 455 | 456 | LIR_URLS = """ 457 | https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz 458 | https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz 459 | """ 460 | 461 | COUNTRY_CODE_URL = ("https://web.archive.org/web/20161122071627if_/" 462 | "http://www.iso.org:80/iso/home/standards/country_codes/" 463 | "country_names_and_code_elements_txt-temp.htm") 464 | 465 | ASN_DESCRIPTION_URL = "https://www.cidr-report.org/as2.0/autnums.html" 466 | 467 | ASN_ASSIGNMENT_URLS = [ 468 | ('https://archive.routeviews.org/oix-route-views/' 469 | 'oix-full-snapshot-latest.dat.bz2'), 470 | ] 471 | 472 | def download_maxmind_files(self): 473 | """ Download all LIR delegation urls. """ 474 | for maxmind_url in self.MAXMIND_URLS.split(): 475 | self._download_to_cache_dir(maxmind_url) 476 | 477 | def download_rir_files(self): 478 | """ Download all RIR delegation files including md5 checksum. """ 479 | for rir_url in self.RIR_URLS.split(): 480 | rir_md5_url = rir_url + '.md5' 481 | self._download_to_cache_dir(rir_url) 482 | self._download_to_cache_dir(rir_md5_url) 483 | 484 | def download_lir_files(self): 485 | """ Download all LIR delegation urls. """ 486 | for lir_url in self.LIR_URLS.split(): 487 | self._download_to_cache_dir(lir_url) 488 | 489 | def download_country_code_file(self): 490 | """ Download and save the latest semicolon-separated open country 491 | codes file. """ 492 | self._download_to_cache_dir(self.COUNTRY_CODE_URL) 493 | 494 | def download_asn_description_file(self): 495 | """ Download and save the latest ASN to Name report from 496 | cidr-report.org""" 497 | self._download_to_cache_dir(self.ASN_DESCRIPTION_URL) 498 | 499 | def download_asn_assignment_files(self): 500 | """ Download and save the latest routing snapshots. """ 501 | for assignment_url in self.ASN_ASSIGNMENT_URLS: 502 | self._download_to_cache_dir(assignment_url) 503 | 504 | def _download_to_cache_dir(self, url): 505 | """ Fetch a resource (with progress bar) and store contents to the 506 | local cache directory under the file name given in the URL. """ 507 | if not os.path.exists(self.cache_dir): 508 | if self.verbose: 509 | print("Initializing the cache directory...") 510 | os.mkdir(self.cache_dir) 511 | filename = url.split('/')[-1] 512 | if self.verbose: 513 | print(url) 514 | req = Request(url) 515 | if self.user_agent: 516 | req.add_header('User-Agent', self.user_agent) 517 | # TODO Allow use of a proxy. 518 | # req.set_proxy(host, type) 519 | try: 520 | fetcher = urlopen(req) 521 | except URLError as err: 522 | msg = "An error occurred while attempting to cache file from:" 523 | print(("%s\n\t%s\n\t%s" % (msg, url, str(err)))) 524 | return 525 | length_header = fetcher.headers.get("Content-Length") 526 | expected_bytes = -1 527 | if length_header: 528 | expected_bytes = int(length_header) 529 | print(("Fetching %d kilobytes" % 530 | round(float(expected_bytes / 1024), 2))) 531 | download_started = time.time() 532 | output_file = open(os.path.join(self.cache_dir, filename), "wb") 533 | received_bytes, seconds_elapsed = 0, 0 534 | while True: 535 | seconds_elapsed = time.time() - download_started 536 | if expected_bytes >= 0: 537 | self._update_progress_bar(received_bytes, expected_bytes, 538 | seconds_elapsed) 539 | chunk = fetcher.read(1024) 540 | if len(chunk) == 0: 541 | if expected_bytes >= 0 and received_bytes != expected_bytes: 542 | print(("Expected %s bytes, only received %s" % 543 | (expected_bytes, received_bytes))) 544 | print("") 545 | break 546 | received_bytes += len(chunk) 547 | output_file.write(chunk) 548 | output_file.close() 549 | 550 | def _update_progress_bar(self, received_bytes, expected_bytes, 551 | seconds_elapsed): 552 | """ Write a progress bar to the console. """ 553 | if is_win32: 554 | rows = 100 # use some WinCon function for these? 555 | columns = 80 # but not really important. 556 | EOL = "\r" 557 | else: 558 | rows, columns = list(map(int, os.popen('stty size', 'r' 559 | ).read().split())) 560 | EOL = "\x1b[G" 561 | if seconds_elapsed == 0: 562 | seconds_elapsed = 1 563 | percent_done = float(received_bytes) / float(expected_bytes) 564 | caption = "%.2f K/s" % (received_bytes / 1024 / seconds_elapsed) 565 | width = columns - 4 - len(caption) 566 | sys.stdout.write("[%s>%s] %s%s" % ( 567 | "=" * int(percent_done * width), 568 | "." * (width - int(percent_done * width)), caption, EOL)) 569 | sys.stdout.flush() 570 | 571 | def check_rir_file_mtimes(self): 572 | """ Return True if the mtime of any RIR file in our cache directory 573 | is > 24 hours, False otherwise. """ 574 | if not os.path.exists(self.cache_dir): 575 | return False 576 | for rir_url in self.RIR_URLS.split(): 577 | rir_path = os.path.join(self.cache_dir, 578 | rir_url.split('/')[-1]) 579 | if os.path.exists(rir_path): 580 | rir_stat = os.stat(rir_path) 581 | if (time.time() - rir_stat.st_mtime) > 86400: 582 | return True 583 | return False 584 | 585 | def verify_rir_files(self): 586 | """ Compute md5 checksums of all RIR files, compare them to the 587 | provided .md5 files, and return True if the two checksums match, 588 | or False otherwise. """ 589 | for rir_url in self.RIR_URLS.split(): 590 | rir_path = os.path.join(self.cache_dir, 591 | rir_url.split('/')[-1]) 592 | rir_md5_path = os.path.join(self.cache_dir, 593 | rir_url.split('/')[-1] + '.md5') 594 | if not os.path.exists(rir_md5_path) or \ 595 | not os.path.exists(rir_path): 596 | continue 597 | rir_md5_file = open(rir_md5_path, 'r') 598 | expected_checksum = rir_md5_file.read() 599 | rir_md5_file.close() 600 | if "=" in expected_checksum: 601 | expected_checksum = expected_checksum.split("=")[-1].strip() 602 | elif expected_checksum == "": 603 | if self.verbose: 604 | print("No checksum... skipping verification...") 605 | continue 606 | else: 607 | regex = re.compile("[a-f0-9]{32}") 608 | regres = regex.findall(expected_checksum) 609 | if len(regres) > 1: 610 | print("Error: mutiple checksum found") 611 | elif len(regres) < 1: 612 | print("Error: no checksum found") 613 | else: 614 | expected_checksum = regres[0] 615 | computed_checksum = "" 616 | rir_file = open(rir_path, 'rb') 617 | rir_data = rir_file.read() 618 | rir_file.close() 619 | computed_checksum = str(hashlib.md5(rir_data).hexdigest()) 620 | if expected_checksum != computed_checksum: 621 | print(("The computed md5 checksum of %s, %s, does *not* " 622 | "match the provided checksum %s!" % 623 | (rir_path, computed_checksum, expected_checksum))) 624 | 625 | def parse_maxmind_files(self, maxmind_urls=None): 626 | """ Parse locally cached MaxMind files and insert assignments to the 627 | local database cache, overwriting any existing MaxMind 628 | assignments. """ 629 | if not maxmind_urls: 630 | maxmind_urls = self.MAXMIND_URLS.split() 631 | self.database_cache.delete_assignments('maxmind') 632 | for maxmind_url in maxmind_urls: 633 | maxmind_path = os.path.join(self.cache_dir, 634 | maxmind_url.split('/')[-1]) 635 | if not os.path.exists(maxmind_path): 636 | print(("Unable to find %s." % maxmind_path)) 637 | continue 638 | if maxmind_path.endswith('.zip'): 639 | maxmind_zip_path = zipfile.ZipFile(maxmind_path) 640 | for contained_filename in maxmind_zip_path.namelist(): 641 | content = maxmind_zip_path.read(contained_filename) 642 | self._parse_maxmind_content(content, 'maxmind', 643 | 'maxmind') 644 | maxmind_zip_path.close() 645 | elif maxmind_path.endswith('.gz'): 646 | gzip_file = gzip.open(maxmind_path) 647 | content = gzip_file.read() 648 | self._parse_maxmind_content(content, 'maxmind', 'maxmind') 649 | gzip_file.close() 650 | self.database_cache.commit_changes() 651 | 652 | def import_maxmind_file(self, maxmind_path): 653 | self.database_cache.delete_assignments(maxmind_path) 654 | if not os.path.exists(maxmind_path): 655 | print(("Unable to find %s." % maxmind_path)) 656 | return 657 | with open(maxmind_path, 'r') as f: 658 | content = f.read() 659 | self._parse_maxmind_content(content, maxmind_path, maxmind_path) 660 | self.database_cache.commit_changes() 661 | 662 | def _parse_maxmind_content(self, content, source_type, source_name): 663 | keys = ['start_str', 'end_str', 'start_num', 'end_num', 664 | 'country_code', 'country_name'] 665 | for line in content.decode('utf-8').split('\n'): 666 | if len(line.strip()) == 0 or line.startswith("#"): 667 | continue 668 | line = line.replace('"', '').replace(' ', '').strip() 669 | parts = line.split(',') 670 | entry = dict((k, v) for k, v in zip(keys, parts)) 671 | start_num = int(entry['start_num']) 672 | end_num = int(entry['end_num']) 673 | country_code = str(entry['country_code']) 674 | start_ipaddr = ipaddr.ip_address(entry['start_str']) 675 | if isinstance(start_ipaddr, ipaddr.IPv4Address): 676 | num_type = 'ipv4' 677 | else: 678 | num_type = 'ipv6' 679 | self.database_cache.insert_assignment( 680 | start_num, end_num, 681 | num_type, 682 | country_code, 683 | source_type, 684 | source_name) 685 | 686 | def parse_rir_files(self, rir_urls=None): 687 | """ Parse locally cached RIR files and insert assignments to the local 688 | database cache, overwriting any existing RIR assignments. """ 689 | if not rir_urls: 690 | rir_urls = self.RIR_URLS.split() 691 | self.database_cache.delete_assignments('rir') 692 | keys = "registry country_code type start value date status" 693 | for rir_url in rir_urls: 694 | rir_path = os.path.join(self.cache_dir, 695 | rir_url.split('/')[-1]) 696 | if not os.path.exists(rir_path): 697 | print(("Unable to find %s." % rir_path)) 698 | continue 699 | rir_file = open(rir_path, 'r') 700 | for line in rir_file: 701 | if line.startswith("#"): 702 | continue 703 | entry = dict((k, v) for k, v in 704 | zip(keys.split(), line.strip().split("|"))) 705 | source_name = str(entry['registry']) 706 | country_code = str(entry['country_code']) 707 | if source_name.replace( 708 | ".", "", 1).isdigit() or country_code == "*": 709 | continue 710 | num_type = entry['type'] 711 | if num_type == 'asn': 712 | start_num = end_num = int(entry['start']) 713 | elif num_type == 'ipv4': 714 | start_num = int(ipaddr.IPv4Address(entry['start'])) 715 | end_num = start_num + int(entry['value']) - 1 716 | elif num_type == 'ipv6': 717 | network_str = entry['start'] + '/' + entry['value'] 718 | network_ipaddr = ipaddr.IPv6Network(network_str) 719 | start_num = int(network_ipaddr.network_address) 720 | end_num = int(network_ipaddr.broadcast_address) 721 | self.database_cache.insert_assignment( 722 | start_num, 723 | end_num, 724 | num_type, 725 | country_code, 726 | 'rir', 727 | source_name) 728 | rir_file.close() 729 | self.database_cache.commit_changes() 730 | 731 | def parse_lir_files(self, lir_urls=None): 732 | """ Parse locally cached LIR files and insert assignments to the local 733 | database cache, overwriting any existing LIR assignments. """ 734 | if not lir_urls: 735 | lir_urls = self.LIR_URLS.split() 736 | self.database_cache.delete_assignments('lir') 737 | for lir_url in lir_urls: 738 | lir_path = os.path.join(self.cache_dir, 739 | lir_url.split('/')[-1]) 740 | if not os.path.exists(lir_path): 741 | print(("Unable to find %s." % lir_path)) 742 | continue 743 | if lir_path.endswith('.gz'): 744 | lir_file = gzip.open(lir_path) 745 | else: 746 | lir_file = open(lir_path) 747 | start_num = 0 748 | end_num = 0 749 | country_code = "" 750 | entry = False 751 | num_type = "" 752 | for line in lir_file: 753 | line = line.decode('utf-8', 'ignore').replace("\n", "") 754 | if line == "": 755 | entry = False 756 | start_num, end_num, country_code, num_type = 0, 0, "", "" 757 | elif not entry and "inetnum:" in line: 758 | try: 759 | line = line.replace("inetnum:", "").strip() 760 | start_str = line.split("-")[0].strip() 761 | end_str = line.split("-")[1].strip() 762 | start_num = int(ipaddr.IPv4Address(start_str)) 763 | end_num = int(ipaddr.IPv4Address(end_str)) 764 | entry = True 765 | num_type = 'ipv4' 766 | except Exception as e: 767 | if self.verbose: 768 | print((repr(e), line)) 769 | elif not entry and "inet6num:" in line: 770 | try: 771 | network_str = line.replace("inet6num:", "").strip() 772 | network_ipaddr = ipaddr.IPv6Network(network_str) 773 | start_num = int(network_ipaddr.network_address) 774 | end_num = int(network_ipaddr.broadcast_address) 775 | entry = True 776 | num_type = 'ipv6' 777 | except Exception as e: 778 | if self.verbose: 779 | print((repr(e), line)) 780 | elif entry and "country:" in line: 781 | country_code = line.replace("country:", "").strip() 782 | self.database_cache.insert_assignment( 783 | start_num, 784 | end_num, 785 | num_type, 786 | country_code, 787 | 'lir', 788 | 'ripencc') 789 | lir_file.close() 790 | self.database_cache.commit_changes() 791 | 792 | def parse_asn_description_file(self, asn_description_url=None): 793 | """ Parse locally cached ASN to Description mappings and insert 794 | mappings to the local database cache, overwriting any existing ASN 795 | to Name assignments. """ 796 | if not asn_description_url: 797 | asn_description_url = self.ASN_DESCRIPTION_URL 798 | self.database_cache.delete_asn_descriptions() 799 | asn_description_path = os.path.join(self.cache_dir, 800 | asn_description_url.split('/')[-1]) 801 | asn_descriptions = open(asn_description_path) 802 | source_name = 'cidr_report' 803 | skiplen = len(' 0: 892 | return country_name[0][0] 893 | 894 | def get_country_code_from_name(self, country_name): 895 | """ Return the country code for a given country name. """ 896 | if not self.knows_country_names(): 897 | return 898 | cc_code = [self.map_co[key] for key in list(self.map_co.keys()) if 899 | key.upper().startswith(country_name.upper())] 900 | if len(cc_code) > 0: 901 | return cc_code[0] 902 | 903 | def lookup_ipv6_address(self, lookup_ipaddr): 904 | print(("Reverse lookup for: " + str(lookup_ipaddr))) 905 | for source_type in ['maxmind', 'rir', 'lir']: 906 | cc = self.database_cache.fetch_country_code( 907 | 'ipv6', 908 | source_type, 909 | int(lookup_ipaddr)) 910 | if cc: 911 | print((source_type.upper(), "country code:", cc)) 912 | cn = self.get_name_from_country_code(cc) 913 | if cn: 914 | print((source_type.upper(), "country name:", cn)) 915 | 916 | def lookup_ipv4_address(self, lookup_ipaddr): 917 | print(("Reverse lookup for: " + str(lookup_ipaddr))) 918 | maxmind_cc = self.database_cache.fetch_country_code('ipv4', 'maxmind', 919 | int(lookup_ipaddr)) 920 | if maxmind_cc: 921 | print(('MaxMind country code:', maxmind_cc)) 922 | maxmind_cn = self.get_name_from_country_code(maxmind_cc) 923 | if maxmind_cn: 924 | print(('MaxMind country name:', maxmind_cn)) 925 | rir_cc = self.database_cache.fetch_country_code('ipv4', 'rir', 926 | int(lookup_ipaddr)) 927 | if rir_cc: 928 | print(('RIR country code:', rir_cc)) 929 | rir_cn = self.get_name_from_country_code(rir_cc) 930 | if rir_cn: 931 | print(('RIR country name:', rir_cn)) 932 | else: 933 | print('Not found in RIR db') 934 | lir_cc = self.database_cache.fetch_country_code('ipv4', 'lir', 935 | int(lookup_ipaddr)) 936 | if lir_cc: 937 | print(('LIR country code:', lir_cc)) 938 | lir_cn = self.get_name_from_country_code(lir_cc) 939 | if lir_cn: 940 | print(('LIR country name:', lir_cn)) 941 | if maxmind_cc and maxmind_cc != rir_cc: 942 | print("It appears that the RIR data conflicts with MaxMind's " 943 | "data. MaxMind's data is likely closer to being " 944 | "correct due to sub-delegation issues with LIR databases.") 945 | 946 | def lookup_ip_address(self, lookup_str): 947 | """ Return the country code and name for a given ip address. """ 948 | try: 949 | lookup_ipaddr = ipaddr.ip_address(lookup_str) 950 | if isinstance(lookup_ipaddr, ipaddr.IPv4Address): 951 | self.lookup_ipv4_address(lookup_ipaddr) 952 | elif isinstance(lookup_ipaddr, ipaddr.IPv6Address): 953 | self.lookup_ipv6_address(lookup_ipaddr) 954 | else: 955 | print(("Did not recognize '%s' as either IPv4 or IPv6 " 956 | "address." % lookup_str)) 957 | except ValueError as e: 958 | print(("'%s' is not a valid IP address." % lookup_str)) 959 | 960 | def asn_lookup(self, asn): 961 | asn_cc = self.database_cache.fetch_country_code('asn', 'rir', asn) 962 | if asn_cc: 963 | print(("AS country code: %s" % asn_cc)) 964 | asn_cn = self.get_name_from_country_code(asn_cc) 965 | if asn_cn: 966 | print(("AS country name: %s" % asn_cn)) 967 | else: 968 | print(("AS%s not found!" % asn)) 969 | 970 | def fetch_rir_blocks_by_country(self, request, country): 971 | if request == "asn": 972 | return [str(start_num) for (start_num, end_num) in 973 | self.database_cache.fetch_assignments(request, country)] 974 | if request != "ipv4" and request != "ipv6": 975 | return [] 976 | seen = set() 977 | result = [] 978 | for (start_num, end_num) in \ 979 | self.database_cache.fetch_assignments(request, country): 980 | start_ipaddr = ipaddr.ip_address(start_num) 981 | end_ipaddr = ipaddr.ip_address(end_num) 982 | for block in (str(x) for x in 983 | ipaddr.summarize_address_range(start_ipaddr, 984 | end_ipaddr)): 985 | if block in seen: 986 | continue 987 | seen.add(block) 988 | result.append(block) 989 | return result 990 | 991 | def lookup_countries_in_different_source(self, first_country_code): 992 | """ Look up all assignments matching the given country code, then 993 | look up to which country code(s) the same number ranges are 994 | assigned in other source types. Print out the result showing 995 | similarities and differences. """ 996 | print((("\nLegend:\n" 997 | " '<' = found assignment range with country code '%s'\n" 998 | " '>' = overlapping assignment range with same country code\n" 999 | " '*' = overlapping assignment range, first conflict\n" 1000 | " '#' = overlapping assignment range, second conflict and " 1001 | "beyond\n ' ' = neighboring assignment range") % ( 1002 | first_country_code, ))) 1003 | results = self.database_cache.fetch_country_blocks_in_other_sources( 1004 | first_country_code) 1005 | prev_first_source_type = '' 1006 | prev_first_start_num = -1 1007 | cur_second_country_codes = [] 1008 | for (first_source_type, first_start_num, first_end_num, 1009 | second_source_type, second_start_num, second_end_num, 1010 | second_country_code, num_type) in results: 1011 | if first_source_type != prev_first_source_type: 1012 | print(("\nAssignments in '%s':" % (first_source_type, ))) 1013 | prev_first_source_type = first_source_type 1014 | if first_start_num != prev_first_start_num: 1015 | cur_second_country_codes = [] 1016 | print("") 1017 | prev_first_start_num = first_start_num 1018 | marker = '' 1019 | if second_end_num >= first_start_num and \ 1020 | second_start_num <= first_end_num: 1021 | if first_country_code != second_country_code and \ 1022 | second_country_code not in cur_second_country_codes: 1023 | cur_second_country_codes.append(second_country_code) 1024 | if first_source_type == second_source_type: 1025 | marker = '<' 1026 | elif len(cur_second_country_codes) == 0: 1027 | marker = '>' 1028 | elif len(cur_second_country_codes) == 1: 1029 | marker = '*' 1030 | else: 1031 | marker = '#' 1032 | if num_type.startswith("ip") and \ 1033 | second_start_num == second_end_num: 1034 | second_range = "%s" % (ipaddr.ip_address(second_start_num), ) 1035 | elif num_type.startswith("ip") and \ 1036 | second_start_num < second_end_num: 1037 | second_range = "%s-%s" % (ipaddr.ip_address(second_start_num), 1038 | ipaddr.ip_address(second_end_num)) 1039 | elif second_start_num < second_end_num: 1040 | second_range = "AS%d-%d" % (second_start_num, second_end_num) 1041 | else: 1042 | second_range = "AS%d" % (second_start_num, ) 1043 | print(("%1s %s %s %s" % (marker, second_country_code, second_range, 1044 | second_source_type, ))) 1045 | 1046 | def _get_network_string_from_range(self, end, start, bits=32): 1047 | start, end = int(start, 16), int(end, 16) 1048 | netbits = bits - int(log(end - start, 2)) 1049 | return ipaddr.IPNetwork("%s/%d" % (ipaddr.IPAddress(start), netbits)) 1050 | 1051 | def lookup_org_by_ip(self, lookup_str): 1052 | """ Return the ASN and AS Description by IP """ 1053 | try: 1054 | lookup_ipaddr = ipaddr.IPAddress(lookup_str) 1055 | if isinstance(lookup_ipaddr, ipaddr.IPv4Address): 1056 | num_type = 'ipv4' 1057 | len_bits = 32 1058 | elif isinstance(lookup_ipaddr, ipaddr.IPv6Address): 1059 | num_type = 'ipv6' 1060 | len_bits = 128 1061 | else: 1062 | raise ValueError 1063 | rs = self.database_cache.fetch_org_by_ip_address( 1064 | lookup_ipaddr, num_type) 1065 | for r in rs: 1066 | network = self._get_network_string_from_range( 1067 | r[3], r[2], bits=len_bits) 1068 | print(("%s in %s announced by AS%s - %s" % 1069 | (lookup_str, network, r[0], r[1]))) 1070 | except ValueError: 1071 | print(("'%s' is not a valid IP address." % lookup_str)) 1072 | except TypeError: 1073 | print(("Did not find any matching announcements containing %s." % 1074 | lookup_str)) 1075 | 1076 | def lookup_org_by_range(self, start_range, end_range): 1077 | output_str = "%s announced by AS%s - %s" 1078 | try: 1079 | a = ipaddr.IPAddress(start_range) 1080 | b = ipaddr.IPAddress(end_range) 1081 | if isinstance(a, ipaddr.IPv4Address) and isinstance( 1082 | b, ipaddr.IPv4Address): 1083 | num_type = 'ipv4' 1084 | len_bits = 32 1085 | elif isinstance(a, ipaddr.IPv6Address) and ( 1086 | isinstance(b, ipaddr.IPv6Address)): 1087 | num_type = 'ipv6' 1088 | len_bits = 128 1089 | else: 1090 | raise ValueError 1091 | rs = self.database_cache.fetch_org_by_ip_range( 1092 | min(a, b), max(a, b), num_type) 1093 | for r in rs: 1094 | network = self._get_network_string_from_range( 1095 | r[3], r[2], bits=len_bits) 1096 | print((output_str % (network, r[0], r[1]))) 1097 | except ValueError: 1098 | print(("%s %s is not a valid IP range." % (start_range, end_range))) 1099 | except TypeError: 1100 | print(("Did not find any matching announcements in range %s %s." % 1101 | (start_range, end_range))) 1102 | 1103 | 1104 | def split_callback(option, opt, value, parser): 1105 | split_value = value.split(':') 1106 | setattr(parser.values, option.dest, split_value[0]) 1107 | if len(split_value) > 1 and split_value[1] != '': 1108 | setattr(parser.values, 'type_filter', split_value[1]) 1109 | 1110 | 1111 | def normalize_country_code(country_code): 1112 | """ Normalize country codes a bit by making capitalization consistent and 1113 | removing trailing comments (and other words). """ 1114 | if not country_code: 1115 | return country_code 1116 | country_code = re.match(r'^(\w+)', country_code).group(1) 1117 | return country_code.upper() 1118 | 1119 | 1120 | def main(): 1121 | """ Where the magic starts. """ 1122 | usage = ("Usage: %prog [options]\n\n" 1123 | "Example: %prog -v -t mm") 1124 | parser = optparse.OptionParser(usage) 1125 | parser.add_option("-v", "--verbose", action="store_true", 1126 | dest="verbose", help="be verbose", default=False) 1127 | parser.add_option("-c", "--cache-dir", action="store", dest="dir", 1128 | help="set cache directory [default: %default]", 1129 | default=str(os.path.expanduser('~')) + "/.blockfinder/") 1130 | parser.add_option("--user-agent", action="store", dest="ua", 1131 | help=('provide a User-Agent which will be used when ' 1132 | 'fetching delegation files [default: "%default"]'), 1133 | default=("Mozilla/5.0 (Windows NT 6.1; rv:17.0) " 1134 | "Gecko/20100101 Firefox/17.0")) 1135 | parser.add_option("-x", "--hack-the-internet", action="store_true", 1136 | dest="hack_the_internet", help=optparse.SUPPRESS_HELP) 1137 | group = optparse.OptionGroup( 1138 | parser, 1139 | "Cache modes", 1140 | "Pick at most one of these modes to initialize or update " 1141 | "the local cache. May not be combined with lookup modes.") 1142 | group.add_option( 1143 | "-m", 1144 | "--init-maxmind", 1145 | action="store_true", 1146 | dest="init_maxmind", 1147 | help="initialize or update MaxMind GeoIP database") 1148 | group.add_option( 1149 | "-g", 1150 | "--reload-maxmind", 1151 | action="store_true", 1152 | dest="reload_maxmind", 1153 | help=("update cache from existing MaxMind GeoIP database")) 1154 | group.add_option( 1155 | "-r", 1156 | "--import-maxmind", 1157 | action="store", 1158 | dest="import_maxmind", 1159 | metavar="FILE", 1160 | help=("import the specified MaxMind GeoIP database file into " 1161 | "the database cache using its file name as source " 1162 | "name")) 1163 | group.add_option("-i", "--init-rir", 1164 | action="store_true", dest="init_del", 1165 | help="initialize or update delegation information") 1166 | group.add_option( 1167 | "-d", 1168 | "--reload-rir", 1169 | action="store_true", 1170 | dest="reload_del", 1171 | help="use existing delegation files to update the database") 1172 | group.add_option( 1173 | "-l", 1174 | "--init-lir", 1175 | action="store_true", 1176 | dest="init_lir", 1177 | help=("initialize or update lir information; can take up to " 1178 | "5 minutes")) 1179 | group.add_option( 1180 | "-z", 1181 | "--reload-lir", 1182 | action="store_true", 1183 | dest="reload_lir", 1184 | help=("use existing lir files to update the database; can " 1185 | "take up to 5 minutes")) 1186 | group.add_option( 1187 | "-o", 1188 | "--download-cc", 1189 | action="store_true", 1190 | dest="download_cc", 1191 | help="download country codes file") 1192 | group.add_option( 1193 | "-e", 1194 | "--erase-cache", 1195 | action="store_true", 1196 | dest="erase_cache", 1197 | help="erase the local database cache") 1198 | group.add_option( 1199 | "-j", 1200 | "--init-asn-descriptions", 1201 | action="store_true", 1202 | dest="init_asn_descriptions", 1203 | help=("initialize or update asn description information")) 1204 | group.add_option( 1205 | "-k", 1206 | "--reload-asn-descriptions", 1207 | action="store_true", 1208 | dest="reload_asn_descriptions", 1209 | help=("Use existing asn descriptions to update database")) 1210 | group.add_option( 1211 | "-y", 1212 | "--init-asn-assignments", 1213 | action="store_true", 1214 | dest="init_asn_assignments", 1215 | help=("initialize or update asn assignment information")) 1216 | group.add_option( 1217 | "-u", 1218 | "--reload-asn-assignments", 1219 | action="store_true", 1220 | dest="reload_asn_assignments", 1221 | help=("Use existing asn assignments to update database")) 1222 | parser.add_option_group(group) 1223 | group = optparse.OptionGroup( 1224 | parser, "Lookup modes", 1225 | "Pick at most one of these modes to look up data in the " 1226 | "local cache. May not be combined with cache modes.") 1227 | group.add_option( 1228 | "-4", 1229 | "--ipv4", 1230 | action="store", 1231 | dest="ipv4", 1232 | help=("look up country code and name for the specified IPv4 " 1233 | "address")) 1234 | group.add_option( 1235 | "-6", 1236 | "--ipv6", 1237 | action="store", 1238 | dest="ipv6", 1239 | help=("look up country code and name for the specified IPv6 " 1240 | "address")) 1241 | group.add_option( 1242 | "-a", 1243 | "--asn", 1244 | action="store", 1245 | dest="asn", 1246 | help="look up country code and name for the specified ASN") 1247 | group.add_option( 1248 | "-t", 1249 | "--code", 1250 | action="callback", 1251 | dest="cc", 1252 | callback=split_callback, 1253 | metavar="CC[:type]", 1254 | type="str", 1255 | help=("look up all allocations (or only those for number " 1256 | "type 'ipv4', 'ipv6', or 'asn' if provided) in the " 1257 | "delegation cache for the specified two-letter country " 1258 | "code")) 1259 | group.add_option( 1260 | "-n", 1261 | "--name", 1262 | action="callback", 1263 | dest="cn", 1264 | callback=split_callback, 1265 | metavar="CN[:type]", 1266 | type="str", 1267 | help=("look up all allocations (or only those for number " 1268 | "type 'ipv4', 'ipv6', or 'asn' if provided) in the " 1269 | "delegation cache for the specified full country " 1270 | "name")) 1271 | group.add_option( 1272 | "-p", 1273 | "--compare", 1274 | action="store", 1275 | dest="compare", 1276 | metavar="CC", 1277 | help=("compare assignments to the specified country code " 1278 | "with overlapping assignments in other data " 1279 | "sources; can take some time and produce some " 1280 | "long output")) 1281 | group.add_option( 1282 | "-w", 1283 | "--what-country", 1284 | action="store", 1285 | dest="what_cc", 1286 | help=("look up country name for specified country code")) 1287 | group.add_option( 1288 | "--lookup-org-by-ip", 1289 | "--lookup-org-by-ip", 1290 | action="store", 1291 | dest="lookup_org_by_ip", 1292 | help=("look up ASN and AS Description for an IP address")) 1293 | group.add_option( 1294 | "--lookup-org-by-range", 1295 | "--lookup-org-by-range", 1296 | action="store_true", 1297 | dest="lookup_org_by_range", 1298 | help=("look up announced networks in a range of addresses; " 1299 | "requires --range-start and --range-end to be set")) 1300 | group.add_option( 1301 | "--range-start", 1302 | "--range-start", 1303 | action="store", 1304 | dest="range_start", 1305 | help=("Specify the start of a range of addresses")) 1306 | group.add_option( 1307 | "--range-end", "--range-end", 1308 | action="store", 1309 | dest="range_end", 1310 | help=("Specify the end of a range of addresses")) 1311 | parser.add_option_group(group) 1312 | group = optparse.OptionGroup(parser, "Export modes") 1313 | group.add_option( 1314 | "--export-geoip", 1315 | "--export-geoip", 1316 | action="store_true", 1317 | dest="export", 1318 | help=("export the lookup database to GeoIPCountryWhois.csv and " 1319 | "v6.csv files in the format used to build the debian " 1320 | "package geoip-database")) 1321 | group.add_option( 1322 | "--geoip-v4-file", 1323 | "--geoip-v4-file", 1324 | action="store", 1325 | dest="geoip_v4_filename", 1326 | help=("The filename to write the IPv4 GeoIP dataset to")) 1327 | group.add_option( 1328 | "--geoip-v6-file", 1329 | "--geoip-v6-file", 1330 | action="store", 1331 | dest="geoip_v6_filename", 1332 | help=("The filename to write the IPv6 GeoIP dataset to")) 1333 | group.add_option( 1334 | "--geoip-asn-file", 1335 | "--geoip-asn-file", 1336 | action="store", 1337 | dest="geoip_asn_filename", 1338 | help=("The filename to write the IPv4 GeoIP ASNum dataset to")) 1339 | parser.add_option_group(group) 1340 | 1341 | group = optparse.OptionGroup(parser, "Network modes") 1342 | (options, args) = parser.parse_args() 1343 | if options.hack_the_internet: 1344 | print("all your bases are belong to us!") 1345 | sys.exit(0) 1346 | options_dict = vars(options) 1347 | modes = 0 1348 | for mode in ["init_maxmind", "reload_maxmind", "import_maxmind", 1349 | "init_del", "init_lir", "reload_del", "reload_lir", 1350 | "download_cc", "erase_cache", "ipv4", "ipv6", "asn", 1351 | "cc", "cn", "compare", "what_cc", "init_asn_descriptions", 1352 | "reload_asn_descriptions", "init_asn_assignments", 1353 | "reload_asn_assignments", "lookup_org_by_ip", 1354 | "lookup_org_by_range", "export"]: 1355 | if mode in options_dict and options_dict.get(mode): 1356 | modes += 1 1357 | if modes > 1: 1358 | parser.error("only 1 cache or lookup mode allowed") 1359 | elif modes == 0: 1360 | parser.error("must provide 1 cache or lookup mode") 1361 | database_cache = DatabaseCache(options.dir, options.verbose) 1362 | if options.erase_cache: 1363 | database_cache.erase_database() 1364 | sys.exit(0) 1365 | if not database_cache.connect_to_database(): 1366 | print("Could not connect to database.") 1367 | print("You may need to erase it using -e and then reload it " 1368 | "using -d/-z. Exiting.") 1369 | sys.exit(1) 1370 | database_cache.set_db_version() 1371 | downloader_parser = DownloaderParser(options.dir, database_cache, 1372 | options.ua) 1373 | lookup = Lookup(options.dir, database_cache) 1374 | if options.ipv4 or options.ipv6 or options.asn or options.cc \ 1375 | or options.cn or options.compare: 1376 | if downloader_parser.check_rir_file_mtimes(): 1377 | print("Your cached RIR files are older than 24 hours; you " 1378 | "probably want to update them.") 1379 | if options.asn: 1380 | lookup.asn_lookup(options.asn) 1381 | elif options.lookup_org_by_ip: 1382 | lookup.lookup_org_by_ip(options.lookup_org_by_ip) 1383 | elif options.lookup_org_by_range: 1384 | if not (options.range_start and options.range_end): 1385 | print("You must specify the start and end addresses; " 1386 | "see --range-start and --range-end") 1387 | else: 1388 | lookup.lookup_org_by_range(options.range_start, options.range_end) 1389 | elif options.ipv4: 1390 | lookup.lookup_ip_address(options.ipv4) 1391 | elif options.ipv6: 1392 | lookup.lookup_ip_address(options.ipv6) 1393 | elif options.cc or options.cn or options.what_cc: 1394 | country = None 1395 | if options.cc: 1396 | country = options.cc.upper() 1397 | elif not lookup.knows_country_names(): 1398 | print("Need to download country codes first before looking " 1399 | "up countries by name.") 1400 | elif options.what_cc: 1401 | country = options.what_cc.upper() 1402 | country_name = lookup.get_name_from_country_code(country) 1403 | if country_name: 1404 | print(("Hmm...%s? That would be %s." 1405 | % (options.what_cc, country_name))) 1406 | sys.exit(0) 1407 | else: 1408 | print(("Hmm, %s? We're not sure either. Are you sure that's " 1409 | "a country code?" % options.what_cc)) 1410 | sys.exit(1) 1411 | else: 1412 | country = lookup.get_country_code_from_name(options.cn) 1413 | if not country: 1414 | print("It appears your search did not match a country.") 1415 | if country: 1416 | types = ["ipv4", "ipv6", "asn"] 1417 | if hasattr(options, 'type_filter') and \ 1418 | options.type_filter.lower() in types: 1419 | types = [options.type_filter.lower()] 1420 | for request in types: 1421 | print(("\n".join(lookup.fetch_rir_blocks_by_country( 1422 | request, country)))) 1423 | elif options.compare: 1424 | print("Comparing assignments with overlapping assignments in other " 1425 | "data sources...") 1426 | lookup.lookup_countries_in_different_source(options.compare) 1427 | elif options.init_maxmind or options.reload_maxmind: 1428 | print("Maxmind data is no longer freely available.") 1429 | sys.exit(2) 1430 | if options.init_maxmind: 1431 | print("Downloading Maxmind GeoIP files...") 1432 | downloader_parser.download_maxmind_files() 1433 | print("Importing Maxmind GeoIP files...") 1434 | downloader_parser.parse_maxmind_files() 1435 | elif options.import_maxmind: 1436 | print("Importing Maxmind GeoIP files...") 1437 | downloader_parser.import_maxmind_file(options.import_maxmind) 1438 | elif options.init_del or options.reload_del: 1439 | if options.init_del: 1440 | print("Downloading RIR files...") 1441 | downloader_parser.download_rir_files() 1442 | print("Verifying RIR files...") 1443 | downloader_parser.verify_rir_files() 1444 | print("Importing RIR files...") 1445 | downloader_parser.parse_rir_files() 1446 | elif options.init_lir or options.reload_lir: 1447 | if options.init_lir: 1448 | print("Downloading LIR delegation files...") 1449 | downloader_parser.download_lir_files() 1450 | print("Importing LIR files...") 1451 | downloader_parser.parse_lir_files() 1452 | elif options.download_cc: 1453 | print("Downloading country code file...") 1454 | downloader_parser.download_country_code_file() 1455 | elif options.init_asn_descriptions or options.reload_asn_descriptions: 1456 | if options.init_asn_descriptions: 1457 | print("Downloading ASN Descriptions...") 1458 | downloader_parser.download_asn_description_file() 1459 | print("Importing ASN Descriptions...") 1460 | downloader_parser.parse_asn_description_file() 1461 | elif options.init_asn_assignments or options.reload_asn_assignments: 1462 | if options.init_asn_assignments: 1463 | print("Downloading ASN Assignments...") 1464 | downloader_parser.download_asn_assignment_files() 1465 | print("Importing ASN Assignments...") 1466 | downloader_parser.parse_asn_assignment_files() 1467 | elif options.export: 1468 | print("Export needs to be refactored.") 1469 | sys.exit(3) 1470 | v4_file = options.geoip_v4_filename or "GeoIPCountryWhois.csv" 1471 | v6_file = options.geoip_v6_filename or "v6.csv" 1472 | asn_file = options.geoip_asn_filename or "GeoIPASNum.csv" 1473 | print(("Exporting GeoIP IPv4 to %s" % v4_file)) 1474 | database_cache.export_geoip(lookup, v4_file, 'ipv4') 1475 | print(("Exporting GeoIP IPv6 to %s" % v6_file)) 1476 | database_cache.export_geoip(lookup, v6_file, 'ipv6') 1477 | print(("Exporting GeoIP IPv4 ASNum to %s" % asn_file)) 1478 | database_cache.export_asn(asn_file, 'ipv4') 1479 | # XXX: Unsupported 1480 | # print("Exporting GeoIP IPv6 ASNum to %s" % asn_file) 1481 | # database_cache.export_geoip(asn_file, 'ipv6') 1482 | database_cache.commit_and_close_database() 1483 | 1484 | if __name__ == "__main__": 1485 | main() 1486 | -------------------------------------------------------------------------------- /block_finder/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import unittest 3 | import os 4 | import shutil 5 | import sys 6 | import tempfile 7 | 8 | from . import blockfinder 9 | from .blockfinder import ipaddr, normalize_country_code 10 | 11 | 12 | class BaseBlockfinderTest(unittest.TestCase): 13 | 14 | def setUp(self): 15 | self.base_test_dir = tempfile.mkdtemp() 16 | self.test_dir = self.base_test_dir + "/test/" 17 | self.database_cache = blockfinder.DatabaseCache(self.test_dir) 18 | self.downloader_parser = blockfinder.DownloaderParser( 19 | self.test_dir, self.database_cache, "Mozilla") 20 | self.lookup = blockfinder.Lookup(self.test_dir, self.database_cache) 21 | self.database_cache.connect_to_database() 22 | self.database_cache.set_db_version() 23 | shutil.copy('test_rir_data', self.test_dir + 'test_rir_data') 24 | shutil.copy('test_lir_data.gz', self.test_dir + 'test_lir_data.gz') 25 | self.downloader_parser.parse_rir_files(['test_rir_data']) 26 | self.downloader_parser.parse_lir_files(['test_lir_data.gz']) 27 | 28 | def tearDown(self): 29 | shutil.rmtree(self.base_test_dir, True) 30 | 31 | 32 | class CheckReverseLookup(BaseBlockfinderTest): 33 | 34 | def test_rir_ipv4_lookup(self): 35 | method = self.database_cache.fetch_country_code 36 | ip_expected_co = ( 37 | (int(ipaddr.IPv4Address('175.45.176.100')), 'KP'), 38 | (int(ipaddr.IPv4Address('193.9.26.0')), 'HU'), 39 | (int(ipaddr.IPv4Address('193.9.25.1')), 'PL'), 40 | (int(ipaddr.IPv4Address('193.9.25.255')), 'PL'), 41 | ) 42 | for ip, expected_country in ip_expected_co: 43 | self.assertEqual(method('ipv4', 'rir', ip), expected_country) 44 | 45 | def test_rir_asn_lookup(self): 46 | self.assertEqual( 47 | self.database_cache.fetch_country_code('asn', 48 | 'rir', 681), 'NZ') 49 | self.assertEqual( 50 | self.database_cache.fetch_country_code('asn', 51 | 'rir', 173), 'JP') 52 | 53 | def test_lir_ipv4_lookup(self): 54 | method = self.database_cache.fetch_country_code 55 | ip_expected_co = ( 56 | (int(ipaddr.IPv4Address('80.16.151.184')), 'IT'), 57 | (int(ipaddr.IPv4Address('80.16.151.180')), 'IT'), 58 | (int(ipaddr.IPv4Address('213.95.6.32')), 'DE'), 59 | 60 | # Check capitalization. 61 | (int(ipaddr.IPv4Address('128.0.0.0')), 'RO'), 62 | 63 | # Check comment-stripping. 64 | # EU # Country is really world wide 65 | (int(ipaddr.IPv4Address('159.245.0.0')), 'EU'), 66 | 67 | # SE# RU UA 68 | (int(ipaddr.IPv4Address('85.195.129.0')), 'SE'), 69 | 70 | ) 71 | for ip, expected_country in ip_expected_co: 72 | self.assertEqual(method('ipv4', 'lir', ip), expected_country) 73 | 74 | def test_lir_ipv6_lookup(self): 75 | method = self.database_cache.fetch_country_code 76 | self.assertEqual( 77 | method( 78 | 'ipv6', 79 | 'lir', 80 | int(ipaddr.IPv6Address('2001:0658:021A::'))), 81 | 'DE') 82 | self.assertEqual( 83 | method( 84 | 'ipv6', 85 | 'lir', 86 | int(ipaddr.IPv6Address('2001:67c:320::'))), 87 | 'DE') 88 | self.assertEqual( 89 | method( 90 | 'ipv6', 91 | 'lir', 92 | int(ipaddr.IPv6Address('2001:670:0085::'))), 93 | 'FI') 94 | 95 | 96 | class CheckBlockFinder(BaseBlockfinderTest): 97 | 98 | def test_ipv4_bf(self): 99 | known_ipv4_assignments = ( 100 | ('MM', ['203.81.64.0/19', '203.81.160.0/20']), 101 | ('KP', ['175.45.176.0/22'])) 102 | for cc, values in known_ipv4_assignments: 103 | expected = [ 104 | (int(ipaddr.IPv4Network(network_str).network_address), 105 | int(ipaddr.IPv4Network(network_str).broadcast_address)) 106 | for network_str in values] 107 | result = self.database_cache.fetch_assignments('ipv4', cc) 108 | self.assertEqual(result, expected) 109 | 110 | def test_ipv6_bf(self): 111 | known_ipv6_assignments = ['2001:200::/35', '2001:200:2000::/35', 112 | '2001:200:4000::/34', '2001:200:8000::/33'] 113 | expected = [(int(ipaddr.IPv6Network(network_str).network_address), 114 | int(ipaddr.IPv6Network(network_str).broadcast_address)) 115 | for network_str in known_ipv6_assignments] 116 | result = self.database_cache.fetch_assignments('ipv6', 'JP') 117 | self.assertEqual(result, expected) 118 | 119 | 120 | class NormalizationTest(unittest.TestCase): 121 | 122 | def test_comment_stripping(self): 123 | # https://github.com/ioerror/blockfinder/issues/51 124 | self.assertEqual(normalize_country_code('EU'), 'EU') 125 | self.assertEqual(normalize_country_code( 126 | 'EU # Country is really world wide'), 'EU') 127 | self.assertEqual(normalize_country_code( 128 | 'DE #AT # IT'), 'DE') 129 | self.assertEqual(normalize_country_code( 130 | 'FR # GF # GP # MQ # RE'), 'FR') 131 | 132 | def test_capitalization(self): 133 | # https://github.com/ioerror/blockfinder/issues/53 134 | self.assertEqual(normalize_country_code('ro'), 'RO') 135 | self.assertEqual(normalize_country_code('RO'), 'RO') 136 | self.assertEqual(normalize_country_code(''), '') 137 | 138 | 139 | if __name__ == '__main__': 140 | failures = 0 141 | for test_class in [CheckReverseLookup, 142 | CheckBlockFinder, NormalizationTest]: 143 | test_suite = unittest.makeSuite(test_class) 144 | test_runner = unittest.TextTestRunner(verbosity=2) 145 | results = test_runner.run(test_suite) 146 | failures += len(results.errors) 147 | failures += len(results.failures) 148 | sys.exit(1 if failures else 0) 149 | -------------------------------------------------------------------------------- /blockfinder: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from block_finder.blockfinder import main 4 | 5 | if __name__ == "__main__": 6 | main() 7 | -------------------------------------------------------------------------------- /blockfinder.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: 3 | :: This .bat file invokes python (on PATH) with the full path of 4 | :: the script 'blockfinder'. This requires 'blockfinder' and 5 | :: 'blockfinder.bat' to be in the same directory. 6 | :: Look at "cmd /c for /?" to understand the "~dp0" label. 7 | :: 8 | python "%~dp0\blockfinder" %* 9 | -------------------------------------------------------------------------------- /embedded_ipaddr/COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2008 Google Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /embedded_ipaddr/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include ipaddr_test.py 3 | include RELEASENOTES 4 | -------------------------------------------------------------------------------- /embedded_ipaddr/OWNERS: -------------------------------------------------------------------------------- 1 | pmoody 2 | harro 3 | mshields 4 | smart 5 | -------------------------------------------------------------------------------- /embedded_ipaddr/README: -------------------------------------------------------------------------------- 1 | ipaddr.py is a library for working with IP addresses, both IPv4 and IPv6. 2 | It was developed by Google for internal use, and is now open source. 3 | 4 | Project home page: http://code.google.com/p/ipaddr-py/ 5 | 6 | Please send contributions to ipaddr-py-dev@googlegroups.com. Code should 7 | include unit tests and follow the Google Python style guide: 8 | http://code.google.com/p/soc/wiki/PythonStyleGuide 9 | -------------------------------------------------------------------------------- /embedded_ipaddr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioerror/blockfinder/3e0f4f0971b63f115e740f57d9298deb5cec0cd8/embedded_ipaddr/__init__.py -------------------------------------------------------------------------------- /embedded_ipaddr/ipaddr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright 2007 Google Inc. 4 | # Licensed to PSF under a Contributor Agreement. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | # implied. See the License for the specific language governing 16 | # permissions and limitations under the License. 17 | 18 | """A fast, lightweight IPv4/IPv6 manipulation library in Python. 19 | 20 | This library is used to create/poke/manipulate IPv4 and IPv6 addresses 21 | and networks. 22 | 23 | """ 24 | 25 | __version__ = 'trunk' 26 | 27 | import struct 28 | 29 | IPV4LENGTH = 32 30 | IPV6LENGTH = 128 31 | 32 | 33 | class AddressValueError(ValueError): 34 | """A Value Error related to the address.""" 35 | 36 | 37 | class NetmaskValueError(ValueError): 38 | """A Value Error related to the netmask.""" 39 | 40 | 41 | def IPAddress(address, version=None): 42 | """Take an IP string/int and return an object of the correct type. 43 | 44 | Args: 45 | address: A string or integer, the IP address. Either IPv4 or 46 | IPv6 addresses may be supplied; integers less than 2**32 will 47 | be considered to be IPv4 by default. 48 | version: An Integer, 4 or 6. If set, don't try to automatically 49 | determine what the IP address type is. important for things 50 | like IPAddress(1), which could be IPv4, '0.0.0.1', or IPv6, 51 | '::1'. 52 | 53 | Returns: 54 | An IPv4Address or IPv6Address object. 55 | 56 | Raises: 57 | ValueError: if the string passed isn't either a v4 or a v6 58 | address. 59 | 60 | """ 61 | if version: 62 | if version == 4: 63 | return IPv4Address(address) 64 | elif version == 6: 65 | return IPv6Address(address) 66 | 67 | try: 68 | return IPv4Address(address) 69 | except (AddressValueError, NetmaskValueError): 70 | pass 71 | 72 | try: 73 | return IPv6Address(address) 74 | except (AddressValueError, NetmaskValueError): 75 | pass 76 | 77 | raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % 78 | address) 79 | 80 | 81 | def IPNetwork(address, version=None, strict=False): 82 | """Take an IP string/int and return an object of the correct type. 83 | 84 | Args: 85 | address: A string or integer, the IP address. Either IPv4 or 86 | IPv6 addresses may be supplied; integers less than 2**32 will 87 | be considered to be IPv4 by default. 88 | version: An Integer, if set, don't try to automatically 89 | determine what the IP address type is. important for things 90 | like IPNetwork(1), which could be IPv4, '0.0.0.1/32', or IPv6, 91 | '::1/128'. 92 | 93 | Returns: 94 | An IPv4Network or IPv6Network object. 95 | 96 | Raises: 97 | ValueError: if the string passed isn't either a v4 or a v6 98 | address. Or if a strict network was requested and a strict 99 | network wasn't given. 100 | 101 | """ 102 | if version: 103 | if version == 4: 104 | return IPv4Network(address, strict) 105 | elif version == 6: 106 | return IPv6Network(address, strict) 107 | 108 | try: 109 | return IPv4Network(address, strict) 110 | except (AddressValueError, NetmaskValueError): 111 | pass 112 | 113 | try: 114 | return IPv6Network(address, strict) 115 | except (AddressValueError, NetmaskValueError): 116 | pass 117 | 118 | raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % 119 | address) 120 | 121 | 122 | def v4_int_to_packed(address): 123 | """The binary representation of this address. 124 | 125 | Args: 126 | address: An integer representation of an IPv4 IP address. 127 | 128 | Returns: 129 | The binary representation of this address. 130 | 131 | Raises: 132 | ValueError: If the integer is too large to be an IPv4 IP 133 | address. 134 | """ 135 | if address > _BaseV4._ALL_ONES: 136 | raise ValueError('Address too large for IPv4') 137 | return Bytes(struct.pack('!I', address)) 138 | 139 | 140 | def v6_int_to_packed(address): 141 | """The binary representation of this address. 142 | 143 | Args: 144 | address: An integer representation of an IPv6 IP address. 145 | 146 | Returns: 147 | The binary representation of this address. 148 | """ 149 | return Bytes(struct.pack('!QQ', address >> 64, address & (2**64 - 1))) 150 | 151 | 152 | def _find_address_range(addresses): 153 | """Find a sequence of addresses. 154 | 155 | Args: 156 | addresses: a list of IPv4 or IPv6 addresses. 157 | 158 | Returns: 159 | A tuple containing the first and last IP addresses in the sequence. 160 | 161 | """ 162 | first = last = addresses[0] 163 | for ip in addresses[1:]: 164 | if ip._ip == last._ip + 1: 165 | last = ip 166 | else: 167 | break 168 | return (first, last) 169 | 170 | def _get_prefix_length(number1, number2, bits): 171 | """Get the number of leading bits that are same for two numbers. 172 | 173 | Args: 174 | number1: an integer. 175 | number2: another integer. 176 | bits: the maximum number of bits to compare. 177 | 178 | Returns: 179 | The number of leading bits that are the same for two numbers. 180 | 181 | """ 182 | for i in range(bits): 183 | if number1 >> i == number2 >> i: 184 | return bits - i 185 | return 0 186 | 187 | def _count_righthand_zero_bits(number, bits): 188 | """Count the number of zero bits on the right hand side. 189 | 190 | Args: 191 | number: an integer. 192 | bits: maximum number of bits to count. 193 | 194 | Returns: 195 | The number of zero bits on the right hand side of the number. 196 | 197 | """ 198 | if number == 0: 199 | return bits 200 | for i in range(bits): 201 | if (number >> i) % 2: 202 | return i 203 | 204 | def summarize_address_range(first, last): 205 | """Summarize a network range given the first and last IP addresses. 206 | 207 | Example: 208 | >>> summarize_address_range(IPv4Address('1.1.1.0'), 209 | IPv4Address('1.1.1.130')) 210 | [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'), 211 | IPv4Network('1.1.1.130/32')] 212 | 213 | Args: 214 | first: the first IPv4Address or IPv6Address in the range. 215 | last: the last IPv4Address or IPv6Address in the range. 216 | 217 | Returns: 218 | The address range collapsed to a list of IPv4Network's or 219 | IPv6Network's. 220 | 221 | Raise: 222 | TypeError: 223 | If the first and last objects are not IP addresses. 224 | If the first and last objects are not the same version. 225 | ValueError: 226 | If the last object is not greater than the first. 227 | If the version is not 4 or 6. 228 | 229 | """ 230 | if not (isinstance(first, _BaseIP) and isinstance(last, _BaseIP)): 231 | raise TypeError('first and last must be IP addresses, not networks') 232 | if first.version != last.version: 233 | raise TypeError("%s and %s are not of the same version" % ( 234 | str(first), str(last))) 235 | if first > last: 236 | raise ValueError('last IP address must be greater than first') 237 | 238 | networks = [] 239 | 240 | if first.version == 4: 241 | ip = IPv4Network 242 | elif first.version == 6: 243 | ip = IPv6Network 244 | else: 245 | raise ValueError('unknown IP version') 246 | 247 | ip_bits = first._max_prefixlen 248 | first_int = first._ip 249 | last_int = last._ip 250 | while first_int <= last_int: 251 | nbits = _count_righthand_zero_bits(first_int, ip_bits) 252 | current = None 253 | while nbits >= 0: 254 | addend = 2**nbits - 1 255 | current = first_int + addend 256 | nbits -= 1 257 | if current <= last_int: 258 | break 259 | prefix = _get_prefix_length(first_int, current, ip_bits) 260 | net = ip('%s/%d' % (str(first), prefix)) 261 | networks.append(net) 262 | if current == ip._ALL_ONES: 263 | break 264 | first_int = current + 1 265 | first = IPAddress(first_int, version=first._version) 266 | return networks 267 | 268 | def _collapse_address_list_recursive(addresses): 269 | """Loops through the addresses, collapsing concurrent netblocks. 270 | 271 | Example: 272 | 273 | ip1 = IPv4Network('1.1.0.0/24') 274 | ip2 = IPv4Network('1.1.1.0/24') 275 | ip3 = IPv4Network('1.1.2.0/24') 276 | ip4 = IPv4Network('1.1.3.0/24') 277 | ip5 = IPv4Network('1.1.4.0/24') 278 | ip6 = IPv4Network('1.1.0.1/22') 279 | 280 | _collapse_address_list_recursive([ip1, ip2, ip3, ip4, ip5, ip6]) -> 281 | [IPv4Network('1.1.0.0/22'), IPv4Network('1.1.4.0/24')] 282 | 283 | This shouldn't be called directly; it is called via 284 | collapse_address_list([]). 285 | 286 | Args: 287 | addresses: A list of IPv4Network's or IPv6Network's 288 | 289 | Returns: 290 | A list of IPv4Network's or IPv6Network's depending on what we were 291 | passed. 292 | 293 | """ 294 | ret_array = [] 295 | optimized = False 296 | 297 | for cur_addr in addresses: 298 | if not ret_array: 299 | ret_array.append(cur_addr) 300 | continue 301 | if cur_addr in ret_array[-1]: 302 | optimized = True 303 | elif cur_addr == ret_array[-1].supernet().subnet()[1]: 304 | ret_array.append(ret_array.pop().supernet()) 305 | optimized = True 306 | else: 307 | ret_array.append(cur_addr) 308 | 309 | if optimized: 310 | return _collapse_address_list_recursive(ret_array) 311 | 312 | return ret_array 313 | 314 | 315 | def collapse_address_list(addresses): 316 | """Collapse a list of IP objects. 317 | 318 | Example: 319 | collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> 320 | [IPv4('1.1.0.0/23')] 321 | 322 | Args: 323 | addresses: A list of IPv4Network or IPv6Network objects. 324 | 325 | Returns: 326 | A list of IPv4Network or IPv6Network objects depending on what we 327 | were passed. 328 | 329 | Raises: 330 | TypeError: If passed a list of mixed version objects. 331 | 332 | """ 333 | i = 0 334 | addrs = [] 335 | ips = [] 336 | nets = [] 337 | 338 | # split IP addresses and networks 339 | for ip in addresses: 340 | if isinstance(ip, _BaseIP): 341 | if ips and ips[-1]._version != ip._version: 342 | raise TypeError("%s and %s are not of the same version" % ( 343 | str(ip), str(ips[-1]))) 344 | ips.append(ip) 345 | elif ip._prefixlen == ip._max_prefixlen: 346 | if ips and ips[-1]._version != ip._version: 347 | raise TypeError("%s and %s are not of the same version" % ( 348 | str(ip), str(ips[-1]))) 349 | ips.append(ip.ip) 350 | else: 351 | if nets and nets[-1]._version != ip._version: 352 | raise TypeError("%s and %s are not of the same version" % ( 353 | str(ip), str(nets[-1]))) 354 | nets.append(ip) 355 | 356 | # sort and dedup 357 | ips = sorted(set(ips)) 358 | nets = sorted(set(nets)) 359 | 360 | while i < len(ips): 361 | (first, last) = _find_address_range(ips[i:]) 362 | i = ips.index(last) + 1 363 | addrs.extend(summarize_address_range(first, last)) 364 | 365 | return _collapse_address_list_recursive(sorted( 366 | addrs + nets, key=_BaseNet._get_networks_key)) 367 | 368 | # backwards compatibility 369 | CollapseAddrList = collapse_address_list 370 | 371 | # We need to distinguish between the string and packed-bytes representations 372 | # of an IP address. For example, b'0::1' is the IPv4 address 48.58.58.49, 373 | # while '0::1' is an IPv6 address. 374 | # 375 | # In Python 3, the native 'bytes' type already provides this functionality, 376 | # so we use it directly. For earlier implementations where bytes is not a 377 | # distinct type, we create a subclass of str to serve as a tag. 378 | # 379 | # Usage example (Python 2): 380 | # ip = ipaddr.IPAddress(ipaddr.Bytes('xxxx')) 381 | # 382 | # Usage example (Python 3): 383 | # ip = ipaddr.IPAddress(b'xxxx') 384 | try: 385 | if bytes is str: 386 | raise TypeError("bytes is not a distinct type") 387 | Bytes = bytes 388 | except (NameError, TypeError): 389 | class Bytes(str): 390 | def __repr__(self): 391 | return 'Bytes(%s)' % str.__repr__(self) 392 | 393 | def get_mixed_type_key(obj): 394 | """Return a key suitable for sorting between networks and addresses. 395 | 396 | Address and Network objects are not sortable by default; they're 397 | fundamentally different so the expression 398 | 399 | IPv4Address('1.1.1.1') <= IPv4Network('1.1.1.1/24') 400 | 401 | doesn't make any sense. There are some times however, where you may wish 402 | to have ipaddr sort these for you anyway. If you need to do this, you 403 | can use this function as the key= argument to sorted(). 404 | 405 | Args: 406 | obj: either a Network or Address object. 407 | Returns: 408 | appropriate key. 409 | 410 | """ 411 | if isinstance(obj, _BaseNet): 412 | return obj._get_networks_key() 413 | elif isinstance(obj, _BaseIP): 414 | return obj._get_address_key() 415 | return NotImplemented 416 | 417 | class _IPAddrBase(object): 418 | 419 | """The mother class.""" 420 | 421 | def __index__(self): 422 | return self._ip 423 | 424 | def __int__(self): 425 | return self._ip 426 | 427 | def __hex__(self): 428 | return hex(self._ip) 429 | 430 | @property 431 | def exploded(self): 432 | """Return the longhand version of the IP address as a string.""" 433 | return self._explode_shorthand_ip_string() 434 | 435 | @property 436 | def compressed(self): 437 | """Return the shorthand version of the IP address as a string.""" 438 | return str(self) 439 | 440 | 441 | class _BaseIP(_IPAddrBase): 442 | 443 | """A generic IP object. 444 | 445 | This IP class contains the version independent methods which are 446 | used by single IP addresses. 447 | 448 | """ 449 | 450 | def __eq__(self, other): 451 | try: 452 | return (self._ip == other._ip 453 | and self._version == other._version) 454 | except AttributeError: 455 | return NotImplemented 456 | 457 | def __ne__(self, other): 458 | eq = self.__eq__(other) 459 | if eq is NotImplemented: 460 | return NotImplemented 461 | return not eq 462 | 463 | def __le__(self, other): 464 | gt = self.__gt__(other) 465 | if gt is NotImplemented: 466 | return NotImplemented 467 | return not gt 468 | 469 | def __ge__(self, other): 470 | lt = self.__lt__(other) 471 | if lt is NotImplemented: 472 | return NotImplemented 473 | return not lt 474 | 475 | def __lt__(self, other): 476 | if self._version != other._version: 477 | raise TypeError('%s and %s are not of the same version' % ( 478 | str(self), str(other))) 479 | if not isinstance(other, _BaseIP): 480 | raise TypeError('%s and %s are not of the same type' % ( 481 | str(self), str(other))) 482 | if self._ip != other._ip: 483 | return self._ip < other._ip 484 | return False 485 | 486 | def __gt__(self, other): 487 | if self._version != other._version: 488 | raise TypeError('%s and %s are not of the same version' % ( 489 | str(self), str(other))) 490 | if not isinstance(other, _BaseIP): 491 | raise TypeError('%s and %s are not of the same type' % ( 492 | str(self), str(other))) 493 | if self._ip != other._ip: 494 | return self._ip > other._ip 495 | return False 496 | 497 | # Shorthand for Integer addition and subtraction. This is not 498 | # meant to ever support addition/subtraction of addresses. 499 | def __add__(self, other): 500 | if not isinstance(other, int): 501 | return NotImplemented 502 | return IPAddress(int(self) + other, version=self._version) 503 | 504 | def __sub__(self, other): 505 | if not isinstance(other, int): 506 | return NotImplemented 507 | return IPAddress(int(self) - other, version=self._version) 508 | 509 | def __repr__(self): 510 | return '%s(%r)' % (self.__class__.__name__, str(self)) 511 | 512 | def __str__(self): 513 | return '%s' % self._string_from_ip_int(self._ip) 514 | 515 | def __hash__(self): 516 | return hash(hex(long(self._ip))) 517 | 518 | def _get_address_key(self): 519 | return (self._version, self) 520 | 521 | @property 522 | def version(self): 523 | raise NotImplementedError('BaseIP has no version') 524 | 525 | 526 | class _BaseNet(_IPAddrBase): 527 | 528 | """A generic IP object. 529 | 530 | This IP class contains the version independent methods which are 531 | used by networks. 532 | 533 | """ 534 | 535 | def __init__(self, address): 536 | self._cache = {} 537 | 538 | def __repr__(self): 539 | return '%s(%r)' % (self.__class__.__name__, str(self)) 540 | 541 | def iterhosts(self): 542 | """Generate Iterator over usable hosts in a network. 543 | 544 | This is like __iter__ except it doesn't return the network 545 | or broadcast addresses. 546 | 547 | """ 548 | cur = int(self.network) + 1 549 | bcast = int(self.broadcast) - 1 550 | while cur <= bcast: 551 | cur += 1 552 | yield IPAddress(cur - 1, version=self._version) 553 | 554 | def __iter__(self): 555 | cur = int(self.network) 556 | bcast = int(self.broadcast) 557 | while cur <= bcast: 558 | cur += 1 559 | yield IPAddress(cur - 1, version=self._version) 560 | 561 | def __getitem__(self, n): 562 | network = int(self.network) 563 | broadcast = int(self.broadcast) 564 | if n >= 0: 565 | if network + n > broadcast: 566 | raise IndexError 567 | return IPAddress(network + n, version=self._version) 568 | else: 569 | n += 1 570 | if broadcast + n < network: 571 | raise IndexError 572 | return IPAddress(broadcast + n, version=self._version) 573 | 574 | def __lt__(self, other): 575 | if self._version != other._version: 576 | raise TypeError('%s and %s are not of the same version' % ( 577 | str(self), str(other))) 578 | if not isinstance(other, _BaseNet): 579 | raise TypeError('%s and %s are not of the same type' % ( 580 | str(self), str(other))) 581 | if self.network != other.network: 582 | return self.network < other.network 583 | if self.netmask != other.netmask: 584 | return self.netmask < other.netmask 585 | return False 586 | 587 | def __gt__(self, other): 588 | if self._version != other._version: 589 | raise TypeError('%s and %s are not of the same version' % ( 590 | str(self), str(other))) 591 | if not isinstance(other, _BaseNet): 592 | raise TypeError('%s and %s are not of the same type' % ( 593 | str(self), str(other))) 594 | if self.network != other.network: 595 | return self.network > other.network 596 | if self.netmask != other.netmask: 597 | return self.netmask > other.netmask 598 | return False 599 | 600 | def __le__(self, other): 601 | gt = self.__gt__(other) 602 | if gt is NotImplemented: 603 | return NotImplemented 604 | return not gt 605 | 606 | def __ge__(self, other): 607 | lt = self.__lt__(other) 608 | if lt is NotImplemented: 609 | return NotImplemented 610 | return not lt 611 | 612 | def __eq__(self, other): 613 | try: 614 | return (self._version == other._version 615 | and self.network == other.network 616 | and int(self.netmask) == int(other.netmask)) 617 | except AttributeError: 618 | if isinstance(other, _BaseIP): 619 | return (self._version == other._version 620 | and self._ip == other._ip) 621 | 622 | def __ne__(self, other): 623 | eq = self.__eq__(other) 624 | if eq is NotImplemented: 625 | return NotImplemented 626 | return not eq 627 | 628 | def __str__(self): 629 | return '%s/%s' % (str(self.ip), 630 | str(self._prefixlen)) 631 | 632 | def __hash__(self): 633 | return hash(int(self.network) ^ int(self.netmask)) 634 | 635 | def __contains__(self, other): 636 | # always false if one is v4 and the other is v6. 637 | if self._version != other._version: 638 | return False 639 | # dealing with another network. 640 | if isinstance(other, _BaseNet): 641 | return (self.network <= other.network and 642 | self.broadcast >= other.broadcast) 643 | # dealing with another address 644 | else: 645 | return (int(self.network) <= int(other._ip) <= 646 | int(self.broadcast)) 647 | 648 | def overlaps(self, other): 649 | """Tell if self is partly contained in other.""" 650 | return self.network in other or self.broadcast in other or ( 651 | other.network in self or other.broadcast in self) 652 | 653 | @property 654 | def network(self): 655 | x = self._cache.get('network') 656 | if x is None: 657 | x = IPAddress(self._ip & int(self.netmask), version=self._version) 658 | self._cache['network'] = x 659 | return x 660 | 661 | @property 662 | def network_address(self): 663 | return self.network 664 | 665 | @property 666 | def broadcast(self): 667 | x = self._cache.get('broadcast') 668 | if x is None: 669 | x = IPAddress(self._ip | int(self.hostmask), version=self._version) 670 | self._cache['broadcast'] = x 671 | return x 672 | 673 | @property 674 | def broadcast_address(self): 675 | return self.broadcast 676 | 677 | @property 678 | def hostmask(self): 679 | x = self._cache.get('hostmask') 680 | if x is None: 681 | x = IPAddress(int(self.netmask) ^ self._ALL_ONES, 682 | version=self._version) 683 | self._cache['hostmask'] = x 684 | return x 685 | 686 | @property 687 | def with_prefixlen(self): 688 | return '%s/%d' % (str(self.ip), self._prefixlen) 689 | 690 | @property 691 | def with_netmask(self): 692 | return '%s/%s' % (str(self.ip), str(self.netmask)) 693 | 694 | @property 695 | def with_hostmask(self): 696 | return '%s/%s' % (str(self.ip), str(self.hostmask)) 697 | 698 | @property 699 | def numhosts(self): 700 | """Number of hosts in the current subnet.""" 701 | return int(self.broadcast) - int(self.network) + 1 702 | 703 | @property 704 | def version(self): 705 | raise NotImplementedError('BaseNet has no version') 706 | 707 | @property 708 | def prefixlen(self): 709 | return self._prefixlen 710 | 711 | def address_exclude(self, other): 712 | """Remove an address from a larger block. 713 | 714 | For example: 715 | 716 | addr1 = IPNetwork('10.1.1.0/24') 717 | addr2 = IPNetwork('10.1.1.0/26') 718 | addr1.address_exclude(addr2) = 719 | [IPNetwork('10.1.1.64/26'), IPNetwork('10.1.1.128/25')] 720 | 721 | or IPv6: 722 | 723 | addr1 = IPNetwork('::1/32') 724 | addr2 = IPNetwork('::1/128') 725 | addr1.address_exclude(addr2) = [IPNetwork('::0/128'), 726 | IPNetwork('::2/127'), 727 | IPNetwork('::4/126'), 728 | IPNetwork('::8/125'), 729 | ... 730 | IPNetwork('0:0:8000::/33')] 731 | 732 | Args: 733 | other: An IPvXNetwork object of the same type. 734 | 735 | Returns: 736 | A sorted list of IPvXNetwork objects addresses which is self 737 | minus other. 738 | 739 | Raises: 740 | TypeError: If self and other are of difffering address 741 | versions, or if other is not a network object. 742 | ValueError: If other is not completely contained by self. 743 | 744 | """ 745 | if not self._version == other._version: 746 | raise TypeError("%s and %s are not of the same version" % ( 747 | str(self), str(other))) 748 | 749 | if not isinstance(other, _BaseNet): 750 | raise TypeError("%s is not a network object" % str(other)) 751 | 752 | if other not in self: 753 | raise ValueError('%s not contained in %s' % (str(other), 754 | str(self))) 755 | if other == self: 756 | return [] 757 | 758 | ret_addrs = [] 759 | 760 | # Make sure we're comparing the network of other. 761 | other = IPNetwork('%s/%s' % (str(other.network), str(other.prefixlen)), 762 | version=other._version) 763 | 764 | s1, s2 = self.subnet() 765 | while s1 != other and s2 != other: 766 | if other in s1: 767 | ret_addrs.append(s2) 768 | s1, s2 = s1.subnet() 769 | elif other in s2: 770 | ret_addrs.append(s1) 771 | s1, s2 = s2.subnet() 772 | else: 773 | # If we got here, there's a bug somewhere. 774 | assert True == False, ('Error performing exclusion: ' 775 | 's1: %s s2: %s other: %s' % 776 | (str(s1), str(s2), str(other))) 777 | if s1 == other: 778 | ret_addrs.append(s2) 779 | elif s2 == other: 780 | ret_addrs.append(s1) 781 | else: 782 | # If we got here, there's a bug somewhere. 783 | assert True == False, ('Error performing exclusion: ' 784 | 's1: %s s2: %s other: %s' % 785 | (str(s1), str(s2), str(other))) 786 | 787 | return sorted(ret_addrs, key=_BaseNet._get_networks_key) 788 | 789 | def compare_networks(self, other): 790 | """Compare two IP objects. 791 | 792 | This is only concerned about the comparison of the integer 793 | representation of the network addresses. This means that the 794 | host bits aren't considered at all in this method. If you want 795 | to compare host bits, you can easily enough do a 796 | 'HostA._ip < HostB._ip' 797 | 798 | Args: 799 | other: An IP object. 800 | 801 | Returns: 802 | If the IP versions of self and other are the same, returns: 803 | 804 | -1 if self < other: 805 | eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24') 806 | IPv6('1080::200C:417A') < IPv6('1080::200B:417B') 807 | 0 if self == other 808 | eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24') 809 | IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96') 810 | 1 if self > other 811 | eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24') 812 | IPv6('1080::1:200C:417A/112') > 813 | IPv6('1080::0:200C:417A/112') 814 | 815 | If the IP versions of self and other are different, returns: 816 | 817 | -1 if self._version < other._version 818 | eg: IPv4('10.0.0.1/24') < IPv6('::1/128') 819 | 1 if self._version > other._version 820 | eg: IPv6('::1/128') > IPv4('255.255.255.0/24') 821 | 822 | """ 823 | if self._version < other._version: 824 | return -1 825 | if self._version > other._version: 826 | return 1 827 | # self._version == other._version below here: 828 | if self.network < other.network: 829 | return -1 830 | if self.network > other.network: 831 | return 1 832 | # self.network == other.network below here: 833 | if self.netmask < other.netmask: 834 | return -1 835 | if self.netmask > other.netmask: 836 | return 1 837 | # self.network == other.network and self.netmask == other.netmask 838 | return 0 839 | 840 | def _get_networks_key(self): 841 | """Network-only key function. 842 | 843 | Returns an object that identifies this address' network and 844 | netmask. This function is a suitable "key" argument for sorted() 845 | and list.sort(). 846 | 847 | """ 848 | return (self._version, self.network, self.netmask) 849 | 850 | def _ip_int_from_prefix(self, prefixlen=None): 851 | """Turn the prefix length netmask into a int for comparison. 852 | 853 | Args: 854 | prefixlen: An integer, the prefix length. 855 | 856 | Returns: 857 | An integer. 858 | 859 | """ 860 | if not prefixlen and prefixlen != 0: 861 | prefixlen = self._prefixlen 862 | return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) 863 | 864 | def _prefix_from_ip_int(self, ip_int, mask=32): 865 | """Return prefix length from the decimal netmask. 866 | 867 | Args: 868 | ip_int: An integer, the IP address. 869 | mask: The netmask. Defaults to 32. 870 | 871 | Returns: 872 | An integer, the prefix length. 873 | 874 | """ 875 | while mask: 876 | if ip_int & 1 == 1: 877 | break 878 | ip_int >>= 1 879 | mask -= 1 880 | 881 | return mask 882 | 883 | def _ip_string_from_prefix(self, prefixlen=None): 884 | """Turn a prefix length into a dotted decimal string. 885 | 886 | Args: 887 | prefixlen: An integer, the netmask prefix length. 888 | 889 | Returns: 890 | A string, the dotted decimal netmask string. 891 | 892 | """ 893 | if not prefixlen: 894 | prefixlen = self._prefixlen 895 | return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) 896 | 897 | def iter_subnets(self, prefixlen_diff=1, new_prefix=None): 898 | """The subnets which join to make the current subnet. 899 | 900 | In the case that self contains only one IP 901 | (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 902 | for IPv6), return a list with just ourself. 903 | 904 | Args: 905 | prefixlen_diff: An integer, the amount the prefix length 906 | should be increased by. This should not be set if 907 | new_prefix is also set. 908 | new_prefix: The desired new prefix length. This must be a 909 | larger number (smaller prefix) than the existing prefix. 910 | This should not be set if prefixlen_diff is also set. 911 | 912 | Returns: 913 | An iterator of IPv(4|6) objects. 914 | 915 | Raises: 916 | ValueError: The prefixlen_diff is too small or too large. 917 | OR 918 | prefixlen_diff and new_prefix are both set or new_prefix 919 | is a smaller number than the current prefix (smaller 920 | number means a larger network) 921 | 922 | """ 923 | if self._prefixlen == self._max_prefixlen: 924 | yield self 925 | return 926 | 927 | if new_prefix is not None: 928 | if new_prefix < self._prefixlen: 929 | raise ValueError('new prefix must be longer') 930 | if prefixlen_diff != 1: 931 | raise ValueError('cannot set prefixlen_diff and new_prefix') 932 | prefixlen_diff = new_prefix - self._prefixlen 933 | 934 | if prefixlen_diff < 0: 935 | raise ValueError('prefix length diff must be > 0') 936 | new_prefixlen = self._prefixlen + prefixlen_diff 937 | 938 | if not self._is_valid_netmask(str(new_prefixlen)): 939 | raise ValueError( 940 | 'prefix length diff %d is invalid for netblock %s' % ( 941 | new_prefixlen, str(self))) 942 | 943 | first = IPNetwork('%s/%s' % (str(self.network), 944 | str(self._prefixlen + prefixlen_diff)), 945 | version=self._version) 946 | 947 | yield first 948 | current = first 949 | while True: 950 | broadcast = current.broadcast 951 | if broadcast == self.broadcast: 952 | return 953 | new_addr = IPAddress(int(broadcast) + 1, version=self._version) 954 | current = IPNetwork('%s/%s' % (str(new_addr), str(new_prefixlen)), 955 | version=self._version) 956 | 957 | yield current 958 | 959 | def masked(self): 960 | """Return the network object with the host bits masked out.""" 961 | return IPNetwork('%s/%d' % (self.network, self._prefixlen), 962 | version=self._version) 963 | 964 | def subnet(self, prefixlen_diff=1, new_prefix=None): 965 | """Return a list of subnets, rather than an iterator.""" 966 | return list(self.iter_subnets(prefixlen_diff, new_prefix)) 967 | 968 | def supernet(self, prefixlen_diff=1, new_prefix=None): 969 | """The supernet containing the current network. 970 | 971 | Args: 972 | prefixlen_diff: An integer, the amount the prefix length of 973 | the network should be decreased by. For example, given a 974 | /24 network and a prefixlen_diff of 3, a supernet with a 975 | /21 netmask is returned. 976 | 977 | Returns: 978 | An IPv4 network object. 979 | 980 | Raises: 981 | ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have a 982 | negative prefix length. 983 | OR 984 | If prefixlen_diff and new_prefix are both set or new_prefix is a 985 | larger number than the current prefix (larger number means a 986 | smaller network) 987 | 988 | """ 989 | if self._prefixlen == 0: 990 | return self 991 | 992 | if new_prefix is not None: 993 | if new_prefix > self._prefixlen: 994 | raise ValueError('new prefix must be shorter') 995 | if prefixlen_diff != 1: 996 | raise ValueError('cannot set prefixlen_diff and new_prefix') 997 | prefixlen_diff = self._prefixlen - new_prefix 998 | 999 | 1000 | if self.prefixlen - prefixlen_diff < 0: 1001 | raise ValueError( 1002 | 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % 1003 | (self.prefixlen, prefixlen_diff)) 1004 | return IPNetwork('%s/%s' % (str(self.network), 1005 | str(self.prefixlen - prefixlen_diff)), 1006 | version=self._version) 1007 | 1008 | # backwards compatibility 1009 | Subnet = subnet 1010 | Supernet = supernet 1011 | AddressExclude = address_exclude 1012 | CompareNetworks = compare_networks 1013 | Contains = __contains__ 1014 | 1015 | 1016 | class _BaseV4(object): 1017 | 1018 | """Base IPv4 object. 1019 | 1020 | The following methods are used by IPv4 objects in both single IP 1021 | addresses and networks. 1022 | 1023 | """ 1024 | 1025 | # Equivalent to 255.255.255.255 or 32 bits of 1's. 1026 | _ALL_ONES = (2**IPV4LENGTH) - 1 1027 | _DECIMAL_DIGITS = frozenset('0123456789') 1028 | 1029 | def __init__(self, address): 1030 | self._version = 4 1031 | self._max_prefixlen = IPV4LENGTH 1032 | 1033 | def _explode_shorthand_ip_string(self): 1034 | return str(self) 1035 | 1036 | def _ip_int_from_string(self, ip_str): 1037 | """Turn the given IP string into an integer for comparison. 1038 | 1039 | Args: 1040 | ip_str: A string, the IP ip_str. 1041 | 1042 | Returns: 1043 | The IP ip_str as an integer. 1044 | 1045 | Raises: 1046 | AddressValueError: if ip_str isn't a valid IPv4 Address. 1047 | 1048 | """ 1049 | octets = ip_str.split('.') 1050 | if len(octets) != 4: 1051 | raise AddressValueError(ip_str) 1052 | 1053 | packed_ip = 0 1054 | for oc in octets: 1055 | try: 1056 | packed_ip = (packed_ip << 8) | self._parse_octet(oc) 1057 | except ValueError: 1058 | raise AddressValueError(ip_str) 1059 | return packed_ip 1060 | 1061 | def _parse_octet(self, octet_str): 1062 | """Convert a decimal octet into an integer. 1063 | 1064 | Args: 1065 | octet_str: A string, the number to parse. 1066 | 1067 | Returns: 1068 | The octet as an integer. 1069 | 1070 | Raises: 1071 | ValueError: if the octet isn't strictly a decimal from [0..255]. 1072 | 1073 | """ 1074 | # Whitelist the characters, since int() allows a lot of bizarre stuff. 1075 | if not self._DECIMAL_DIGITS.issuperset(octet_str): 1076 | raise ValueError 1077 | octet_int = int(octet_str, 10) 1078 | # Disallow leading zeroes, because no clear standard exists on 1079 | # whether these should be interpreted as decimal or octal. 1080 | if octet_int > 255 or (octet_str[0] == '0' and len(octet_str) > 1): 1081 | raise ValueError 1082 | return octet_int 1083 | 1084 | def _string_from_ip_int(self, ip_int): 1085 | """Turns a 32-bit integer into dotted decimal notation. 1086 | 1087 | Args: 1088 | ip_int: An integer, the IP address. 1089 | 1090 | Returns: 1091 | The IP address as a string in dotted decimal notation. 1092 | 1093 | """ 1094 | octets = [] 1095 | for _ in xrange(4): 1096 | octets.insert(0, str(ip_int & 0xFF)) 1097 | ip_int >>= 8 1098 | return '.'.join(octets) 1099 | 1100 | @property 1101 | def max_prefixlen(self): 1102 | return self._max_prefixlen 1103 | 1104 | @property 1105 | def packed(self): 1106 | """The binary representation of this address.""" 1107 | return v4_int_to_packed(self._ip) 1108 | 1109 | @property 1110 | def version(self): 1111 | return self._version 1112 | 1113 | @property 1114 | def is_reserved(self): 1115 | """Test if the address is otherwise IETF reserved. 1116 | 1117 | Returns: 1118 | A boolean, True if the address is within the 1119 | reserved IPv4 Network range. 1120 | 1121 | """ 1122 | return self in IPv4Network('240.0.0.0/4') 1123 | 1124 | @property 1125 | def is_private(self): 1126 | """Test if this address is allocated for private networks. 1127 | 1128 | Returns: 1129 | A boolean, True if the address is reserved per RFC 1918. 1130 | 1131 | """ 1132 | return (self in IPv4Network('10.0.0.0/8') or 1133 | self in IPv4Network('172.16.0.0/12') or 1134 | self in IPv4Network('192.168.0.0/16')) 1135 | 1136 | @property 1137 | def is_multicast(self): 1138 | """Test if the address is reserved for multicast use. 1139 | 1140 | Returns: 1141 | A boolean, True if the address is multicast. 1142 | See RFC 3171 for details. 1143 | 1144 | """ 1145 | return self in IPv4Network('224.0.0.0/4') 1146 | 1147 | @property 1148 | def is_unspecified(self): 1149 | """Test if the address is unspecified. 1150 | 1151 | Returns: 1152 | A boolean, True if this is the unspecified address as defined in 1153 | RFC 5735 3. 1154 | 1155 | """ 1156 | return self in IPv4Network('0.0.0.0') 1157 | 1158 | @property 1159 | def is_loopback(self): 1160 | """Test if the address is a loopback address. 1161 | 1162 | Returns: 1163 | A boolean, True if the address is a loopback per RFC 3330. 1164 | 1165 | """ 1166 | return self in IPv4Network('127.0.0.0/8') 1167 | 1168 | @property 1169 | def is_link_local(self): 1170 | """Test if the address is reserved for link-local. 1171 | 1172 | Returns: 1173 | A boolean, True if the address is link-local per RFC 3927. 1174 | 1175 | """ 1176 | return self in IPv4Network('169.254.0.0/16') 1177 | 1178 | 1179 | class IPv4Address(_BaseV4, _BaseIP): 1180 | 1181 | """Represent and manipulate single IPv4 Addresses.""" 1182 | 1183 | def __init__(self, address): 1184 | 1185 | """ 1186 | Args: 1187 | address: A string or integer representing the IP 1188 | '192.168.1.1' 1189 | 1190 | Additionally, an integer can be passed, so 1191 | IPv4Address('192.168.1.1') == IPv4Address(3232235777). 1192 | or, more generally 1193 | IPv4Address(int(IPv4Address('192.168.1.1'))) == 1194 | IPv4Address('192.168.1.1') 1195 | 1196 | Raises: 1197 | AddressValueError: If ipaddr isn't a valid IPv4 address. 1198 | 1199 | """ 1200 | _BaseV4.__init__(self, address) 1201 | 1202 | # Efficient constructor from integer. 1203 | if isinstance(address, (int, long)): 1204 | self._ip = address 1205 | if address < 0 or address > self._ALL_ONES: 1206 | raise AddressValueError(address) 1207 | return 1208 | 1209 | # Constructing from a packed address 1210 | if isinstance(address, Bytes): 1211 | try: 1212 | self._ip, = struct.unpack('!I', address) 1213 | except struct.error: 1214 | raise AddressValueError(address) # Wrong length. 1215 | return 1216 | 1217 | # Assume input argument to be string or any object representation 1218 | # which converts into a formatted IP string. 1219 | addr_str = str(address) 1220 | self._ip = self._ip_int_from_string(addr_str) 1221 | 1222 | 1223 | class IPv4Network(_BaseV4, _BaseNet): 1224 | 1225 | """This class represents and manipulates 32-bit IPv4 networks. 1226 | 1227 | Attributes: [examples for IPv4Network('1.2.3.4/27')] 1228 | ._ip: 16909060 1229 | .ip: IPv4Address('1.2.3.4') 1230 | .network: IPv4Address('1.2.3.0') 1231 | .hostmask: IPv4Address('0.0.0.31') 1232 | .broadcast: IPv4Address('1.2.3.31') 1233 | .netmask: IPv4Address('255.255.255.224') 1234 | .prefixlen: 27 1235 | 1236 | """ 1237 | 1238 | # the valid octets for host and netmasks. only useful for IPv4. 1239 | _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) 1240 | 1241 | def __init__(self, address, strict=False): 1242 | """Instantiate a new IPv4 network object. 1243 | 1244 | Args: 1245 | address: A string or integer representing the IP [& network]. 1246 | '192.168.1.1/24' 1247 | '192.168.1.1/255.255.255.0' 1248 | '192.168.1.1/0.0.0.255' 1249 | are all functionally the same in IPv4. Similarly, 1250 | '192.168.1.1' 1251 | '192.168.1.1/255.255.255.255' 1252 | '192.168.1.1/32' 1253 | are also functionaly equivalent. That is to say, failing to 1254 | provide a subnetmask will create an object with a mask of /32. 1255 | 1256 | If the mask (portion after the / in the argument) is given in 1257 | dotted quad form, it is treated as a netmask if it starts with a 1258 | non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it 1259 | starts with a zero field (e.g. 0.255.255.255 == /8), with the 1260 | single exception of an all-zero mask which is treated as a 1261 | netmask == /0. If no mask is given, a default of /32 is used. 1262 | 1263 | Additionally, an integer can be passed, so 1264 | IPv4Network('192.168.1.1') == IPv4Network(3232235777). 1265 | or, more generally 1266 | IPv4Network(int(IPv4Network('192.168.1.1'))) == 1267 | IPv4Network('192.168.1.1') 1268 | 1269 | strict: A boolean. If true, ensure that we have been passed 1270 | A true network address, eg, 192.168.1.0/24 and not an 1271 | IP address on a network, eg, 192.168.1.1/24. 1272 | 1273 | Raises: 1274 | AddressValueError: If ipaddr isn't a valid IPv4 address. 1275 | NetmaskValueError: If the netmask isn't valid for 1276 | an IPv4 address. 1277 | ValueError: If strict was True and a network address was not 1278 | supplied. 1279 | 1280 | """ 1281 | _BaseNet.__init__(self, address) 1282 | _BaseV4.__init__(self, address) 1283 | 1284 | # Constructing from an integer or packed bytes. 1285 | if isinstance(address, (int, long, Bytes)): 1286 | self.ip = IPv4Address(address) 1287 | self._ip = self.ip._ip 1288 | self._prefixlen = self._max_prefixlen 1289 | self.netmask = IPv4Address(self._ALL_ONES) 1290 | return 1291 | 1292 | # Assume input argument to be string or any object representation 1293 | # which converts into a formatted IP prefix string. 1294 | addr = str(address).split('/') 1295 | 1296 | if len(addr) > 2: 1297 | raise AddressValueError(address) 1298 | 1299 | self._ip = self._ip_int_from_string(addr[0]) 1300 | self.ip = IPv4Address(self._ip) 1301 | 1302 | if len(addr) == 2: 1303 | mask = addr[1].split('.') 1304 | if len(mask) == 4: 1305 | # We have dotted decimal netmask. 1306 | if self._is_valid_netmask(addr[1]): 1307 | self.netmask = IPv4Address(self._ip_int_from_string( 1308 | addr[1])) 1309 | elif self._is_hostmask(addr[1]): 1310 | self.netmask = IPv4Address( 1311 | self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) 1312 | else: 1313 | raise NetmaskValueError('%s is not a valid netmask' 1314 | % addr[1]) 1315 | 1316 | self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) 1317 | else: 1318 | # We have a netmask in prefix length form. 1319 | if not self._is_valid_netmask(addr[1]): 1320 | raise NetmaskValueError(addr[1]) 1321 | self._prefixlen = int(addr[1]) 1322 | self.netmask = IPv4Address(self._ip_int_from_prefix( 1323 | self._prefixlen)) 1324 | else: 1325 | self._prefixlen = self._max_prefixlen 1326 | self.netmask = IPv4Address(self._ip_int_from_prefix( 1327 | self._prefixlen)) 1328 | if strict: 1329 | if self.ip != self.network: 1330 | raise ValueError('%s has host bits set' % 1331 | self.ip) 1332 | if self._prefixlen == (self._max_prefixlen - 1): 1333 | self.iterhosts = self.__iter__ 1334 | 1335 | def _is_hostmask(self, ip_str): 1336 | """Test if the IP string is a hostmask (rather than a netmask). 1337 | 1338 | Args: 1339 | ip_str: A string, the potential hostmask. 1340 | 1341 | Returns: 1342 | A boolean, True if the IP string is a hostmask. 1343 | 1344 | """ 1345 | bits = ip_str.split('.') 1346 | try: 1347 | parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] 1348 | except ValueError: 1349 | return False 1350 | if len(parts) != len(bits): 1351 | return False 1352 | if parts[0] < parts[-1]: 1353 | return True 1354 | return False 1355 | 1356 | def _is_valid_netmask(self, netmask): 1357 | """Verify that the netmask is valid. 1358 | 1359 | Args: 1360 | netmask: A string, either a prefix or dotted decimal 1361 | netmask. 1362 | 1363 | Returns: 1364 | A boolean, True if the prefix represents a valid IPv4 1365 | netmask. 1366 | 1367 | """ 1368 | mask = netmask.split('.') 1369 | if len(mask) == 4: 1370 | if [x for x in mask if int(x) not in self._valid_mask_octets]: 1371 | return False 1372 | if [y for idx, y in enumerate(mask) if idx > 0 and 1373 | y > mask[idx - 1]]: 1374 | return False 1375 | return True 1376 | try: 1377 | netmask = int(netmask) 1378 | except ValueError: 1379 | return False 1380 | return 0 <= netmask <= self._max_prefixlen 1381 | 1382 | # backwards compatibility 1383 | IsRFC1918 = lambda self: self.is_private 1384 | IsMulticast = lambda self: self.is_multicast 1385 | IsLoopback = lambda self: self.is_loopback 1386 | IsLinkLocal = lambda self: self.is_link_local 1387 | 1388 | 1389 | class _BaseV6(object): 1390 | 1391 | """Base IPv6 object. 1392 | 1393 | The following methods are used by IPv6 objects in both single IP 1394 | addresses and networks. 1395 | 1396 | """ 1397 | 1398 | _ALL_ONES = (2**IPV6LENGTH) - 1 1399 | _HEXTET_COUNT = 8 1400 | _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') 1401 | 1402 | def __init__(self, address): 1403 | self._version = 6 1404 | self._max_prefixlen = IPV6LENGTH 1405 | 1406 | def _ip_int_from_string(self, ip_str): 1407 | """Turn an IPv6 ip_str into an integer. 1408 | 1409 | Args: 1410 | ip_str: A string, the IPv6 ip_str. 1411 | 1412 | Returns: 1413 | A long, the IPv6 ip_str. 1414 | 1415 | Raises: 1416 | AddressValueError: if ip_str isn't a valid IPv6 Address. 1417 | 1418 | """ 1419 | parts = ip_str.split(':') 1420 | 1421 | # An IPv6 address needs at least 2 colons (3 parts). 1422 | if len(parts) < 3: 1423 | raise AddressValueError(ip_str) 1424 | 1425 | # If the address has an IPv4-style suffix, convert it to hexadecimal. 1426 | if '.' in parts[-1]: 1427 | ipv4_int = IPv4Address(parts.pop())._ip 1428 | parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) 1429 | parts.append('%x' % (ipv4_int & 0xFFFF)) 1430 | 1431 | # An IPv6 address can't have more than 8 colons (9 parts). 1432 | if len(parts) > self._HEXTET_COUNT + 1: 1433 | raise AddressValueError(ip_str) 1434 | 1435 | # Disregarding the endpoints, find '::' with nothing in between. 1436 | # This indicates that a run of zeroes has been skipped. 1437 | try: 1438 | skip_index, = ( 1439 | [i for i in xrange(1, len(parts) - 1) if not parts[i]] or 1440 | [None]) 1441 | except ValueError: 1442 | # Can't have more than one '::' 1443 | raise AddressValueError(ip_str) 1444 | 1445 | # parts_hi is the number of parts to copy from above/before the '::' 1446 | # parts_lo is the number of parts to copy from below/after the '::' 1447 | if skip_index is not None: 1448 | # If we found a '::', then check if it also covers the endpoints. 1449 | parts_hi = skip_index 1450 | parts_lo = len(parts) - skip_index - 1 1451 | if not parts[0]: 1452 | parts_hi -= 1 1453 | if parts_hi: 1454 | raise AddressValueError(ip_str) # ^: requires ^:: 1455 | if not parts[-1]: 1456 | parts_lo -= 1 1457 | if parts_lo: 1458 | raise AddressValueError(ip_str) # :$ requires ::$ 1459 | parts_skipped = self._HEXTET_COUNT - (parts_hi + parts_lo) 1460 | if parts_skipped < 1: 1461 | raise AddressValueError(ip_str) 1462 | else: 1463 | # Otherwise, allocate the entire address to parts_hi. The endpoints 1464 | # could still be empty, but _parse_hextet() will check for that. 1465 | if len(parts) != self._HEXTET_COUNT: 1466 | raise AddressValueError(ip_str) 1467 | parts_hi = len(parts) 1468 | parts_lo = 0 1469 | parts_skipped = 0 1470 | 1471 | try: 1472 | # Now, parse the hextets into a 128-bit integer. 1473 | ip_int = 0L 1474 | for i in xrange(parts_hi): 1475 | ip_int <<= 16 1476 | ip_int |= self._parse_hextet(parts[i]) 1477 | ip_int <<= 16 * parts_skipped 1478 | for i in xrange(-parts_lo, 0): 1479 | ip_int <<= 16 1480 | ip_int |= self._parse_hextet(parts[i]) 1481 | return ip_int 1482 | except ValueError: 1483 | raise AddressValueError(ip_str) 1484 | 1485 | def _parse_hextet(self, hextet_str): 1486 | """Convert an IPv6 hextet string into an integer. 1487 | 1488 | Args: 1489 | hextet_str: A string, the number to parse. 1490 | 1491 | Returns: 1492 | The hextet as an integer. 1493 | 1494 | Raises: 1495 | ValueError: if the input isn't strictly a hex number from [0..FFFF]. 1496 | 1497 | """ 1498 | # Whitelist the characters, since int() allows a lot of bizarre stuff. 1499 | if not self._HEX_DIGITS.issuperset(hextet_str): 1500 | raise ValueError 1501 | if len(hextet_str) > 4: 1502 | raise ValueError 1503 | hextet_int = int(hextet_str, 16) 1504 | if hextet_int > 0xFFFF: 1505 | raise ValueError 1506 | return hextet_int 1507 | 1508 | def _compress_hextets(self, hextets): 1509 | """Compresses a list of hextets. 1510 | 1511 | Compresses a list of strings, replacing the longest continuous 1512 | sequence of "0" in the list with "" and adding empty strings at 1513 | the beginning or at the end of the string such that subsequently 1514 | calling ":".join(hextets) will produce the compressed version of 1515 | the IPv6 address. 1516 | 1517 | Args: 1518 | hextets: A list of strings, the hextets to compress. 1519 | 1520 | Returns: 1521 | A list of strings. 1522 | 1523 | """ 1524 | best_doublecolon_start = -1 1525 | best_doublecolon_len = 0 1526 | doublecolon_start = -1 1527 | doublecolon_len = 0 1528 | for index in range(len(hextets)): 1529 | if hextets[index] == '0': 1530 | doublecolon_len += 1 1531 | if doublecolon_start == -1: 1532 | # Start of a sequence of zeros. 1533 | doublecolon_start = index 1534 | if doublecolon_len > best_doublecolon_len: 1535 | # This is the longest sequence of zeros so far. 1536 | best_doublecolon_len = doublecolon_len 1537 | best_doublecolon_start = doublecolon_start 1538 | else: 1539 | doublecolon_len = 0 1540 | doublecolon_start = -1 1541 | 1542 | if best_doublecolon_len > 1: 1543 | best_doublecolon_end = (best_doublecolon_start + 1544 | best_doublecolon_len) 1545 | # For zeros at the end of the address. 1546 | if best_doublecolon_end == len(hextets): 1547 | hextets += [''] 1548 | hextets[best_doublecolon_start:best_doublecolon_end] = [''] 1549 | # For zeros at the beginning of the address. 1550 | if best_doublecolon_start == 0: 1551 | hextets = [''] + hextets 1552 | 1553 | return hextets 1554 | 1555 | def _string_from_ip_int(self, ip_int=None): 1556 | """Turns a 128-bit integer into hexadecimal notation. 1557 | 1558 | Args: 1559 | ip_int: An integer, the IP address. 1560 | 1561 | Returns: 1562 | A string, the hexadecimal representation of the address. 1563 | 1564 | Raises: 1565 | ValueError: The address is bigger than 128 bits of all ones. 1566 | 1567 | """ 1568 | if not ip_int and ip_int != 0: 1569 | ip_int = int(self._ip) 1570 | 1571 | if ip_int > self._ALL_ONES: 1572 | raise ValueError('IPv6 address is too large') 1573 | 1574 | hex_str = '%032x' % ip_int 1575 | hextets = [] 1576 | for x in range(0, 32, 4): 1577 | hextets.append('%x' % int(hex_str[x:x+4], 16)) 1578 | 1579 | hextets = self._compress_hextets(hextets) 1580 | return ':'.join(hextets) 1581 | 1582 | def _explode_shorthand_ip_string(self): 1583 | """Expand a shortened IPv6 address. 1584 | 1585 | Args: 1586 | ip_str: A string, the IPv6 address. 1587 | 1588 | Returns: 1589 | A string, the expanded IPv6 address. 1590 | 1591 | """ 1592 | if isinstance(self, _BaseNet): 1593 | ip_str = str(self.ip) 1594 | else: 1595 | ip_str = str(self) 1596 | 1597 | ip_int = self._ip_int_from_string(ip_str) 1598 | parts = [] 1599 | for i in xrange(self._HEXTET_COUNT): 1600 | parts.append('%04x' % (ip_int & 0xFFFF)) 1601 | ip_int >>= 16 1602 | parts.reverse() 1603 | if isinstance(self, _BaseNet): 1604 | return '%s/%d' % (':'.join(parts), self.prefixlen) 1605 | return ':'.join(parts) 1606 | 1607 | @property 1608 | def max_prefixlen(self): 1609 | return self._max_prefixlen 1610 | 1611 | @property 1612 | def packed(self): 1613 | """The binary representation of this address.""" 1614 | return v6_int_to_packed(self._ip) 1615 | 1616 | @property 1617 | def version(self): 1618 | return self._version 1619 | 1620 | @property 1621 | def is_multicast(self): 1622 | """Test if the address is reserved for multicast use. 1623 | 1624 | Returns: 1625 | A boolean, True if the address is a multicast address. 1626 | See RFC 2373 2.7 for details. 1627 | 1628 | """ 1629 | return self in IPv6Network('ff00::/8') 1630 | 1631 | @property 1632 | def is_reserved(self): 1633 | """Test if the address is otherwise IETF reserved. 1634 | 1635 | Returns: 1636 | A boolean, True if the address is within one of the 1637 | reserved IPv6 Network ranges. 1638 | 1639 | """ 1640 | return (self in IPv6Network('::/8') or 1641 | self in IPv6Network('100::/8') or 1642 | self in IPv6Network('200::/7') or 1643 | self in IPv6Network('400::/6') or 1644 | self in IPv6Network('800::/5') or 1645 | self in IPv6Network('1000::/4') or 1646 | self in IPv6Network('4000::/3') or 1647 | self in IPv6Network('6000::/3') or 1648 | self in IPv6Network('8000::/3') or 1649 | self in IPv6Network('A000::/3') or 1650 | self in IPv6Network('C000::/3') or 1651 | self in IPv6Network('E000::/4') or 1652 | self in IPv6Network('F000::/5') or 1653 | self in IPv6Network('F800::/6') or 1654 | self in IPv6Network('FE00::/9')) 1655 | 1656 | @property 1657 | def is_unspecified(self): 1658 | """Test if the address is unspecified. 1659 | 1660 | Returns: 1661 | A boolean, True if this is the unspecified address as defined in 1662 | RFC 2373 2.5.2. 1663 | 1664 | """ 1665 | return self._ip == 0 and getattr(self, '_prefixlen', 128) == 128 1666 | 1667 | @property 1668 | def is_loopback(self): 1669 | """Test if the address is a loopback address. 1670 | 1671 | Returns: 1672 | A boolean, True if the address is a loopback address as defined in 1673 | RFC 2373 2.5.3. 1674 | 1675 | """ 1676 | return self._ip == 1 and getattr(self, '_prefixlen', 128) == 128 1677 | 1678 | @property 1679 | def is_link_local(self): 1680 | """Test if the address is reserved for link-local. 1681 | 1682 | Returns: 1683 | A boolean, True if the address is reserved per RFC 4291. 1684 | 1685 | """ 1686 | return self in IPv6Network('fe80::/10') 1687 | 1688 | @property 1689 | def is_site_local(self): 1690 | """Test if the address is reserved for site-local. 1691 | 1692 | Note that the site-local address space has been deprecated by RFC 3879. 1693 | Use is_private to test if this address is in the space of unique local 1694 | addresses as defined by RFC 4193. 1695 | 1696 | Returns: 1697 | A boolean, True if the address is reserved per RFC 3513 2.5.6. 1698 | 1699 | """ 1700 | return self in IPv6Network('fec0::/10') 1701 | 1702 | @property 1703 | def is_private(self): 1704 | """Test if this address is allocated for private networks. 1705 | 1706 | Returns: 1707 | A boolean, True if the address is reserved per RFC 4193. 1708 | 1709 | """ 1710 | return self in IPv6Network('fc00::/7') 1711 | 1712 | @property 1713 | def ipv4_mapped(self): 1714 | """Return the IPv4 mapped address. 1715 | 1716 | Returns: 1717 | If the IPv6 address is a v4 mapped address, return the 1718 | IPv4 mapped address. Return None otherwise. 1719 | 1720 | """ 1721 | if (self._ip >> 32) != 0xFFFF: 1722 | return None 1723 | return IPv4Address(self._ip & 0xFFFFFFFF) 1724 | 1725 | @property 1726 | def teredo(self): 1727 | """Tuple of embedded teredo IPs. 1728 | 1729 | Returns: 1730 | Tuple of the (server, client) IPs or None if the address 1731 | doesn't appear to be a teredo address (doesn't start with 1732 | 2001::/32) 1733 | 1734 | """ 1735 | if (self._ip >> 96) != 0x20010000: 1736 | return None 1737 | return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), 1738 | IPv4Address(~self._ip & 0xFFFFFFFF)) 1739 | 1740 | @property 1741 | def sixtofour(self): 1742 | """Return the IPv4 6to4 embedded address. 1743 | 1744 | Returns: 1745 | The IPv4 6to4-embedded address if present or None if the 1746 | address doesn't appear to contain a 6to4 embedded address. 1747 | 1748 | """ 1749 | if (self._ip >> 112) != 0x2002: 1750 | return None 1751 | return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) 1752 | 1753 | 1754 | class IPv6Address(_BaseV6, _BaseIP): 1755 | 1756 | """Represent and manipulate single IPv6 Addresses. 1757 | """ 1758 | 1759 | def __init__(self, address): 1760 | """Instantiate a new IPv6 address object. 1761 | 1762 | Args: 1763 | address: A string or integer representing the IP 1764 | 1765 | Additionally, an integer can be passed, so 1766 | IPv6Address('2001:4860::') == 1767 | IPv6Address(42541956101370907050197289607612071936L). 1768 | or, more generally 1769 | IPv6Address(IPv6Address('2001:4860::')._ip) == 1770 | IPv6Address('2001:4860::') 1771 | 1772 | Raises: 1773 | AddressValueError: If address isn't a valid IPv6 address. 1774 | 1775 | """ 1776 | _BaseV6.__init__(self, address) 1777 | 1778 | # Efficient constructor from integer. 1779 | if isinstance(address, (int, long)): 1780 | self._ip = address 1781 | if address < 0 or address > self._ALL_ONES: 1782 | raise AddressValueError(address) 1783 | return 1784 | 1785 | # Constructing from a packed address 1786 | if isinstance(address, Bytes): 1787 | try: 1788 | hi, lo = struct.unpack('!QQ', address) 1789 | except struct.error: 1790 | raise AddressValueError(address) # Wrong length. 1791 | self._ip = (hi << 64) | lo 1792 | return 1793 | 1794 | # Assume input argument to be string or any object representation 1795 | # which converts into a formatted IP string. 1796 | addr_str = str(address) 1797 | if not addr_str: 1798 | raise AddressValueError('') 1799 | 1800 | self._ip = self._ip_int_from_string(addr_str) 1801 | 1802 | 1803 | class IPv6Network(_BaseV6, _BaseNet): 1804 | 1805 | """This class represents and manipulates 128-bit IPv6 networks. 1806 | 1807 | Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')] 1808 | .ip: IPv6Address('2001:658:22a:cafe:200::1') 1809 | .network: IPv6Address('2001:658:22a:cafe::') 1810 | .hostmask: IPv6Address('::ffff:ffff:ffff:ffff') 1811 | .broadcast: IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff') 1812 | .netmask: IPv6Address('ffff:ffff:ffff:ffff::') 1813 | .prefixlen: 64 1814 | 1815 | """ 1816 | 1817 | 1818 | def __init__(self, address, strict=False): 1819 | """Instantiate a new IPv6 Network object. 1820 | 1821 | Args: 1822 | address: A string or integer representing the IPv6 network or the IP 1823 | and prefix/netmask. 1824 | '2001:4860::/128' 1825 | '2001:4860:0000:0000:0000:0000:0000:0000/128' 1826 | '2001:4860::' 1827 | are all functionally the same in IPv6. That is to say, 1828 | failing to provide a subnetmask will create an object with 1829 | a mask of /128. 1830 | 1831 | Additionally, an integer can be passed, so 1832 | IPv6Network('2001:4860::') == 1833 | IPv6Network(42541956101370907050197289607612071936L). 1834 | or, more generally 1835 | IPv6Network(IPv6Network('2001:4860::')._ip) == 1836 | IPv6Network('2001:4860::') 1837 | 1838 | strict: A boolean. If true, ensure that we have been passed 1839 | A true network address, eg, 192.168.1.0/24 and not an 1840 | IP address on a network, eg, 192.168.1.1/24. 1841 | 1842 | Raises: 1843 | AddressValueError: If address isn't a valid IPv6 address. 1844 | NetmaskValueError: If the netmask isn't valid for 1845 | an IPv6 address. 1846 | ValueError: If strict was True and a network address was not 1847 | supplied. 1848 | 1849 | """ 1850 | _BaseNet.__init__(self, address) 1851 | _BaseV6.__init__(self, address) 1852 | 1853 | # Constructing from an integer or packed bytes. 1854 | if isinstance(address, (int, long, Bytes)): 1855 | self.ip = IPv6Address(address) 1856 | self._ip = self.ip._ip 1857 | self._prefixlen = self._max_prefixlen 1858 | self.netmask = IPv6Address(self._ALL_ONES) 1859 | return 1860 | 1861 | # Assume input argument to be string or any object representation 1862 | # which converts into a formatted IP prefix string. 1863 | addr = str(address).split('/') 1864 | 1865 | if len(addr) > 2: 1866 | raise AddressValueError(address) 1867 | 1868 | self._ip = self._ip_int_from_string(addr[0]) 1869 | self.ip = IPv6Address(self._ip) 1870 | 1871 | if len(addr) == 2: 1872 | if self._is_valid_netmask(addr[1]): 1873 | self._prefixlen = int(addr[1]) 1874 | else: 1875 | raise NetmaskValueError(addr[1]) 1876 | else: 1877 | self._prefixlen = self._max_prefixlen 1878 | 1879 | self.netmask = IPv6Address(self._ip_int_from_prefix(self._prefixlen)) 1880 | 1881 | if strict: 1882 | if self.ip != self.network: 1883 | raise ValueError('%s has host bits set' % 1884 | self.ip) 1885 | if self._prefixlen == (self._max_prefixlen - 1): 1886 | self.iterhosts = self.__iter__ 1887 | 1888 | def _is_valid_netmask(self, prefixlen): 1889 | """Verify that the netmask/prefixlen is valid. 1890 | 1891 | Args: 1892 | prefixlen: A string, the netmask in prefix length format. 1893 | 1894 | Returns: 1895 | A boolean, True if the prefix represents a valid IPv6 1896 | netmask. 1897 | 1898 | """ 1899 | try: 1900 | prefixlen = int(prefixlen) 1901 | except ValueError: 1902 | return False 1903 | return 0 <= prefixlen <= self._max_prefixlen 1904 | 1905 | @property 1906 | def with_netmask(self): 1907 | return self.with_prefixlen 1908 | -------------------------------------------------------------------------------- /embedded_ipaddr/ipaddr_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright 2007 Google Inc. 4 | # Licensed to PSF under a Contributor Agreement. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """Unittest for ipaddr module.""" 19 | 20 | 21 | import unittest 22 | import time 23 | import ipaddr 24 | 25 | # Compatibility function to cast str to bytes objects 26 | if issubclass(ipaddr.Bytes, str): 27 | _cb = ipaddr.Bytes 28 | else: 29 | _cb = lambda bytestr: bytes(bytestr, 'charmap') 30 | 31 | class IpaddrUnitTest(unittest.TestCase): 32 | 33 | def setUp(self): 34 | self.ipv4 = ipaddr.IPv4Network('1.2.3.4/24') 35 | self.ipv4_hostmask = ipaddr.IPv4Network('10.0.0.1/0.255.255.255') 36 | self.ipv6 = ipaddr.IPv6Network('2001:658:22a:cafe:200:0:0:1/64') 37 | 38 | def tearDown(self): 39 | del(self.ipv4) 40 | del(self.ipv4_hostmask) 41 | del(self.ipv6) 42 | del(self) 43 | 44 | def testRepr(self): 45 | self.assertEqual("IPv4Network('1.2.3.4/32')", 46 | repr(ipaddr.IPv4Network('1.2.3.4'))) 47 | self.assertEqual("IPv6Network('::1/128')", 48 | repr(ipaddr.IPv6Network('::1'))) 49 | 50 | def testAutoMasking(self): 51 | addr1 = ipaddr.IPv4Network('1.1.1.255/24') 52 | addr1_masked = ipaddr.IPv4Network('1.1.1.0/24') 53 | self.assertEqual(addr1_masked, addr1.masked()) 54 | 55 | addr2 = ipaddr.IPv6Network('2000:cafe::efac:100/96') 56 | addr2_masked = ipaddr.IPv6Network('2000:cafe::/96') 57 | self.assertEqual(addr2_masked, addr2.masked()) 58 | 59 | # issue57 60 | def testAddressIntMath(self): 61 | self.assertEqual(ipaddr.IPv4Address('1.1.1.1') + 255, 62 | ipaddr.IPv4Address('1.1.2.0')) 63 | self.assertEqual(ipaddr.IPv4Address('1.1.1.1') - 256, 64 | ipaddr.IPv4Address('1.1.0.1')) 65 | self.assertEqual(ipaddr.IPv6Address('::1') + (2**16 - 2), 66 | ipaddr.IPv6Address('::ffff')) 67 | self.assertEqual(ipaddr.IPv6Address('::ffff') - (2**16 - 2), 68 | ipaddr.IPv6Address('::1')) 69 | 70 | def testInvalidStrings(self): 71 | def AssertInvalidIP(ip_str): 72 | self.assertRaises(ValueError, ipaddr.IPAddress, ip_str) 73 | AssertInvalidIP("") 74 | AssertInvalidIP("016.016.016.016") 75 | AssertInvalidIP("016.016.016") 76 | AssertInvalidIP("016.016") 77 | AssertInvalidIP("016") 78 | AssertInvalidIP("000.000.000.000") 79 | AssertInvalidIP("000") 80 | AssertInvalidIP("0x0a.0x0a.0x0a.0x0a") 81 | AssertInvalidIP("0x0a.0x0a.0x0a") 82 | AssertInvalidIP("0x0a.0x0a") 83 | AssertInvalidIP("0x0a") 84 | AssertInvalidIP("42.42.42.42.42") 85 | AssertInvalidIP("42.42.42") 86 | AssertInvalidIP("42.42") 87 | AssertInvalidIP("42") 88 | AssertInvalidIP("42..42.42") 89 | AssertInvalidIP("42..42.42.42") 90 | AssertInvalidIP("42.42.42.42.") 91 | AssertInvalidIP("42.42.42.42...") 92 | AssertInvalidIP(".42.42.42.42") 93 | AssertInvalidIP("...42.42.42.42") 94 | AssertInvalidIP("42.42.42.-0") 95 | AssertInvalidIP("42.42.42.+0") 96 | AssertInvalidIP(".") 97 | AssertInvalidIP("...") 98 | AssertInvalidIP("bogus") 99 | AssertInvalidIP("bogus.com") 100 | AssertInvalidIP("192.168.0.1.com") 101 | AssertInvalidIP("12345.67899.-54321.-98765") 102 | AssertInvalidIP("257.0.0.0") 103 | AssertInvalidIP("42.42.42.-42") 104 | AssertInvalidIP("3ffe::1.net") 105 | AssertInvalidIP("3ffe::1::1") 106 | AssertInvalidIP("1::2::3::4:5") 107 | AssertInvalidIP("::7:6:5:4:3:2:") 108 | AssertInvalidIP(":6:5:4:3:2:1::") 109 | AssertInvalidIP("2001::db:::1") 110 | AssertInvalidIP("FEDC:9878") 111 | AssertInvalidIP("+1.+2.+3.4") 112 | AssertInvalidIP("1.2.3.4e0") 113 | AssertInvalidIP("::7:6:5:4:3:2:1:0") 114 | AssertInvalidIP("7:6:5:4:3:2:1:0::") 115 | AssertInvalidIP("9:8:7:6:5:4:3::2:1") 116 | AssertInvalidIP("0:1:2:3::4:5:6:7") 117 | AssertInvalidIP("3ffe:0:0:0:0:0:0:0:1") 118 | AssertInvalidIP("3ffe::10000") 119 | AssertInvalidIP("3ffe::goog") 120 | AssertInvalidIP("3ffe::-0") 121 | AssertInvalidIP("3ffe::+0") 122 | AssertInvalidIP("3ffe::-1") 123 | AssertInvalidIP(":") 124 | AssertInvalidIP(":::") 125 | AssertInvalidIP("::1.2.3") 126 | AssertInvalidIP("::1.2.3.4.5") 127 | AssertInvalidIP("::1.2.3.4:") 128 | AssertInvalidIP("1.2.3.4::") 129 | AssertInvalidIP("2001:db8::1:") 130 | AssertInvalidIP(":2001:db8::1") 131 | AssertInvalidIP(":1:2:3:4:5:6:7") 132 | AssertInvalidIP("1:2:3:4:5:6:7:") 133 | AssertInvalidIP(":1:2:3:4:5:6:") 134 | AssertInvalidIP("192.0.2.1/32") 135 | AssertInvalidIP("2001:db8::1/128") 136 | AssertInvalidIP("02001:db8::") 137 | 138 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, '') 139 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, 140 | 'google.com') 141 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, 142 | '::1.2.3.4') 143 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, '') 144 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 145 | 'google.com') 146 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 147 | '1.2.3.4') 148 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 149 | 'cafe:cafe::/128/190') 150 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 151 | '1234:axy::b') 152 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address, 153 | '1234:axy::b') 154 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address, 155 | '2001:db8:::1') 156 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Address, 157 | '2001:888888::1') 158 | self.assertRaises(ipaddr.AddressValueError, 159 | ipaddr.IPv4Address(1)._ip_int_from_string, 160 | '1.a.2.3') 161 | self.assertEqual(False, ipaddr.IPv4Network(1)._is_hostmask('1.a.2.3')) 162 | 163 | def testGetNetwork(self): 164 | self.assertEqual(int(self.ipv4.network), 16909056) 165 | self.assertEqual(str(self.ipv4.network), '1.2.3.0') 166 | self.assertEqual(str(self.ipv4_hostmask.network), '10.0.0.0') 167 | 168 | self.assertEqual(int(self.ipv6.network), 169 | 42540616829182469433403647294022090752) 170 | self.assertEqual(str(self.ipv6.network), 171 | '2001:658:22a:cafe::') 172 | self.assertEqual(str(self.ipv6.hostmask), 173 | '::ffff:ffff:ffff:ffff') 174 | 175 | def testBadVersionComparison(self): 176 | # These should always raise TypeError 177 | v4addr = ipaddr.IPAddress('1.1.1.1') 178 | v4net = ipaddr.IPNetwork('1.1.1.1') 179 | v6addr = ipaddr.IPAddress('::1') 180 | v6net = ipaddr.IPAddress('::1') 181 | 182 | self.assertRaises(TypeError, v4addr.__lt__, v6addr) 183 | self.assertRaises(TypeError, v4addr.__gt__, v6addr) 184 | self.assertRaises(TypeError, v4net.__lt__, v6net) 185 | self.assertRaises(TypeError, v4net.__gt__, v6net) 186 | 187 | self.assertRaises(TypeError, v6addr.__lt__, v4addr) 188 | self.assertRaises(TypeError, v6addr.__gt__, v4addr) 189 | self.assertRaises(TypeError, v6net.__lt__, v4net) 190 | self.assertRaises(TypeError, v6net.__gt__, v4net) 191 | 192 | def testMixedTypeComparison(self): 193 | v4addr = ipaddr.IPAddress('1.1.1.1') 194 | v4net = ipaddr.IPNetwork('1.1.1.1/32') 195 | v6addr = ipaddr.IPAddress('::1') 196 | v6net = ipaddr.IPNetwork('::1/128') 197 | 198 | self.assertFalse(v4net.__contains__(v6net)) 199 | self.assertFalse(v6net.__contains__(v4net)) 200 | 201 | self.assertRaises(TypeError, lambda: v4addr < v4net) 202 | self.assertRaises(TypeError, lambda: v4addr > v4net) 203 | self.assertRaises(TypeError, lambda: v4net < v4addr) 204 | self.assertRaises(TypeError, lambda: v4net > v4addr) 205 | 206 | self.assertRaises(TypeError, lambda: v6addr < v6net) 207 | self.assertRaises(TypeError, lambda: v6addr > v6net) 208 | self.assertRaises(TypeError, lambda: v6net < v6addr) 209 | self.assertRaises(TypeError, lambda: v6net > v6addr) 210 | 211 | # with get_mixed_type_key, you can sort addresses and network. 212 | self.assertEqual([v4addr, v4net], sorted([v4net, v4addr], 213 | key=ipaddr.get_mixed_type_key)) 214 | self.assertEqual([v6addr, v6net], sorted([v6net, v6addr], 215 | key=ipaddr.get_mixed_type_key)) 216 | 217 | def testIpFromInt(self): 218 | self.assertEqual(self.ipv4.ip, ipaddr.IPv4Network(16909060).ip) 219 | self.assertRaises(ipaddr.AddressValueError, 220 | ipaddr.IPv4Network, 2**32) 221 | self.assertRaises(ipaddr.AddressValueError, 222 | ipaddr.IPv4Network, -1) 223 | 224 | ipv4 = ipaddr.IPNetwork('1.2.3.4') 225 | ipv6 = ipaddr.IPNetwork('2001:658:22a:cafe:200:0:0:1') 226 | self.assertEqual(ipv4, ipaddr.IPNetwork(int(ipv4))) 227 | self.assertEqual(ipv6, ipaddr.IPNetwork(int(ipv6))) 228 | 229 | v6_int = 42540616829182469433547762482097946625 230 | self.assertEqual(self.ipv6.ip, ipaddr.IPv6Network(v6_int).ip) 231 | self.assertRaises(ipaddr.AddressValueError, 232 | ipaddr.IPv6Network, 2**128) 233 | self.assertRaises(ipaddr.AddressValueError, 234 | ipaddr.IPv6Network, -1) 235 | 236 | self.assertEqual(ipaddr.IPNetwork(self.ipv4.ip).version, 4) 237 | self.assertEqual(ipaddr.IPNetwork(self.ipv6.ip).version, 6) 238 | 239 | def testIpFromPacked(self): 240 | ip = ipaddr.IPNetwork 241 | 242 | self.assertEqual(self.ipv4.ip, 243 | ip(_cb('\x01\x02\x03\x04')).ip) 244 | self.assertEqual(ip('255.254.253.252'), 245 | ip(_cb('\xff\xfe\xfd\xfc'))) 246 | self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 3)) 247 | self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 5)) 248 | self.assertEqual(self.ipv6.ip, 249 | ip(_cb('\x20\x01\x06\x58\x02\x2a\xca\xfe' 250 | '\x02\x00\x00\x00\x00\x00\x00\x01')).ip) 251 | self.assertEqual(ip('ffff:2:3:4:ffff::'), 252 | ip(_cb('\xff\xff\x00\x02\x00\x03\x00\x04' + 253 | '\xff\xff' + '\x00' * 6))) 254 | self.assertEqual(ip('::'), 255 | ip(_cb('\x00' * 16))) 256 | self.assertRaises(ValueError, ip, _cb('\x00' * 15)) 257 | self.assertRaises(ValueError, ip, _cb('\x00' * 17)) 258 | 259 | def testGetIp(self): 260 | self.assertEqual(int(self.ipv4.ip), 16909060) 261 | self.assertEqual(str(self.ipv4.ip), '1.2.3.4') 262 | self.assertEqual(str(self.ipv4_hostmask.ip), '10.0.0.1') 263 | 264 | self.assertEqual(int(self.ipv6.ip), 265 | 42540616829182469433547762482097946625) 266 | self.assertEqual(str(self.ipv6.ip), 267 | '2001:658:22a:cafe:200::1') 268 | 269 | def testGetNetmask(self): 270 | self.assertEqual(int(self.ipv4.netmask), 4294967040L) 271 | self.assertEqual(str(self.ipv4.netmask), '255.255.255.0') 272 | self.assertEqual(str(self.ipv4_hostmask.netmask), '255.0.0.0') 273 | self.assertEqual(int(self.ipv6.netmask), 274 | 340282366920938463444927863358058659840) 275 | self.assertEqual(self.ipv6.prefixlen, 64) 276 | 277 | def testZeroNetmask(self): 278 | ipv4_zero_netmask = ipaddr.IPv4Network('1.2.3.4/0') 279 | self.assertEqual(int(ipv4_zero_netmask.netmask), 0) 280 | self.assertTrue(ipv4_zero_netmask._is_valid_netmask(str(0))) 281 | 282 | ipv6_zero_netmask = ipaddr.IPv6Network('::1/0') 283 | self.assertEqual(int(ipv6_zero_netmask.netmask), 0) 284 | self.assertTrue(ipv6_zero_netmask._is_valid_netmask(str(0))) 285 | 286 | def testGetBroadcast(self): 287 | self.assertEqual(int(self.ipv4.broadcast), 16909311L) 288 | self.assertEqual(str(self.ipv4.broadcast), '1.2.3.255') 289 | 290 | self.assertEqual(int(self.ipv6.broadcast), 291 | 42540616829182469451850391367731642367) 292 | self.assertEqual(str(self.ipv6.broadcast), 293 | '2001:658:22a:cafe:ffff:ffff:ffff:ffff') 294 | 295 | def testGetPrefixlen(self): 296 | self.assertEqual(self.ipv4.prefixlen, 24) 297 | 298 | self.assertEqual(self.ipv6.prefixlen, 64) 299 | 300 | def testGetSupernet(self): 301 | self.assertEqual(self.ipv4.supernet().prefixlen, 23) 302 | self.assertEqual(str(self.ipv4.supernet().network), '1.2.2.0') 303 | self.assertEqual(ipaddr.IPv4Network('0.0.0.0/0').supernet(), 304 | ipaddr.IPv4Network('0.0.0.0/0')) 305 | 306 | self.assertEqual(self.ipv6.supernet().prefixlen, 63) 307 | self.assertEqual(str(self.ipv6.supernet().network), 308 | '2001:658:22a:cafe::') 309 | self.assertEqual(ipaddr.IPv6Network('::0/0').supernet(), 310 | ipaddr.IPv6Network('::0/0')) 311 | 312 | def testGetSupernet3(self): 313 | self.assertEqual(self.ipv4.supernet(3).prefixlen, 21) 314 | self.assertEqual(str(self.ipv4.supernet(3).network), '1.2.0.0') 315 | 316 | self.assertEqual(self.ipv6.supernet(3).prefixlen, 61) 317 | self.assertEqual(str(self.ipv6.supernet(3).network), 318 | '2001:658:22a:caf8::') 319 | 320 | def testGetSupernet4(self): 321 | self.assertRaises(ValueError, self.ipv4.supernet, prefixlen_diff=2, 322 | new_prefix=1) 323 | self.assertRaises(ValueError, self.ipv4.supernet, new_prefix=25) 324 | self.assertEqual(self.ipv4.supernet(prefixlen_diff=2), 325 | self.ipv4.supernet(new_prefix=22)) 326 | 327 | self.assertRaises(ValueError, self.ipv6.supernet, prefixlen_diff=2, 328 | new_prefix=1) 329 | self.assertRaises(ValueError, self.ipv6.supernet, new_prefix=65) 330 | self.assertEqual(self.ipv6.supernet(prefixlen_diff=2), 331 | self.ipv6.supernet(new_prefix=62)) 332 | 333 | def testIterSubnets(self): 334 | self.assertEqual(self.ipv4.subnet(), list(self.ipv4.iter_subnets())) 335 | self.assertEqual(self.ipv6.subnet(), list(self.ipv6.iter_subnets())) 336 | 337 | def testIterHosts(self): 338 | self.assertEqual([ipaddr.IPv4Address('2.0.0.0'), 339 | ipaddr.IPv4Address('2.0.0.1')], 340 | list(ipaddr.IPNetwork('2.0.0.0/31').iterhosts())) 341 | 342 | def testFancySubnetting(self): 343 | self.assertEqual(sorted(self.ipv4.subnet(prefixlen_diff=3)), 344 | sorted(self.ipv4.subnet(new_prefix=27))) 345 | self.assertRaises(ValueError, self.ipv4.subnet, new_prefix=23) 346 | self.assertRaises(ValueError, self.ipv4.subnet, 347 | prefixlen_diff=3, new_prefix=27) 348 | self.assertEqual(sorted(self.ipv6.subnet(prefixlen_diff=4)), 349 | sorted(self.ipv6.subnet(new_prefix=68))) 350 | self.assertRaises(ValueError, self.ipv6.subnet, new_prefix=63) 351 | self.assertRaises(ValueError, self.ipv6.subnet, 352 | prefixlen_diff=4, new_prefix=68) 353 | 354 | def testGetSubnet(self): 355 | self.assertEqual(self.ipv4.subnet()[0].prefixlen, 25) 356 | self.assertEqual(str(self.ipv4.subnet()[0].network), '1.2.3.0') 357 | self.assertEqual(str(self.ipv4.subnet()[1].network), '1.2.3.128') 358 | 359 | self.assertEqual(self.ipv6.subnet()[0].prefixlen, 65) 360 | 361 | def testGetSubnetForSingle32(self): 362 | ip = ipaddr.IPv4Network('1.2.3.4/32') 363 | subnets1 = [str(x) for x in ip.subnet()] 364 | subnets2 = [str(x) for x in ip.subnet(2)] 365 | self.assertEqual(subnets1, ['1.2.3.4/32']) 366 | self.assertEqual(subnets1, subnets2) 367 | 368 | def testGetSubnetForSingle128(self): 369 | ip = ipaddr.IPv6Network('::1/128') 370 | subnets1 = [str(x) for x in ip.subnet()] 371 | subnets2 = [str(x) for x in ip.subnet(2)] 372 | self.assertEqual(subnets1, ['::1/128']) 373 | self.assertEqual(subnets1, subnets2) 374 | 375 | def testSubnet2(self): 376 | ips = [str(x) for x in self.ipv4.subnet(2)] 377 | self.assertEqual( 378 | ips, 379 | ['1.2.3.0/26', '1.2.3.64/26', '1.2.3.128/26', '1.2.3.192/26']) 380 | 381 | ipsv6 = [str(x) for x in self.ipv6.subnet(2)] 382 | self.assertEqual( 383 | ipsv6, 384 | ['2001:658:22a:cafe::/66', 385 | '2001:658:22a:cafe:4000::/66', 386 | '2001:658:22a:cafe:8000::/66', 387 | '2001:658:22a:cafe:c000::/66']) 388 | 389 | def testSubnetFailsForLargeCidrDiff(self): 390 | self.assertRaises(ValueError, self.ipv4.subnet, 9) 391 | self.assertRaises(ValueError, self.ipv6.subnet, 65) 392 | 393 | def testSupernetFailsForLargeCidrDiff(self): 394 | self.assertRaises(ValueError, self.ipv4.supernet, 25) 395 | self.assertRaises(ValueError, self.ipv6.supernet, 65) 396 | 397 | def testSubnetFailsForNegativeCidrDiff(self): 398 | self.assertRaises(ValueError, self.ipv4.subnet, -1) 399 | self.assertRaises(ValueError, self.ipv6.subnet, -1) 400 | 401 | def testGetNumHosts(self): 402 | self.assertEqual(self.ipv4.numhosts, 256) 403 | self.assertEqual(self.ipv4.subnet()[0].numhosts, 128) 404 | self.assertEqual(self.ipv4.supernet().numhosts, 512) 405 | 406 | self.assertEqual(self.ipv6.numhosts, 18446744073709551616) 407 | self.assertEqual(self.ipv6.subnet()[0].numhosts, 9223372036854775808) 408 | self.assertEqual(self.ipv6.supernet().numhosts, 36893488147419103232) 409 | 410 | def testContains(self): 411 | self.assertTrue(ipaddr.IPv4Network('1.2.3.128/25') in self.ipv4) 412 | self.assertFalse(ipaddr.IPv4Network('1.2.4.1/24') in self.ipv4) 413 | self.assertTrue(self.ipv4 in self.ipv4) 414 | self.assertTrue(self.ipv6 in self.ipv6) 415 | # We can test addresses and string as well. 416 | addr1 = ipaddr.IPv4Address('1.2.3.37') 417 | self.assertTrue(addr1 in self.ipv4) 418 | # issue 61, bad network comparison on like-ip'd network objects 419 | # with identical broadcast addresses. 420 | self.assertFalse(ipaddr.IPv4Network('1.1.0.0/16').__contains__( 421 | ipaddr.IPv4Network('1.0.0.0/15'))) 422 | 423 | def testBadAddress(self): 424 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, 425 | 'poop') 426 | self.assertRaises(ipaddr.AddressValueError, 427 | ipaddr.IPv4Network, '1.2.3.256') 428 | 429 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 430 | 'poopv6') 431 | self.assertRaises(ipaddr.AddressValueError, 432 | ipaddr.IPv4Network, '1.2.3.4/32/24') 433 | self.assertRaises(ipaddr.AddressValueError, 434 | ipaddr.IPv4Network, '10/8') 435 | self.assertRaises(ipaddr.AddressValueError, 436 | ipaddr.IPv6Network, '10/8') 437 | 438 | 439 | def testBadNetMask(self): 440 | self.assertRaises(ipaddr.NetmaskValueError, 441 | ipaddr.IPv4Network, '1.2.3.4/') 442 | self.assertRaises(ipaddr.NetmaskValueError, 443 | ipaddr.IPv4Network, '1.2.3.4/33') 444 | self.assertRaises(ipaddr.NetmaskValueError, 445 | ipaddr.IPv4Network, '1.2.3.4/254.254.255.256') 446 | self.assertRaises(ipaddr.NetmaskValueError, 447 | ipaddr.IPv4Network, '1.1.1.1/240.255.0.0') 448 | self.assertRaises(ipaddr.NetmaskValueError, 449 | ipaddr.IPv6Network, '::1/') 450 | self.assertRaises(ipaddr.NetmaskValueError, 451 | ipaddr.IPv6Network, '::1/129') 452 | 453 | def testNth(self): 454 | self.assertEqual(str(self.ipv4[5]), '1.2.3.5') 455 | self.assertRaises(IndexError, self.ipv4.__getitem__, 256) 456 | 457 | self.assertEqual(str(self.ipv6[5]), 458 | '2001:658:22a:cafe::5') 459 | 460 | def testGetitem(self): 461 | # http://code.google.com/p/ipaddr-py/issues/detail?id=15 462 | addr = ipaddr.IPv4Network('172.31.255.128/255.255.255.240') 463 | self.assertEqual(28, addr.prefixlen) 464 | addr_list = list(addr) 465 | self.assertEqual('172.31.255.128', str(addr_list[0])) 466 | self.assertEqual('172.31.255.128', str(addr[0])) 467 | self.assertEqual('172.31.255.143', str(addr_list[-1])) 468 | self.assertEqual('172.31.255.143', str(addr[-1])) 469 | self.assertEqual(addr_list[-1], addr[-1]) 470 | 471 | def testEqual(self): 472 | self.assertTrue(self.ipv4 == ipaddr.IPv4Network('1.2.3.4/24')) 473 | self.assertFalse(self.ipv4 == ipaddr.IPv4Network('1.2.3.4/23')) 474 | self.assertFalse(self.ipv4 == ipaddr.IPv6Network('::1.2.3.4/24')) 475 | self.assertFalse(self.ipv4 == '') 476 | self.assertFalse(self.ipv4 == []) 477 | self.assertFalse(self.ipv4 == 2) 478 | self.assertTrue(ipaddr.IPNetwork('1.1.1.1/32') == 479 | ipaddr.IPAddress('1.1.1.1')) 480 | self.assertTrue(ipaddr.IPNetwork('1.1.1.1/24') == 481 | ipaddr.IPAddress('1.1.1.1')) 482 | self.assertFalse(ipaddr.IPNetwork('1.1.1.0/24') == 483 | ipaddr.IPAddress('1.1.1.1')) 484 | 485 | self.assertTrue(self.ipv6 == 486 | ipaddr.IPv6Network('2001:658:22a:cafe:200::1/64')) 487 | self.assertTrue(ipaddr.IPNetwork('::1/128') == 488 | ipaddr.IPAddress('::1')) 489 | self.assertTrue(ipaddr.IPNetwork('::1/127') == 490 | ipaddr.IPAddress('::1')) 491 | self.assertFalse(ipaddr.IPNetwork('::0/127') == 492 | ipaddr.IPAddress('::1')) 493 | self.assertFalse(self.ipv6 == 494 | ipaddr.IPv6Network('2001:658:22a:cafe:200::1/63')) 495 | self.assertFalse(self.ipv6 == ipaddr.IPv4Network('1.2.3.4/23')) 496 | self.assertFalse(self.ipv6 == '') 497 | self.assertFalse(self.ipv6 == []) 498 | self.assertFalse(self.ipv6 == 2) 499 | 500 | def testNotEqual(self): 501 | self.assertFalse(self.ipv4 != ipaddr.IPv4Network('1.2.3.4/24')) 502 | self.assertTrue(self.ipv4 != ipaddr.IPv4Network('1.2.3.4/23')) 503 | self.assertTrue(self.ipv4 != ipaddr.IPv6Network('::1.2.3.4/24')) 504 | self.assertTrue(self.ipv4 != '') 505 | self.assertTrue(self.ipv4 != []) 506 | self.assertTrue(self.ipv4 != 2) 507 | 508 | addr2 = ipaddr.IPAddress('2001:658:22a:cafe:200::1') 509 | self.assertFalse(self.ipv6 != 510 | ipaddr.IPv6Network('2001:658:22a:cafe:200::1/64')) 511 | self.assertTrue(self.ipv6 != 512 | ipaddr.IPv6Network('2001:658:22a:cafe:200::1/63')) 513 | self.assertTrue(self.ipv6 != ipaddr.IPv4Network('1.2.3.4/23')) 514 | self.assertTrue(self.ipv6 != '') 515 | self.assertTrue(self.ipv6 != []) 516 | self.assertTrue(self.ipv6 != 2) 517 | 518 | def testSlash32Constructor(self): 519 | self.assertEqual(str(ipaddr.IPv4Network('1.2.3.4/255.255.255.255')), 520 | '1.2.3.4/32') 521 | 522 | def testSlash128Constructor(self): 523 | self.assertEqual(str(ipaddr.IPv6Network('::1/128')), 524 | '::1/128') 525 | 526 | def testSlash0Constructor(self): 527 | self.assertEqual(str(ipaddr.IPv4Network('1.2.3.4/0.0.0.0')), 528 | '1.2.3.4/0') 529 | 530 | def testCollapsing(self): 531 | # test only IP addresses including some duplicates 532 | ip1 = ipaddr.IPv4Address('1.1.1.0') 533 | ip2 = ipaddr.IPv4Address('1.1.1.1') 534 | ip3 = ipaddr.IPv4Address('1.1.1.2') 535 | ip4 = ipaddr.IPv4Address('1.1.1.3') 536 | ip5 = ipaddr.IPv4Address('1.1.1.4') 537 | ip6 = ipaddr.IPv4Address('1.1.1.0') 538 | # check that addreses are subsumed properly. 539 | collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3, ip4, ip5, ip6]) 540 | self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.1.0/30'), 541 | ipaddr.IPv4Network('1.1.1.4/32')]) 542 | 543 | # test a mix of IP addresses and networks including some duplicates 544 | ip1 = ipaddr.IPv4Address('1.1.1.0') 545 | ip2 = ipaddr.IPv4Address('1.1.1.1') 546 | ip3 = ipaddr.IPv4Address('1.1.1.2') 547 | ip4 = ipaddr.IPv4Address('1.1.1.3') 548 | ip5 = ipaddr.IPv4Network('1.1.1.4/30') 549 | ip6 = ipaddr.IPv4Network('1.1.1.4/30') 550 | # check that addreses are subsumed properly. 551 | collapsed = ipaddr.collapse_address_list([ip5, ip1, ip2, ip3, ip4, ip6]) 552 | self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.1.0/29')]) 553 | 554 | # test only IP networks 555 | ip1 = ipaddr.IPv4Network('1.1.0.0/24') 556 | ip2 = ipaddr.IPv4Network('1.1.1.0/24') 557 | ip3 = ipaddr.IPv4Network('1.1.2.0/24') 558 | ip4 = ipaddr.IPv4Network('1.1.3.0/24') 559 | ip5 = ipaddr.IPv4Network('1.1.4.0/24') 560 | # stored in no particular order b/c we want CollapseAddr to call [].sort 561 | ip6 = ipaddr.IPv4Network('1.1.0.0/22') 562 | # check that addreses are subsumed properly. 563 | collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3, ip4, ip5, ip6]) 564 | self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.0.0/22'), 565 | ipaddr.IPv4Network('1.1.4.0/24')]) 566 | 567 | # test that two addresses are supernet'ed properly 568 | collapsed = ipaddr.collapse_address_list([ip1, ip2]) 569 | self.assertEqual(collapsed, [ipaddr.IPv4Network('1.1.0.0/23')]) 570 | 571 | # test same IP networks 572 | ip_same1 = ip_same2 = ipaddr.IPv4Network('1.1.1.1/32') 573 | self.assertEqual(ipaddr.collapse_address_list([ip_same1, ip_same2]), 574 | [ip_same1]) 575 | 576 | # test same IP addresses 577 | ip_same1 = ip_same2 = ipaddr.IPv4Address('1.1.1.1') 578 | self.assertEqual(ipaddr.collapse_address_list([ip_same1, ip_same2]), 579 | [ipaddr.IPNetwork('1.1.1.1/32')]) 580 | ip1 = ipaddr.IPv6Network('::2001:1/100') 581 | ip2 = ipaddr.IPv6Network('::2002:1/120') 582 | ip3 = ipaddr.IPv6Network('::2001:1/96') 583 | # test that ipv6 addresses are subsumed properly. 584 | collapsed = ipaddr.collapse_address_list([ip1, ip2, ip3]) 585 | self.assertEqual(collapsed, [ip3]) 586 | 587 | # the toejam test 588 | ip1 = ipaddr.IPAddress('1.1.1.1') 589 | ip2 = ipaddr.IPAddress('::1') 590 | self.assertRaises(TypeError, ipaddr.collapse_address_list, 591 | [ip1, ip2]) 592 | 593 | def testSummarizing(self): 594 | #ip = ipaddr.IPAddress 595 | #ipnet = ipaddr.IPNetwork 596 | summarize = ipaddr.summarize_address_range 597 | ip1 = ipaddr.IPAddress('1.1.1.0') 598 | ip2 = ipaddr.IPAddress('1.1.1.255') 599 | # test a /24 is sumamrized properly 600 | self.assertEqual(summarize(ip1, ip2)[0], ipaddr.IPNetwork('1.1.1.0/24')) 601 | # test an IPv4 range that isn't on a network byte boundary 602 | ip2 = ipaddr.IPAddress('1.1.1.8') 603 | self.assertEqual(summarize(ip1, ip2), [ipaddr.IPNetwork('1.1.1.0/29'), 604 | ipaddr.IPNetwork('1.1.1.8')]) 605 | 606 | ip1 = ipaddr.IPAddress('1::') 607 | ip2 = ipaddr.IPAddress('1:ffff:ffff:ffff:ffff:ffff:ffff:ffff') 608 | # test a IPv6 is sumamrized properly 609 | self.assertEqual(summarize(ip1, ip2)[0], ipaddr.IPNetwork('1::/16')) 610 | # test an IPv6 range that isn't on a network byte boundary 611 | ip2 = ipaddr.IPAddress('2::') 612 | self.assertEqual(summarize(ip1, ip2), [ipaddr.IPNetwork('1::/16'), 613 | ipaddr.IPNetwork('2::/128')]) 614 | 615 | # test exception raised when first is greater than last 616 | self.assertRaises(ValueError, summarize, ipaddr.IPAddress('1.1.1.0'), 617 | ipaddr.IPAddress('1.1.0.0')) 618 | # test exception raised when first and last aren't IP addresses 619 | self.assertRaises(TypeError, summarize, 620 | ipaddr.IPNetwork('1.1.1.0'), 621 | ipaddr.IPNetwork('1.1.0.0')) 622 | self.assertRaises(TypeError, summarize, 623 | ipaddr.IPNetwork('1.1.1.0'), ipaddr.IPNetwork('1.1.0.0')) 624 | # test exception raised when first and last are not same version 625 | self.assertRaises(TypeError, summarize, ipaddr.IPAddress('::'), 626 | ipaddr.IPNetwork('1.1.0.0')) 627 | 628 | def testAddressComparison(self): 629 | self.assertTrue(ipaddr.IPAddress('1.1.1.1') <= 630 | ipaddr.IPAddress('1.1.1.1')) 631 | self.assertTrue(ipaddr.IPAddress('1.1.1.1') <= 632 | ipaddr.IPAddress('1.1.1.2')) 633 | self.assertTrue(ipaddr.IPAddress('::1') <= ipaddr.IPAddress('::1')) 634 | self.assertTrue(ipaddr.IPAddress('::1') <= ipaddr.IPAddress('::2')) 635 | 636 | def testNetworkComparison(self): 637 | # ip1 and ip2 have the same network address 638 | ip1 = ipaddr.IPv4Network('1.1.1.0/24') 639 | ip2 = ipaddr.IPv4Network('1.1.1.1/24') 640 | ip3 = ipaddr.IPv4Network('1.1.2.0/24') 641 | 642 | self.assertTrue(ip1 < ip3) 643 | self.assertTrue(ip3 > ip2) 644 | 645 | self.assertEqual(ip1.compare_networks(ip2), 0) 646 | self.assertTrue(ip1._get_networks_key() == ip2._get_networks_key()) 647 | self.assertEqual(ip1.compare_networks(ip3), -1) 648 | self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key()) 649 | 650 | ip1 = ipaddr.IPv6Network('2001::2000/96') 651 | ip2 = ipaddr.IPv6Network('2001::2001/96') 652 | ip3 = ipaddr.IPv6Network('2001:ffff::2000/96') 653 | 654 | self.assertTrue(ip1 < ip3) 655 | self.assertTrue(ip3 > ip2) 656 | self.assertEqual(ip1.compare_networks(ip2), 0) 657 | self.assertTrue(ip1._get_networks_key() == ip2._get_networks_key()) 658 | self.assertEqual(ip1.compare_networks(ip3), -1) 659 | self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key()) 660 | 661 | # Test comparing different protocols. 662 | # Should always raise a TypeError. 663 | ipv6 = ipaddr.IPv6Network('::/0') 664 | ipv4 = ipaddr.IPv4Network('0.0.0.0/0') 665 | self.assertRaises(TypeError, ipv4.__lt__, ipv6) 666 | self.assertRaises(TypeError, ipv4.__gt__, ipv6) 667 | self.assertRaises(TypeError, ipv6.__lt__, ipv4) 668 | self.assertRaises(TypeError, ipv6.__gt__, ipv4) 669 | 670 | # Regression test for issue 19. 671 | ip1 = ipaddr.IPNetwork('10.1.2.128/25') 672 | self.assertFalse(ip1 < ip1) 673 | self.assertFalse(ip1 > ip1) 674 | ip2 = ipaddr.IPNetwork('10.1.3.0/24') 675 | self.assertTrue(ip1 < ip2) 676 | self.assertFalse(ip2 < ip1) 677 | self.assertFalse(ip1 > ip2) 678 | self.assertTrue(ip2 > ip1) 679 | ip3 = ipaddr.IPNetwork('10.1.3.0/25') 680 | self.assertTrue(ip2 < ip3) 681 | self.assertFalse(ip3 < ip2) 682 | self.assertFalse(ip2 > ip3) 683 | self.assertTrue(ip3 > ip2) 684 | 685 | # Regression test for issue 28. 686 | ip1 = ipaddr.IPNetwork('10.10.10.0/31') 687 | ip2 = ipaddr.IPNetwork('10.10.10.0') 688 | ip3 = ipaddr.IPNetwork('10.10.10.2/31') 689 | ip4 = ipaddr.IPNetwork('10.10.10.2') 690 | sorted = [ip1, ip2, ip3, ip4] 691 | unsorted = [ip2, ip4, ip1, ip3] 692 | unsorted.sort() 693 | self.assertEqual(sorted, unsorted) 694 | unsorted = [ip4, ip1, ip3, ip2] 695 | unsorted.sort() 696 | self.assertEqual(sorted, unsorted) 697 | self.assertRaises(TypeError, ip1.__lt__, ipaddr.IPAddress('10.10.10.0')) 698 | self.assertRaises(TypeError, ip2.__lt__, ipaddr.IPAddress('10.10.10.0')) 699 | 700 | # <=, >= 701 | self.assertTrue(ipaddr.IPNetwork('1.1.1.1') <= 702 | ipaddr.IPNetwork('1.1.1.1')) 703 | self.assertTrue(ipaddr.IPNetwork('1.1.1.1') <= 704 | ipaddr.IPNetwork('1.1.1.2')) 705 | self.assertFalse(ipaddr.IPNetwork('1.1.1.2') <= 706 | ipaddr.IPNetwork('1.1.1.1')) 707 | self.assertTrue(ipaddr.IPNetwork('::1') <= ipaddr.IPNetwork('::1')) 708 | self.assertTrue(ipaddr.IPNetwork('::1') <= ipaddr.IPNetwork('::2')) 709 | self.assertFalse(ipaddr.IPNetwork('::2') <= ipaddr.IPNetwork('::1')) 710 | 711 | def testStrictNetworks(self): 712 | self.assertRaises(ValueError, ipaddr.IPNetwork, '192.168.1.1/24', 713 | strict=True) 714 | self.assertRaises(ValueError, ipaddr.IPNetwork, '::1/120', strict=True) 715 | 716 | def testOverlaps(self): 717 | other = ipaddr.IPv4Network('1.2.3.0/30') 718 | other2 = ipaddr.IPv4Network('1.2.2.0/24') 719 | other3 = ipaddr.IPv4Network('1.2.2.64/26') 720 | self.assertTrue(self.ipv4.overlaps(other)) 721 | self.assertFalse(self.ipv4.overlaps(other2)) 722 | self.assertTrue(other2.overlaps(other3)) 723 | 724 | def testEmbeddedIpv4(self): 725 | ipv4_string = '192.168.0.1' 726 | ipv4 = ipaddr.IPv4Network(ipv4_string) 727 | v4compat_ipv6 = ipaddr.IPv6Network('::%s' % ipv4_string) 728 | self.assertEqual(int(v4compat_ipv6.ip), int(ipv4.ip)) 729 | v4mapped_ipv6 = ipaddr.IPv6Network('::ffff:%s' % ipv4_string) 730 | self.assertNotEqual(v4mapped_ipv6.ip, ipv4.ip) 731 | self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv6Network, 732 | '2001:1.1.1.1:1.1.1.1') 733 | 734 | # Issue 67: IPv6 with embedded IPv4 address not recognized. 735 | def testIPv6AddressTooLarge(self): 736 | # RFC4291 2.5.5.2 737 | self.assertEqual(ipaddr.IPAddress('::FFFF:192.0.2.1'), 738 | ipaddr.IPAddress('::FFFF:c000:201')) 739 | # RFC4291 2.2 (part 3) x::d.d.d.d 740 | self.assertEqual(ipaddr.IPAddress('FFFF::192.0.2.1'), 741 | ipaddr.IPAddress('FFFF::c000:201')) 742 | 743 | def testIPVersion(self): 744 | self.assertEqual(self.ipv4.version, 4) 745 | self.assertEqual(self.ipv6.version, 6) 746 | 747 | def testMaxPrefixLength(self): 748 | self.assertEqual(self.ipv4.max_prefixlen, 32) 749 | self.assertEqual(self.ipv6.max_prefixlen, 128) 750 | 751 | def testPacked(self): 752 | self.assertEqual(self.ipv4.packed, 753 | _cb('\x01\x02\x03\x04')) 754 | self.assertEqual(ipaddr.IPv4Network('255.254.253.252').packed, 755 | _cb('\xff\xfe\xfd\xfc')) 756 | self.assertEqual(self.ipv6.packed, 757 | _cb('\x20\x01\x06\x58\x02\x2a\xca\xfe' 758 | '\x02\x00\x00\x00\x00\x00\x00\x01')) 759 | self.assertEqual(ipaddr.IPv6Network('ffff:2:3:4:ffff::').packed, 760 | _cb('\xff\xff\x00\x02\x00\x03\x00\x04\xff\xff' 761 | + '\x00' * 6)) 762 | self.assertEqual(ipaddr.IPv6Network('::1:0:0:0:0').packed, 763 | _cb('\x00' * 6 + '\x00\x01' + '\x00' * 8)) 764 | 765 | def testIpStrFromPrefixlen(self): 766 | ipv4 = ipaddr.IPv4Network('1.2.3.4/24') 767 | self.assertEqual(ipv4._ip_string_from_prefix(), '255.255.255.0') 768 | self.assertEqual(ipv4._ip_string_from_prefix(28), '255.255.255.240') 769 | 770 | def testIpType(self): 771 | ipv4net = ipaddr.IPNetwork('1.2.3.4') 772 | ipv4addr = ipaddr.IPAddress('1.2.3.4') 773 | ipv6net = ipaddr.IPNetwork('::1.2.3.4') 774 | ipv6addr = ipaddr.IPAddress('::1.2.3.4') 775 | self.assertEqual(ipaddr.IPv4Network, type(ipv4net)) 776 | self.assertEqual(ipaddr.IPv4Address, type(ipv4addr)) 777 | self.assertEqual(ipaddr.IPv6Network, type(ipv6net)) 778 | self.assertEqual(ipaddr.IPv6Address, type(ipv6addr)) 779 | 780 | def testReservedIpv4(self): 781 | # test networks 782 | self.assertEqual(True, ipaddr.IPNetwork('224.1.1.1/31').is_multicast) 783 | self.assertEqual(False, ipaddr.IPNetwork('240.0.0.0').is_multicast) 784 | 785 | self.assertEqual(True, ipaddr.IPNetwork('192.168.1.1/17').is_private) 786 | self.assertEqual(False, ipaddr.IPNetwork('192.169.0.0').is_private) 787 | self.assertEqual(True, ipaddr.IPNetwork('10.255.255.255').is_private) 788 | self.assertEqual(False, ipaddr.IPNetwork('11.0.0.0').is_private) 789 | self.assertEqual(True, ipaddr.IPNetwork('172.31.255.255').is_private) 790 | self.assertEqual(False, ipaddr.IPNetwork('172.32.0.0').is_private) 791 | 792 | self.assertEqual(True, 793 | ipaddr.IPNetwork('169.254.100.200/24').is_link_local) 794 | self.assertEqual(False, 795 | ipaddr.IPNetwork('169.255.100.200/24').is_link_local) 796 | 797 | self.assertEqual(True, 798 | ipaddr.IPNetwork('127.100.200.254/32').is_loopback) 799 | self.assertEqual(True, ipaddr.IPNetwork('127.42.0.0/16').is_loopback) 800 | self.assertEqual(False, ipaddr.IPNetwork('128.0.0.0').is_loopback) 801 | 802 | # test addresses 803 | self.assertEqual(True, ipaddr.IPAddress('224.1.1.1').is_multicast) 804 | self.assertEqual(False, ipaddr.IPAddress('240.0.0.0').is_multicast) 805 | 806 | self.assertEqual(True, ipaddr.IPAddress('192.168.1.1').is_private) 807 | self.assertEqual(False, ipaddr.IPAddress('192.169.0.0').is_private) 808 | self.assertEqual(True, ipaddr.IPAddress('10.255.255.255').is_private) 809 | self.assertEqual(False, ipaddr.IPAddress('11.0.0.0').is_private) 810 | self.assertEqual(True, ipaddr.IPAddress('172.31.255.255').is_private) 811 | self.assertEqual(False, ipaddr.IPAddress('172.32.0.0').is_private) 812 | 813 | self.assertEqual(True, 814 | ipaddr.IPAddress('169.254.100.200').is_link_local) 815 | self.assertEqual(False, 816 | ipaddr.IPAddress('169.255.100.200').is_link_local) 817 | 818 | self.assertEqual(True, 819 | ipaddr.IPAddress('127.100.200.254').is_loopback) 820 | self.assertEqual(True, ipaddr.IPAddress('127.42.0.0').is_loopback) 821 | self.assertEqual(False, ipaddr.IPAddress('128.0.0.0').is_loopback) 822 | self.assertEqual(True, ipaddr.IPNetwork('0.0.0.0').is_unspecified) 823 | 824 | def testReservedIpv6(self): 825 | 826 | self.assertEqual(True, ipaddr.IPNetwork('ffff::').is_multicast) 827 | self.assertEqual(True, ipaddr.IPNetwork(2**128-1).is_multicast) 828 | self.assertEqual(True, ipaddr.IPNetwork('ff00::').is_multicast) 829 | self.assertEqual(False, ipaddr.IPNetwork('fdff::').is_multicast) 830 | 831 | self.assertEqual(True, ipaddr.IPNetwork('fecf::').is_site_local) 832 | self.assertEqual(True, ipaddr.IPNetwork( 833 | 'feff:ffff:ffff:ffff::').is_site_local) 834 | self.assertEqual(False, ipaddr.IPNetwork('fbf:ffff::').is_site_local) 835 | self.assertEqual(False, ipaddr.IPNetwork('ff00::').is_site_local) 836 | 837 | self.assertEqual(True, ipaddr.IPNetwork('fc00::').is_private) 838 | self.assertEqual(True, ipaddr.IPNetwork( 839 | 'fc00:ffff:ffff:ffff::').is_private) 840 | self.assertEqual(False, ipaddr.IPNetwork('fbff:ffff::').is_private) 841 | self.assertEqual(False, ipaddr.IPNetwork('fe00::').is_private) 842 | 843 | self.assertEqual(True, ipaddr.IPNetwork('fea0::').is_link_local) 844 | self.assertEqual(True, ipaddr.IPNetwork('febf:ffff::').is_link_local) 845 | self.assertEqual(False, ipaddr.IPNetwork('fe7f:ffff::').is_link_local) 846 | self.assertEqual(False, ipaddr.IPNetwork('fec0::').is_link_local) 847 | 848 | self.assertEqual(True, ipaddr.IPNetwork('0:0::0:01').is_loopback) 849 | self.assertEqual(False, ipaddr.IPNetwork('::1/127').is_loopback) 850 | self.assertEqual(False, ipaddr.IPNetwork('::').is_loopback) 851 | self.assertEqual(False, ipaddr.IPNetwork('::2').is_loopback) 852 | 853 | self.assertEqual(True, ipaddr.IPNetwork('0::0').is_unspecified) 854 | self.assertEqual(False, ipaddr.IPNetwork('::1').is_unspecified) 855 | self.assertEqual(False, ipaddr.IPNetwork('::/127').is_unspecified) 856 | 857 | # test addresses 858 | self.assertEqual(True, ipaddr.IPAddress('ffff::').is_multicast) 859 | self.assertEqual(True, ipaddr.IPAddress(2**128-1).is_multicast) 860 | self.assertEqual(True, ipaddr.IPAddress('ff00::').is_multicast) 861 | self.assertEqual(False, ipaddr.IPAddress('fdff::').is_multicast) 862 | 863 | self.assertEqual(True, ipaddr.IPAddress('fecf::').is_site_local) 864 | self.assertEqual(True, ipaddr.IPAddress( 865 | 'feff:ffff:ffff:ffff::').is_site_local) 866 | self.assertEqual(False, ipaddr.IPAddress('fbf:ffff::').is_site_local) 867 | self.assertEqual(False, ipaddr.IPAddress('ff00::').is_site_local) 868 | 869 | self.assertEqual(True, ipaddr.IPAddress('fc00::').is_private) 870 | self.assertEqual(True, ipaddr.IPAddress( 871 | 'fc00:ffff:ffff:ffff::').is_private) 872 | self.assertEqual(False, ipaddr.IPAddress('fbff:ffff::').is_private) 873 | self.assertEqual(False, ipaddr.IPAddress('fe00::').is_private) 874 | 875 | self.assertEqual(True, ipaddr.IPAddress('fea0::').is_link_local) 876 | self.assertEqual(True, ipaddr.IPAddress('febf:ffff::').is_link_local) 877 | self.assertEqual(False, ipaddr.IPAddress('fe7f:ffff::').is_link_local) 878 | self.assertEqual(False, ipaddr.IPAddress('fec0::').is_link_local) 879 | 880 | self.assertEqual(True, ipaddr.IPAddress('0:0::0:01').is_loopback) 881 | self.assertEqual(True, ipaddr.IPAddress('::1').is_loopback) 882 | self.assertEqual(False, ipaddr.IPAddress('::2').is_loopback) 883 | 884 | self.assertEqual(True, ipaddr.IPAddress('0::0').is_unspecified) 885 | self.assertEqual(False, ipaddr.IPAddress('::1').is_unspecified) 886 | 887 | # some generic IETF reserved addresses 888 | self.assertEqual(True, ipaddr.IPAddress('100::').is_reserved) 889 | self.assertEqual(True, ipaddr.IPNetwork('4000::1/128').is_reserved) 890 | 891 | def testIpv4Mapped(self): 892 | self.assertEqual(ipaddr.IPAddress('::ffff:192.168.1.1').ipv4_mapped, 893 | ipaddr.IPAddress('192.168.1.1')) 894 | self.assertEqual(ipaddr.IPAddress('::c0a8:101').ipv4_mapped, None) 895 | self.assertEqual(ipaddr.IPAddress('::ffff:c0a8:101').ipv4_mapped, 896 | ipaddr.IPAddress('192.168.1.1')) 897 | 898 | def testAddrExclude(self): 899 | addr1 = ipaddr.IPNetwork('10.1.1.0/24') 900 | addr2 = ipaddr.IPNetwork('10.1.1.0/26') 901 | addr3 = ipaddr.IPNetwork('10.2.1.0/24') 902 | addr4 = ipaddr.IPAddress('10.1.1.0') 903 | self.assertEqual(addr1.address_exclude(addr2), 904 | [ipaddr.IPNetwork('10.1.1.64/26'), 905 | ipaddr.IPNetwork('10.1.1.128/25')]) 906 | self.assertRaises(ValueError, addr1.address_exclude, addr3) 907 | self.assertRaises(TypeError, addr1.address_exclude, addr4) 908 | self.assertEqual(addr1.address_exclude(addr1), []) 909 | 910 | def testHash(self): 911 | self.assertEqual(hash(ipaddr.IPNetwork('10.1.1.0/24')), 912 | hash(ipaddr.IPNetwork('10.1.1.0/24'))) 913 | self.assertEqual(hash(ipaddr.IPAddress('10.1.1.0')), 914 | hash(ipaddr.IPAddress('10.1.1.0'))) 915 | # i70 916 | self.assertEqual(hash(ipaddr.IPAddress('1.2.3.4')), 917 | hash(ipaddr.IPAddress( 918 | long(ipaddr.IPAddress('1.2.3.4')._ip)))) 919 | ip1 = ipaddr.IPAddress('10.1.1.0') 920 | ip2 = ipaddr.IPAddress('1::') 921 | dummy = {} 922 | dummy[self.ipv4] = None 923 | dummy[self.ipv6] = None 924 | dummy[ip1] = None 925 | dummy[ip2] = None 926 | self.assertTrue(self.ipv4 in dummy) 927 | self.assertTrue(ip2 in dummy) 928 | 929 | def testCopyConstructor(self): 930 | addr1 = ipaddr.IPNetwork('10.1.1.0/24') 931 | addr2 = ipaddr.IPNetwork(addr1) 932 | addr3 = ipaddr.IPNetwork('2001:658:22a:cafe:200::1/64') 933 | addr4 = ipaddr.IPNetwork(addr3) 934 | addr5 = ipaddr.IPv4Address('1.1.1.1') 935 | addr6 = ipaddr.IPv6Address('2001:658:22a:cafe:200::1') 936 | 937 | self.assertEqual(addr1, addr2) 938 | self.assertEqual(addr3, addr4) 939 | self.assertEqual(addr5, ipaddr.IPv4Address(addr5)) 940 | self.assertEqual(addr6, ipaddr.IPv6Address(addr6)) 941 | 942 | def testCompressIPv6Address(self): 943 | test_addresses = { 944 | '1:2:3:4:5:6:7:8': '1:2:3:4:5:6:7:8/128', 945 | '2001:0:0:4:0:0:0:8': '2001:0:0:4::8/128', 946 | '2001:0:0:4:5:6:7:8': '2001::4:5:6:7:8/128', 947 | '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128', 948 | '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128', 949 | '0:0:3:0:0:0:0:ffff': '0:0:3::ffff/128', 950 | '0:0:0:4:0:0:0:ffff': '::4:0:0:0:ffff/128', 951 | '0:0:0:0:5:0:0:ffff': '::5:0:0:ffff/128', 952 | '1:0:0:4:0:0:7:8': '1::4:0:0:7:8/128', 953 | '0:0:0:0:0:0:0:0': '::/128', 954 | '0:0:0:0:0:0:0:0/0': '::/0', 955 | '0:0:0:0:0:0:0:1': '::1/128', 956 | '2001:0658:022a:cafe:0000:0000:0000:0000/66': 957 | '2001:658:22a:cafe::/66', 958 | '::1.2.3.4': '::102:304/128', 959 | '1:2:3:4:5:ffff:1.2.3.4': '1:2:3:4:5:ffff:102:304/128', 960 | '::7:6:5:4:3:2:1': '0:7:6:5:4:3:2:1/128', 961 | '::7:6:5:4:3:2:0': '0:7:6:5:4:3:2:0/128', 962 | '7:6:5:4:3:2:1::': '7:6:5:4:3:2:1:0/128', 963 | '0:6:5:4:3:2:1::': '0:6:5:4:3:2:1:0/128', 964 | } 965 | for uncompressed, compressed in test_addresses.items(): 966 | self.assertEqual(compressed, str(ipaddr.IPv6Network(uncompressed))) 967 | 968 | def testExplodeShortHandIpStr(self): 969 | addr1 = ipaddr.IPv6Network('2001::1') 970 | addr2 = ipaddr.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1') 971 | self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0001/128', 972 | addr1.exploded) 973 | self.assertEqual('0000:0000:0000:0000:0000:0000:0000:0001/128', 974 | ipaddr.IPv6Network('::1/128').exploded) 975 | # issue 77 976 | self.assertEqual('2001:0000:5ef5:79fd:0000:059d:a0e5:0ba1', 977 | addr2.exploded) 978 | 979 | def testIntRepresentation(self): 980 | self.assertEqual(16909060, int(self.ipv4)) 981 | self.assertEqual(42540616829182469433547762482097946625, int(self.ipv6)) 982 | 983 | def testHexRepresentation(self): 984 | self.assertEqual(hex(0x1020304), 985 | hex(self.ipv4)) 986 | 987 | self.assertEqual(hex(0x20010658022ACAFE0200000000000001), 988 | hex(self.ipv6)) 989 | 990 | # backwards compatibility 991 | def testBackwardsCompability(self): 992 | self.assertEqual(ipaddr.CollapseAddrList( 993 | [ipaddr.IPNetwork('1.1.0.0/24'), ipaddr.IPNetwork('1.1.1.0/24')]), 994 | [ipaddr.IPNetwork('1.1.0.0/23')]) 995 | 996 | self.assertEqual(ipaddr.IPNetwork('::42:0/112').AddressExclude( 997 | ipaddr.IPNetwork('::42:8000/113')), 998 | [ipaddr.IPNetwork('::42:0/113')]) 999 | 1000 | self.assertTrue(ipaddr.IPNetwork('1::/8').CompareNetworks( 1001 | ipaddr.IPNetwork('2::/9')) < 0) 1002 | 1003 | self.assertEqual(ipaddr.IPNetwork('1::/16').Contains( 1004 | ipaddr.IPNetwork('2::/16')), False) 1005 | 1006 | self.assertEqual(ipaddr.IPNetwork('0.0.0.0/0').Subnet(), 1007 | [ipaddr.IPNetwork('0.0.0.0/1'), 1008 | ipaddr.IPNetwork('128.0.0.0/1')]) 1009 | self.assertEqual(ipaddr.IPNetwork('::/127').Subnet(), 1010 | [ipaddr.IPNetwork('::/128'), 1011 | ipaddr.IPNetwork('::1/128')]) 1012 | 1013 | self.assertEqual(ipaddr.IPNetwork('1.0.0.0/32').Supernet(), 1014 | ipaddr.IPNetwork('1.0.0.0/31')) 1015 | self.assertEqual(ipaddr.IPNetwork('::/121').Supernet(), 1016 | ipaddr.IPNetwork('::/120')) 1017 | 1018 | self.assertEqual(ipaddr.IPNetwork('10.0.0.2').IsRFC1918(), True) 1019 | self.assertEqual(ipaddr.IPNetwork('10.0.0.0').IsMulticast(), False) 1020 | self.assertEqual(ipaddr.IPNetwork('127.255.255.255').IsLoopback(), True) 1021 | self.assertEqual(ipaddr.IPNetwork('169.255.255.255').IsLinkLocal(), 1022 | False) 1023 | 1024 | def testForceVersion(self): 1025 | self.assertEqual(ipaddr.IPNetwork(1).version, 4) 1026 | self.assertEqual(ipaddr.IPNetwork(1, version=6).version, 6) 1027 | 1028 | def testWithStar(self): 1029 | self.assertEqual(str(self.ipv4.with_prefixlen), "1.2.3.4/24") 1030 | self.assertEqual(str(self.ipv4.with_netmask), "1.2.3.4/255.255.255.0") 1031 | self.assertEqual(str(self.ipv4.with_hostmask), "1.2.3.4/0.0.0.255") 1032 | 1033 | self.assertEqual(str(self.ipv6.with_prefixlen), 1034 | '2001:658:22a:cafe:200::1/64') 1035 | # rfc3513 sec 2.3 says that ipv6 only uses cidr notation for 1036 | # subnets 1037 | self.assertEqual(str(self.ipv6.with_netmask), 1038 | '2001:658:22a:cafe:200::1/64') 1039 | # this probably don't make much sense, but it's included for 1040 | # compatibility with ipv4 1041 | self.assertEqual(str(self.ipv6.with_hostmask), 1042 | '2001:658:22a:cafe:200::1/::ffff:ffff:ffff:ffff') 1043 | 1044 | def testNetworkElementCaching(self): 1045 | # V4 - make sure we're empty 1046 | self.assertFalse(self.ipv4._cache.has_key('network')) 1047 | self.assertFalse(self.ipv4._cache.has_key('broadcast')) 1048 | self.assertFalse(self.ipv4._cache.has_key('hostmask')) 1049 | 1050 | # V4 - populate and test 1051 | self.assertEqual(self.ipv4.network, ipaddr.IPv4Address('1.2.3.0')) 1052 | self.assertEqual(self.ipv4.broadcast, ipaddr.IPv4Address('1.2.3.255')) 1053 | self.assertEqual(self.ipv4.hostmask, ipaddr.IPv4Address('0.0.0.255')) 1054 | 1055 | # V4 - check we're cached 1056 | self.assertTrue(self.ipv4._cache.has_key('network')) 1057 | self.assertTrue(self.ipv4._cache.has_key('broadcast')) 1058 | self.assertTrue(self.ipv4._cache.has_key('hostmask')) 1059 | 1060 | # V6 - make sure we're empty 1061 | self.assertFalse(self.ipv6._cache.has_key('network')) 1062 | self.assertFalse(self.ipv6._cache.has_key('broadcast')) 1063 | self.assertFalse(self.ipv6._cache.has_key('hostmask')) 1064 | 1065 | # V6 - populate and test 1066 | self.assertEqual(self.ipv6.network, 1067 | ipaddr.IPv6Address('2001:658:22a:cafe::')) 1068 | self.assertEqual(self.ipv6.broadcast, ipaddr.IPv6Address( 1069 | '2001:658:22a:cafe:ffff:ffff:ffff:ffff')) 1070 | self.assertEqual(self.ipv6.hostmask, 1071 | ipaddr.IPv6Address('::ffff:ffff:ffff:ffff')) 1072 | 1073 | # V6 - check we're cached 1074 | self.assertTrue(self.ipv6._cache.has_key('network')) 1075 | self.assertTrue(self.ipv6._cache.has_key('broadcast')) 1076 | self.assertTrue(self.ipv6._cache.has_key('hostmask')) 1077 | 1078 | def testTeredo(self): 1079 | # stolen from wikipedia 1080 | server = ipaddr.IPv4Address('65.54.227.120') 1081 | client = ipaddr.IPv4Address('192.0.2.45') 1082 | teredo_addr = '2001:0000:4136:e378:8000:63bf:3fff:fdd2' 1083 | self.assertEqual((server, client), 1084 | ipaddr.IPAddress(teredo_addr).teredo) 1085 | bad_addr = '2000::4136:e378:8000:63bf:3fff:fdd2' 1086 | self.assertFalse(ipaddr.IPAddress(bad_addr).teredo) 1087 | bad_addr = '2001:0001:4136:e378:8000:63bf:3fff:fdd2' 1088 | self.assertFalse(ipaddr.IPAddress(bad_addr).teredo) 1089 | 1090 | # i77 1091 | teredo_addr = ipaddr.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1') 1092 | self.assertEqual((ipaddr.IPv4Address('94.245.121.253'), 1093 | ipaddr.IPv4Address('95.26.244.94')), 1094 | teredo_addr.teredo) 1095 | 1096 | 1097 | def testsixtofour(self): 1098 | sixtofouraddr = ipaddr.IPAddress('2002:ac1d:2d64::1') 1099 | bad_addr = ipaddr.IPAddress('2000:ac1d:2d64::1') 1100 | self.assertEqual(ipaddr.IPv4Address('172.29.45.100'), 1101 | sixtofouraddr.sixtofour) 1102 | self.assertFalse(bad_addr.sixtofour) 1103 | 1104 | 1105 | if __name__ == '__main__': 1106 | unittest.main() 1107 | -------------------------------------------------------------------------------- /embedded_ipaddr/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright 2008 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from distutils.core import setup 18 | 19 | import ipaddr 20 | 21 | 22 | setup(name='ipaddr', 23 | maintainer='Google', 24 | maintainer_email='ipaddr-py-dev@googlegroups.com', 25 | version=ipaddr.__version__, 26 | url='http://code.google.com/p/ipaddr-py/', 27 | license='Apache License, Version 2.0', 28 | classifiers=[ 29 | 'Development Status :: 5 - Production/Stable', 30 | 'Intended Audience :: Developers', 31 | 'License :: OSI Approved :: Apache Software License', 32 | 'Operating System :: OS Independent', 33 | 'Topic :: Internet', 34 | 'Topic :: Software Development :: Libraries', 35 | 'Topic :: System :: Networking'], 36 | py_modules=['ipaddr']) 37 | -------------------------------------------------------------------------------- /embedded_ipaddr/test-2to3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2007 Google Inc. 3 | # Licensed to PSF under a Contributor Agreement. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Converts the python2 ipaddr files to python3 and runs the unit tests 18 | # with both python versions. 19 | 20 | mkdir -p 2to3output && \ 21 | cp -f *.py 2to3output && \ 22 | ( cd 2to3output && 2to3 . | patch -p0 ) && \ 23 | py3version=$(python3 --version 2>&1) && \ 24 | echo -e "\nTesting with ${py3version}" && \ 25 | python3 2to3output/ipaddr_test.py && \ 26 | rm -r 2to3output && \ 27 | pyversion=$(python --version 2>&1) && \ 28 | echo -e "\nTesting with ${pyversion}" && \ 29 | ./ipaddr_test.py 30 | -------------------------------------------------------------------------------- /requirements-py2.txt: -------------------------------------------------------------------------------- 1 | py2-ipaddress==3.4.1 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = blockfinder 3 | author = Jacob Appelbaum 4 | author_email = jacob@appelbaum.net 5 | maintainer = Jacob Appelbaum 6 | maintainer_email = jacob@appelbaum.net 7 | summary = Blockfinder enumerates network information for countries. 8 | description-file = README 9 | license = BSD 10 | home-page = https://github.com/ioerror/blockfinder 11 | classifier = 12 | Development Status :: 4 - Beta 13 | Environment :: Console 14 | Intended Audience :: Developers 15 | Operating System :: OS Independent 16 | Programming Language :: Python 17 | Programming Language :: Python :: 2.6 18 | Programming Language :: Python :: 2.7 19 | Programming Language :: Python :: 3 20 | Programming Language :: Python :: 3.3 21 | Programming Language :: Python :: 3.4 22 | Programming Language :: Python :: 3.5 23 | License :: OSI Approved :: BSD License 24 | 25 | [files] 26 | packages = 27 | block_finder 28 | 29 | [bdist_wheel] 30 | universal = 1 31 | 32 | [entry_points] 33 | console_scripts = 34 | blockfinder = block_finder.blockfinder:main 35 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | 6 | setup( 7 | pbr=True, 8 | setup_requires=['pbr'], 9 | test_suite='block_finder.test', 10 | ) 11 | -------------------------------------------------------------------------------- /test_lir_data.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioerror/blockfinder/3e0f4f0971b63f115e740f57d9298deb5cec0cd8/test_lir_data.gz -------------------------------------------------------------------------------- /test_rir_data: -------------------------------------------------------------------------------- 1 | 2|apnic|20110113|23486|19850701|20110112|+1000 2 | apnic|*|asn|*|3986|summary 3 | apnic|*|ipv4|*|17947|summary 4 | apnic|*|ipv6|*|1553|summary 5 | apnic|JP|asn|173|1|20020801|allocated 6 | apnic|NZ|asn|681|1|20020801|allocated 7 | apnic|MM|ipv4|203.81.64.0|8192|20100504|assigned 8 | apnic|MM|ipv4|203.81.160.0|4096|20100122|assigned 9 | apnic|KP|ipv4|175.45.176.0|1024|20100122|assigned 10 | apnic|JP|ipv6|2001:200::|35|19990813|allocated 11 | apnic|JP|ipv6|2001:200:2000::|35|20030423|allocated 12 | apnic|JP|ipv6|2001:200:4000::|34|20030423|allocated 13 | apnic|JP|ipv6|2001:200:8000::|33|20030423|allocated 14 | ripencc|PL|ipv4|193.9.25.0|256|20090225|assigned 15 | ripencc|HU|ipv4|193.9.26.0|512|20081222|assigned 16 | --------------------------------------------------------------------------------