add my-route.py
This commit is contained in:
parent
d7639e9de2
commit
9395460d86
48
README.md
48
README.md
|
@ -5,6 +5,7 @@ Small tools needed to solve immediate tasks independently or as part of a projec
|
||||||
* [`build-python`.sh](https://git.hmp.today/pavel.muhortov/utils#build-python-sh)
|
* [`build-python`.sh](https://git.hmp.today/pavel.muhortov/utils#build-python-sh)
|
||||||
* [`cronutil`](https://git.hmp.today/pavel.muhortov/utils#cronutil)
|
* [`cronutil`](https://git.hmp.today/pavel.muhortov/utils#cronutil)
|
||||||
* [`confutil`.py](https://git.hmp.today/pavel.muhortov/utils#confutil-py)
|
* [`confutil`.py](https://git.hmp.today/pavel.muhortov/utils#confutil-py)
|
||||||
|
* [`my-route`.py](https://git.hmp.today/pavel.muhortov/utils#my-route-py)
|
||||||
* [`sendmail`.py](https://git.hmp.today/pavel.muhortov/utils#sendmail-py)
|
* [`sendmail`.py](https://git.hmp.today/pavel.muhortov/utils#sendmail-py)
|
||||||
* [`simplewc`.py](https://git.hmp.today/pavel.muhortov/utils#simplewc-py)
|
* [`simplewc`.py](https://git.hmp.today/pavel.muhortov/utils#simplewc-py)
|
||||||
|
|
||||||
|
@ -126,6 +127,53 @@ if path.exists(conf):
|
||||||
|
|
||||||
____
|
____
|
||||||
|
|
||||||
|
## `my-route`.py
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
> Route management by CIDR lists.
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
>
|
||||||
|
> * privileged rights
|
||||||
|
> * [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
|
||||||
|
|
||||||
|
| PARAMETERS | DESCRIPTION | DEFAULT|
|
||||||
|
|-------------|-------------|--------|
|
||||||
|
|**[-h]**|print help and exit||
|
||||||
|
|**[-a, --add]**|add routes specified by config|`None`|
|
||||||
|
|**[-d, --del]**|del routes specified by config|`None`|
|
||||||
|
|**[-u, --update]**|update cidr db, del old routes, add new routes|`None`|
|
||||||
|
|**[--config]**|custom configuration file path|`./my-route.conf`|
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# download
|
||||||
|
sudo wget https://git.hmp.today/pavel.muhortov/utils/raw/branch/master/my-route.py -O /usr/local/bin/my-route.py
|
||||||
|
sudo chmod +x /usr/local/bin/my-route.py
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# download and edit config file
|
||||||
|
sudo wget https://git.hmp.today/pavel.muhortov/utils/raw/branch/master/my-route.conf -O /usr/local/bin/my-route.conf
|
||||||
|
sudo nano /usr/local/bin/my-route.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create and edit cidr file
|
||||||
|
sudo mkdir /usr/local/bin/my-route.db
|
||||||
|
sudo tee /usr/local/bin/my-route.db/_custom.cidr > /dev/null <<'EOF'
|
||||||
|
34.117.59.81/32 # ipinfo.io
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# sudo crontab -e
|
||||||
|
* * * * * /usr/bin/python3 /usr/local/bin/my-route.py --update
|
||||||
|
```
|
||||||
|
|
||||||
|
____
|
||||||
|
|
||||||
## `sendmail`.py
|
## `sendmail`.py
|
||||||
|
|
||||||
**Description:**
|
**Description:**
|
||||||
|
|
265
my-route.conf
Executable file
265
my-route.conf
Executable file
|
@ -0,0 +1,265 @@
|
||||||
|
[common]
|
||||||
|
# By default, a database directory is created in the same path where the script is located.
|
||||||
|
# If you need change it, uncomment the parameter and set the path you want.
|
||||||
|
#cidr_root = /tmp/my-route.db
|
||||||
|
#
|
||||||
|
# By default, logs use the same directory where the script is located.
|
||||||
|
# If you need change it, uncomment the parameter and set the path you want.
|
||||||
|
#log_root = /var/log/my-route
|
||||||
|
#
|
||||||
|
# The default log level is "INFO".
|
||||||
|
# If you get errors or want to change the logging level, uncomment the parameter and set the level you want:
|
||||||
|
# DEBUG, INFO, WARNING, ERROR, CRITICAL.
|
||||||
|
#log_level = DEBUG
|
||||||
|
|
||||||
|
[enable-gateway]
|
||||||
|
# List the gateway block names. Only blocks with the TRUE value will be used.
|
||||||
|
via-192.168.0.1 = false
|
||||||
|
dev-wg1 = true
|
||||||
|
|
||||||
|
[via-192.168.0.1]
|
||||||
|
# List of CIDR. Only CIDR with the TRUE value will be used.
|
||||||
|
_custom.cidr = false
|
||||||
|
|
||||||
|
[dev-wg1]
|
||||||
|
_custom.cidr = true
|
||||||
|
ad.cidr
|
||||||
|
ae.cidr
|
||||||
|
af.cidr
|
||||||
|
ag.cidr
|
||||||
|
ai.cidr
|
||||||
|
al.cidr
|
||||||
|
am.cidr
|
||||||
|
ao.cidr
|
||||||
|
ap.cidr
|
||||||
|
aq.cidr
|
||||||
|
ar.cidr
|
||||||
|
as.cidr
|
||||||
|
at.cidr
|
||||||
|
au.cidr
|
||||||
|
aw.cidr
|
||||||
|
ax.cidr
|
||||||
|
az.cidr
|
||||||
|
ba.cidr
|
||||||
|
bb.cidr
|
||||||
|
bd.cidr
|
||||||
|
be.cidr
|
||||||
|
bf.cidr
|
||||||
|
bg.cidr
|
||||||
|
bh.cidr
|
||||||
|
bi.cidr
|
||||||
|
bj.cidr
|
||||||
|
bl.cidr
|
||||||
|
bm.cidr
|
||||||
|
bn.cidr
|
||||||
|
bo.cidr
|
||||||
|
bq.cidr
|
||||||
|
br.cidr
|
||||||
|
bs.cidr
|
||||||
|
bt.cidr
|
||||||
|
bw.cidr
|
||||||
|
by.cidr
|
||||||
|
bz.cidr
|
||||||
|
ca.cidr
|
||||||
|
cd.cidr
|
||||||
|
cf.cidr
|
||||||
|
cg.cidr
|
||||||
|
ch.cidr
|
||||||
|
ci.cidr
|
||||||
|
ck.cidr
|
||||||
|
cl.cidr
|
||||||
|
cm.cidr
|
||||||
|
cn.cidr
|
||||||
|
co.cidr
|
||||||
|
cr.cidr
|
||||||
|
cu.cidr
|
||||||
|
cv.cidr
|
||||||
|
cw.cidr
|
||||||
|
cy.cidr
|
||||||
|
cz.cidr
|
||||||
|
de.cidr
|
||||||
|
dj.cidr
|
||||||
|
dk.cidr
|
||||||
|
dm.cidr
|
||||||
|
do.cidr
|
||||||
|
dz.cidr
|
||||||
|
ec.cidr
|
||||||
|
ee.cidr
|
||||||
|
eg.cidr
|
||||||
|
er.cidr
|
||||||
|
es.cidr
|
||||||
|
et.cidr
|
||||||
|
fi.cidr
|
||||||
|
fj.cidr
|
||||||
|
fk.cidr
|
||||||
|
fm.cidr
|
||||||
|
fo.cidr
|
||||||
|
fr.cidr
|
||||||
|
ga.cidr
|
||||||
|
gb.cidr
|
||||||
|
gd.cidr
|
||||||
|
ge.cidr
|
||||||
|
gf.cidr
|
||||||
|
gg.cidr
|
||||||
|
gh.cidr
|
||||||
|
gi.cidr
|
||||||
|
gl.cidr
|
||||||
|
gm.cidr
|
||||||
|
gn.cidr
|
||||||
|
gp.cidr
|
||||||
|
gq.cidr
|
||||||
|
gr.cidr
|
||||||
|
gt.cidr
|
||||||
|
gu.cidr
|
||||||
|
gw.cidr
|
||||||
|
gy.cidr
|
||||||
|
hk.cidr
|
||||||
|
hn.cidr
|
||||||
|
hr.cidr
|
||||||
|
ht.cidr
|
||||||
|
hu.cidr
|
||||||
|
id.cidr
|
||||||
|
ie.cidr
|
||||||
|
il.cidr
|
||||||
|
im.cidr
|
||||||
|
in.cidr
|
||||||
|
io.cidr
|
||||||
|
iq.cidr
|
||||||
|
ir.cidr
|
||||||
|
is.cidr
|
||||||
|
it.cidr
|
||||||
|
je.cidr
|
||||||
|
jm.cidr
|
||||||
|
jo.cidr
|
||||||
|
jp.cidr
|
||||||
|
ke.cidr
|
||||||
|
kg.cidr
|
||||||
|
kh.cidr
|
||||||
|
ki.cidr
|
||||||
|
km.cidr
|
||||||
|
kn.cidr
|
||||||
|
kp.cidr
|
||||||
|
kr.cidr
|
||||||
|
kw.cidr
|
||||||
|
ky.cidr
|
||||||
|
kz.cidr
|
||||||
|
la.cidr
|
||||||
|
lb.cidr
|
||||||
|
lc.cidr
|
||||||
|
li.cidr
|
||||||
|
lk.cidr
|
||||||
|
lr.cidr
|
||||||
|
ls.cidr
|
||||||
|
lt.cidr
|
||||||
|
lu.cidr
|
||||||
|
lv.cidr
|
||||||
|
ly.cidr
|
||||||
|
ma.cidr
|
||||||
|
mc.cidr
|
||||||
|
md.cidr
|
||||||
|
me.cidr
|
||||||
|
mf.cidr
|
||||||
|
mg.cidr
|
||||||
|
mh.cidr
|
||||||
|
mk.cidr
|
||||||
|
ml.cidr
|
||||||
|
mm.cidr
|
||||||
|
mn.cidr
|
||||||
|
mo.cidr
|
||||||
|
mp.cidr
|
||||||
|
mq.cidr
|
||||||
|
mr.cidr
|
||||||
|
ms.cidr
|
||||||
|
mt.cidr
|
||||||
|
mu.cidr
|
||||||
|
mv.cidr
|
||||||
|
mw.cidr
|
||||||
|
mx.cidr
|
||||||
|
my.cidr
|
||||||
|
mz.cidr
|
||||||
|
na.cidr
|
||||||
|
nc.cidr
|
||||||
|
ne.cidr
|
||||||
|
nf.cidr
|
||||||
|
ng.cidr
|
||||||
|
ni.cidr
|
||||||
|
nl.cidr
|
||||||
|
no.cidr
|
||||||
|
np.cidr
|
||||||
|
nr.cidr
|
||||||
|
nu.cidr
|
||||||
|
nz.cidr
|
||||||
|
om.cidr
|
||||||
|
pa.cidr
|
||||||
|
pe.cidr
|
||||||
|
pf.cidr
|
||||||
|
pg.cidr
|
||||||
|
ph.cidr
|
||||||
|
pk.cidr
|
||||||
|
pl.cidr
|
||||||
|
pm.cidr
|
||||||
|
pr.cidr
|
||||||
|
ps.cidr
|
||||||
|
pt.cidr
|
||||||
|
pw.cidr
|
||||||
|
py.cidr
|
||||||
|
qa.cidr
|
||||||
|
re.cidr
|
||||||
|
ro.cidr
|
||||||
|
rs.cidr
|
||||||
|
ru.cidr
|
||||||
|
rw.cidr
|
||||||
|
sa.cidr
|
||||||
|
sb.cidr
|
||||||
|
sc.cidr
|
||||||
|
sd.cidr
|
||||||
|
se.cidr
|
||||||
|
sg.cidr
|
||||||
|
si.cidr
|
||||||
|
sk.cidr
|
||||||
|
sl.cidr
|
||||||
|
sm.cidr
|
||||||
|
sn.cidr
|
||||||
|
so.cidr
|
||||||
|
sr.cidr
|
||||||
|
ss.cidr
|
||||||
|
st.cidr
|
||||||
|
sv.cidr
|
||||||
|
sx.cidr
|
||||||
|
sy.cidr
|
||||||
|
sz.cidr
|
||||||
|
tc.cidr
|
||||||
|
td.cidr
|
||||||
|
tg.cidr
|
||||||
|
th.cidr
|
||||||
|
tj.cidr
|
||||||
|
tk.cidr
|
||||||
|
tl.cidr
|
||||||
|
tm.cidr
|
||||||
|
tn.cidr
|
||||||
|
to.cidr
|
||||||
|
tr.cidr
|
||||||
|
tt.cidr
|
||||||
|
tv.cidr
|
||||||
|
tw.cidr
|
||||||
|
tz.cidr
|
||||||
|
ua.cidr
|
||||||
|
ug.cidr
|
||||||
|
us.cidr
|
||||||
|
uy.cidr
|
||||||
|
uz.cidr
|
||||||
|
va.cidr
|
||||||
|
vc.cidr
|
||||||
|
ve.cidr
|
||||||
|
vg.cidr
|
||||||
|
vi.cidr
|
||||||
|
vn.cidr
|
||||||
|
vu.cidr
|
||||||
|
wf.cidr
|
||||||
|
ws.cidr
|
||||||
|
ye.cidr
|
||||||
|
yt.cidr
|
||||||
|
za.cidr
|
||||||
|
zm.cidr
|
||||||
|
zw.cidr
|
||||||
|
zz.cidr
|
454
my-route.py
Executable file
454
my-route.py
Executable file
|
@ -0,0 +1,454 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# pylint: disable=C0103,C0114
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import urllib.request
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from datetime import datetime
|
||||||
|
from os import path, sep, makedirs
|
||||||
|
from re import match
|
||||||
|
from sys import platform
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
|
|
||||||
|
class Parse:
|
||||||
|
"""Parser of configs, arguments, parameters.
|
||||||
|
"""
|
||||||
|
def __init__(self, parameters, block: str = None) -> None:
|
||||||
|
"""Object constructor.
|
||||||
|
Args:
|
||||||
|
parameters: dictionary as "key":"value" or
|
||||||
|
ArgumentParser class object or
|
||||||
|
string path to the file or
|
||||||
|
string as "var1=val1;var2=val2".
|
||||||
|
block (str, optional): name of target block from text. Defaults to None.
|
||||||
|
"""
|
||||||
|
self.path = ''
|
||||||
|
self.data = {}
|
||||||
|
if isinstance(parameters, dict):
|
||||||
|
self._dict2dict(parameters)
|
||||||
|
if isinstance(parameters, ArgumentParser):
|
||||||
|
self._dict2dict(self.argv2dict(parameters))
|
||||||
|
if isinstance(parameters, str):
|
||||||
|
if path.exists(parameters):
|
||||||
|
self._dict2dict(
|
||||||
|
self.strs2dict(
|
||||||
|
self.conf2strs(parameters),
|
||||||
|
block
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.path = parameters
|
||||||
|
else:
|
||||||
|
self._dict2dict(self.strs2dict(parameters, block))
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Overrides method for print(object).
|
||||||
|
Returns:
|
||||||
|
str: string with contents of the object's dictionary.
|
||||||
|
"""
|
||||||
|
string = ''
|
||||||
|
for key, val in self.data.items():
|
||||||
|
string += str(type(val)) + ' ' + str(key) + ' = ' + str(val) + '\n'
|
||||||
|
return string
|
||||||
|
def _dict2dict(self, dictionary: dict) -> None:
|
||||||
|
"""Updates or adds dictionary data.
|
||||||
|
Args:
|
||||||
|
dictionary (dict): dictionary as "key":"value".
|
||||||
|
"""
|
||||||
|
self.data.update(dictionary)
|
||||||
|
def expand(self, store: str = None) -> dict:
|
||||||
|
"""Expand dictionary "key":"name.conf" to dictionary "key":{subkey: subval}.
|
||||||
|
Args:
|
||||||
|
store (str, optional): path to directory with name.conf. Defaults to None.
|
||||||
|
Returns:
|
||||||
|
dict: expanded dictionary as "key":{subkey: subval}.
|
||||||
|
"""
|
||||||
|
for key in self.data.items():
|
||||||
|
if store:
|
||||||
|
config = store + sep + self.data[key]
|
||||||
|
else:
|
||||||
|
config = self.data[key]
|
||||||
|
with open(config, mode='r', encoding='UTF-8') as file:
|
||||||
|
self.data[key] = Parse(file.read()).data
|
||||||
|
return self.data
|
||||||
|
@classmethod
|
||||||
|
def argv2dict(cls, parser: ArgumentParser) -> dict:
|
||||||
|
"""Converts startup arguments to a dictionary.
|
||||||
|
Args:
|
||||||
|
parser (ArgumentParser): argparse.ArgumentParser class object.
|
||||||
|
Returns:
|
||||||
|
dict: dictionary as "key":"value".
|
||||||
|
"""
|
||||||
|
parser = ArgumentParser(add_help=False, parents=[parser])
|
||||||
|
return vars(parser.parse_args())
|
||||||
|
@classmethod
|
||||||
|
def conf2strs(cls, config: str) -> str:
|
||||||
|
"""Builds a dictionary from a file containing parameters.
|
||||||
|
Args:
|
||||||
|
config (str): path to the config file.
|
||||||
|
Returns:
|
||||||
|
str: string as "var1=val1;\nvar2=val2;".
|
||||||
|
"""
|
||||||
|
with open(config, mode='r', encoding='UTF-8') as file:
|
||||||
|
raw = file.read()
|
||||||
|
strs = ''
|
||||||
|
for line in raw.splitlines():
|
||||||
|
if not line.lstrip().startswith('#'):
|
||||||
|
strs += line + '\n'
|
||||||
|
return strs
|
||||||
|
@classmethod
|
||||||
|
def strs2dict(cls, strings: str, blockname: str) -> dict:
|
||||||
|
"""Builds a dictionary from a strings containing parameters.
|
||||||
|
Args:
|
||||||
|
strings (str): string as "var1=val1;var2=val2;".
|
||||||
|
blockname (str): name of target block from text.
|
||||||
|
Returns:
|
||||||
|
dict: dictionary as "key":"value".
|
||||||
|
"""
|
||||||
|
dictionary = {}
|
||||||
|
if blockname:
|
||||||
|
strings = cls.block(blockname, strings)
|
||||||
|
for line in strings.replace('\n', ';').split(';'):
|
||||||
|
if not line.lstrip().startswith('#') and "=" in line:
|
||||||
|
key = line.split('=')[0].strip()
|
||||||
|
val = line.split('=')[1].strip().split(';')[0].strip()
|
||||||
|
dictionary[key] = val
|
||||||
|
return dictionary
|
||||||
|
@classmethod
|
||||||
|
def str2bool(cls, value: str) -> bool:
|
||||||
|
"""Converts a string value to boolean.
|
||||||
|
Args:
|
||||||
|
value (str): string containing "true" or "false", "yes" or "no", "1" or "0".
|
||||||
|
Returns:
|
||||||
|
bool: bool True or False.
|
||||||
|
"""
|
||||||
|
return str(value).lower() in ("true", "yes", "1")
|
||||||
|
@classmethod
|
||||||
|
def block(cls, blockname: str, text: str) -> str:
|
||||||
|
"""Cuts a block of text between line [blockname] and line [next block] or EOF.
|
||||||
|
Args:
|
||||||
|
blockname (str): string in [] after which the block starts.
|
||||||
|
text (str): string of text from which the block is needed.
|
||||||
|
Returns:
|
||||||
|
str: string of text between line [block name] and line [next block].
|
||||||
|
"""
|
||||||
|
level = 1
|
||||||
|
save = False
|
||||||
|
result = ''
|
||||||
|
for line in text.splitlines():
|
||||||
|
if line.startswith('[') and blockname in line:
|
||||||
|
level = line.count('[')
|
||||||
|
save = True
|
||||||
|
elif line.startswith('[') and '['*level in line:
|
||||||
|
save = False
|
||||||
|
elif save:
|
||||||
|
result += line + '\n'
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Connect:
|
||||||
|
# pylint: disable=W0718
|
||||||
|
"""Set of connection methods (functions) for various protocols.
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def http(
|
||||||
|
url: str, method: str = 'GET',
|
||||||
|
username: str = '', password: str = '', authtype: str = None,
|
||||||
|
contenttype: str = 'text/plain', contentdata: str = ''
|
||||||
|
) -> str:
|
||||||
|
"""Handling HTTP request.
|
||||||
|
Args:
|
||||||
|
url (str): request url.
|
||||||
|
method (str, optional): HTTP request method. Defaults to 'GET'.
|
||||||
|
username (str, optional): username for url authentication. Defaults to ''.
|
||||||
|
password (str, optional): password for url authentication. Defaults to ''.
|
||||||
|
authtype (str, optional): digest|basic authentication type. Defaults to None.
|
||||||
|
contenttype (str, optional): 'Content-Type' header. Defaults to 'text/plain'.
|
||||||
|
contentdata (str, optional): content data. Defaults to ''.
|
||||||
|
Returns:
|
||||||
|
str: HTTP response or 'ERROR'.
|
||||||
|
"""
|
||||||
|
# Preparing authorization
|
||||||
|
if authtype:
|
||||||
|
pswd = urllib.request.HTTPPasswordMgrWithDefaultRealm()
|
||||||
|
pswd.add_password(None, url, username, password)
|
||||||
|
if authtype == 'basic':
|
||||||
|
auth = urllib.request.HTTPBasicAuthHandler(pswd)
|
||||||
|
if authtype == 'digest':
|
||||||
|
auth = urllib.request.HTTPDigestAuthHandler(pswd)
|
||||||
|
urllib.request.install_opener(urllib.request.build_opener(auth))
|
||||||
|
# Preparing request
|
||||||
|
request = urllib.request.Request(
|
||||||
|
url=url,
|
||||||
|
data=bytes(contentdata.encode('utf-8')),
|
||||||
|
method=method
|
||||||
|
)
|
||||||
|
request.add_header('Content-Type', contenttype)
|
||||||
|
# Response
|
||||||
|
try:
|
||||||
|
response = urllib.request.urlopen(request).read()
|
||||||
|
logging.debug(
|
||||||
|
msg=''
|
||||||
|
+ '\n' + 'uri: ' + url
|
||||||
|
+ '\n' + 'method: ' + method
|
||||||
|
+ '\n' + 'username: ' + username
|
||||||
|
+ '\n' + 'password: ' + password
|
||||||
|
+ '\n' + 'authtype: ' + str(authtype)
|
||||||
|
+ '\n' + 'content-type: ' + contenttype
|
||||||
|
+ '\n' + 'content-data: ' + contentdata
|
||||||
|
)
|
||||||
|
if response.startswith(b'\xff\xd8'):
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
return str(response.decode('utf-8'))
|
||||||
|
except Exception as error:
|
||||||
|
logging.debug(msg='\n' + 'error: ' + str(error))
|
||||||
|
return 'ERROR'
|
||||||
|
|
||||||
|
|
||||||
|
class Route(Connect):
|
||||||
|
"""Handling route operations.
|
||||||
|
"""
|
||||||
|
def __init__(self, gateway: str, cidr_root_path: str, cidr_name_list: list) -> None:
|
||||||
|
"""Object constructor.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gateway (str): route gateway ip address.
|
||||||
|
cidr_root_path (str): cidr db path.
|
||||||
|
cidr_name_list (list): list of cidr files.
|
||||||
|
"""
|
||||||
|
self._gateway = gateway
|
||||||
|
self._cidr_root_path = cidr_root_path
|
||||||
|
self._cidr_name_list = cidr_name_list
|
||||||
|
self._route_list = self.__cidr_name_list_to_route_list()
|
||||||
|
|
||||||
|
def __cidr_name_list_to_route_list(self) -> list:
|
||||||
|
"""Convert files content to route list.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: route list.
|
||||||
|
"""
|
||||||
|
route_list = []
|
||||||
|
for cidr_name in self._cidr_name_list:
|
||||||
|
cidr_path = self._cidr_root_path + sep + cidr_name
|
||||||
|
if not path.exists(cidr_path):
|
||||||
|
if not cidr_name.startswith('_'):
|
||||||
|
if not self.__cidr_download(cidr_name):
|
||||||
|
self._cidr_name_list.remove(cidr_name)
|
||||||
|
else:
|
||||||
|
self._cidr_name_list.remove(cidr_name)
|
||||||
|
for cidr_name in self._cidr_name_list:
|
||||||
|
cidr_path = self._cidr_root_path + sep + cidr_name
|
||||||
|
with open(cidr_path, mode='r', encoding='UTF-8') as file:
|
||||||
|
cidr_data = file.read()
|
||||||
|
for cidr in cidr_data.splitlines():
|
||||||
|
cidr = cidr.split('#')[0].strip()
|
||||||
|
if match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}$", cidr):
|
||||||
|
route_list.append(cidr + ' ' + self._gateway)
|
||||||
|
return route_list
|
||||||
|
|
||||||
|
def __cidr_download(self, cidr_name: str) -> bool:
|
||||||
|
# pylint: disable=W0718,W0719
|
||||||
|
"""Download CIDR file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cidr_name (str): file name.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: downloading failed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True - CIDR downloaded, False - there are exceptions.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cidr_url = ''.join(''
|
||||||
|
+ 'https://raw.githubusercontent.com/'
|
||||||
|
+ 'herrbischoff/'
|
||||||
|
+ 'country-ip-blocks/master/'
|
||||||
|
+ 'ipv4/' + cidr_name
|
||||||
|
)
|
||||||
|
response = self.http(url=cidr_url, method='GET')
|
||||||
|
cidr_path = self._cidr_root_path + sep + cidr_name
|
||||||
|
if response != 'ERROR':
|
||||||
|
makedirs(self._cidr_root_path, exist_ok=True)
|
||||||
|
with open(cidr_path, mode='w+', encoding='UTF-8') as file:
|
||||||
|
file.write(response)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise Exception('downloading ' + cidr_name + ' failed')
|
||||||
|
except Exception as error:
|
||||||
|
logging.warning(
|
||||||
|
msg=''
|
||||||
|
+ str(error)
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __cmd(self, command: list) -> None:
|
||||||
|
"""Executing command by terminal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command (list): splitted command by words.
|
||||||
|
"""
|
||||||
|
out, err = Popen(
|
||||||
|
command,
|
||||||
|
stdout=PIPE,
|
||||||
|
stderr=PIPE
|
||||||
|
).communicate()
|
||||||
|
for line in out.splitlines():
|
||||||
|
logging.info(msg=line.decode('utf-8'))
|
||||||
|
for line in err.splitlines():
|
||||||
|
logging.warning(msg=line.decode('utf-8'))
|
||||||
|
|
||||||
|
def ro_add(self) -> None:
|
||||||
|
"""Add routes specified by config.
|
||||||
|
"""
|
||||||
|
for route in self._route_list:
|
||||||
|
if platform.startswith('linux') or platform.startswith('darwin'):
|
||||||
|
command = ['ip', 'ro', 'add'] + route.split()
|
||||||
|
logging.info(msg=' '.join(command))
|
||||||
|
self.__cmd(command=command)
|
||||||
|
elif platform.startswith('win32'):
|
||||||
|
# todo: windows
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
logging.info(msg='added ' + str(len(self._route_list)) + ' records')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def ro_del(self) -> None:
|
||||||
|
"""Del routes specified by config.
|
||||||
|
"""
|
||||||
|
for route in self._route_list:
|
||||||
|
if platform.startswith('linux') or platform.startswith('darwin'):
|
||||||
|
command = ['ip', 'ro', 'del'] + route.split()
|
||||||
|
logging.info(msg=' '.join(command))
|
||||||
|
self.__cmd(command=command)
|
||||||
|
elif platform.startswith('win32'):
|
||||||
|
# todo: windows
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
logging.info(msg='deleted ' + str(len(self._route_list)) + ' records')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update(self) -> None:
|
||||||
|
"""Update CIDR db, del and add routes specified by config.
|
||||||
|
"""
|
||||||
|
for cidr_name in self._cidr_name_list:
|
||||||
|
if not cidr_name.startswith('_'):
|
||||||
|
self.__cidr_download(cidr_name=cidr_name)
|
||||||
|
self.ro_del()
|
||||||
|
self.ro_add()
|
||||||
|
|
||||||
|
|
||||||
|
def checkroot() -> bool:
|
||||||
|
# pylint: disable=C0415
|
||||||
|
"""Crossplatform privileged rights checker.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True - if privileged rights, False - if not privileged rights
|
||||||
|
"""
|
||||||
|
if platform.startswith('linux') or platform.startswith('darwin'):
|
||||||
|
from os import geteuid
|
||||||
|
if geteuid() == 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
elif platform.startswith('win32'):
|
||||||
|
import ctypes
|
||||||
|
return ctypes.windll.shell32.IsUserAnAdmin()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
time_start = datetime.now()
|
||||||
|
|
||||||
|
args = ArgumentParser(
|
||||||
|
prog='my-route',
|
||||||
|
description='Route management by CIDR lists.',
|
||||||
|
epilog='Dependencies: '
|
||||||
|
'- Python 3 (tested version 3.9.5), '
|
||||||
|
'- privileged rights '
|
||||||
|
)
|
||||||
|
args.add_argument(
|
||||||
|
'--config',
|
||||||
|
type=str,
|
||||||
|
default=path.splitext(__file__)[0] + '.conf',
|
||||||
|
required=False,
|
||||||
|
help='custom configuration file path'
|
||||||
|
)
|
||||||
|
args.add_argument('-a', '--add', action='store_true', required=False,
|
||||||
|
help='add routes specified by config')
|
||||||
|
args.add_argument('-d', '--del', action='store_true', required=False,
|
||||||
|
help='del routes specified by config')
|
||||||
|
args.add_argument('-u', '--update', action='store_true', required=False,
|
||||||
|
help='update cidr db, del old routes, add new routes')
|
||||||
|
args = vars(args.parse_args())
|
||||||
|
|
||||||
|
cidr_root = path.dirname(path.realpath(__file__)) + sep + 'my-route.db'
|
||||||
|
log_level = 'INFO'
|
||||||
|
log_root = path.dirname(path.realpath(__file__))
|
||||||
|
gateways = {}
|
||||||
|
if path.exists(args['config']):
|
||||||
|
|
||||||
|
conf = Parse(parameters=args['config'], block='common')
|
||||||
|
if 'cidr_root' in conf.data:
|
||||||
|
cidr_root = conf.data['cidr_root']
|
||||||
|
if 'log_root' in conf.data:
|
||||||
|
log_root = conf.data['log_root']
|
||||||
|
if 'log_level' in conf.data:
|
||||||
|
if conf.data['log_level'] == 'DEBUG':
|
||||||
|
log_level = logging.DEBUG
|
||||||
|
elif conf.data['log_level'] == 'INFO':
|
||||||
|
log_level = logging.INFO
|
||||||
|
elif conf.data['log_level'] == 'WARNING':
|
||||||
|
log_level = logging.WARNING
|
||||||
|
elif conf.data['log_level'] == 'ERROR':
|
||||||
|
log_level = logging.ERROR
|
||||||
|
elif conf.data['log_level'] == 'CRITICAL':
|
||||||
|
log_level = logging.CRITICAL
|
||||||
|
|
||||||
|
conf = Parse(parameters=args['config'], block='enable-gateway')
|
||||||
|
for key, value in conf.data.items():
|
||||||
|
if value == 'true':
|
||||||
|
gateway_config = Parse(
|
||||||
|
parameters=args['config'],
|
||||||
|
block=key
|
||||||
|
)
|
||||||
|
gateways[key] = []
|
||||||
|
for cidr, enable in gateway_config.data.items():
|
||||||
|
if enable == 'true':
|
||||||
|
gateways[key].append(cidr)
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
format='%(asctime)s %(levelname)s: %(message)s',
|
||||||
|
datefmt='%Y-%m-%d_%H.%M.%S',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(
|
||||||
|
filename=log_root + sep + path.splitext(path.basename(__file__))[0] + '.log',
|
||||||
|
mode='a'
|
||||||
|
),
|
||||||
|
logging.StreamHandler()
|
||||||
|
],
|
||||||
|
level=log_level
|
||||||
|
)
|
||||||
|
|
||||||
|
if checkroot():
|
||||||
|
for key, value in gateways.items():
|
||||||
|
ro_list = Route(
|
||||||
|
gateway=key.replace('-', ' '),
|
||||||
|
cidr_root_path=cidr_root,
|
||||||
|
cidr_name_list=value
|
||||||
|
)
|
||||||
|
|
||||||
|
if args['update']:
|
||||||
|
ro_list.update()
|
||||||
|
elif args['del']:
|
||||||
|
ro_list.ro_del()
|
||||||
|
elif args['add']:
|
||||||
|
ro_list.ro_add()
|
||||||
|
else:
|
||||||
|
logging.info(msg='No start arguments selected. Exit.')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.warning(msg='Restart this as root!')
|
||||||
|
|
||||||
|
time_execute = datetime.now() - time_start
|
||||||
|
logging.info(msg='execution time is ' + str(time_execute) + '. Exit.')
|
Loading…
Reference in New Issue
Block a user