]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/python/location-exporter.in
3074b9004a5093e505a4eaedd8bc10fb24ab2694
2 ###############################################################################
4 # libloc - A library to determine the location of someone on the Internet #
6 # Copyright (C) 2019 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 ###############################################################################
25 import logging
.handlers
30 # Load our location module
33 def setup_logging(level
=logging
.INFO
):
34 l
= logging
.getLogger("location-downloader")
38 h
= logging
.StreamHandler()
39 h
.setLevel(logging
.DEBUG
)
43 h
= logging
.handlers
.SysLogHandler(address
="/dev/log",
44 facility
=logging
.handlers
.SysLogHandler
.LOG_DAEMON
)
45 h
.setLevel(logging
.INFO
)
48 # Format syslog messages
49 formatter
= logging
.Formatter("location-exporter[%(process)d]: %(message)s")
50 h
.setFormatter(formatter
)
58 def _(singular
, plural
=None, n
=None):
60 return gettext
.dngettext("libloc", singular
, plural
, n
)
62 return gettext
.dgettext("libloc", singular
)
64 class OutputWriter(object):
67 def __init__(self
, family
, country_code
=None, asn
=None):
68 self
.family
, self
.country_code
, self
.asn
= family
, country_code
, asn
72 def write_out(self
, directory
):
73 # Make the output filename
74 filename
= os
.path
.join(
75 directory
, self
._make
_filename
(),
78 with
open(filename
, "wb") as f
:
81 # Copy all data into the file
82 f
.write(self
.f
.getbuffer())
86 def _make_filename(self
):
88 self
.country_code
or "AS%s" % self
.asn
,
90 "6" if self
.family
== socket
.AF_INET6
else "4"
96 return "CC_%s" % self
.country_code
99 return "AS%s" % self
.asn
101 def _write_header(self
, f
):
103 The header of the file
107 def _write_footer(self
, f
):
109 The footer of the file
113 def write(self
, network
):
116 self
.f
.write(s
.encode("ascii"))
119 class IpsetOutputWriter(OutputWriter
):
125 def _write_header(self
, f
):
126 h
= "create %s hash:net family inet hashsize 1024 maxelem 65536\n" % self
.name
128 f
.write(h
.encode("ascii"))
130 def write(self
, network
):
131 s
= "add %s %s\n" % (self
.name
, network
)
133 self
.f
.write(s
.encode("ascii"))
136 class NftablesOutputWriter(OutputWriter
):
142 def _write_header(self
, f
):
143 h
= "define %s = {\n" % self
.name
145 f
.write(h
.encode("ascii"))
147 def _write_footer(self
, f
):
150 def write(self
, network
):
151 s
= " %s,\n" % network
153 self
.f
.write(s
.encode("ascii"))
156 class XTGeoIPOutputWriter(OutputWriter
):
158 Formats the output in that way, that it can be loaded by
159 the xt_geoip kernel module from xtables-addons.
163 def write(self
, network
):
164 n
= ipaddress
.ip_network("%s" % network
)
166 for address
in (n
.network_address
, n
.broadcast_address
):
167 bytes
= socket
.inet_pton(
168 socket
.AF_INET6
if address
.version
== 6 else socket
.AF_INET
,
175 class Exporter(object):
176 def __init__(self
, db
, writer
):
180 def export(self
, directory
, families
, countries
, asns
):
181 for family
in families
:
182 log
.debug("Exporting family %s" % family
)
186 # Create writers for countries
187 for country_code
in countries
:
188 writers
[country_code
] = self
.writer(family
, country_code
=country_code
)
190 # Create writers for ASNs
192 writers
[asn
] = self
.writer(family
, asn
=asn
)
194 # Get all networks that match the family
195 networks
= self
.db
.search_networks(family
=family
)
197 # Walk through all networks
198 for network
in networks
:
199 # Write matching countries
200 if network
.country_code
in countries
:
201 writers
[network
.country_code
].write(network
)
203 # Write matching ASNs
204 if network
.asn
in asns
:
205 writers
[network
.asn
].write(network
)
207 # Write everything to the filesystem
208 for writer
in writers
.values():
209 writer
.write_out(directory
)
214 "ipset" : IpsetOutputWriter
,
215 "list" : OutputWriter
,
216 "nftables" : NftablesOutputWriter
,
217 "xt_geoip" : XTGeoIPOutputWriter
,
221 parser
= argparse
.ArgumentParser(
222 description
=_("Location Exporter Command Line Interface"),
225 # Global configuration flags
226 parser
.add_argument("--debug", action
="store_true",
227 help=_("Enable debug output"))
230 parser
.add_argument("--version", action
="version",
231 version
="%%(prog)s %s" % location
.__version
__)
234 parser
.add_argument("--database", "-d",
235 default
="@databasedir@/database.db", help=_("Path to database"),
239 parser
.add_argument("--format", help=_("Output format"),
240 default
="list", choices
=self
.output_formats
.keys())
243 parser
.add_argument("--directory", help=_("Output directory"), required
=True)
246 parser
.add_argument("--family", help=_("Specify address family"), choices
=("ipv6", "ipv4"))
248 # Countries and Autonomous Systems
249 parser
.add_argument("objects", nargs
="+")
251 args
= parser
.parse_args()
253 # Enable debug logging
255 log
.setLevel(logging
.DEBUG
)
260 # Parse command line arguments
261 args
= self
.parse_cli()
264 ret
= self
.handle_export(args
)
266 # Return with exit code
270 # Otherwise just exit
273 def handle_export(self
, ns
):
274 countries
, asns
= [], []
277 if ns
.family
== "ipv6":
278 families
= [ socket
.AF_INET6
]
279 elif ns
.family
== "ipv4":
280 families
= [ socket
.AF_INET
]
282 families
= [ socket
.AF_INET6
, socket
.AF_INET
]
284 for object in ns
.objects
:
285 if object.startswith("AS"):
287 object = int(object[2:])
289 log
.error("Invalid argument: %s" % object)
294 elif location
.country_code_is_valid(object):
295 countries
.append(object)
298 log
.error("Invalid argument: %s" % object)
303 db
= location
.Database(ns
.database
)
304 except FileNotFoundError
as e
:
305 log
.error("Count not open database: %s" % ns
.database
)
308 # Select the output format
309 writer
= self
.output_formats
.get(ns
.format
)
312 e
= Exporter(db
, writer
)
313 e
.export(ns
.directory
, countries
=countries
, asns
=asns
, families
=families
)
317 # Run the command line interface