mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-09 18:45:54 +02:00
libloc: Update to 0.9.2
Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
This commit is contained in:
24
lfs/libloc
24
lfs/libloc
@@ -24,7 +24,7 @@
|
||||
|
||||
include Config
|
||||
|
||||
VER = 0.9.1
|
||||
VER = 0.9.2
|
||||
DB_DATE = 2020-06-10
|
||||
|
||||
THISAPP = libloc-$(VER)
|
||||
@@ -40,10 +40,10 @@ TARGET = $(DIR_INFO)/$(THISAPP)
|
||||
objects = $(DL_FILE) \
|
||||
location-$(DB_DATE).db.xz
|
||||
|
||||
$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
|
||||
$(DL_FILE) = https://source.ipfire.org/releases/libloc//$(DL_FILE)
|
||||
location-$(DB_DATE).db.xz = https://location.ipfire.org/databases/1/archive/location-$(DB_DATE).db.xz
|
||||
|
||||
$(DL_FILE)_MD5 = b62331e7a5bc5299bdd35f340342fc51
|
||||
$(DL_FILE)_MD5 = c6ed4fcbdb2ce4ca7e6df8abbd985411
|
||||
location-$(DB_DATE).db.xz_MD5 = 268b6d58a26c6d36081ed1e899b89020
|
||||
|
||||
install : $(TARGET)
|
||||
@@ -78,24 +78,6 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
|
||||
@$(PREBUILD)
|
||||
@rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar xvf $(DIR_DL)/$(DL_FILE)
|
||||
|
||||
# Add upstream patches.
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-location-downloader-do-not-change-content-of-open-database.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-remove-python-path-overrides-for-debian.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-location-query-require-at-least-one-flag.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-location-exporter-do-not-mistake-country-as-for-an-as-number.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-location-exporter-warn-but-do-not-fail-on-invalid-input.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-move-location-downloader-functionality-into-location-query.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-merge-location-downloader-manpage-into-location-query.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-downloader-rename-user-agent-to-location.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-rename-location-query-to-location.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-merge-location-exporter-into-location.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-remove-accidently-commited-hacks-for-debian.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-add-option-to-iterate-over-all-contries-and-print-them.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-export-all-countries-by-default.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-export-flagged-networks-with-their-faked-country-names-too.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-database-fix-brocken-search-for-networks-with-flags.patch
|
||||
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.1-adjust-format-to-print-ASes.patch
|
||||
|
||||
cd $(DIR_APP) && ./autogen.sh
|
||||
cd $(DIR_APP) && ./configure \
|
||||
--prefix=/usr \
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
commit fa9a3663cb2dfb2490da43f6967f1a3a2948fc8a
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Fri Jun 5 09:41:28 2020 +0000
|
||||
|
||||
Add option to iterate over all countries and print them to the console
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/man/location.txt b/man/location.txt
|
||||
index 672c2b2..0d70e0b 100644
|
||||
--- a/man/location.txt
|
||||
+++ b/man/location.txt
|
||||
@@ -13,6 +13,7 @@ location - Query the location database
|
||||
`location list-networks-by-as ASN`
|
||||
`location list-networks-by-cc COUNTRY_CODE`
|
||||
`location list-networks-by-flags [--anonymous-proxy|--satellite-provider|--anycast]`
|
||||
+`location list-countries [--show-name] [--show-continent]`
|
||||
|
||||
== DESCRIPTION
|
||||
`location` retrieves information from the location database.
|
||||
@@ -86,6 +87,12 @@ or countries.
|
||||
+
|
||||
See above for usage of the '--family' and '--output-format' parameters.
|
||||
|
||||
+'list-countries [--show-name] [--show-continent]'::
|
||||
+ Lists all countries known to the database.
|
||||
+ +
|
||||
+ With the optional parameters '--show-name' and '--show-continent', the name and
|
||||
+ continent code will be printed, too.
|
||||
+
|
||||
'--help'::
|
||||
Shows a short help text on using this program.
|
||||
|
||||
diff --git a/src/database.c b/src/database.c
|
||||
index d919278..8e6c5ab 100644
|
||||
--- a/src/database.c
|
||||
+++ b/src/database.c
|
||||
@@ -107,6 +107,9 @@ struct loc_database_enumerator {
|
||||
// Index of the AS we are looking at
|
||||
unsigned int as_index;
|
||||
|
||||
+ // Index of the country we are looking at
|
||||
+ unsigned int country_index;
|
||||
+
|
||||
// Network state
|
||||
struct in6_addr network_address;
|
||||
struct loc_node_stack network_stack[MAX_STACK_DEPTH];
|
||||
@@ -1219,3 +1222,30 @@ LOC_EXPORT int loc_database_enumerator_next_network(
|
||||
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
+LOC_EXPORT int loc_database_enumerator_next_country(
|
||||
+ struct loc_database_enumerator* enumerator, struct loc_country** country) {
|
||||
+ *country = NULL;
|
||||
+
|
||||
+ // Do not do anything if not in country mode
|
||||
+ if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
|
||||
+ return 0;
|
||||
+
|
||||
+ struct loc_database* db = enumerator->db;
|
||||
+
|
||||
+ while (enumerator->country_index < db->countries_count) {
|
||||
+ // Fetch the next country
|
||||
+ int r = loc_database_fetch_country(db, country, enumerator->country_index++);
|
||||
+ if (r)
|
||||
+ return r;
|
||||
+
|
||||
+ // We do not filter here, so it always is a match
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ // Reset the index
|
||||
+ enumerator->country_index = 0;
|
||||
+
|
||||
+ // We have searched through all of them
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/src/libloc.sym b/src/libloc.sym
|
||||
index e9e8549..9a1e6f0 100644
|
||||
--- a/src/libloc.sym
|
||||
+++ b/src/libloc.sym
|
||||
@@ -68,6 +68,7 @@ global:
|
||||
# Database Enumerator
|
||||
loc_database_enumerator_new;
|
||||
loc_database_enumerator_next_as;
|
||||
+ loc_database_enumerator_next_country;
|
||||
loc_database_enumerator_next_network;
|
||||
loc_database_enumerator_ref;
|
||||
loc_database_enumerator_set_asn;
|
||||
diff --git a/src/loc/database.h b/src/loc/database.h
|
||||
index ab9ef72..43173dd 100644
|
||||
--- a/src/loc/database.h
|
||||
+++ b/src/loc/database.h
|
||||
@@ -50,8 +50,9 @@ int loc_database_get_country(struct loc_database* db,
|
||||
struct loc_country** country, const char* code);
|
||||
|
||||
enum loc_database_enumerator_mode {
|
||||
- LOC_DB_ENUMERATE_NETWORKS = 1,
|
||||
- LOC_DB_ENUMERATE_ASES = 2,
|
||||
+ LOC_DB_ENUMERATE_NETWORKS = 1,
|
||||
+ LOC_DB_ENUMERATE_ASES = 2,
|
||||
+ LOC_DB_ENUMERATE_COUNTRIES = 3,
|
||||
};
|
||||
|
||||
struct loc_database_enumerator;
|
||||
@@ -69,5 +70,7 @@ int loc_database_enumerator_next_as(
|
||||
struct loc_database_enumerator* enumerator, struct loc_as** as);
|
||||
int loc_database_enumerator_next_network(
|
||||
struct loc_database_enumerator* enumerator, struct loc_network** network);
|
||||
+int loc_database_enumerator_next_country(
|
||||
+ struct loc_database_enumerator* enumerator, struct loc_country** country);
|
||||
|
||||
#endif
|
||||
diff --git a/src/python/database.c b/src/python/database.c
|
||||
index 581ed5b..1013a58 100644
|
||||
--- a/src/python/database.c
|
||||
+++ b/src/python/database.c
|
||||
@@ -316,6 +316,10 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
|
||||
return obj;
|
||||
}
|
||||
|
||||
+static PyObject* Database_countries(DatabaseObject* self) {
|
||||
+ return Database_iterate_all(self, LOC_DB_ENUMERATE_COUNTRIES);
|
||||
+}
|
||||
+
|
||||
static struct PyMethodDef Database_methods[] = {
|
||||
{
|
||||
"get_as",
|
||||
@@ -364,6 +368,13 @@ static struct PyGetSetDef Database_getsetters[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
+ {
|
||||
+ "countries",
|
||||
+ (getter)Database_countries,
|
||||
+ NULL,
|
||||
+ NULL,
|
||||
+ NULL,
|
||||
+ },
|
||||
{
|
||||
"created_at",
|
||||
(getter)Database_get_created_at,
|
||||
@@ -462,6 +473,22 @@ static PyObject* DatabaseEnumerator_next(DatabaseEnumeratorObject* self) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
+ // Enumerate all countries
|
||||
+ struct loc_country* country = NULL;
|
||||
+
|
||||
+ r = loc_database_enumerator_next_country(self->enumerator, &country);
|
||||
+ if (r) {
|
||||
+ PyErr_SetFromErrno(PyExc_ValueError);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (country) {
|
||||
+ PyObject* obj = new_country(&CountryType, country);
|
||||
+ loc_country_unref(country);
|
||||
+
|
||||
+ return obj;
|
||||
+ }
|
||||
+
|
||||
// Nothing found, that means the end
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
diff --git a/src/python/location.in b/src/python/location.in
|
||||
index 7614cae..5c1effd 100644
|
||||
--- a/src/python/location.in
|
||||
+++ b/src/python/location.in
|
||||
@@ -147,6 +147,18 @@ class CLI(object):
|
||||
choices=location.export.formats.keys(), default="list")
|
||||
list_networks_by_flags.set_defaults(func=self.handle_list_networks_by_flags)
|
||||
|
||||
+ # List countries
|
||||
+ list_countries = subparsers.add_parser("list-countries",
|
||||
+ help=_("Lists all countries"),
|
||||
+ )
|
||||
+ list_countries.add_argument("--show-name",
|
||||
+ action="store_true", help=_("Show the name of the country"),
|
||||
+ )
|
||||
+ list_countries.add_argument("--show-continent",
|
||||
+ action="store_true", help=_("Show the continent"),
|
||||
+ )
|
||||
+ list_countries.set_defaults(func=self.handle_list_countries)
|
||||
+
|
||||
# Export
|
||||
export = subparsers.add_parser("export",
|
||||
help=_("Exports data in many formats to load it into packet filters"),
|
||||
@@ -435,6 +447,24 @@ class CLI(object):
|
||||
|
||||
return cls
|
||||
|
||||
+ def handle_list_countries(self, db, ns):
|
||||
+ for country in db.countries:
|
||||
+ line = [
|
||||
+ country.code,
|
||||
+ ]
|
||||
+
|
||||
+ if ns.show_continent:
|
||||
+ line.append(country.continent_code)
|
||||
+
|
||||
+ if ns.show_name:
|
||||
+ line.append(country.name)
|
||||
+
|
||||
+ # Format the output
|
||||
+ line = " ".join(line)
|
||||
+
|
||||
+ # Print the output
|
||||
+ print(line)
|
||||
+
|
||||
def handle_list_networks_by_as(self, db, ns):
|
||||
writer = self.__get_output_formatter(ns)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
commit 864dd22e17f7487a90e165274cf3f7898966028d
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Fri Jun 5 10:01:47 2020 +0000
|
||||
|
||||
database: Fix broken search for networks with flags
|
||||
|
||||
The search was ended after the first network. No matter if
|
||||
it matched, or not.
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/database.c b/src/database.c
|
||||
index 8e6c5ab..fa1dad0 100644
|
||||
--- a/src/database.c
|
||||
+++ b/src/database.c
|
||||
@@ -1208,6 +1208,8 @@ LOC_EXPORT int loc_database_enumerator_next_network(
|
||||
!loc_network_match_flag(*network, enumerator->flags)) {
|
||||
loc_network_unref(*network);
|
||||
*network = NULL;
|
||||
+
|
||||
+ continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1,21 +0,0 @@
|
||||
commit dc1df0f469668ef3dc4e1a9a7623a0ebba2b051e
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 17:15:27 2020 +0000
|
||||
|
||||
downloader: Change user-agent to location
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/downloader.py b/src/python/downloader.py
|
||||
index c9e6e00..eb28007 100644
|
||||
--- a/src/python/downloader.py
|
||||
+++ b/src/python/downloader.py
|
||||
@@ -71,7 +71,7 @@ class Downloader(object):
|
||||
|
||||
# Update headers
|
||||
headers.update({
|
||||
- "User-Agent" : "location-downloader/@VERSION@",
|
||||
+ "User-Agent" : "location/@VERSION@",
|
||||
})
|
||||
|
||||
# Set headers
|
||||
@@ -1,33 +0,0 @@
|
||||
commit 10fa313b392a269e15bdaf316218a114d9b23b55
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Fri Jun 5 09:47:36 2020 +0000
|
||||
|
||||
location(8): Export all countries by default
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/location.in b/src/python/location.in
|
||||
index 5c1effd..6ced5f5 100644
|
||||
--- a/src/python/location.in
|
||||
+++ b/src/python/location.in
|
||||
@@ -169,7 +169,7 @@ class CLI(object):
|
||||
export.add_argument("--family",
|
||||
help=_("Specify address family"), choices=("ipv6", "ipv4"),
|
||||
)
|
||||
- export.add_argument("objects", nargs="+", help=_("List country codes or ASNs to export"))
|
||||
+ export.add_argument("objects", nargs="*", help=_("List country codes or ASNs to export"))
|
||||
export.set_defaults(func=self.handle_export)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -539,9 +539,9 @@ class CLI(object):
|
||||
log.warning("Invalid argument: %s" % object)
|
||||
continue
|
||||
|
||||
+ # Default to exporting all countries
|
||||
if not countries and not asns:
|
||||
- log.error("Nothing to export")
|
||||
- return 2
|
||||
+ countries = ["A1", "A2", "A3"] + [country.code for country in db.countries]
|
||||
|
||||
# Select the output format
|
||||
writer = self.__get_output_formatter(ns)
|
||||
@@ -1,54 +0,0 @@
|
||||
commit fae36e81a32717ac43c0ce48702f6ff05b7cd029
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Fri Jun 5 09:57:41 2020 +0000
|
||||
|
||||
export flagged networks with their faked country names, too
|
||||
|
||||
This will lead to some networks showing up twice. Once with
|
||||
their real country and once with their faked one.
|
||||
|
||||
It is likely that the first one will match.
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/export.py b/src/python/export.py
|
||||
index 69fe964..d0bbe77 100644
|
||||
--- a/src/python/export.py
|
||||
+++ b/src/python/export.py
|
||||
@@ -23,10 +23,18 @@ import logging
|
||||
import os
|
||||
import socket
|
||||
|
||||
+import _location
|
||||
+
|
||||
# Initialise logging
|
||||
log = logging.getLogger("location.export")
|
||||
log.propagate = 1
|
||||
|
||||
+flags = {
|
||||
+ _location.NETWORK_FLAG_ANONYMOUS_PROXY : "A1",
|
||||
+ _location.NETWORK_FLAG_SATELLITE_PROVIDER : "A2",
|
||||
+ _location.NETWORK_FLAG_ANYCAST : "A3",
|
||||
+}
|
||||
+
|
||||
class OutputWriter(object):
|
||||
suffix = "networks"
|
||||
mode = "w"
|
||||
@@ -173,6 +181,17 @@ class Exporter(object):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
+ # Handle flags
|
||||
+ for flag in flags:
|
||||
+ if network.has_flag(flag):
|
||||
+ # Fetch the "fake" country code
|
||||
+ country = flags[flag]
|
||||
+
|
||||
+ try:
|
||||
+ writers[country].write(network)
|
||||
+ except KeyError:
|
||||
+ pass
|
||||
+
|
||||
# Write everything to the filesystem
|
||||
for writer in writers.values():
|
||||
writer.finish()
|
||||
@@ -1,81 +0,0 @@
|
||||
commit 679e5ae2e45b98e254c651cb3102a4331c2c22eb
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Mon Jun 1 13:47:44 2020 +0000
|
||||
|
||||
location-downloader: Do not change content of open database files
|
||||
|
||||
The database might be opened by another process. When modified,
|
||||
it will return random results.
|
||||
|
||||
Fixes: #12420
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/location-downloader.in b/src/python/location-downloader.in
|
||||
index 7d06030..bf0d682 100644
|
||||
--- a/src/python/location-downloader.in
|
||||
+++ b/src/python/location-downloader.in
|
||||
@@ -24,6 +24,7 @@ import lzma
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
+import stat
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@@ -116,7 +117,7 @@ class Downloader(object):
|
||||
|
||||
return res
|
||||
|
||||
- def download(self, url, public_key, timestamp=None, **kwargs):
|
||||
+ def download(self, url, public_key, timestamp=None, tmpdir=None, **kwargs):
|
||||
headers = {}
|
||||
|
||||
if timestamp:
|
||||
@@ -124,7 +125,7 @@ class Downloader(object):
|
||||
"%a, %d %b %Y %H:%M:%S GMT",
|
||||
)
|
||||
|
||||
- t = tempfile.NamedTemporaryFile(delete=False)
|
||||
+ t = tempfile.NamedTemporaryFile(dir=tmpdir, delete=False)
|
||||
with t:
|
||||
# Try all mirrors
|
||||
for mirror in self.mirrors:
|
||||
@@ -175,6 +176,9 @@ class Downloader(object):
|
||||
t.truncate()
|
||||
continue
|
||||
|
||||
+ # Make the file readable for everyone
|
||||
+ os.chmod(t.name, stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)
|
||||
+
|
||||
# Return temporary file
|
||||
return t
|
||||
|
||||
@@ -296,10 +300,13 @@ class CLI(object):
|
||||
except FileNotFoundError as e:
|
||||
db = None
|
||||
|
||||
+ # Download the database into the correct directory
|
||||
+ tmpdir = os.path.dirname(ns.database)
|
||||
+
|
||||
# Try downloading a new database
|
||||
try:
|
||||
t = self.downloader.download("%s/%s" % (self.version, DATABASE_FILENAME),
|
||||
- public_key=ns.public_key, timestamp=timestamp)
|
||||
+ public_key=ns.public_key, timestamp=timestamp, tmpdir=tmpdir)
|
||||
|
||||
# If no file could be downloaded, log a message
|
||||
except FileNotFoundError as e:
|
||||
@@ -310,11 +317,8 @@ class CLI(object):
|
||||
if not t:
|
||||
return 3
|
||||
|
||||
- # Write temporary file to destination
|
||||
- shutil.copyfile(t.name, ns.database)
|
||||
-
|
||||
- # Remove temporary file
|
||||
- os.unlink(t.name)
|
||||
+ # Move temporary file to destination
|
||||
+ shutil.move(t.name, ns.database)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
commit 141b10999b280b2563580c705d5d23dc4c442deb
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 16:31:44 2020 +0000
|
||||
|
||||
location-exporter: Do not mistake country AS for an AS number
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/location-exporter.in b/src/python/location-exporter.in
|
||||
index 894bb44..5454561 100644
|
||||
--- a/src/python/location-exporter.in
|
||||
+++ b/src/python/location-exporter.in
|
||||
@@ -22,6 +22,7 @@ import io
|
||||
import ipaddress
|
||||
import logging
|
||||
import os.path
|
||||
+import re
|
||||
import socket
|
||||
import sys
|
||||
|
||||
@@ -258,12 +259,9 @@ class CLI(object):
|
||||
families = [ socket.AF_INET6, socket.AF_INET ]
|
||||
|
||||
for object in ns.objects:
|
||||
- if object.startswith("AS"):
|
||||
- try:
|
||||
- object = int(object[2:])
|
||||
- except ValueError:
|
||||
- log.error("Invalid argument: %s" % object)
|
||||
- return 2
|
||||
+ m = re.match("^AS(\d+)$", object)
|
||||
+ if m:
|
||||
+ object = int(m.group(1))
|
||||
|
||||
asns.append(object)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
commit 92af07adfb1e06fe1b055fbcf5ba61159637cd73
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 16:33:44 2020 +0000
|
||||
|
||||
location-exporter: Warn, but do not fail on invalid input
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/location-exporter.in b/src/python/location-exporter.in
|
||||
index 5454561..d82f1d3 100644
|
||||
--- a/src/python/location-exporter.in
|
||||
+++ b/src/python/location-exporter.in
|
||||
@@ -270,8 +270,12 @@ class CLI(object):
|
||||
countries.append(object)
|
||||
|
||||
else:
|
||||
- log.error("Invalid argument: %s" % object)
|
||||
- return 2
|
||||
+ log.warning("Invalid argument: %s" % object)
|
||||
+ continue
|
||||
+
|
||||
+ if not countries and not asns:
|
||||
+ log.error("Nothing to export")
|
||||
+ return 2
|
||||
|
||||
# Open the database
|
||||
try:
|
||||
@@ -1,37 +0,0 @@
|
||||
commit 228d0e74ec47c9954d3a0e1da2e1c0fc6c1b518f
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 16:15:24 2020 +0000
|
||||
|
||||
location-query: Require at least one flag
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/src/python/location-query.in b/src/python/location-query.in
|
||||
index 5f05b5c..dfdff8c 100644
|
||||
--- a/src/python/location-query.in
|
||||
+++ b/src/python/location-query.in
|
||||
@@ -246,7 +246,13 @@ class CLI(object):
|
||||
args.family = 0
|
||||
|
||||
# Call function
|
||||
- ret = args.func(db, args)
|
||||
+ try:
|
||||
+ ret = args.func(db, args)
|
||||
+
|
||||
+ # Catch invalid inputs
|
||||
+ except ValueError as e:
|
||||
+ sys.stderr.write("%s\n" % e)
|
||||
+ ret = 2
|
||||
|
||||
# Return with exit code
|
||||
if ret:
|
||||
@@ -451,6 +457,9 @@ class CLI(object):
|
||||
if ns.anycast:
|
||||
flags |= location.NETWORK_FLAG_ANYCAST
|
||||
|
||||
+ if not flags:
|
||||
+ raise ValueError(_("You must at least pass one flag"))
|
||||
+
|
||||
with self.__get_output_formatter(ns) as f:
|
||||
for n in db.search_networks(flags=flags, family=ns.family):
|
||||
f.network(n)
|
||||
@@ -1,140 +0,0 @@
|
||||
commit 889b932aa6172c96872be545af37d351f7c1c705
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 17:10:35 2020 +0000
|
||||
|
||||
location-downloader: Merge man page into location-query
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index c0b1300..91f0436 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -37,6 +37,9 @@ LIBLOC_CURRENT=0
|
||||
LIBLOC_REVISION=0
|
||||
LIBLOC_AGE=0
|
||||
|
||||
+pythondir = $(prefix)/lib/python3/dist-packages
|
||||
+pyexecdir = $(prefix)/lib/python$(PYTHON_VERSION)/lib-dynload
|
||||
+
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@@ -371,7 +374,6 @@ src_test_signature_LDADD = \
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
MANPAGES = \
|
||||
- man/location-downloader.8 \
|
||||
man/location-query.8
|
||||
|
||||
MANPAGES_TXT = $(patsubst %.8,%.txt,$(MANPAGES))
|
||||
diff --git a/man/location-downloader.txt b/man/location-downloader.txt
|
||||
deleted file mode 100644
|
||||
index d733923..0000000
|
||||
--- a/man/location-downloader.txt
|
||||
+++ /dev/null
|
||||
@@ -1,61 +0,0 @@
|
||||
-= location-downloader(8)
|
||||
-
|
||||
-== NAME
|
||||
-location-downloader - Download a location database
|
||||
-
|
||||
-== SYNOPSIS
|
||||
-[verse]
|
||||
-`location-downloader update`
|
||||
-
|
||||
-== DESCRIPTION
|
||||
-The `location-downloader` command updates the local version of the
|
||||
-location database.
|
||||
-
|
||||
-== OPTIONS
|
||||
-
|
||||
---database FILE::
|
||||
--d FILE::
|
||||
- The path of the database which is being updated.
|
||||
- +
|
||||
- If this option is omitted, the system's database will be opened.
|
||||
-
|
||||
---quiet::
|
||||
- Enable quiet mode
|
||||
-
|
||||
---debug::
|
||||
- Enable debugging mode
|
||||
-
|
||||
-== COMMANDS
|
||||
-
|
||||
-'update'::
|
||||
- This command will try to update the local database.
|
||||
- +
|
||||
- It will terminate with a return code of zero if the database has been
|
||||
- successfully updated. 1 on error, 2 on invalid call and 3 if the
|
||||
- database was already the latest version.
|
||||
-
|
||||
-'verify'::
|
||||
- Verifies the downloaded database.
|
||||
-
|
||||
-'--help'::
|
||||
- Shows a short help text on using this program.
|
||||
-
|
||||
-'--version'::
|
||||
- Shows the program's version and exists.
|
||||
-
|
||||
-== EXIT CODES
|
||||
-The 'location-downloader' command will normally exit with code zero.
|
||||
-If there has been a problem and the requested action could not be performed,
|
||||
-the exit code is unequal to zero.
|
||||
-
|
||||
-== HOW IT WORKS
|
||||
-The downloader checks a DNS record for the latest version of the database.
|
||||
-It will then try to download a file with that version from a mirror server.
|
||||
-If the downloaded file is outdated, the next mirror will be tried until we
|
||||
-have found a file that is recent enough.
|
||||
-
|
||||
-== BUGS
|
||||
-Please report all bugs to the bugtracker at https://bugzilla.ipfire.org/.
|
||||
-
|
||||
-== AUTHORS
|
||||
-Michael Tremer
|
||||
diff --git a/man/location-query.txt b/man/location-query.txt
|
||||
index b91e8e1..acb43cd 100644
|
||||
--- a/man/location-query.txt
|
||||
+++ b/man/location-query.txt
|
||||
@@ -8,6 +8,8 @@ location-query - Query the location database
|
||||
`location-query lookup ADDRESS [ADDRESS...]`
|
||||
`location-query get-as ASN [ASN...]`
|
||||
`location-query search-as STRING`
|
||||
+`location-query update`
|
||||
+`location-query verify`
|
||||
`location-query list-networks-by-as ASN`
|
||||
`location-query list-networks-by-cc COUNTRY_CODE`
|
||||
`location-query list-networks-by-flags [--anonymous-proxy|--satellite-provider|--anycast]`
|
||||
@@ -47,6 +49,16 @@ or countries.
|
||||
+
|
||||
The search will be performed case-insensitively.
|
||||
|
||||
+'update'::
|
||||
+ This command will try to update the local database.
|
||||
+ +
|
||||
+ It will terminate with a return code of zero if the database has been
|
||||
+ successfully updated. 1 on error, 2 on invalid call and 3 if the
|
||||
+ database was already the latest version.
|
||||
+
|
||||
+'verify'::
|
||||
+ Verifies the downloaded database.
|
||||
+
|
||||
'list-networks-by-as [--family=[ipv6|ipv4]] [--output-format FORMAT] ASN'::
|
||||
Lists all networks which belong to this Autonomous System.
|
||||
+
|
||||
@@ -85,6 +97,12 @@ The 'location-query' command will normally exit with code zero.
|
||||
If there has been a problem and the requested action could not be performed,
|
||||
the exit code is unequal to zero.
|
||||
|
||||
+== HOW IT WORKS
|
||||
+The downloader checks a DNS record for the latest version of the database.
|
||||
+It will then try to download a file with that version from a mirror server.
|
||||
+If the downloaded file is outdated, the next mirror will be tried until we
|
||||
+have found a file that is recent enough.
|
||||
+
|
||||
== BUGS
|
||||
Please report all bugs to the bugtracker at https://bugzilla.ipfire.org/.
|
||||
|
||||
@@ -1,796 +0,0 @@
|
||||
commit 88ef7e9cd4b3a1a5662c7dc071bd7a44e1242cba
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 18:36:28 2020 +0000
|
||||
|
||||
Merge location-exporter(8) into location(8)
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index 59870b1..9f520cc 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -150,6 +150,7 @@ dist_pkgpython_PYTHON = \
|
||||
src/python/__init__.py \
|
||||
src/python/database.py \
|
||||
src/python/downloader.py \
|
||||
+ src/python/export.py \
|
||||
src/python/i18n.py \
|
||||
src/python/importer.py \
|
||||
src/python/logger.py
|
||||
@@ -239,17 +240,14 @@ uninstall-perl:
|
||||
|
||||
bin_SCRIPTS = \
|
||||
src/python/location \
|
||||
- src/python/location-exporter \
|
||||
src/python/location-importer
|
||||
|
||||
EXTRA_DIST += \
|
||||
src/python/location.in \
|
||||
- src/python/location-exporter.in \
|
||||
src/python/location-importer.in
|
||||
|
||||
CLEANFILES += \
|
||||
src/python/location \
|
||||
- src/python/location-exporter \
|
||||
src/python/location-importer
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
diff --git a/src/python/export.py b/src/python/export.py
|
||||
new file mode 100644
|
||||
index 0000000..69fe964
|
||||
--- /dev/null
|
||||
+++ b/src/python/export.py
|
||||
@@ -0,0 +1,185 @@
|
||||
+#!/usr/bin/python3
|
||||
+###############################################################################
|
||||
+# #
|
||||
+# libloc - A library to determine the location of someone on the Internet #
|
||||
+# #
|
||||
+# Copyright (C) 2020 IPFire Development Team <info@ipfire.org> #
|
||||
+# #
|
||||
+# This library is free software; you can redistribute it and/or #
|
||||
+# modify it under the terms of the GNU Lesser General Public #
|
||||
+# License as published by the Free Software Foundation; either #
|
||||
+# version 2.1 of the License, or (at your option) any later version. #
|
||||
+# #
|
||||
+# This library is distributed in the hope that it will be useful, #
|
||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
+# Lesser General Public License for more details. #
|
||||
+# #
|
||||
+###############################################################################
|
||||
+
|
||||
+import io
|
||||
+import ipaddress
|
||||
+import logging
|
||||
+import os
|
||||
+import socket
|
||||
+
|
||||
+# Initialise logging
|
||||
+log = logging.getLogger("location.export")
|
||||
+log.propagate = 1
|
||||
+
|
||||
+class OutputWriter(object):
|
||||
+ suffix = "networks"
|
||||
+ mode = "w"
|
||||
+
|
||||
+ def __init__(self, f, prefix=None):
|
||||
+ self.f, self.prefix = f, prefix
|
||||
+
|
||||
+ # Immediately write the header
|
||||
+ self._write_header()
|
||||
+
|
||||
+ @classmethod
|
||||
+ def open(cls, filename, **kwargs):
|
||||
+ """
|
||||
+ Convenience function to open a file
|
||||
+ """
|
||||
+ f = open(filename, cls.mode)
|
||||
+
|
||||
+ return cls(f, **kwargs)
|
||||
+
|
||||
+ def __repr__(self):
|
||||
+ return "<%s f=%s>" % (self.__class__.__name__, self.f)
|
||||
+
|
||||
+ def _write_header(self):
|
||||
+ """
|
||||
+ The header of the file
|
||||
+ """
|
||||
+ pass
|
||||
+
|
||||
+ def _write_footer(self):
|
||||
+ """
|
||||
+ The footer of the file
|
||||
+ """
|
||||
+ pass
|
||||
+
|
||||
+ def write(self, network):
|
||||
+ self.f.write("%s\n" % network)
|
||||
+
|
||||
+ def finish(self):
|
||||
+ """
|
||||
+ Called when all data has been written
|
||||
+ """
|
||||
+ self._write_footer()
|
||||
+
|
||||
+ # Close the file
|
||||
+ self.f.close()
|
||||
+
|
||||
+
|
||||
+class IpsetOutputWriter(OutputWriter):
|
||||
+ """
|
||||
+ For ipset
|
||||
+ """
|
||||
+ suffix = "ipset"
|
||||
+
|
||||
+ def _write_header(self):
|
||||
+ self.f.write("create %s hash:net family inet hashsize 1024 maxelem 65536\n" % self.prefix)
|
||||
+
|
||||
+ def write(self, network):
|
||||
+ self.f.write("add %s %s\n" % (self.prefix, network))
|
||||
+
|
||||
+
|
||||
+class NftablesOutputWriter(OutputWriter):
|
||||
+ """
|
||||
+ For nftables
|
||||
+ """
|
||||
+ suffix = "set"
|
||||
+
|
||||
+ def _write_header(self):
|
||||
+ self.f.write("define %s = {\n" % self.prefix)
|
||||
+
|
||||
+ def _write_footer(self):
|
||||
+ self.f.write("}\n")
|
||||
+
|
||||
+ def write(self, network):
|
||||
+ self.f.write(" %s,\n" % network)
|
||||
+
|
||||
+
|
||||
+class XTGeoIPOutputWriter(OutputWriter):
|
||||
+ """
|
||||
+ Formats the output in that way, that it can be loaded by
|
||||
+ the xt_geoip kernel module from xtables-addons.
|
||||
+ """
|
||||
+ suffix = "iv"
|
||||
+ mode = "wb"
|
||||
+
|
||||
+ def write(self, network):
|
||||
+ n = ipaddress.ip_network("%s" % network)
|
||||
+
|
||||
+ for address in (n.network_address, n.broadcast_address):
|
||||
+ bytes = socket.inet_pton(
|
||||
+ socket.AF_INET6 if address.version == 6 else socket.AF_INET,
|
||||
+ "%s" % address,
|
||||
+ )
|
||||
+
|
||||
+ self.f.write(bytes)
|
||||
+
|
||||
+
|
||||
+formats = {
|
||||
+ "ipset" : IpsetOutputWriter,
|
||||
+ "list" : OutputWriter,
|
||||
+ "nftables" : NftablesOutputWriter,
|
||||
+ "xt_geoip" : XTGeoIPOutputWriter,
|
||||
+}
|
||||
+
|
||||
+class Exporter(object):
|
||||
+ def __init__(self, db, writer):
|
||||
+ self.db, self.writer = db, writer
|
||||
+
|
||||
+ def export(self, directory, families, countries, asns):
|
||||
+ for family in families:
|
||||
+ log.debug("Exporting family %s" % family)
|
||||
+
|
||||
+ writers = {}
|
||||
+
|
||||
+ # Create writers for countries
|
||||
+ for country_code in countries:
|
||||
+ filename = self._make_filename(
|
||||
+ directory, prefix=country_code, suffix=self.writer.suffix, family=family,
|
||||
+ )
|
||||
+
|
||||
+ writers[country_code] = self.writer.open(filename, prefix="CC_%s" % country_code)
|
||||
+
|
||||
+ # Create writers for ASNs
|
||||
+ for asn in asns:
|
||||
+ filename = self._make_filename(
|
||||
+ directory, "AS%s" % asn, suffix=self.writer.suffix, family=family,
|
||||
+ )
|
||||
+
|
||||
+ writers[asn] = self.writer.open(filename, prefix="AS%s" % asn)
|
||||
+
|
||||
+ # Get all networks that match the family
|
||||
+ networks = self.db.search_networks(family=family)
|
||||
+
|
||||
+ # Walk through all networks
|
||||
+ for network in networks:
|
||||
+ # Write matching countries
|
||||
+ try:
|
||||
+ writers[network.country_code].write(network)
|
||||
+ except KeyError:
|
||||
+ pass
|
||||
+
|
||||
+ # Write matching ASNs
|
||||
+ try:
|
||||
+ writers[network.asn].write(network)
|
||||
+ except KeyError:
|
||||
+ pass
|
||||
+
|
||||
+ # Write everything to the filesystem
|
||||
+ for writer in writers.values():
|
||||
+ writer.finish()
|
||||
+
|
||||
+ def _make_filename(self, directory, prefix, suffix, family):
|
||||
+ filename = "%s.%s%s" % (
|
||||
+ prefix, suffix, "6" if family == socket.AF_INET6 else "4"
|
||||
+ )
|
||||
+
|
||||
+ return os.path.join(directory, filename)
|
||||
diff --git a/src/python/location-exporter.in b/src/python/location-exporter.in
|
||||
deleted file mode 100644
|
||||
index d82f1d3..0000000
|
||||
--- a/src/python/location-exporter.in
|
||||
+++ /dev/null
|
||||
@@ -1,300 +0,0 @@
|
||||
-#!/usr/bin/python3
|
||||
-###############################################################################
|
||||
-# #
|
||||
-# libloc - A library to determine the location of someone on the Internet #
|
||||
-# #
|
||||
-# Copyright (C) 2019 IPFire Development Team <info@ipfire.org> #
|
||||
-# #
|
||||
-# This library is free software; you can redistribute it and/or #
|
||||
-# modify it under the terms of the GNU Lesser General Public #
|
||||
-# License as published by the Free Software Foundation; either #
|
||||
-# version 2.1 of the License, or (at your option) any later version. #
|
||||
-# #
|
||||
-# This library is distributed in the hope that it will be useful, #
|
||||
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
-# Lesser General Public License for more details. #
|
||||
-# #
|
||||
-###############################################################################
|
||||
-
|
||||
-import argparse
|
||||
-import io
|
||||
-import ipaddress
|
||||
-import logging
|
||||
-import os.path
|
||||
-import re
|
||||
-import socket
|
||||
-import sys
|
||||
-
|
||||
-# Load our location module
|
||||
-import location
|
||||
-from location.i18n import _
|
||||
-
|
||||
-# Initialise logging
|
||||
-log = logging.getLogger("location.exporter")
|
||||
-log.propagate = 1
|
||||
-
|
||||
-class OutputWriter(object):
|
||||
- suffix = "networks"
|
||||
-
|
||||
- def __init__(self, family, country_code=None, asn=None):
|
||||
- self.family, self.country_code, self.asn = family, country_code, asn
|
||||
-
|
||||
- self.f = io.BytesIO()
|
||||
-
|
||||
- def write_out(self, directory):
|
||||
- # Make the output filename
|
||||
- filename = os.path.join(
|
||||
- directory, self._make_filename(),
|
||||
- )
|
||||
-
|
||||
- with open(filename, "wb") as f:
|
||||
- self._write_header(f)
|
||||
-
|
||||
- # Copy all data into the file
|
||||
- f.write(self.f.getbuffer())
|
||||
-
|
||||
- self._write_footer(f)
|
||||
-
|
||||
- def _make_filename(self):
|
||||
- return "%s.%s%s" % (
|
||||
- self.country_code or "AS%s" % self.asn,
|
||||
- self.suffix,
|
||||
- "6" if self.family == socket.AF_INET6 else "4"
|
||||
- )
|
||||
-
|
||||
- @property
|
||||
- def name(self):
|
||||
- if self.country_code:
|
||||
- return "CC_%s" % self.country_code
|
||||
-
|
||||
- if self.asn:
|
||||
- return "AS%s" % self.asn
|
||||
-
|
||||
- def _write_header(self, f):
|
||||
- """
|
||||
- The header of the file
|
||||
- """
|
||||
- pass
|
||||
-
|
||||
- def _write_footer(self, f):
|
||||
- """
|
||||
- The footer of the file
|
||||
- """
|
||||
- pass
|
||||
-
|
||||
- def write(self, network):
|
||||
- s = "%s\n" % network
|
||||
-
|
||||
- self.f.write(s.encode("ascii"))
|
||||
-
|
||||
-
|
||||
-class IpsetOutputWriter(OutputWriter):
|
||||
- """
|
||||
- For ipset
|
||||
- """
|
||||
- suffix = "ipset"
|
||||
-
|
||||
- def _write_header(self, f):
|
||||
- h = "create %s hash:net family inet hashsize 1024 maxelem 65536\n" % self.name
|
||||
-
|
||||
- f.write(h.encode("ascii"))
|
||||
-
|
||||
- def write(self, network):
|
||||
- s = "add %s %s\n" % (self.name, network)
|
||||
-
|
||||
- self.f.write(s.encode("ascii"))
|
||||
-
|
||||
-
|
||||
-class NftablesOutputWriter(OutputWriter):
|
||||
- """
|
||||
- For nftables
|
||||
- """
|
||||
- suffix = "set"
|
||||
-
|
||||
- def _write_header(self, f):
|
||||
- h = "define %s = {\n" % self.name
|
||||
-
|
||||
- f.write(h.encode("ascii"))
|
||||
-
|
||||
- def _write_footer(self, f):
|
||||
- f.write(b"}")
|
||||
-
|
||||
- def write(self, network):
|
||||
- s = " %s,\n" % network
|
||||
-
|
||||
- self.f.write(s.encode("ascii"))
|
||||
-
|
||||
-
|
||||
-class XTGeoIPOutputWriter(OutputWriter):
|
||||
- """
|
||||
- Formats the output in that way, that it can be loaded by
|
||||
- the xt_geoip kernel module from xtables-addons.
|
||||
- """
|
||||
- suffix = "iv"
|
||||
-
|
||||
- def write(self, network):
|
||||
- n = ipaddress.ip_network("%s" % network)
|
||||
-
|
||||
- for address in (n.network_address, n.broadcast_address):
|
||||
- bytes = socket.inet_pton(
|
||||
- socket.AF_INET6 if address.version == 6 else socket.AF_INET,
|
||||
- "%s" % address,
|
||||
- )
|
||||
-
|
||||
- self.f.write(bytes)
|
||||
-
|
||||
-
|
||||
-class Exporter(object):
|
||||
- def __init__(self, db, writer):
|
||||
- self.db = db
|
||||
- self.writer = writer
|
||||
-
|
||||
- def export(self, directory, families, countries, asns):
|
||||
- for family in families:
|
||||
- log.debug("Exporting family %s" % family)
|
||||
-
|
||||
- writers = {}
|
||||
-
|
||||
- # Create writers for countries
|
||||
- for country_code in countries:
|
||||
- writers[country_code] = self.writer(family, country_code=country_code)
|
||||
-
|
||||
- # Create writers for ASNs
|
||||
- for asn in asns:
|
||||
- writers[asn] = self.writer(family, asn=asn)
|
||||
-
|
||||
- # Get all networks that match the family
|
||||
- networks = self.db.search_networks(family=family)
|
||||
-
|
||||
- # Walk through all networks
|
||||
- for network in networks:
|
||||
- # Write matching countries
|
||||
- if network.country_code in countries:
|
||||
- writers[network.country_code].write(network)
|
||||
-
|
||||
- # Write matching ASNs
|
||||
- if network.asn in asns:
|
||||
- writers[network.asn].write(network)
|
||||
-
|
||||
- # Write everything to the filesystem
|
||||
- for writer in writers.values():
|
||||
- writer.write_out(directory)
|
||||
-
|
||||
-
|
||||
-class CLI(object):
|
||||
- output_formats = {
|
||||
- "ipset" : IpsetOutputWriter,
|
||||
- "list" : OutputWriter,
|
||||
- "nftables" : NftablesOutputWriter,
|
||||
- "xt_geoip" : XTGeoIPOutputWriter,
|
||||
- }
|
||||
-
|
||||
- def parse_cli(self):
|
||||
- parser = argparse.ArgumentParser(
|
||||
- description=_("Location Exporter Command Line Interface"),
|
||||
- )
|
||||
-
|
||||
- # Global configuration flags
|
||||
- parser.add_argument("--debug", action="store_true",
|
||||
- help=_("Enable debug output"))
|
||||
- parser.add_argument("--quiet", action="store_true",
|
||||
- help=_("Enable quiet mode"))
|
||||
-
|
||||
- # version
|
||||
- parser.add_argument("--version", action="version",
|
||||
- version="%(prog)s @VERSION@")
|
||||
-
|
||||
- # database
|
||||
- parser.add_argument("--database", "-d",
|
||||
- default="@databasedir@/database.db", help=_("Path to database"),
|
||||
- )
|
||||
-
|
||||
- # format
|
||||
- parser.add_argument("--format", help=_("Output format"),
|
||||
- default="list", choices=self.output_formats.keys())
|
||||
-
|
||||
- # directory
|
||||
- parser.add_argument("--directory", help=_("Output directory"), required=True)
|
||||
-
|
||||
- # family
|
||||
- parser.add_argument("--family", help=_("Specify address family"), choices=("ipv6", "ipv4"))
|
||||
-
|
||||
- # Countries and Autonomous Systems
|
||||
- parser.add_argument("objects", nargs="+")
|
||||
-
|
||||
- args = parser.parse_args()
|
||||
-
|
||||
- # Configure logging
|
||||
- if args.debug:
|
||||
- location.logger.set_level(logging.DEBUG)
|
||||
- elif args.quiet:
|
||||
- location.logger.set_level(logging.WARNING)
|
||||
-
|
||||
- return args
|
||||
-
|
||||
- def run(self):
|
||||
- # Parse command line arguments
|
||||
- args = self.parse_cli()
|
||||
-
|
||||
- # Call function
|
||||
- ret = self.handle_export(args)
|
||||
-
|
||||
- # Return with exit code
|
||||
- if ret:
|
||||
- sys.exit(ret)
|
||||
-
|
||||
- # Otherwise just exit
|
||||
- sys.exit(0)
|
||||
-
|
||||
- def handle_export(self, ns):
|
||||
- countries, asns = [], []
|
||||
-
|
||||
- # Translate family
|
||||
- if ns.family == "ipv6":
|
||||
- families = [ socket.AF_INET6 ]
|
||||
- elif ns.family == "ipv4":
|
||||
- families = [ socket.AF_INET ]
|
||||
- else:
|
||||
- families = [ socket.AF_INET6, socket.AF_INET ]
|
||||
-
|
||||
- for object in ns.objects:
|
||||
- m = re.match("^AS(\d+)$", object)
|
||||
- if m:
|
||||
- object = int(m.group(1))
|
||||
-
|
||||
- asns.append(object)
|
||||
-
|
||||
- elif location.country_code_is_valid(object) \
|
||||
- or object in ("A1", "A2", "A3"):
|
||||
- countries.append(object)
|
||||
-
|
||||
- else:
|
||||
- log.warning("Invalid argument: %s" % object)
|
||||
- continue
|
||||
-
|
||||
- if not countries and not asns:
|
||||
- log.error("Nothing to export")
|
||||
- return 2
|
||||
-
|
||||
- # Open the database
|
||||
- try:
|
||||
- db = location.Database(ns.database)
|
||||
- except FileNotFoundError as e:
|
||||
- log.error("Count not open database: %s" % ns.database)
|
||||
- return 1
|
||||
-
|
||||
- # Select the output format
|
||||
- writer = self.output_formats.get(ns.format)
|
||||
- assert writer
|
||||
-
|
||||
- e = Exporter(db, writer)
|
||||
- e.export(ns.directory, countries=countries, asns=asns, families=families)
|
||||
-
|
||||
-
|
||||
-def main():
|
||||
- # Run the command line interface
|
||||
- c = CLI()
|
||||
- c.run()
|
||||
-
|
||||
-main()
|
||||
diff --git a/src/python/location.in b/src/python/location.in
|
||||
index 10618e2..7614cae 100644
|
||||
--- a/src/python/location.in
|
||||
+++ b/src/python/location.in
|
||||
@@ -22,6 +22,7 @@ import datetime
|
||||
import ipaddress
|
||||
import logging
|
||||
import os
|
||||
+import re
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
@@ -30,6 +31,8 @@ import time
|
||||
# Load our location module
|
||||
import location
|
||||
import location.downloader
|
||||
+import location.export
|
||||
+
|
||||
from location.i18n import _
|
||||
|
||||
# Setup logging
|
||||
@@ -37,88 +40,7 @@ log = logging.getLogger("location")
|
||||
|
||||
# Output formatters
|
||||
|
||||
-class OutputFormatter(object):
|
||||
- def __init__(self, ns):
|
||||
- self.ns = ns
|
||||
-
|
||||
- def __enter__(self):
|
||||
- # Open the output
|
||||
- self.open()
|
||||
-
|
||||
- return self
|
||||
-
|
||||
- def __exit__(self, type, value, tb):
|
||||
- if tb is None:
|
||||
- self.close()
|
||||
-
|
||||
- @property
|
||||
- def name(self):
|
||||
- if "country_code" in self.ns:
|
||||
- return "networks_country_%s" % self.ns.country_code[0]
|
||||
-
|
||||
- elif "asn" in self.ns:
|
||||
- return "networks_AS%s" % self.ns.asn[0]
|
||||
-
|
||||
- def open(self):
|
||||
- pass
|
||||
-
|
||||
- def close(self):
|
||||
- pass
|
||||
-
|
||||
- def network(self, network):
|
||||
- print(network)
|
||||
-
|
||||
-
|
||||
-class IpsetOutputFormatter(OutputFormatter):
|
||||
- """
|
||||
- For nftables
|
||||
- """
|
||||
- def open(self):
|
||||
- print("create %s hash:net family inet hashsize 1024 maxelem 65536" % self.name)
|
||||
-
|
||||
- def network(self, network):
|
||||
- print("add %s %s" % (self.name, network))
|
||||
-
|
||||
-
|
||||
-class NftablesOutputFormatter(OutputFormatter):
|
||||
- """
|
||||
- For nftables
|
||||
- """
|
||||
- def open(self):
|
||||
- print("define %s = {" % self.name)
|
||||
-
|
||||
- def close(self):
|
||||
- print("}")
|
||||
-
|
||||
- def network(self, network):
|
||||
- print(" %s," % network)
|
||||
-
|
||||
-
|
||||
-class XTGeoIPOutputFormatter(OutputFormatter):
|
||||
- """
|
||||
- Formats the output in that way, that it can be loaded by
|
||||
- the xt_geoip kernel module from xtables-addons.
|
||||
- """
|
||||
- def network(self, network):
|
||||
- n = ipaddress.ip_network("%s" % network)
|
||||
-
|
||||
- for address in (n.network_address, n.broadcast_address):
|
||||
- bytes = socket.inet_pton(
|
||||
- socket.AF_INET6 if address.version == 6 else socket.AF_INET,
|
||||
- "%s" % address,
|
||||
- )
|
||||
-
|
||||
- os.write(1, bytes)
|
||||
-
|
||||
-
|
||||
class CLI(object):
|
||||
- output_formats = {
|
||||
- "ipset" : IpsetOutputFormatter,
|
||||
- "list" : OutputFormatter,
|
||||
- "nftables" : NftablesOutputFormatter,
|
||||
- "xt_geoip" : XTGeoIPOutputFormatter,
|
||||
- }
|
||||
-
|
||||
def parse_cli(self):
|
||||
parser = argparse.ArgumentParser(
|
||||
description=_("Location Database Command Line Interface"),
|
||||
@@ -193,8 +115,8 @@ class CLI(object):
|
||||
)
|
||||
list_networks_by_as.add_argument("asn", nargs=1, type=int)
|
||||
list_networks_by_as.add_argument("--family", choices=("ipv6", "ipv4"))
|
||||
- list_networks_by_as.add_argument("--output-format",
|
||||
- choices=self.output_formats.keys(), default="list")
|
||||
+ list_networks_by_as.add_argument("--format",
|
||||
+ choices=location.export.formats.keys(), default="list")
|
||||
list_networks_by_as.set_defaults(func=self.handle_list_networks_by_as)
|
||||
|
||||
# List all networks in a country
|
||||
@@ -203,8 +125,8 @@ class CLI(object):
|
||||
)
|
||||
list_networks_by_cc.add_argument("country_code", nargs=1)
|
||||
list_networks_by_cc.add_argument("--family", choices=("ipv6", "ipv4"))
|
||||
- list_networks_by_cc.add_argument("--output-format",
|
||||
- choices=self.output_formats.keys(), default="list")
|
||||
+ list_networks_by_cc.add_argument("--format",
|
||||
+ choices=location.export.formats.keys(), default="list")
|
||||
list_networks_by_cc.set_defaults(func=self.handle_list_networks_by_cc)
|
||||
|
||||
# List all networks with flags
|
||||
@@ -221,10 +143,23 @@ class CLI(object):
|
||||
action="store_true", help=_("Anycasts"),
|
||||
)
|
||||
list_networks_by_flags.add_argument("--family", choices=("ipv6", "ipv4"))
|
||||
- list_networks_by_flags.add_argument("--output-format",
|
||||
- choices=self.output_formats.keys(), default="list")
|
||||
+ list_networks_by_flags.add_argument("--format",
|
||||
+ choices=location.export.formats.keys(), default="list")
|
||||
list_networks_by_flags.set_defaults(func=self.handle_list_networks_by_flags)
|
||||
|
||||
+ # Export
|
||||
+ export = subparsers.add_parser("export",
|
||||
+ help=_("Exports data in many formats to load it into packet filters"),
|
||||
+ )
|
||||
+ export.add_argument("--format", help=_("Output format"),
|
||||
+ choices=location.export.formats.keys(), default="list")
|
||||
+ export.add_argument("--directory", help=_("Output directory"), required=True)
|
||||
+ export.add_argument("--family",
|
||||
+ help=_("Specify address family"), choices=("ipv6", "ipv4"),
|
||||
+ )
|
||||
+ export.add_argument("objects", nargs="+", help=_("List country codes or ASNs to export"))
|
||||
+ export.set_defaults(func=self.handle_export)
|
||||
+
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
@@ -494,25 +429,36 @@ class CLI(object):
|
||||
|
||||
def __get_output_formatter(self, ns):
|
||||
try:
|
||||
- cls = self.output_formats[ns.output_format]
|
||||
+ cls = location.export.formats[ns.format]
|
||||
except KeyError:
|
||||
- cls = OutputFormatter
|
||||
+ cls = location.export.OutputFormatter
|
||||
|
||||
- return cls(ns)
|
||||
+ return cls
|
||||
|
||||
def handle_list_networks_by_as(self, db, ns):
|
||||
- with self.__get_output_formatter(ns) as f:
|
||||
- for asn in ns.asn:
|
||||
- # Print all matching networks
|
||||
- for n in db.search_networks(asn=asn, family=ns.family):
|
||||
- f.network(n)
|
||||
+ writer = self.__get_output_formatter(ns)
|
||||
+
|
||||
+ for asn in ns.asn:
|
||||
+ f = writer(sys.stdout, prefix="AS%s" % asn)
|
||||
+
|
||||
+ # Print all matching networks
|
||||
+ for n in db.search_networks(asn=asn, family=ns.family):
|
||||
+ f.write(n)
|
||||
+
|
||||
+ f.finish()
|
||||
|
||||
def handle_list_networks_by_cc(self, db, ns):
|
||||
- with self.__get_output_formatter(ns) as f:
|
||||
- for country_code in ns.country_code:
|
||||
- # Print all matching networks
|
||||
- for n in db.search_networks(country_code=country_code, family=ns.family):
|
||||
- f.network(n)
|
||||
+ writer = self.__get_output_formatter(ns)
|
||||
+
|
||||
+ for country_code in ns.country_code:
|
||||
+ # Open standard output
|
||||
+ f = writer(sys.stdout, prefix=country_code)
|
||||
+
|
||||
+ # Print all matching networks
|
||||
+ for n in db.search_networks(country_code=country_code, family=ns.family):
|
||||
+ f.write(n)
|
||||
+
|
||||
+ f.finish()
|
||||
|
||||
def handle_list_networks_by_flags(self, db, ns):
|
||||
flags = 0
|
||||
@@ -529,9 +475,49 @@ class CLI(object):
|
||||
if not flags:
|
||||
raise ValueError(_("You must at least pass one flag"))
|
||||
|
||||
- with self.__get_output_formatter(ns) as f:
|
||||
- for n in db.search_networks(flags=flags, family=ns.family):
|
||||
- f.network(n)
|
||||
+ writer = self.__get_output_formatter(ns)
|
||||
+ f = writer(sys.stdout, prefix="custom")
|
||||
+
|
||||
+ for n in db.search_networks(flags=flags, family=ns.family):
|
||||
+ f.write(n)
|
||||
+
|
||||
+ f.finish()
|
||||
+
|
||||
+ def handle_export(self, db, ns):
|
||||
+ countries, asns = [], []
|
||||
+
|
||||
+ # Translate family
|
||||
+ if ns.family == "ipv6":
|
||||
+ families = [ socket.AF_INET6 ]
|
||||
+ elif ns.family == "ipv4":
|
||||
+ families = [ socket.AF_INET ]
|
||||
+ else:
|
||||
+ families = [ socket.AF_INET6, socket.AF_INET ]
|
||||
+
|
||||
+ for object in ns.objects:
|
||||
+ m = re.match("^AS(\d+)$", object)
|
||||
+ if m:
|
||||
+ object = int(m.group(1))
|
||||
+
|
||||
+ asns.append(object)
|
||||
+
|
||||
+ elif location.country_code_is_valid(object) \
|
||||
+ or object in ("A1", "A2", "A3"):
|
||||
+ countries.append(object)
|
||||
+
|
||||
+ else:
|
||||
+ log.warning("Invalid argument: %s" % object)
|
||||
+ continue
|
||||
+
|
||||
+ if not countries and not asns:
|
||||
+ log.error("Nothing to export")
|
||||
+ return 2
|
||||
+
|
||||
+ # Select the output format
|
||||
+ writer = self.__get_output_formatter(ns)
|
||||
+
|
||||
+ e = location.export.Exporter(db, writer)
|
||||
+ e.export(ns.directory, countries=countries, asns=asns, families=families)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -1,383 +0,0 @@
|
||||
commit a6f1e3463d4c2085c203ad58072d7a154b663904
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 17:06:13 2020 +0000
|
||||
|
||||
Move location-downloader functionality into location-query
|
||||
|
||||
The commands are very long and confusion. Hence we merge this
|
||||
all into one command.
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index 31869e0..c0b1300 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -146,6 +146,7 @@ CLEANFILES += \
|
||||
dist_pkgpython_PYTHON = \
|
||||
src/python/__init__.py \
|
||||
src/python/database.py \
|
||||
+ src/python/downloader.py \
|
||||
src/python/i18n.py \
|
||||
src/python/importer.py \
|
||||
src/python/logger.py
|
||||
@@ -234,19 +235,16 @@ uninstall-perl:
|
||||
$(DESTDIR)/$(prefix)/man/man3/Location.3pm
|
||||
|
||||
bin_SCRIPTS = \
|
||||
- src/python/location-downloader \
|
||||
src/python/location-exporter \
|
||||
src/python/location-importer \
|
||||
src/python/location-query
|
||||
|
||||
EXTRA_DIST += \
|
||||
- src/python/location-downloader.in \
|
||||
src/python/location-exporter.in \
|
||||
src/python/location-importer.in \
|
||||
src/python/location-query.in
|
||||
|
||||
CLEANFILES += \
|
||||
- src/python/location-downloader \
|
||||
src/python/location-exporter \
|
||||
src/python/location-importer \
|
||||
src/python/location-query
|
||||
diff --git a/src/python/location-downloader.in b/src/python/downloader.py
|
||||
similarity index 60%
|
||||
rename from src/python/location-downloader.in
|
||||
rename to src/python/downloader.py
|
||||
index bf0d682..c9e6e00 100644
|
||||
--- a/src/python/location-downloader.in
|
||||
+++ b/src/python/downloader.py
|
||||
@@ -3,7 +3,7 @@
|
||||
# #
|
||||
# libloc - A library to determine the location of someone on the Internet #
|
||||
# #
|
||||
-# Copyright (C) 2019 IPFire Development Team <info@ipfire.org> #
|
||||
+# Copyright (C) 2020 IPFire Development Team <info@ipfire.org> #
|
||||
# #
|
||||
# This library is free software; you can redistribute it and/or #
|
||||
# modify it under the terms of the GNU Lesser General Public #
|
||||
@@ -17,24 +17,18 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
-import argparse
|
||||
-import datetime
|
||||
import logging
|
||||
import lzma
|
||||
import os
|
||||
import random
|
||||
-import shutil
|
||||
import stat
|
||||
-import sys
|
||||
import tempfile
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
-# Load our location module
|
||||
-import location
|
||||
-from location.i18n import _
|
||||
+from _location import Database, DATABASE_VERSION_LATEST
|
||||
|
||||
DATABASE_FILENAME = "location.db.xz"
|
||||
MIRRORS = (
|
||||
@@ -46,9 +40,11 @@ log = logging.getLogger("location.downloader")
|
||||
log.propagate = 1
|
||||
|
||||
class Downloader(object):
|
||||
- def __init__(self, version, mirrors):
|
||||
+ def __init__(self, version=DATABASE_VERSION_LATEST, mirrors=None):
|
||||
self.version = version
|
||||
- self.mirrors = list(mirrors)
|
||||
+
|
||||
+ # Set mirrors or use defaults
|
||||
+ self.mirrors = list(mirrors or MIRRORS)
|
||||
|
||||
# Randomize mirrors
|
||||
random.shuffle(self.mirrors)
|
||||
@@ -117,9 +113,10 @@ class Downloader(object):
|
||||
|
||||
return res
|
||||
|
||||
- def download(self, url, public_key, timestamp=None, tmpdir=None, **kwargs):
|
||||
- headers = {}
|
||||
+ def download(self, public_key, timestamp=None, tmpdir=None, **kwargs):
|
||||
+ url = "%s/%s" % (self.version, DATABASE_FILENAME)
|
||||
|
||||
+ headers = {}
|
||||
if timestamp:
|
||||
headers["If-Modified-Since"] = timestamp.strftime(
|
||||
"%a, %d %b %Y %H:%M:%S GMT",
|
||||
@@ -191,7 +188,7 @@ class Downloader(object):
|
||||
"""
|
||||
log.debug("Opening downloaded database at %s" % f.name)
|
||||
|
||||
- db = location.Database(f.name)
|
||||
+ db = Database(f.name)
|
||||
|
||||
# Database is not recent
|
||||
if timestamp and db.created_at < timestamp.timestamp():
|
||||
@@ -208,141 +205,3 @@ class Downloader(object):
|
||||
return False
|
||||
|
||||
return True
|
||||
-
|
||||
-
|
||||
-class CLI(object):
|
||||
- def __init__(self):
|
||||
- # Which version are we downloading?
|
||||
- self.version = location.DATABASE_VERSION_LATEST
|
||||
-
|
||||
- self.downloader = Downloader(version=self.version, mirrors=MIRRORS)
|
||||
-
|
||||
- def parse_cli(self):
|
||||
- parser = argparse.ArgumentParser(
|
||||
- description=_("Location Downloader Command Line Interface"),
|
||||
- )
|
||||
- subparsers = parser.add_subparsers()
|
||||
-
|
||||
- # Global configuration flags
|
||||
- parser.add_argument("--debug", action="store_true",
|
||||
- help=_("Enable debug output"))
|
||||
- parser.add_argument("--quiet", action="store_true",
|
||||
- help=_("Enable quiet mode"))
|
||||
-
|
||||
- # version
|
||||
- parser.add_argument("--version", action="version",
|
||||
- version="%(prog)s @VERSION@")
|
||||
-
|
||||
- # database
|
||||
- parser.add_argument("--database", "-d",
|
||||
- default="@databasedir@/database.db", help=_("Path to database"),
|
||||
- )
|
||||
-
|
||||
- # public key
|
||||
- parser.add_argument("--public-key", "-k",
|
||||
- default="@databasedir@/signing-key.pem", help=_("Public Signing Key"),
|
||||
- )
|
||||
-
|
||||
- # Update
|
||||
- update = subparsers.add_parser("update", help=_("Update database"))
|
||||
- update.set_defaults(func=self.handle_update)
|
||||
-
|
||||
- # Verify
|
||||
- verify = subparsers.add_parser("verify",
|
||||
- help=_("Verify the downloaded database"))
|
||||
- verify.set_defaults(func=self.handle_verify)
|
||||
-
|
||||
- args = parser.parse_args()
|
||||
-
|
||||
- # Configure logging
|
||||
- if args.debug:
|
||||
- location.logger.set_level(logging.DEBUG)
|
||||
- elif args.quiet:
|
||||
- location.logger.set_level(logging.WARNING)
|
||||
-
|
||||
- # Print usage if no action was given
|
||||
- if not "func" in args:
|
||||
- parser.print_usage()
|
||||
- sys.exit(2)
|
||||
-
|
||||
- return args
|
||||
-
|
||||
- def run(self):
|
||||
- # Parse command line arguments
|
||||
- args = self.parse_cli()
|
||||
-
|
||||
- # Call function
|
||||
- ret = args.func(args)
|
||||
-
|
||||
- # Return with exit code
|
||||
- if ret:
|
||||
- sys.exit(ret)
|
||||
-
|
||||
- # Otherwise just exit
|
||||
- sys.exit(0)
|
||||
-
|
||||
- def handle_update(self, ns):
|
||||
- # Fetch the timestamp we need from DNS
|
||||
- t = location.discover_latest_version(self.version)
|
||||
-
|
||||
- # Parse timestamp into datetime format
|
||||
- timestamp = datetime.datetime.fromtimestamp(t) if t else None
|
||||
-
|
||||
- # Open database
|
||||
- try:
|
||||
- db = location.Database(ns.database)
|
||||
-
|
||||
- # Check if we are already on the latest version
|
||||
- if timestamp and db.created_at >= timestamp.timestamp():
|
||||
- log.info("Already on the latest version")
|
||||
- return
|
||||
-
|
||||
- except FileNotFoundError as e:
|
||||
- db = None
|
||||
-
|
||||
- # Download the database into the correct directory
|
||||
- tmpdir = os.path.dirname(ns.database)
|
||||
-
|
||||
- # Try downloading a new database
|
||||
- try:
|
||||
- t = self.downloader.download("%s/%s" % (self.version, DATABASE_FILENAME),
|
||||
- public_key=ns.public_key, timestamp=timestamp, tmpdir=tmpdir)
|
||||
-
|
||||
- # If no file could be downloaded, log a message
|
||||
- except FileNotFoundError as e:
|
||||
- log.error("Could not download a new database")
|
||||
- return 1
|
||||
-
|
||||
- # If we have not received a new file, there is nothing to do
|
||||
- if not t:
|
||||
- return 3
|
||||
-
|
||||
- # Move temporary file to destination
|
||||
- shutil.move(t.name, ns.database)
|
||||
-
|
||||
- return 0
|
||||
-
|
||||
- def handle_verify(self, ns):
|
||||
- try:
|
||||
- db = location.Database(ns.database)
|
||||
- except FileNotFoundError as e:
|
||||
- log.error("%s: %s" % (ns.database, e))
|
||||
- return 127
|
||||
-
|
||||
- # Verify the database
|
||||
- with open(ns.public_key, "r") as f:
|
||||
- if not db.verify(f):
|
||||
- log.error("Could not verify database")
|
||||
- return 1
|
||||
-
|
||||
- # Success
|
||||
- log.debug("Database successfully verified")
|
||||
- return 0
|
||||
-
|
||||
-
|
||||
-def main():
|
||||
- # Run the command line interface
|
||||
- c = CLI()
|
||||
- c.run()
|
||||
-
|
||||
-main()
|
||||
diff --git a/src/python/location-query.in b/src/python/location-query.in
|
||||
index dfdff8c..0291786 100644
|
||||
--- a/src/python/location-query.in
|
||||
+++ b/src/python/location-query.in
|
||||
@@ -18,16 +18,23 @@
|
||||
###############################################################################
|
||||
|
||||
import argparse
|
||||
+import datetime
|
||||
import ipaddress
|
||||
+import logging
|
||||
import os
|
||||
+import shutil
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Load our location module
|
||||
import location
|
||||
+import location.downloader
|
||||
from location.i18n import _
|
||||
|
||||
+# Setup logging
|
||||
+log = logging.getLogger("location")
|
||||
+
|
||||
# Output formatters
|
||||
|
||||
class OutputFormatter(object):
|
||||
@@ -157,6 +164,15 @@ class CLI(object):
|
||||
dump.add_argument("output", nargs="?", type=argparse.FileType("w"))
|
||||
dump.set_defaults(func=self.handle_dump)
|
||||
|
||||
+ # Update
|
||||
+ update = subparsers.add_parser("update", help=_("Update database"))
|
||||
+ update.set_defaults(func=self.handle_update)
|
||||
+
|
||||
+ # Verify
|
||||
+ verify = subparsers.add_parser("verify",
|
||||
+ help=_("Verify the downloaded database"))
|
||||
+ verify.set_defaults(func=self.handle_verify)
|
||||
+
|
||||
# Get AS
|
||||
get_as = subparsers.add_parser("get-as",
|
||||
help=_("Get information about one or multiple Autonomous Systems"),
|
||||
@@ -423,6 +439,59 @@ class CLI(object):
|
||||
for a in db.search_as(query):
|
||||
print(a)
|
||||
|
||||
+ def handle_update(self, db, ns):
|
||||
+ # Fetch the timestamp we need from DNS
|
||||
+ t = location.discover_latest_version()
|
||||
+
|
||||
+ # Parse timestamp into datetime format
|
||||
+ timestamp = datetime.datetime.fromtimestamp(t) if t else None
|
||||
+
|
||||
+ # Check the version of the local database
|
||||
+ if db and timestamp and db.created_at >= timestamp.timestamp():
|
||||
+ log.info("Already on the latest version")
|
||||
+ return
|
||||
+
|
||||
+ # Download the database into the correct directory
|
||||
+ tmpdir = os.path.dirname(ns.database)
|
||||
+
|
||||
+ # Create a downloader
|
||||
+ d = location.downloader.Downloader()
|
||||
+
|
||||
+ # Try downloading a new database
|
||||
+ try:
|
||||
+ t = d.download(public_key=ns.public_key, timestamp=timestamp, tmpdir=tmpdir)
|
||||
+
|
||||
+ # If no file could be downloaded, log a message
|
||||
+ except FileNotFoundError as e:
|
||||
+ log.error("Could not download a new database")
|
||||
+ return 1
|
||||
+
|
||||
+ # If we have not received a new file, there is nothing to do
|
||||
+ if not t:
|
||||
+ return 3
|
||||
+
|
||||
+ # Move temporary file to destination
|
||||
+ shutil.move(t.name, ns.database)
|
||||
+
|
||||
+ return 0
|
||||
+
|
||||
+ def handle_verify(self, ns):
|
||||
+ try:
|
||||
+ db = location.Database(ns.database)
|
||||
+ except FileNotFoundError as e:
|
||||
+ log.error("%s: %s" % (ns.database, e))
|
||||
+ return 127
|
||||
+
|
||||
+ # Verify the database
|
||||
+ with open(ns.public_key, "r") as f:
|
||||
+ if not db.verify(f):
|
||||
+ log.error("Could not verify database")
|
||||
+ return 1
|
||||
+
|
||||
+ # Success
|
||||
+ log.debug("Database successfully verified")
|
||||
+ return 0
|
||||
+
|
||||
def __get_output_formatter(self, ns):
|
||||
try:
|
||||
cls = self.output_formats[ns.output_format]
|
||||
diff --git a/src/python/locationmodule.c b/src/python/locationmodule.c
|
||||
index a04cab7..5b72be9 100644
|
||||
--- a/src/python/locationmodule.c
|
||||
+++ b/src/python/locationmodule.c
|
||||
@@ -50,9 +50,9 @@ static PyObject* set_log_level(PyObject* m, PyObject* args) {
|
||||
}
|
||||
|
||||
static PyObject* discover_latest_version(PyObject* m, PyObject* args) {
|
||||
- unsigned int version = 0;
|
||||
+ unsigned int version = LOC_DATABASE_VERSION_LATEST;
|
||||
|
||||
- if (!PyArg_ParseTuple(args, "i", &version))
|
||||
+ if (!PyArg_ParseTuple(args, "|i", &version))
|
||||
return NULL;
|
||||
|
||||
time_t t = 0;
|
||||
@@ -1,22 +0,0 @@
|
||||
commit 6bfde1447d237d2a345b99677c5b74e54cbd5739
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Thu Jun 4 10:37:50 2020 +0000
|
||||
|
||||
Makefile: Remove accidentially committed hacks for Debian
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index ef57551..c75839c 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -37,9 +37,6 @@ LIBLOC_CURRENT=0
|
||||
LIBLOC_REVISION=0
|
||||
LIBLOC_AGE=0
|
||||
|
||||
-pythondir = $(prefix)/lib/python3/dist-packages
|
||||
-pyexecdir = $(prefix)/lib/python$(PYTHON_VERSION)/lib-dynload
|
||||
-
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
commit 9df8db2ae6268b0901961625fd27b4dde1ed451f
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Mon Jun 1 18:23:50 2020 +0000
|
||||
|
||||
Makefile: Remove Python path overrides for Debian
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index 570ec3a..31869e0 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -54,10 +54,6 @@ SED_PROCESS = \
|
||||
databasedir = $(localstatedir)/lib/location
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
||||
-# XXX hardcode path for Debian
|
||||
-pythondir = $(prefix)/lib/python3/dist-packages
|
||||
-pyexecdir = $(prefix)/lib/python$(PYTHON_VERSION)/lib-dynload
|
||||
-
|
||||
# Overwrite Python path
|
||||
pkgpythondir = $(pythondir)/location
|
||||
|
||||
diff --git a/debian/libloc.install b/debian/libloc.install
|
||||
index eb3b49a..30bbeca 100644
|
||||
--- a/debian/libloc.install
|
||||
+++ b/debian/libloc.install
|
||||
@@ -2,8 +2,7 @@ usr/bin/location-downloader
|
||||
usr/bin/location-exporter
|
||||
usr/bin/location-query
|
||||
usr/lib/*/libloc.so.*
|
||||
-usr/lib/python3*/dist-packages
|
||||
-usr/lib/python3*/lib-dynload/*.so
|
||||
+usr/lib/python3*/site-packages
|
||||
src/systemd/*.service /lib/systemd/system/
|
||||
src/systemd/*.timer /lib/systemd/system/
|
||||
var/lib/location/signing-key.pem
|
||||
@@ -1,111 +0,0 @@
|
||||
commit 1d237439676e8b9ee10a6dde2c64f5ba3a057210
|
||||
Author: Michael Tremer <michael.tremer@ipfire.org>
|
||||
Date: Wed Jun 3 17:21:31 2020 +0000
|
||||
|
||||
Rename location-query(8) to location(8)
|
||||
|
||||
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index bf204d8..59870b1 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -238,19 +238,19 @@ uninstall-perl:
|
||||
$(DESTDIR)/$(prefix)/man/man3/Location.3pm
|
||||
|
||||
bin_SCRIPTS = \
|
||||
+ src/python/location \
|
||||
src/python/location-exporter \
|
||||
- src/python/location-importer \
|
||||
- src/python/location-query
|
||||
+ src/python/location-importer
|
||||
|
||||
EXTRA_DIST += \
|
||||
+ src/python/location.in \
|
||||
src/python/location-exporter.in \
|
||||
- src/python/location-importer.in \
|
||||
- src/python/location-query.in
|
||||
+ src/python/location-importer.in
|
||||
|
||||
CLEANFILES += \
|
||||
+ src/python/location \
|
||||
src/python/location-exporter \
|
||||
- src/python/location-importer \
|
||||
- src/python/location-query
|
||||
+ src/python/location-importer
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -374,7 +374,7 @@ src_test_signature_LDADD = \
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
MANPAGES = \
|
||||
- man/location-query.8
|
||||
+ man/location.8
|
||||
|
||||
MANPAGES_TXT = $(patsubst %.8,%.txt,$(MANPAGES))
|
||||
MANPAGES_HTML = $(patsubst %.txt,%.html,$(MANPAGES_TXT))
|
||||
diff --git a/man/location-query.txt b/man/location.txt
|
||||
similarity index 84%
|
||||
rename from man/location-query.txt
|
||||
rename to man/location.txt
|
||||
index acb43cd..672c2b2 100644
|
||||
--- a/man/location-query.txt
|
||||
+++ b/man/location.txt
|
||||
@@ -1,21 +1,21 @@
|
||||
-= location-query(8)
|
||||
+= location(8)
|
||||
|
||||
== NAME
|
||||
-location-query - Query the location database
|
||||
+location - Query the location database
|
||||
|
||||
== SYNOPSIS
|
||||
[verse]
|
||||
-`location-query lookup ADDRESS [ADDRESS...]`
|
||||
-`location-query get-as ASN [ASN...]`
|
||||
-`location-query search-as STRING`
|
||||
-`location-query update`
|
||||
-`location-query verify`
|
||||
-`location-query list-networks-by-as ASN`
|
||||
-`location-query list-networks-by-cc COUNTRY_CODE`
|
||||
-`location-query list-networks-by-flags [--anonymous-proxy|--satellite-provider|--anycast]`
|
||||
+`location lookup ADDRESS [ADDRESS...]`
|
||||
+`location get-as ASN [ASN...]`
|
||||
+`location search-as STRING`
|
||||
+`location update`
|
||||
+`location verify`
|
||||
+`location list-networks-by-as ASN`
|
||||
+`location list-networks-by-cc COUNTRY_CODE`
|
||||
+`location list-networks-by-flags [--anonymous-proxy|--satellite-provider|--anycast]`
|
||||
|
||||
== DESCRIPTION
|
||||
-The `location-query` retrieves information from the location database.
|
||||
+`location` retrieves information from the location database.
|
||||
This data can be used to determine someone's location on the Internet
|
||||
and for building firewall rulesets to block access from certain ASes
|
||||
or countries.
|
||||
@@ -93,7 +93,7 @@ or countries.
|
||||
Shows the program's version and exists.
|
||||
|
||||
== EXIT CODES
|
||||
-The 'location-query' command will normally exit with code zero.
|
||||
+The 'location' command will normally exit with code zero.
|
||||
If there has been a problem and the requested action could not be performed,
|
||||
the exit code is unequal to zero.
|
||||
|
||||
diff --git a/src/python/location-query.in b/src/python/location.in
|
||||
similarity index 99%
|
||||
rename from src/python/location-query.in
|
||||
rename to src/python/location.in
|
||||
index 0291786..10618e2 100644
|
||||
--- a/src/python/location-query.in
|
||||
+++ b/src/python/location.in
|
||||
@@ -248,7 +248,7 @@ class CLI(object):
|
||||
try:
|
||||
db = location.Database(args.database)
|
||||
except FileNotFoundError as e:
|
||||
- sys.stderr.write("location-query: Could not open database %s: %s\n" \
|
||||
+ sys.stderr.write("location: Could not open database %s: %s\n" \
|
||||
% (args.database, e))
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user