]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/python/location-importer.in
2 ###############################################################################
4 # libloc - A library to determine the location of someone on the Internet #
6 # Copyright (C) 2020 IPFire Development Team <info@ipfire.org> #
8 # This library is free software; you can redistribute it and/or #
9 # modify it under the terms of the GNU Lesser General Public #
10 # License as published by the Free Software Foundation; either #
11 # version 2.1 of the License, or (at your option) any later version. #
13 # This library is distributed in the hope that it will be useful, #
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
16 # Lesser General Public License for more details. #
18 ###############################################################################
27 # Load our location module
29 import location
.database
30 import location
.importer
31 from location
.i18n
import _
34 log
= logging
.getLogger("location.importer")
39 parser
= argparse
.ArgumentParser(
40 description
=_("Location Importer Command Line Interface"),
42 subparsers
= parser
.add_subparsers()
44 # Global configuration flags
45 parser
.add_argument("--debug", action
="store_true",
46 help=_("Enable debug output"))
49 parser
.add_argument("--version", action
="version",
50 version
="%(prog)s @VERSION@")
53 parser
.add_argument("--database-host", required
=True,
54 help=_("Database Hostname"), metavar
=_("HOST"))
55 parser
.add_argument("--database-name", required
=True,
56 help=_("Database Name"), metavar
=_("NAME"))
57 parser
.add_argument("--database-username", required
=True,
58 help=_("Database Username"), metavar
=_("USERNAME"))
59 parser
.add_argument("--database-password", required
=True,
60 help=_("Database Password"), metavar
=_("PASSWORD"))
63 update_whois
= subparsers
.add_parser("update-whois", help=_("Update WHOIS Information"))
64 update_whois
.set_defaults(func
=self
.handle_update_whois
)
66 args
= parser
.parse_args()
68 # Enable debug logging
70 log
.setLevel(logging
.DEBUG
)
72 # Print usage if no action was given
73 if not "func" in args
:
80 # Parse command line arguments
81 args
= self
.parse_cli()
84 self
.db
= self
._setup
_database
(args
)
89 # Return with exit code
96 def _setup_database(self
, ns
):
98 Initialise the database
100 # Connect to database
101 db
= location
.database
.Connection(
102 host
=ns
.database_host
, database
=ns
.database_name
,
103 user
=ns
.database_username
, password
=ns
.database_password
,
106 with db
.transaction():
109 CREATE TABLE IF NOT EXISTS autnums(number integer, name text);
110 CREATE UNIQUE INDEX IF NOT EXISTS autnums_number ON autnums(number);
113 CREATE TABLE IF NOT EXISTS networks(network inet, autnum integer, country text);
114 CREATE UNIQUE INDEX IF NOT EXISTS networks_network ON networks(network);
119 def handle_update_whois(self
, ns
):
120 downloader
= location
.importer
.Downloader()
122 # Download all sources
123 with self
.db
.transaction():
124 # Create some temporary tables to store parsed data
126 CREATE TEMPORARY TABLE _autnums(number integer, organization text)
128 CREATE UNIQUE INDEX _autnums_number ON _autnums(number);
130 CREATE TEMPORARY TABLE _organizations(handle text, name text)
132 CREATE UNIQUE INDEX _organizations_handle ON _organizations(handle);
135 for source
in location
.importer
.WHOIS_SOURCES
:
136 with downloader
.request(source
, return_blocks
=True) as f
:
138 self
._parse
_block
(block
)
141 INSERT INTO autnums(number, name)
142 SELECT _autnums.number, _organizations.name FROM _autnums
143 LEFT JOIN _organizations ON _autnums.organization = _organizations.handle
144 ON CONFLICT (number) DO UPDATE SET name = excluded.name;
147 # Download all extended sources
148 for source
in location
.importer
.EXTENDED_SOURCES
:
149 with self
.db
.transaction():
151 with downloader
.request(source
) as f
:
153 self
._parse
_line
(line
)
155 def _parse_block(self
, block
):
156 # Get first line to find out what type of block this is
160 if line
.startswith("aut-num:"):
161 return self
._parse
_autnum
_block
(block
)
164 elif line
.startswith("organisation:"):
165 return self
._parse
_org
_block
(block
)
167 def _parse_autnum_block(self
, block
):
171 key
, val
= split_line(line
)
174 m
= re
.match(r
"^(AS|as)(\d+)", val
)
176 autnum
["asn"] = m
.group(2)
185 # Insert into database
186 self
.db
.execute("INSERT INTO _autnums(number, organization) \
187 VALUES(%s, %s) ON CONFLICT (number) DO UPDATE SET \
188 organization = excluded.organization",
189 autnum
.get("asn"), autnum
.get("org"),
192 def _parse_org_block(self
, block
):
196 key
, val
= split_line(line
)
198 if key
in ("organisation", "org-name"):
205 self
.db
.execute("INSERT INTO _organizations(handle, name) \
206 VALUES(%s, %s) ON CONFLICT (handle) DO \
207 UPDATE SET name = excluded.name",
208 org
.get("organisation"), org
.get("org-name"),
211 def _parse_line(self
, line
):
213 if line
.startswith("2"):
217 if line
.startswith("#"):
221 registry
, country_code
, type, line
= line
.split("|", 3)
223 log
.warning("Could not parse line: %s" % line
)
226 # Skip any lines that are for stats only
227 if country_code
== "*":
230 if type in ("ipv6", "ipv4"):
231 return self
._parse
_ip
_line
(country_code
, type, line
)
233 def _parse_ip_line(self
, country
, type, line
):
235 address
, prefix
, date
, status
, organization
= line
.split("|")
239 # Try parsing the line without organization
241 address
, prefix
, date
, status
= line
.split("|")
243 log
.warning("Unhandled line format: %s" % line
)
246 # Skip anything that isn't properly assigned
247 if not status
in ("assigned", "allocated"):
250 # Cast prefix into an integer
254 log
.warning("Invalid prefix: %s" % prefix
)
256 # Fix prefix length for IPv4
258 prefix
= 32 - int(math
.log(prefix
, 2))
260 # Try to parse the address
262 network
= ipaddress
.ip_network("%s/%s" % (address
, prefix
), strict
=False)
264 log
.warning("Invalid IP address: %s" % address
)
267 self
.db
.execute("INSERT INTO networks(network, country) \
268 VALUES(%s, %s) ON CONFLICT (network) DO \
269 UPDATE SET country = excluded.country",
270 "%s" % network
, country
,
274 def split_line(line
):
275 key
, colon
, val
= line
.partition(":")
277 # Strip any excess space
284 # Run the command line interface