Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
R
re6stnet
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
2
Issues
2
List
Boards
Labels
Milestones
Merge Requests
4
Merge Requests
4
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
re6stnet
Commits
b3f3c505
Commit
b3f3c505
authored
Jun 18, 2024
by
Tom Niget
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
py2to3: add lots of type hints and fix some python 2 migration bugs
parent
6467f1e0
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
124 additions
and
115 deletions
+124
-115
re6st/cache.py
re6st/cache.py
+14
-14
re6st/cli/node.py
re6st/cli/node.py
+1
-1
re6st/ctl.py
re6st/ctl.py
+2
-2
re6st/debug.py
re6st/debug.py
+6
-6
re6st/plib.py
re6st/plib.py
+6
-6
re6st/registry.py
re6st/registry.py
+29
-26
re6st/tests/test_unit/test_registry.py
re6st/tests/test_unit/test_registry.py
+7
-5
re6st/tests/tools.py
re6st/tests/tools.py
+6
-9
re6st/tunnel.py
re6st/tunnel.py
+15
-12
re6st/upnpigd.py
re6st/upnpigd.py
+2
-2
re6st/utils.py
re6st/utils.py
+12
-11
re6st/x509.py
re6st/x509.py
+24
-21
No files found.
re6st/cache.py
View file @
b3f3c505
...
@@ -5,7 +5,7 @@ from . import utils, version, x509
...
@@ -5,7 +5,7 @@ from . import utils, version, x509
class
Cache
:
class
Cache
:
def
__init__
(
self
,
db_path
,
registry
,
cert
:
x509
.
Cert
,
db_size
=
200
):
def
__init__
(
self
,
db_path
:
str
,
registry
,
cert
:
x509
.
Cert
,
db_size
=
200
):
self
.
_prefix
=
cert
.
prefix
self
.
_prefix
=
cert
.
prefix
self
.
_db_size
=
db_size
self
.
_db_size
=
db_size
self
.
_decrypt
=
cert
.
decrypt
self
.
_decrypt
=
cert
.
decrypt
...
@@ -50,7 +50,7 @@ class Cache:
...
@@ -50,7 +50,7 @@ class Cache:
self
.
warnProtocol
()
self
.
warnProtocol
()
logging
.
info
(
"Cache initialized."
)
logging
.
info
(
"Cache initialized."
)
def
_open
(
self
,
path
)
:
def
_open
(
self
,
path
:
str
)
->
sqlite3
.
Connection
:
db
=
sqlite3
.
connect
(
path
,
isolation_level
=
None
)
db
=
sqlite3
.
connect
(
path
,
isolation_level
=
None
)
db
.
text_factory
=
str
db
.
text_factory
=
str
db
.
execute
(
"PRAGMA synchronous = OFF"
)
db
.
execute
(
"PRAGMA synchronous = OFF"
)
...
@@ -147,7 +147,7 @@ class Cache:
...
@@ -147,7 +147,7 @@ class Cache:
logging
.
warning
(
"There's a new version of re6stnet:"
logging
.
warning
(
"There's a new version of re6stnet:"
" you should update."
)
" you should update."
)
def
getDh
(
self
,
path
):
def
getDh
(
self
,
path
:
str
):
# We'd like to do a full check here but
# We'd like to do a full check here but
# from OpenSSL import SSL
# from OpenSSL import SSL
# SSL.Context(SSL.TLSv1_METHOD).load_tmp_dh(path)
# SSL.Context(SSL.TLSv1_METHOD).load_tmp_dh(path)
...
@@ -179,11 +179,11 @@ class Cache:
...
@@ -179,11 +179,11 @@ class Cache:
logging
.
trace
(
"- %s: %s%s"
,
prefix
,
address
,
logging
.
trace
(
"- %s: %s%s"
,
prefix
,
address
,
' (blacklisted)'
if
_try
else
''
)
' (blacklisted)'
if
_try
else
''
)
def
cacheMinimize
(
self
,
size
):
def
cacheMinimize
(
self
,
size
:
int
):
with
self
.
_db
:
with
self
.
_db
:
self
.
_cacheMinimize
(
size
)
self
.
_cacheMinimize
(
size
)
def
_cacheMinimize
(
self
,
size
):
def
_cacheMinimize
(
self
,
size
:
int
):
a
=
self
.
_db
.
execute
(
a
=
self
.
_db
.
execute
(
"SELECT peer FROM volatile.stat ORDER BY try, RANDOM() LIMIT ?,-1"
,
"SELECT peer FROM volatile.stat ORDER BY try, RANDOM() LIMIT ?,-1"
,
(
size
,)).
fetchall
()
(
size
,)).
fetchall
()
...
@@ -192,26 +192,26 @@ class Cache:
...
@@ -192,26 +192,26 @@ class Cache:
q
(
"DELETE FROM peer WHERE prefix IN (?)"
,
a
)
q
(
"DELETE FROM peer WHERE prefix IN (?)"
,
a
)
q
(
"DELETE FROM volatile.stat WHERE peer IN (?)"
,
a
)
q
(
"DELETE FROM volatile.stat WHERE peer IN (?)"
,
a
)
def
connecting
(
self
,
prefix
,
connecting
):
def
connecting
(
self
,
prefix
:
str
,
connecting
:
bool
):
self
.
_db
.
execute
(
"UPDATE volatile.stat SET try=? WHERE peer=?"
,
self
.
_db
.
execute
(
"UPDATE volatile.stat SET try=? WHERE peer=?"
,
(
connecting
,
prefix
))
(
connecting
,
prefix
))
def
resetConnecting
(
self
):
def
resetConnecting
(
self
):
self
.
_db
.
execute
(
"UPDATE volatile.stat SET try=0"
)
self
.
_db
.
execute
(
"UPDATE volatile.stat SET try=0"
)
def
getAddress
(
self
,
prefix
)
:
def
getAddress
(
self
,
prefix
:
str
)
->
bool
:
r
=
self
.
_db
.
execute
(
"SELECT address FROM peer, volatile.stat"
r
=
self
.
_db
.
execute
(
"SELECT address FROM peer, volatile.stat"
" WHERE prefix=? AND prefix=peer AND try=0"
,
" WHERE prefix=? AND prefix=peer AND try=0"
,
(
prefix
,)).
fetchone
()
(
prefix
,)).
fetchone
()
return
r
and
r
[
0
]
return
r
and
r
[
0
]
@
property
@
property
def
my_address
(
self
):
def
my_address
(
self
)
->
str
:
for
x
,
in
self
.
_db
.
execute
(
"SELECT address FROM peer WHERE prefix=''"
):
for
x
,
in
self
.
_db
.
execute
(
"SELECT address FROM peer WHERE prefix=''"
):
return
x
return
x
@
my_address
.
setter
@
my_address
.
setter
def
my_address
(
self
,
value
):
def
my_address
(
self
,
value
:
str
):
if
value
:
if
value
:
with
self
.
_db
as
db
:
with
self
.
_db
as
db
:
db
.
execute
(
"INSERT OR REPLACE INTO peer VALUES ('', ?)"
,
db
.
execute
(
"INSERT OR REPLACE INTO peer VALUES ('', ?)"
,
...
@@ -229,14 +229,14 @@ class Cache:
...
@@ -229,14 +229,14 @@ class Cache:
# IOW, one should probably always put our own address there.
# IOW, one should probably always put our own address there.
_get_peer_sql
=
"SELECT %s FROM peer, volatile.stat"
\
_get_peer_sql
=
"SELECT %s FROM peer, volatile.stat"
\
" WHERE prefix=peer AND prefix!=? AND try=?"
" WHERE prefix=peer AND prefix!=? AND try=?"
def
getPeerList
(
self
,
failed
=
0
,
__sql
=
_get_peer_sql
%
"prefix, address"
def
getPeerList
(
self
,
failed
=
False
,
__sql
=
_get_peer_sql
%
"prefix, address"
+
" ORDER BY RANDOM()"
):
+
" ORDER BY RANDOM()"
):
return
self
.
_db
.
execute
(
__sql
,
(
self
.
_prefix
,
failed
))
return
self
.
_db
.
execute
(
__sql
,
(
self
.
_prefix
,
failed
))
def
getPeerCount
(
self
,
failed
=
0
,
__sql
=
_get_peer_sql
%
"COUNT(*)"
)
->
int
:
def
getPeerCount
(
self
,
failed
=
False
,
__sql
=
_get_peer_sql
%
"COUNT(*)"
)
->
int
:
return
self
.
_db
.
execute
(
__sql
,
(
self
.
_prefix
,
failed
)).
next
()[
0
]
return
self
.
_db
.
execute
(
__sql
,
(
self
.
_prefix
,
failed
)).
next
()[
0
]
def
getBootstrapPeer
(
self
):
def
getBootstrapPeer
(
self
)
->
tuple
[
str
,
str
]
:
logging
.
info
(
'Getting Boot peer...'
)
logging
.
info
(
'Getting Boot peer...'
)
try
:
try
:
bootpeer
=
self
.
_registry
.
getBootstrapPeer
(
self
.
_prefix
)
bootpeer
=
self
.
_registry
.
getBootstrapPeer
(
self
.
_prefix
)
...
@@ -250,7 +250,7 @@ class Cache:
...
@@ -250,7 +250,7 @@ class Cache:
return
prefix
,
address
return
prefix
,
address
logging
.
warning
(
'Buggy registry sent us our own address'
)
logging
.
warning
(
'Buggy registry sent us our own address'
)
def
addPeer
(
self
,
prefix
,
address
,
set_preferred
=
False
):
def
addPeer
(
self
,
prefix
:
str
,
address
:
str
,
set_preferred
=
False
):
logging
.
debug
(
'Adding peer %s: %s'
,
prefix
,
address
)
logging
.
debug
(
'Adding peer %s: %s'
,
prefix
,
address
)
with
self
.
_db
:
with
self
.
_db
:
q
=
self
.
_db
.
execute
q
=
self
.
_db
.
execute
...
@@ -274,7 +274,7 @@ class Cache:
...
@@ -274,7 +274,7 @@ class Cache:
q
(
"INSERT OR REPLACE INTO peer VALUES (?,?)"
,
(
prefix
,
address
))
q
(
"INSERT OR REPLACE INTO peer VALUES (?,?)"
,
(
prefix
,
address
))
q
(
"INSERT OR REPLACE INTO volatile.stat VALUES (?,0)"
,
(
prefix
,))
q
(
"INSERT OR REPLACE INTO volatile.stat VALUES (?,0)"
,
(
prefix
,))
def
getCountry
(
self
,
ip
)
:
def
getCountry
(
self
,
ip
:
str
)
->
str
:
try
:
try
:
return
self
.
_registry
.
getCountry
(
self
.
_prefix
,
ip
).
decode
()
return
self
.
_registry
.
getCountry
(
self
.
_prefix
,
ip
).
decode
()
except
socket
.
error
as
e
:
except
socket
.
error
as
e
:
...
...
re6st/cli/node.py
View file @
b3f3c505
...
@@ -272,7 +272,7 @@ def main():
...
@@ -272,7 +272,7 @@ def main():
call
(
args
)
call
(
args
)
args
[
3
]
=
'del'
args
[
3
]
=
'del'
cleanup
.
append
(
lambda
:
subprocess
.
call
(
args
))
cleanup
.
append
(
lambda
:
subprocess
.
call
(
args
))
def
ip
(
object
,
*
args
):
def
ip
(
object
:
str
,
*
args
):
args
=
[
'ip'
,
'-6'
,
object
,
'add'
]
+
list
(
args
)
args
=
[
'ip'
,
'-6'
,
object
,
'add'
]
+
list
(
args
)
call
(
args
)
call
(
args
)
args
[
3
]
=
'del'
args
[
3
]
=
'del'
...
...
re6st/ctl.py
View file @
b3f3c505
...
@@ -171,7 +171,7 @@ class Babel:
...
@@ -171,7 +171,7 @@ class Babel:
_decode
=
None
_decode
=
None
def
__init__
(
self
,
socket_path
,
handler
,
network
):
def
__init__
(
self
,
socket_path
:
str
,
handler
,
network
:
str
):
self
.
socket_path
=
socket_path
self
.
socket_path
=
socket_path
self
.
handler
=
handler
self
.
handler
=
handler
self
.
network
=
network
self
.
network
=
network
...
@@ -304,7 +304,7 @@ class iterRoutes:
...
@@ -304,7 +304,7 @@ class iterRoutes:
_waiting
=
True
_waiting
=
True
def
__new__
(
cls
,
control_socket
,
network
):
def
__new__
(
cls
,
control_socket
:
str
,
network
:
str
):
self
=
object
.
__new__
(
cls
)
self
=
object
.
__new__
(
cls
)
c
=
Babel
(
control_socket
,
self
,
network
)
c
=
Babel
(
control_socket
,
self
,
network
)
c
.
request_dump
()
c
.
request_dump
()
...
...
re6st/debug.py
View file @
b3f3c505
...
@@ -3,30 +3,30 @@ import errno, os, socket, stat, threading
...
@@ -3,30 +3,30 @@ import errno, os, socket, stat, threading
class
Socket
:
class
Socket
:
def
__init__
(
self
,
socket
):
def
__init__
(
self
,
socket
:
socket
.
socket
):
# In case that the default timeout is not None.
# In case that the default timeout is not None.
socket
.
settimeout
(
None
)
socket
.
settimeout
(
None
)
self
.
_socket
=
socket
self
.
_socket
=
socket
self
.
_buf
=
''
self
.
_buf
=
b
''
def
close
(
self
):
def
close
(
self
):
self
.
_socket
.
close
()
self
.
_socket
.
close
()
def
write
(
self
,
data
):
def
write
(
self
,
data
:
bytes
):
self
.
_socket
.
send
(
data
)
self
.
_socket
.
send
(
data
)
def
readline
(
self
):
def
readline
(
self
)
->
bytes
:
recv
=
self
.
_socket
.
recv
recv
=
self
.
_socket
.
recv
data
=
self
.
_buf
data
=
self
.
_buf
while
True
:
while
True
:
i
=
1
+
data
.
find
(
'
\
n
'
)
i
=
1
+
data
.
find
(
b
'
\
n
'
)
if
i
:
if
i
:
self
.
_buf
=
data
[
i
:]
self
.
_buf
=
data
[
i
:]
return
data
[:
i
]
return
data
[:
i
]
d
=
recv
(
4096
)
d
=
recv
(
4096
)
data
+=
d
data
+=
d
if
not
d
:
if
not
d
:
self
.
_buf
=
''
self
.
_buf
=
b
''
return
data
return
data
def
flush
(
self
):
def
flush
(
self
):
...
...
re6st/plib.py
View file @
b3f3c505
...
@@ -8,7 +8,7 @@ ovpn_server = os.path.join(here, 'ovpn-server')
...
@@ -8,7 +8,7 @@ ovpn_server = os.path.join(here, 'ovpn-server')
ovpn_client
=
os
.
path
.
join
(
here
,
'ovpn-client'
)
ovpn_client
=
os
.
path
.
join
(
here
,
'ovpn-client'
)
ovpn_log
:
Optional
[
str
]
=
None
ovpn_log
:
Optional
[
str
]
=
None
def
openvpn
(
iface
,
encrypt
,
*
args
,
**
kw
)
:
def
openvpn
(
iface
:
str
,
encrypt
,
*
args
,
**
kw
)
->
utils
.
Popen
:
args
=
[
'openvpn'
,
args
=
[
'openvpn'
,
'--dev-type'
,
'tap'
,
'--dev-type'
,
'tap'
,
'--dev'
,
iface
,
'--dev'
,
iface
,
...
@@ -28,7 +28,7 @@ def openvpn(iface, encrypt, *args, **kw):
...
@@ -28,7 +28,7 @@ def openvpn(iface, encrypt, *args, **kw):
ovpn_link_mtu_dict
=
{
'udp4'
:
1432
,
'udp6'
:
1450
}
ovpn_link_mtu_dict
=
{
'udp4'
:
1432
,
'udp6'
:
1450
}
def
server
(
iface
,
max_clients
,
dh_path
,
fd
,
port
,
proto
,
encrypt
,
*
args
,
**
kw
)
:
def
server
(
iface
:
str
,
max_clients
:
int
,
dh_path
:
str
,
fd
:
int
,
port
:
int
,
proto
:
str
,
encrypt
:
bool
,
*
args
,
**
kw
)
->
utils
.
Popen
:
if
proto
==
'udp'
:
if
proto
==
'udp'
:
proto
=
'udp4'
proto
=
'udp4'
client_script
=
'%s %s'
%
(
ovpn_server
,
fd
)
client_script
=
'%s %s'
%
(
ovpn_server
,
fd
)
...
@@ -49,7 +49,7 @@ def server(iface, max_clients, dh_path, fd, port, proto, encrypt, *args, **kw):
...
@@ -49,7 +49,7 @@ def server(iface, max_clients, dh_path, fd, port, proto, encrypt, *args, **kw):
*
args
,
pass_fds
=
[
fd
],
**
kw
)
*
args
,
pass_fds
=
[
fd
],
**
kw
)
def
client
(
iface
,
address_list
,
encrypt
,
*
args
,
**
kw
)
:
def
client
(
iface
:
str
,
address_list
:
list
[
tuple
[
str
,
int
,
str
]],
encrypt
:
bool
,
*
args
,
**
kw
)
->
utils
.
Popen
:
remote
=
[
'--nobind'
,
'--client'
]
remote
=
[
'--nobind'
,
'--client'
]
# XXX: We'd like to pass <connection> sections at command-line.
# XXX: We'd like to pass <connection> sections at command-line.
link_mtu
=
set
()
link_mtu
=
set
()
...
@@ -65,8 +65,8 @@ def client(iface, address_list, encrypt, *args, **kw):
...
@@ -65,8 +65,8 @@ def client(iface, address_list, encrypt, *args, **kw):
return
openvpn
(
iface
,
encrypt
,
*
remote
,
**
kw
)
return
openvpn
(
iface
,
encrypt
,
*
remote
,
**
kw
)
def
router
(
ip
,
ip4
,
rt6
,
hello_interval
,
log_path
,
state_path
,
pidfile
,
def
router
(
ip
:
tuple
[
str
,
int
],
ip4
,
rt6
:
tuple
[
str
,
bool
,
bool
],
hello_interval
:
int
,
log_path
:
str
,
state_path
:
str
,
pidfile
:
str
,
control_socket
,
default
,
hmac
,
*
args
,
**
kw
)
:
control_socket
:
str
,
default
:
str
,
hmac
:
tuple
[
bytes
|
None
,
bytes
|
None
],
*
args
,
**
kw
)
->
utils
.
Popen
:
network
,
gateway
,
has_ipv6_subtrees
=
rt6
network
,
gateway
,
has_ipv6_subtrees
=
rt6
network_mask
=
int
(
network
[
network
.
index
(
'/'
)
+
1
:])
network_mask
=
int
(
network
[
network
.
index
(
'/'
)
+
1
:])
ip
,
n
=
ip
ip
,
n
=
ip
...
@@ -83,7 +83,7 @@ def router(ip, ip4, rt6, hello_interval, log_path, state_path, pidfile,
...
@@ -83,7 +83,7 @@ def router(ip, ip4, rt6, hello_interval, log_path, state_path, pidfile,
'-C'
,
'redistribute local deny'
,
'-C'
,
'redistribute local deny'
,
'-C'
,
'redistribute ip %s/%s eq %s'
%
(
ip
,
n
,
n
)]
'-C'
,
'redistribute ip %s/%s eq %s'
%
(
ip
,
n
,
n
)]
if
hmac_sign
:
if
hmac_sign
:
def
key
(
cmd
,
id
:
str
,
value
):
def
key
(
cmd
:
list
[
str
],
id
:
str
,
value
:
bytes
):
cmd
+=
'-C'
,
(
'key type blake2s128 id %s value %s'
%
cmd
+=
'-C'
,
(
'key type blake2s128 id %s value %s'
%
(
id
,
binascii
.
hexlify
(
value
).
decode
()))
(
id
,
binascii
.
hexlify
(
value
).
decode
()))
key
(
cmd
,
'sign'
,
hmac_sign
)
key
(
cmd
,
'sign'
,
hmac_sign
)
...
...
re6st/registry.py
View file @
b3f3c505
...
@@ -22,10 +22,13 @@ import base64, hmac, hashlib, http.client, inspect, json, logging
...
@@ -22,10 +22,13 @@ import base64, hmac, hashlib, http.client, inspect, json, logging
import
mailbox
,
os
,
platform
,
random
,
select
,
smtplib
,
socket
,
sqlite3
import
mailbox
,
os
,
platform
,
random
,
select
,
smtplib
,
socket
,
sqlite3
import
string
,
sys
,
threading
,
time
,
weakref
,
zlib
import
string
,
sys
,
threading
,
time
,
weakref
,
zlib
from
collections
import
defaultdict
,
deque
from
collections
import
defaultdict
,
deque
from
collections.abc
import
Iterator
from
datetime
import
datetime
from
datetime
import
datetime
from
http.server
import
HTTPServer
,
BaseHTTPRequestHandler
from
http.server
import
HTTPServer
,
BaseHTTPRequestHandler
from
email.mime.text
import
MIMEText
from
email.mime.text
import
MIMEText
from
operator
import
itemgetter
from
operator
import
itemgetter
from
typing
import
Tuple
from
OpenSSL
import
crypto
from
OpenSSL
import
crypto
from
urllib.parse
import
urlparse
,
unquote
,
urlencode
from
urllib.parse
import
urlparse
,
unquote
,
urlencode
from
.
import
ctl
,
tunnel
,
utils
,
version
,
x509
from
.
import
ctl
,
tunnel
,
utils
,
version
,
x509
...
@@ -139,10 +142,10 @@ class RegistryServer:
...
@@ -139,10 +142,10 @@ class RegistryServer:
if
self
.
geoip_db
:
if
self
.
geoip_db
:
from
geoip2
import
database
,
errors
from
geoip2
import
database
,
errors
country
=
database
.
Reader
(
self
.
geoip_db
).
country
country
=
database
.
Reader
(
self
.
geoip_db
).
country
def
geoiplookup
(
ip
)
:
def
geoiplookup
(
ip
:
str
)
->
Tuple
[
str
,
str
]
:
try
:
try
:
req
=
country
(
ip
)
req
=
country
(
ip
)
return
req
.
country
.
iso_code
.
encode
(),
req
.
continent
.
code
.
encode
()
return
req
.
country
.
iso_code
,
req
.
continent
.
code
except
(
errors
.
AddressNotFoundError
,
ValueError
):
except
(
errors
.
AddressNotFoundError
,
ValueError
):
return
'*'
,
'*'
return
'*'
,
'*'
self
.
_geoiplookup
=
geoiplookup
self
.
_geoiplookup
=
geoiplookup
...
@@ -243,7 +246,7 @@ class RegistryServer:
...
@@ -243,7 +246,7 @@ class RegistryServer:
def
babel_dump
(
self
):
def
babel_dump
(
self
):
self
.
_wait_dump
=
False
self
.
_wait_dump
=
False
def
iterCert
(
self
):
def
iterCert
(
self
)
->
Iterator
[
Tuple
[
crypto
.
X509
,
str
,
str
]]
:
for
prefix
,
email
,
cert
in
self
.
db
.
execute
(
for
prefix
,
email
,
cert
in
self
.
db
.
execute
(
"SELECT * FROM cert WHERE cert IS NOT NULL"
):
"SELECT * FROM cert WHERE cert IS NOT NULL"
):
try
:
try
:
...
@@ -356,7 +359,7 @@ class RegistryServer:
...
@@ -356,7 +359,7 @@ class RegistryServer:
assert
len
(
key
)
==
len
(
sign
)
assert
len
(
key
)
==
len
(
sign
)
return
key
+
sign
return
key
+
sign
def
getCert
(
self
,
client_prefix
)
:
def
getCert
(
self
,
client_prefix
:
str
)
->
bytes
:
assert
self
.
lock
.
locked
()
assert
self
.
lock
.
locked
()
cert
=
self
.
db
.
execute
(
"SELECT cert FROM cert"
cert
=
self
.
db
.
execute
(
"SELECT cert FROM cert"
" WHERE prefix=? AND cert IS NOT NULL"
,
(
client_prefix
,)).
fetchone
()
" WHERE prefix=? AND cert IS NOT NULL"
,
(
client_prefix
,)).
fetchone
()
...
@@ -365,19 +368,19 @@ class RegistryServer:
...
@@ -365,19 +368,19 @@ class RegistryServer:
return
cert
[
0
]
return
cert
[
0
]
@
rpc_private
@
rpc_private
def
isToken
(
self
,
token
):
def
isToken
(
self
,
token
:
str
):
with
self
.
lock
:
with
self
.
lock
:
if
self
.
db
.
execute
(
"SELECT 1 FROM token WHERE token = ?"
,
if
self
.
db
.
execute
(
"SELECT 1 FROM token WHERE token = ?"
,
(
token
,)).
fetchone
():
(
token
,)).
fetchone
():
return
b"1"
return
b"1"
@
rpc_private
@
rpc_private
def
deleteToken
(
self
,
token
):
def
deleteToken
(
self
,
token
:
str
):
with
self
.
lock
:
with
self
.
lock
:
self
.
db
.
execute
(
"DELETE FROM token WHERE token = ?"
,
(
token
,))
self
.
db
.
execute
(
"DELETE FROM token WHERE token = ?"
,
(
token
,))
@
rpc_private
@
rpc_private
def
addToken
(
self
,
email
,
token
)
:
def
addToken
(
self
,
email
:
str
,
token
:
str
|
None
)
->
str
:
prefix_len
=
self
.
config
.
prefix_length
prefix_len
=
self
.
config
.
prefix_length
if
not
prefix_len
:
if
not
prefix_len
:
raise
HTTPError
(
http
.
client
.
FORBIDDEN
)
raise
HTTPError
(
http
.
client
.
FORBIDDEN
)
...
@@ -505,7 +508,7 @@ class RegistryServer:
...
@@ -505,7 +508,7 @@ class RegistryServer:
q
(
"UPDATE cert SET cert = 'reserved' WHERE prefix = ?"
,
(
prefix
,))
q
(
"UPDATE cert SET cert = 'reserved' WHERE prefix = ?"
,
(
prefix
,))
@
rpc
@
rpc
def
requestCertificate
(
self
,
token
,
req
,
location
=
''
,
ip
=
''
):
def
requestCertificate
(
self
,
token
:
str
|
None
,
req
:
bytes
,
location
:
str
=
''
,
ip
:
str
=
''
):
logging
.
debug
(
"Requesting certificate with token %s"
,
token
)
logging
.
debug
(
"Requesting certificate with token %s"
,
token
)
req
=
crypto
.
load_certificate_request
(
crypto
.
FILETYPE_PEM
,
req
)
req
=
crypto
.
load_certificate_request
(
crypto
.
FILETYPE_PEM
,
req
)
with
self
.
lock
:
with
self
.
lock
:
...
@@ -579,7 +582,7 @@ class RegistryServer:
...
@@ -579,7 +582,7 @@ class RegistryServer:
return
cert
return
cert
@
rpc
@
rpc
def
renewCertificate
(
self
,
cn
)
:
def
renewCertificate
(
self
,
cn
:
str
)
->
bytes
:
with
self
.
lock
:
with
self
.
lock
:
with
self
.
db
as
db
:
with
self
.
db
as
db
:
pem
=
self
.
getCert
(
cn
)
pem
=
self
.
getCert
(
cn
)
...
@@ -595,16 +598,16 @@ class RegistryServer:
...
@@ -595,16 +598,16 @@ class RegistryServer:
cert
.
get_subject
(),
cert
.
get_pubkey
(),
not_after
)
cert
.
get_subject
(),
cert
.
get_pubkey
(),
not_after
)
@
rpc
@
rpc
def
getCa
(
self
):
def
getCa
(
self
)
->
bytes
:
return
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
self
.
cert
.
ca
)
return
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
self
.
cert
.
ca
)
@
rpc
@
rpc
def
getDh
(
self
,
cn
)
:
def
getDh
(
self
,
cn
:
str
)
->
bytes
:
with
open
(
self
.
config
.
dh
)
as
f
:
with
open
(
self
.
config
.
dh
,
"rb"
)
as
f
:
return
f
.
read
()
return
f
.
read
()
@
rpc
@
rpc
def
getNetworkConfig
(
self
,
cn
)
:
def
getNetworkConfig
(
self
,
cn
:
str
)
->
bytes
:
with
self
.
lock
:
with
self
.
lock
:
cert
=
self
.
getCert
(
cn
)
cert
=
self
.
getCert
(
cn
)
config
=
self
.
network_config
.
copy
()
config
=
self
.
network_config
.
copy
()
...
@@ -614,7 +617,7 @@ class RegistryServer:
...
@@ -614,7 +617,7 @@ class RegistryServer:
v
and
base64
.
b64encode
(
x509
.
encrypt
(
cert
,
v
)).
decode
()
v
and
base64
.
b64encode
(
x509
.
encrypt
(
cert
,
v
)).
decode
()
return
zlib
.
compress
(
json
.
dumps
(
config
).
encode
(
"utf-8"
))
return
zlib
.
compress
(
json
.
dumps
(
config
).
encode
(
"utf-8"
))
def
_queryAddress
(
self
,
peer
)
->
str
:
def
_queryAddress
(
self
,
peer
:
str
)
->
str
:
logging
.
info
(
"Querying address for %s/%s %r"
,
int
(
peer
,
2
),
len
(
peer
),
peer
)
logging
.
info
(
"Querying address for %s/%s %r"
,
int
(
peer
,
2
),
len
(
peer
),
peer
)
self
.
sendto
(
peer
,
1
)
self
.
sendto
(
peer
,
1
)
s
=
self
.
sock
,
s
=
self
.
sock
,
...
@@ -631,12 +634,12 @@ class RegistryServer:
...
@@ -631,12 +634,12 @@ class RegistryServer:
int
(
peer
,
2
),
len
(
peer
))
int
(
peer
,
2
),
len
(
peer
))
@
rpc
@
rpc
def
getCountry
(
self
,
cn
,
address
)
->
bytes
:
def
getCountry
(
self
,
cn
:
str
,
address
:
str
)
->
bytes
|
None
:
country
=
self
.
_geoiplookup
(
address
)[
0
]
country
=
self
.
_geoiplookup
(
address
)[
0
]
return
None
if
country
==
'*'
else
country
.
encode
()
return
None
if
country
==
'*'
else
country
.
encode
()
@
rpc
@
rpc
def
getBootstrapPeer
(
self
,
cn
)
:
def
getBootstrapPeer
(
self
,
cn
:
str
)
->
bytes
|
None
:
logging
.
info
(
"Answering bootstrap peer for %s"
,
cn
)
logging
.
info
(
"Answering bootstrap peer for %s"
,
cn
)
with
self
.
peers_lock
:
with
self
.
peers_lock
:
age
,
peers
=
self
.
peers
age
,
peers
=
self
.
peers
...
@@ -671,7 +674,7 @@ class RegistryServer:
...
@@ -671,7 +674,7 @@ class RegistryServer:
return
x509
.
encrypt
(
cert
,
msg
.
encode
())
return
x509
.
encrypt
(
cert
,
msg
.
encode
())
@
rpc_private
@
rpc_private
def
revoke
(
self
,
cn_or_serial
):
def
revoke
(
self
,
cn_or_serial
:
int
|
str
):
with
self
.
lock
,
self
.
db
:
with
self
.
lock
,
self
.
db
:
q
=
self
.
db
.
execute
q
=
self
.
db
.
execute
try
:
try
:
...
@@ -692,12 +695,12 @@ class RegistryServer:
...
@@ -692,12 +695,12 @@ class RegistryServer:
q
(
"INSERT INTO crl VALUES (?,?)"
,
(
serial
,
not_after
))
q
(
"INSERT INTO crl VALUES (?,?)"
,
(
serial
,
not_after
))
self
.
updateNetworkConfig
()
self
.
updateNetworkConfig
()
def
newHMAC
(
self
,
i
,
key
=
None
):
def
newHMAC
(
self
,
i
:
int
,
key
:
bytes
=
None
):
if
key
is
None
:
if
key
is
None
:
key
=
os
.
urandom
(
16
)
key
=
os
.
urandom
(
16
)
self
.
setConfig
(
BABEL_HMAC
[
i
],
key
)
self
.
setConfig
(
BABEL_HMAC
[
i
],
key
)
def
delHMAC
(
self
,
i
):
def
delHMAC
(
self
,
i
:
int
):
self
.
db
.
execute
(
"DELETE FROM config WHERE name=?"
,
(
BABEL_HMAC
[
i
],))
self
.
db
.
execute
(
"DELETE FROM config WHERE name=?"
,
(
BABEL_HMAC
[
i
],))
@
rpc_private
@
rpc_private
...
@@ -724,7 +727,7 @@ class RegistryServer:
...
@@ -724,7 +727,7 @@ class RegistryServer:
self
.
sendto
(
self
.
prefix
,
0
)
self
.
sendto
(
self
.
prefix
,
0
)
@
rpc_private
@
rpc_private
def
getNodePrefix
(
self
,
email
)
:
def
getNodePrefix
(
self
,
email
:
str
)
->
str
|
None
:
with
self
.
lock
,
self
.
db
:
with
self
.
lock
,
self
.
db
:
try
:
try
:
cert
,
=
next
(
self
.
db
.
execute
(
"SELECT cert FROM cert WHERE email = ?"
,
cert
,
=
next
(
self
.
db
.
execute
(
"SELECT cert FROM cert WHERE email = ?"
,
...
@@ -735,7 +738,7 @@ class RegistryServer:
...
@@ -735,7 +738,7 @@ class RegistryServer:
return
x509
.
subnetFromCert
(
certificate
)
return
x509
.
subnetFromCert
(
certificate
)
@
rpc_private
@
rpc_private
def
getIPv6Address
(
self
,
email
)
:
def
getIPv6Address
(
self
,
email
:
str
)
->
str
:
cn
=
self
.
getNodePrefix
(
email
)
cn
=
self
.
getNodePrefix
(
email
)
if
cn
:
if
cn
:
return
utils
.
ipFromBin
(
return
utils
.
ipFromBin
(
...
@@ -743,7 +746,7 @@ class RegistryServer:
...
@@ -743,7 +746,7 @@ class RegistryServer:
+
utils
.
binFromSubnet
(
cn
))
+
utils
.
binFromSubnet
(
cn
))
@
rpc_private
@
rpc_private
def
getIPv4Information
(
self
,
email
)
:
def
getIPv4Information
(
self
,
email
:
str
)
->
bytes
|
None
:
peer
=
self
.
getNodePrefix
(
email
)
peer
=
self
.
getNodePrefix
(
email
)
if
peer
:
if
peer
:
peer
=
utils
.
binFromSubnet
(
peer
)
peer
=
utils
.
binFromSubnet
(
peer
)
...
@@ -762,7 +765,7 @@ class RegistryServer:
...
@@ -762,7 +765,7 @@ class RegistryServer:
return
msg
.
split
(
','
)[
0
].
encode
()
return
msg
.
split
(
','
)[
0
].
encode
()
@
rpc_private
@
rpc_private
def
versions
(
self
):
def
versions
(
self
)
->
str
:
with
self
.
peers_lock
:
with
self
.
peers_lock
:
self
.
request_dump
()
self
.
request_dump
()
peers
=
{
prefix
peers
=
{
prefix
...
@@ -788,7 +791,7 @@ class RegistryServer:
...
@@ -788,7 +791,7 @@ class RegistryServer:
return
json
.
dumps
(
peer_dict
)
return
json
.
dumps
(
peer_dict
)
@
rpc_private
@
rpc_private
def
topology
(
self
):
def
topology
(
self
)
->
str
:
logging
.
info
(
"Computing topology"
)
logging
.
info
(
"Computing topology"
)
p
=
lambda
p
:
'%s/%s'
%
(
int
(
p
,
2
),
len
(
p
))
p
=
lambda
p
:
'%s/%s'
%
(
int
(
p
,
2
),
len
(
p
))
peers
=
deque
((
p
(
self
.
prefix
),))
peers
=
deque
((
p
(
self
.
prefix
),))
...
@@ -828,7 +831,7 @@ class RegistryClient:
...
@@ -828,7 +831,7 @@ class RegistryClient:
_hmac
=
None
_hmac
=
None
user_agent
=
"re6stnet/%s, %s"
%
(
version
.
version
,
platform
.
platform
())
user_agent
=
"re6stnet/%s, %s"
%
(
version
.
version
,
platform
.
platform
())
def
__init__
(
self
,
url
,
cert
:
x509
.
Cert
=
None
,
auto_close
=
True
):
def
__init__
(
self
,
url
:
str
,
cert
:
x509
.
Cert
=
None
,
auto_close
=
True
):
self
.
cert
=
cert
self
.
cert
=
cert
self
.
auto_close
=
auto_close
self
.
auto_close
=
auto_close
url_parsed
=
urlparse
(
url
)
url_parsed
=
urlparse
(
url
)
...
@@ -838,7 +841,7 @@ class RegistryClient:
...
@@ -838,7 +841,7 @@ class RegistryClient:
)[
scheme
](
unquote
(
host
),
timeout
=
60
)
)[
scheme
](
unquote
(
host
),
timeout
=
60
)
self
.
_path
=
path
.
rstrip
(
'/'
)
self
.
_path
=
path
.
rstrip
(
'/'
)
def
__getattr__
(
self
,
name
):
def
__getattr__
(
self
,
name
:
str
):
getcallargs
=
getattr
(
RegistryServer
,
name
).
getcallargs
getcallargs
=
getattr
(
RegistryServer
,
name
).
getcallargs
def
rpc
(
*
args
,
**
kw
)
->
bytes
:
def
rpc
(
*
args
,
**
kw
)
->
bytes
:
kw
=
getcallargs
(
*
args
,
**
kw
)
kw
=
getcallargs
(
*
args
,
**
kw
)
...
...
re6st/tests/test_unit/test_registry.py
View file @
b3f3c505
...
@@ -11,11 +11,13 @@ import hashlib
...
@@ -11,11 +11,13 @@ import hashlib
import
time
import
time
import
tempfile
import
tempfile
from
argparse
import
Namespace
from
argparse
import
Namespace
from
sqlite3
import
Cursor
from
OpenSSL
import
crypto
from
OpenSSL
import
crypto
from
mock
import
Mock
,
patch
from
mock
import
Mock
,
patch
from
pathlib
import
Path
from
pathlib
import
Path
from
re6st
import
registry
from
re6st
import
registry
,
x509
from
re6st.tests.tools
import
*
from
re6st.tests.tools
import
*
from
re6st.tests
import
DEMO_PATH
from
re6st.tests
import
DEMO_PATH
...
@@ -23,7 +25,7 @@ from re6st.tests import DEMO_PATH
...
@@ -23,7 +25,7 @@ from re6st.tests import DEMO_PATH
# TODO test for request_dump, requestToken, getNetworkConfig, getBoostrapPeer
# TODO test for request_dump, requestToken, getNetworkConfig, getBoostrapPeer
# getIPV4Information, versions
# getIPV4Information, versions
def
load_config
(
filename
=
"registry.json"
)
:
def
load_config
(
filename
:
str
=
"registry.json"
)
->
Namespace
:
with
open
(
filename
)
as
f
:
with
open
(
filename
)
as
f
:
config
=
json
.
load
(
f
)
config
=
json
.
load
(
f
)
config
[
"dh"
]
=
DEMO_PATH
/
"dh2048.pem"
config
[
"dh"
]
=
DEMO_PATH
/
"dh2048.pem"
...
@@ -37,13 +39,13 @@ def load_config(filename="registry.json"):
...
@@ -37,13 +39,13 @@ def load_config(filename="registry.json"):
return
Namespace
(
**
config
)
return
Namespace
(
**
config
)
def
get_cert
(
cur
,
prefix
):
def
get_cert
(
cur
:
Cursor
,
prefix
:
str
):
res
=
cur
.
execute
(
res
=
cur
.
execute
(
"SELECT cert FROM cert WHERE prefix=?"
,
(
prefix
,)).
fetchone
()
"SELECT cert FROM cert WHERE prefix=?"
,
(
prefix
,)).
fetchone
()
return
res
[
0
]
return
res
[
0
]
def
insert_cert
(
cur
,
ca
,
prefix
,
not_after
=
None
,
email
=
None
):
def
insert_cert
(
cur
:
Cursor
,
ca
:
x509
.
Cert
,
prefix
:
str
,
not_after
=
None
,
email
=
None
):
key
,
csr
=
generate_csr
()
key
,
csr
=
generate_csr
()
cert
=
generate_cert
(
ca
.
ca
,
ca
.
key
,
csr
,
prefix
,
insert_cert
.
serial
,
not_after
)
cert
=
generate_cert
(
ca
.
ca
,
ca
.
key
,
csr
,
prefix
,
insert_cert
.
serial
,
not_after
)
cur
.
execute
(
"INSERT INTO cert VALUES (?,?,?)"
,
(
prefix
,
email
,
cert
))
cur
.
execute
(
"INSERT INTO cert VALUES (?,?,?)"
,
(
prefix
,
email
,
cert
))
...
@@ -54,7 +56,7 @@ def insert_cert(cur, ca, prefix, not_after=None, email=None):
...
@@ -54,7 +56,7 @@ def insert_cert(cur, ca, prefix, not_after=None, email=None):
insert_cert
.
serial
=
0
insert_cert
.
serial
=
0
def
delete_cert
(
cur
,
prefix
):
def
delete_cert
(
cur
:
Cursor
,
prefix
:
str
):
cur
.
execute
(
"DELETE FROM cert WHERE prefix = ?"
,
(
prefix
,))
cur
.
execute
(
"DELETE FROM cert WHERE prefix = ?"
,
(
prefix
,))
...
...
re6st/tests/tools.py
View file @
b3f3c505
...
@@ -92,18 +92,15 @@ def create_ca_file(pkey_file, cert_file, serial=0x120010db80042):
...
@@ -92,18 +92,15 @@ def create_ca_file(pkey_file, cert_file, serial=0x120010db80042):
return
key
,
cert
return
key
,
cert
def
prefix2cn
(
prefix
)
:
def
prefix2cn
(
prefix
:
str
)
->
str
:
return
"%u/%u"
%
(
int
(
prefix
,
2
),
len
(
prefix
))
return
"%u/%u"
%
(
int
(
prefix
,
2
),
len
(
prefix
))
def
serial2prefix
(
serial
)
:
def
serial2prefix
(
serial
:
int
)
->
str
:
return
bin
(
serial
)[
2
:].
rjust
(
16
,
'0'
)
return
bin
(
serial
)[
2
:].
rjust
(
16
,
'0'
)
# pkey: private key
# pkey: private key
def
decrypt
(
pkey
,
incontent
)
:
def
decrypt
(
pkey
:
bytes
,
incontent
:
bytes
)
->
bytes
:
with
open
(
"node.key"
,
'w'
)
as
f
:
with
open
(
"node.key"
,
'w
b
'
)
as
f
:
f
.
write
(
pkey
.
decode
()
)
f
.
write
(
pkey
)
args
=
"openssl rsautl -decrypt -inkey node.key"
.
split
()
args
=
"openssl rsautl -decrypt -inkey node.key"
.
split
()
with
subprocess
.
Popen
(
return
subprocess
.
run
(
args
,
input
=
incontent
,
stdout
=
subprocess
.
PIPE
).
stdout
args
,
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
as
p
:
outcontent
,
err
=
p
.
communicate
(
incontent
)
return
outcontent
re6st/tunnel.py
View file @
b3f3c505
...
@@ -2,8 +2,11 @@ import errno, json, logging, os, platform, random, socket
...
@@ -2,8 +2,11 @@ import errno, json, logging, os, platform, random, socket
import
subprocess
,
struct
,
sys
,
time
,
weakref
import
subprocess
,
struct
,
sys
,
time
,
weakref
from
collections
import
defaultdict
,
deque
from
collections
import
defaultdict
,
deque
from
bisect
import
bisect
,
insort
from
bisect
import
bisect
,
insort
from
collections.abc
import
Iterator
,
Sequence
from
typing
import
Callable
from
OpenSSL
import
crypto
from
OpenSSL
import
crypto
from
.
import
ctl
,
plib
,
utils
,
version
,
x509
from
.
import
c
ache
,
c
tl
,
plib
,
utils
,
version
,
x509
PORT
=
326
PORT
=
326
...
@@ -21,7 +24,7 @@ proto_dict = {
...
@@ -21,7 +24,7 @@ proto_dict = {
proto_dict
[
'tcp'
]
=
proto_dict
[
'tcp4'
]
proto_dict
[
'tcp'
]
=
proto_dict
[
'tcp4'
]
proto_dict
[
'udp'
]
=
proto_dict
[
'udp4'
]
proto_dict
[
'udp'
]
=
proto_dict
[
'udp4'
]
def
resolve
(
ip
,
port
,
proto
)
:
def
resolve
(
ip
,
port
,
proto
:
str
)
->
tuple
[
socket
.
AddressFamily
|
None
,
Iterator
[
str
]]
:
try
:
try
:
family
,
proto
=
proto_dict
[
proto
]
family
,
proto
=
proto_dict
[
proto
]
except
KeyError
:
except
KeyError
:
...
@@ -31,16 +34,16 @@ def resolve(ip, port, proto):
...
@@ -31,16 +34,16 @@ def resolve(ip, port, proto):
class
MultiGatewayManager
(
dict
):
class
MultiGatewayManager
(
dict
):
def
__init__
(
self
,
gateway
):
def
__init__
(
self
,
gateway
:
Callable
[[
str
],
str
]
):
self
.
_gw
=
gateway
self
.
_gw
=
gateway
def
_route
(
self
,
cmd
,
dest
,
gw
):
def
_route
(
self
,
cmd
:
str
,
dest
:
str
,
gw
:
str
):
if
gw
:
if
gw
:
cmd
=
'ip'
,
'-4'
,
'route'
,
cmd
,
'%s/32'
%
dest
,
'via'
,
gw
cmd
=
'ip'
,
'-4'
,
'route'
,
cmd
,
'%s/32'
%
dest
,
'via'
,
gw
logging
.
trace
(
'%r'
,
cmd
)
logging
.
trace
(
'%r'
,
cmd
)
subprocess
.
check_call
(
cmd
)
subprocess
.
check_call
(
cmd
)
def
add
(
self
,
dest
,
route
):
def
add
(
self
,
dest
:
str
,
route
:
bool
):
try
:
try
:
self
[
dest
][
1
]
+=
1
self
[
dest
][
1
]
+=
1
except
KeyError
:
except
KeyError
:
...
@@ -48,7 +51,7 @@ class MultiGatewayManager(dict):
...
@@ -48,7 +51,7 @@ class MultiGatewayManager(dict):
self
[
dest
]
=
[
gw
,
0
]
self
[
dest
]
=
[
gw
,
0
]
self
.
_route
(
'add'
,
dest
,
gw
)
self
.
_route
(
'add'
,
dest
,
gw
)
def
remove
(
self
,
dest
):
def
remove
(
self
,
dest
:
str
):
gw
,
count
=
self
[
dest
]
gw
,
count
=
self
[
dest
]
if
count
:
if
count
:
self
[
dest
][
1
]
=
count
-
1
self
[
dest
][
1
]
=
count
-
1
...
@@ -65,7 +68,7 @@ class Connection:
...
@@ -65,7 +68,7 @@ class Connection:
serial
=
None
serial
=
None
time
=
float
(
'inf'
)
time
=
float
(
'inf'
)
def
__init__
(
self
,
tunnel_manager
,
address_list
,
iface
,
prefix
):
def
__init__
(
self
,
tunnel_manager
:
"TunnelManager"
,
address_list
,
iface
,
prefix
):
self
.
tunnel_manager
=
tunnel_manager
self
.
tunnel_manager
=
tunnel_manager
self
.
address_list
=
address_list
self
.
address_list
=
address_list
self
.
iface
=
iface
self
.
iface
=
iface
...
@@ -109,7 +112,7 @@ class Connection:
...
@@ -109,7 +112,7 @@ class Connection:
if
i
:
if
i
:
cache
.
addPeer
(
self
.
_prefix
,
','
.
join
(
self
.
address_list
[
i
]),
True
)
cache
.
addPeer
(
self
.
_prefix
,
','
.
join
(
self
.
address_list
[
i
]),
True
)
else
:
else
:
cache
.
connecting
(
self
.
_prefix
,
0
)
cache
.
connecting
(
self
.
_prefix
,
False
)
def
close
(
self
):
def
close
(
self
):
try
:
try
:
...
@@ -198,7 +201,7 @@ class BaseTunnelManager:
...
@@ -198,7 +201,7 @@ class BaseTunnelManager:
_geoiplookup
=
None
_geoiplookup
=
None
_forward
=
None
_forward
=
None
def
__init__
(
self
,
control_socket
,
cache
,
c
ert
,
conf_country
,
address
=
()):
def
__init__
(
self
,
control_socket
,
cache
:
cache
.
Cache
,
cert
:
x509
.
C
ert
,
conf_country
,
address
=
()):
self
.
cert
=
cert
self
.
cert
=
cert
self
.
_network
=
cert
.
network
self
.
_network
=
cert
.
network
self
.
_prefix
=
cert
.
prefix
self
.
_prefix
=
cert
.
prefix
...
@@ -581,7 +584,7 @@ class BaseTunnelManager:
...
@@ -581,7 +584,7 @@ class BaseTunnelManager:
self
.
_gateway_manager
.
add
(
trusted_ip
,
False
)
self
.
_gateway_manager
.
add
(
trusted_ip
,
False
)
if
prefix
in
self
.
_connection_dict
and
self
.
_prefix
<
prefix
:
if
prefix
in
self
.
_connection_dict
and
self
.
_prefix
<
prefix
:
self
.
_kill
(
prefix
)
self
.
_kill
(
prefix
)
self
.
cache
.
connecting
(
prefix
,
0
)
self
.
cache
.
connecting
(
prefix
,
False
)
return
True
return
True
def
_ovpn_client_disconnect
(
self
,
common_name
,
iface
,
serial
,
trusted_ip
):
def
_ovpn_client_disconnect
(
self
,
common_name
,
iface
,
serial
,
trusted_ip
):
...
@@ -665,7 +668,7 @@ class TunnelManager(BaseTunnelManager):
...
@@ -665,7 +668,7 @@ class TunnelManager(BaseTunnelManager):
def
__init__
(
self
,
control_socket
,
cache
,
cert
,
openvpn_args
,
def
__init__
(
self
,
control_socket
,
cache
,
cert
,
openvpn_args
,
timeout
,
client_count
,
iface_list
,
conf_country
,
address
,
timeout
,
client_count
,
iface_list
,
conf_country
,
address
,
ip_changed
,
remote_gateway
,
disable_proto
,
neighbour_list
=
()):
ip_changed
,
remote_gateway
:
Callable
[[
str
],
str
],
disable_proto
:
Sequence
[
str
]
,
neighbour_list
=
()):
super
(
TunnelManager
,
self
).
__init__
(
control_socket
,
super
(
TunnelManager
,
self
).
__init__
(
control_socket
,
cache
,
cert
,
conf_country
,
address
)
cache
,
cert
,
conf_country
,
address
)
self
.
ovpn_args
=
openvpn_args
self
.
ovpn_args
=
openvpn_args
...
@@ -877,7 +880,7 @@ class TunnelManager(BaseTunnelManager):
...
@@ -877,7 +880,7 @@ class TunnelManager(BaseTunnelManager):
address_list
.
append
((
ip
,
x
[
1
],
x
[
2
]))
address_list
.
append
((
ip
,
x
[
1
],
x
[
2
]))
continue
continue
address_list
.
append
(
x
[:
3
])
address_list
.
append
(
x
[:
3
])
self
.
cache
.
connecting
(
prefix
,
1
)
self
.
cache
.
connecting
(
prefix
,
True
)
if
not
address_list
:
if
not
address_list
:
return
False
return
False
logging
.
info
(
'Establishing a connection with %u/%u'
,
logging
.
info
(
'Establishing a connection with %u/%u'
,
...
...
re6st/upnpigd.py
View file @
b3f3c505
...
@@ -17,7 +17,7 @@ class Forwarder:
...
@@ -17,7 +17,7 @@ class Forwarder:
_lcg_n
=
0
_lcg_n
=
0
@
classmethod
@
classmethod
def
_getExternalPort
(
cls
):
def
_getExternalPort
(
cls
)
->
int
:
# Since _refresh() does not test all ports in a row, we prefer to
# Since _refresh() does not test all ports in a row, we prefer to
# return random ports to maximize the chance to find a free port.
# return random ports to maximize the chance to find a free port.
# A linear congruential generator should be random enough, without
# A linear congruential generator should be random enough, without
...
@@ -35,7 +35,7 @@ class Forwarder:
...
@@ -35,7 +35,7 @@ class Forwarder:
self
.
_u
.
discoverdelay
=
200
self
.
_u
.
discoverdelay
=
200
self
.
_rules
=
[]
self
.
_rules
=
[]
def
__getattr__
(
self
,
name
):
def
__getattr__
(
self
,
name
:
str
):
wrapped
=
getattr
(
self
.
_u
,
name
)
wrapped
=
getattr
(
self
.
_u
,
name
)
def
wrapper
(
*
args
,
**
kw
):
def
wrapper
(
*
args
,
**
kw
):
try
:
try
:
...
...
re6st/utils.py
View file @
b3f3c505
import
argparse
,
errno
,
fcntl
,
hashlib
,
logging
,
os
,
select
as
_select
import
argparse
,
errno
,
fcntl
,
hashlib
,
logging
,
os
,
select
as
_select
import
shlex
,
signal
,
socket
,
sqlite3
,
struct
,
subprocess
import
shlex
,
signal
,
socket
,
sqlite3
,
struct
,
subprocess
import
sys
,
textwrap
,
threading
,
time
,
traceback
import
sys
,
textwrap
,
threading
,
time
,
traceback
from
typing
import
Optional
from
collections.abc
import
Iterator
,
Mapping
from
typing
import
Optional
,
Callable
HMAC_LEN
=
len
(
hashlib
.
sha1
(
b''
).
digest
())
HMAC_LEN
=
len
(
hashlib
.
sha1
(
b''
).
digest
())
...
@@ -40,7 +41,7 @@ class FileHandler(logging.FileHandler):
...
@@ -40,7 +41,7 @@ class FileHandler(logging.FileHandler):
if
self
.
lock
.
acquire
(
False
):
if
self
.
lock
.
acquire
(
False
):
self
.
release
()
self
.
release
()
def
setupLog
(
log_level
,
filenam
e
=
None
,
**
kw
):
def
setupLog
(
log_level
:
int
,
filename
:
str
|
Non
e
=
None
,
**
kw
):
if
log_level
and
filename
:
if
log_level
and
filename
:
makedirs
(
os
.
path
.
dirname
(
filename
))
makedirs
(
os
.
path
.
dirname
(
filename
))
handler
=
FileHandler
(
filename
)
handler
=
FileHandler
(
filename
)
...
@@ -184,7 +185,7 @@ def setCloexec(fd):
...
@@ -184,7 +185,7 @@ def setCloexec(fd):
flags
=
fcntl
.
fcntl
(
fd
,
fcntl
.
F_GETFD
)
flags
=
fcntl
.
fcntl
(
fd
,
fcntl
.
F_GETFD
)
fcntl
.
fcntl
(
fd
,
fcntl
.
F_SETFD
,
flags
|
fcntl
.
FD_CLOEXEC
)
fcntl
.
fcntl
(
fd
,
fcntl
.
F_SETFD
,
flags
|
fcntl
.
FD_CLOEXEC
)
def
select
(
R
,
W
,
T
):
def
select
(
R
:
Mapping
,
W
:
Mapping
,
T
):
try
:
try
:
r
,
w
,
_
=
_select
.
select
(
R
,
W
,
(),
r
,
w
,
_
=
_select
.
select
(
R
,
W
,
(),
max
(
0
,
min
(
T
)[
0
]
-
time
.
time
())
if
T
else
None
)
max
(
0
,
min
(
T
)[
0
]
-
time
.
time
())
if
T
else
None
)
...
@@ -208,15 +209,15 @@ def makedirs(*args):
...
@@ -208,15 +209,15 @@ def makedirs(*args):
if
e
.
errno
!=
errno
.
EEXIST
:
if
e
.
errno
!=
errno
.
EEXIST
:
raise
raise
def
binFromIp
(
ip
)
:
def
binFromIp
(
ip
:
str
)
->
str
:
return
binFromRawIp
(
socket
.
inet_pton
(
socket
.
AF_INET6
,
ip
))
return
binFromRawIp
(
socket
.
inet_pton
(
socket
.
AF_INET6
,
ip
))
def
binFromRawIp
(
ip
)
:
def
binFromRawIp
(
ip
:
bytes
)
->
str
:
ip1
,
ip2
=
struct
.
unpack
(
'>QQ'
,
ip
)
ip1
,
ip2
=
struct
.
unpack
(
'>QQ'
,
ip
)
return
bin
(
ip1
)[
2
:].
rjust
(
64
,
'0'
)
+
bin
(
ip2
)[
2
:].
rjust
(
64
,
'0'
)
return
bin
(
ip1
)[
2
:].
rjust
(
64
,
'0'
)
+
bin
(
ip2
)[
2
:].
rjust
(
64
,
'0'
)
def
ipFromBin
(
ip
,
suffix
=
''
)
:
def
ipFromBin
(
ip
:
str
,
suffix
=
''
)
->
str
:
suffix_len
=
128
-
len
(
ip
)
suffix_len
=
128
-
len
(
ip
)
if
suffix_len
>
0
:
if
suffix_len
>
0
:
ip
+=
suffix
.
rjust
(
suffix_len
,
'0'
)
ip
+=
suffix
.
rjust
(
suffix_len
,
'0'
)
...
@@ -225,11 +226,11 @@ def ipFromBin(ip, suffix=''):
...
@@ -225,11 +226,11 @@ def ipFromBin(ip, suffix=''):
return
socket
.
inet_ntop
(
socket
.
AF_INET6
,
return
socket
.
inet_ntop
(
socket
.
AF_INET6
,
struct
.
pack
(
'>QQ'
,
int
(
ip
[:
64
],
2
),
int
(
ip
[
64
:],
2
)))
struct
.
pack
(
'>QQ'
,
int
(
ip
[:
64
],
2
),
int
(
ip
[
64
:],
2
)))
def
dump_address
(
address
)
:
def
dump_address
(
address
:
str
)
->
str
:
return
';'
.
join
(
map
(
','
.
join
,
address
))
return
';'
.
join
(
map
(
','
.
join
,
address
))
# Yield ip, port, protocol, and country if it is in the address
# Yield ip, port, protocol, and country if it is in the address
def
parse_address
(
address_list
)
:
def
parse_address
(
address_list
:
str
)
->
Iterator
[
tuple
[
str
,
str
,
str
,
str
]]
:
for
address
in
address_list
.
split
(
';'
):
for
address
in
address_list
.
split
(
';'
):
try
:
try
:
a
=
address
.
split
(
','
)
a
=
address
.
split
(
','
)
...
@@ -239,17 +240,17 @@ def parse_address(address_list):
...
@@ -239,17 +240,17 @@ def parse_address(address_list):
logging
.
warning
(
"Failed to parse node address %r (%s)"
,
logging
.
warning
(
"Failed to parse node address %r (%s)"
,
address
,
e
)
address
,
e
)
def
binFromSubnet
(
subnet
)
:
def
binFromSubnet
(
subnet
:
str
)
->
str
:
p
,
l
=
subnet
.
split
(
'/'
)
p
,
l
=
subnet
.
split
(
'/'
)
return
bin
(
int
(
p
))[
2
:].
rjust
(
int
(
l
),
'0'
)
return
bin
(
int
(
p
))[
2
:].
rjust
(
int
(
l
),
'0'
)
def
newHmacSecret
()
:
def
_newHmacSecret
()
->
Callable
[[
Optional
[
int
]],
bytes
]
:
"""returns bytes"""
"""returns bytes"""
from
random
import
getrandbits
as
g
from
random
import
getrandbits
as
g
pack
=
struct
.
Struct
(
">QQI"
).
pack
pack
=
struct
.
Struct
(
">QQI"
).
pack
assert
len
(
pack
(
0
,
0
,
0
))
==
HMAC_LEN
assert
len
(
pack
(
0
,
0
,
0
))
==
HMAC_LEN
return
lambda
x
=
None
:
pack
(
g
(
64
)
if
x
is
None
else
x
,
g
(
64
),
g
(
32
))
return
lambda
x
=
None
:
pack
(
g
(
64
)
if
x
is
None
else
x
,
g
(
64
),
g
(
32
))
newHmacSecret
=
newHmacSecret
()
newHmacSecret
=
_
newHmacSecret
()
### Integer serialization
### Integer serialization
# - supports values from 0 to 0x202020202020201f
# - supports values from 0 to 0x202020202020201f
...
...
re6st/x509.py
View file @
b3f3c505
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import
calendar
,
hashlib
,
hmac
,
logging
,
os
,
struct
,
subprocess
,
threading
,
time
import
calendar
,
hashlib
,
hmac
,
logging
,
os
,
struct
,
subprocess
,
threading
,
time
from
typing
import
Callable
,
Any
from
OpenSSL
import
crypto
from
OpenSSL
import
crypto
from
cryptography.hazmat.primitives
import
hashes
from
cryptography.hazmat.primitives
import
hashes
from
cryptography.hazmat.primitives.asymmetric
import
padding
from
cryptography.hazmat.primitives.asymmetric
import
padding
...
@@ -9,29 +11,29 @@ from cryptography.x509 import load_pem_x509_certificate
...
@@ -9,29 +11,29 @@ from cryptography.x509 import load_pem_x509_certificate
from
.
import
utils
from
.
import
utils
from
.version
import
protocol
from
.version
import
protocol
def
newHmacSecret
():
def
newHmacSecret
()
->
bytes
:
return
utils
.
newHmacSecret
(
int
(
time
.
time
()
*
1000000
))
return
utils
.
newHmacSecret
(
int
(
time
.
time
()
*
1000000
))
def
networkFromCa
(
ca
)
:
def
networkFromCa
(
ca
:
crypto
.
X509
)
->
str
:
# TODO: will be ca.serial_number after migration to cryptography
# TODO: will be ca.serial_number after migration to cryptography
return
bin
(
ca
.
get_serial_number
())[
3
:]
return
bin
(
ca
.
get_serial_number
())[
3
:]
def
subnetFromCert
(
cert
)
:
def
subnetFromCert
(
cert
:
crypto
.
X509
)
->
str
:
return
cert
.
get_subject
().
CN
return
cert
.
get_subject
().
CN
def
notBefore
(
cert
)
:
def
notBefore
(
cert
:
crypto
.
X509
)
->
int
:
return
calendar
.
timegm
(
time
.
strptime
(
cert
.
get_notBefore
().
decode
(),
'%Y%m%d%H%M%SZ'
))
return
calendar
.
timegm
(
time
.
strptime
(
cert
.
get_notBefore
().
decode
(),
'%Y%m%d%H%M%SZ'
))
def
notAfter
(
cert
)
:
def
notAfter
(
cert
:
crypto
.
X509
)
->
int
:
return
calendar
.
timegm
(
time
.
strptime
(
cert
.
get_notAfter
().
decode
(),
'%Y%m%d%H%M%SZ'
))
return
calendar
.
timegm
(
time
.
strptime
(
cert
.
get_notAfter
().
decode
(),
'%Y%m%d%H%M%SZ'
))
def
openssl
(
*
args
,
fds
=
[])
:
def
openssl
(
*
args
:
str
,
fds
=
[])
->
utils
.
Popen
:
return
utils
.
Popen
((
'openssl'
,)
+
args
,
return
utils
.
Popen
((
'openssl'
,)
+
args
,
stdin
=
subprocess
.
PIPE
,
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
pass_fds
=
fds
)
stderr
=
subprocess
.
PIPE
,
pass_fds
=
fds
)
def
encrypt
(
cert
,
data
:
bytes
)
->
bytes
:
def
encrypt
(
cert
:
bytes
,
data
:
bytes
)
->
bytes
:
assert
isinstance
(
data
,
bytes
)
assert
isinstance
(
data
,
bytes
)
r
,
w
=
os
.
pipe
()
r
,
w
=
os
.
pipe
()
try
:
try
:
...
@@ -46,10 +48,10 @@ def encrypt(cert, data: bytes) -> bytes:
...
@@ -46,10 +48,10 @@ def encrypt(cert, data: bytes) -> bytes:
raise
subprocess
.
CalledProcessError
(
p
.
returncode
,
'openssl'
,
err
)
raise
subprocess
.
CalledProcessError
(
p
.
returncode
,
'openssl'
,
err
)
return
out
return
out
def
fingerprint
(
cert
,
alg
=
'sha1'
):
def
fingerprint
(
cert
:
crypto
.
X509
,
alg
=
'sha1'
):
return
hashlib
.
new
(
alg
,
crypto
.
dump_certificate
(
crypto
.
FILETYPE_ASN1
,
cert
))
return
hashlib
.
new
(
alg
,
crypto
.
dump_certificate
(
crypto
.
FILETYPE_ASN1
,
cert
))
def
maybe_renew
(
path
,
cert
,
info
,
renew
,
force
=
False
)
:
def
maybe_renew
(
path
:
str
,
cert
:
crypto
.
X509
,
info
:
str
,
renew
:
Callable
[[],
Any
],
force
=
False
)
->
tuple
[
crypto
.
X509
,
int
]
:
from
.registry
import
RENEW_PERIOD
from
.registry
import
RENEW_PERIOD
while
True
:
while
True
:
if
force
:
if
force
:
...
@@ -93,7 +95,7 @@ class NewSessionError(Exception):
...
@@ -93,7 +95,7 @@ class NewSessionError(Exception):
class
Cert
:
class
Cert
:
def
__init__
(
self
,
ca
,
key
,
cert
=
None
):
def
__init__
(
self
,
ca
:
str
,
key
:
str
,
cert
:
str
|
None
=
None
):
self
.
ca_path
=
ca
self
.
ca_path
=
ca
self
.
cert_path
=
cert
self
.
cert_path
=
cert
self
.
key_path
=
key
self
.
key_path
=
key
...
@@ -111,24 +113,24 @@ class Cert:
...
@@ -111,24 +113,24 @@ class Cert:
self
.
cert
=
self
.
loadVerify
(
f
.
read
().
encode
())
self
.
cert
=
self
.
loadVerify
(
f
.
read
().
encode
())
@
property
@
property
def
prefix
(
self
):
def
prefix
(
self
)
->
str
:
return
utils
.
binFromSubnet
(
subnetFromCert
(
self
.
cert
))
return
utils
.
binFromSubnet
(
subnetFromCert
(
self
.
cert
))
@
property
@
property
def
network
(
self
):
def
network
(
self
)
->
str
:
return
networkFromCa
(
self
.
ca
)
return
networkFromCa
(
self
.
ca
)
@
property
@
property
def
subject_serial
(
self
):
def
subject_serial
(
self
)
->
int
:
return
int
(
self
.
cert
.
get_subject
().
serialNumber
)
return
int
(
self
.
cert
.
get_subject
().
serialNumber
)
@
property
@
property
def
openvpn_args
(
self
):
def
openvpn_args
(
self
)
->
tuple
[
str
,
...]
:
return
(
'--ca'
,
self
.
ca_path
,
return
(
'--ca'
,
self
.
ca_path
,
'--cert'
,
self
.
cert_path
,
'--cert'
,
self
.
cert_path
,
'--key'
,
self
.
key_path
)
'--key'
,
self
.
key_path
)
def
maybeRenew
(
self
,
registry
,
crl
):
def
maybeRenew
(
self
,
registry
,
crl
)
->
int
:
self
.
cert
,
next_renew
=
maybe_renew
(
self
.
cert_path
,
self
.
cert
,
self
.
cert
,
next_renew
=
maybe_renew
(
self
.
cert_path
,
self
.
cert
,
"Certificate"
,
lambda
:
registry
.
renewCertificate
(
self
.
prefix
),
"Certificate"
,
lambda
:
registry
.
renewCertificate
(
self
.
prefix
),
self
.
cert
.
get_serial_number
()
in
crl
)
self
.
cert
.
get_serial_number
()
in
crl
)
...
@@ -232,6 +234,7 @@ class Peer:
...
@@ -232,6 +234,7 @@ class Peer:
serial
=
None
serial
=
None
stop_date
=
float
(
'inf'
)
stop_date
=
float
(
'inf'
)
version
=
b''
version
=
b''
cert
:
crypto
.
X509
def
__init__
(
self
,
prefix
:
str
):
def
__init__
(
self
,
prefix
:
str
):
self
.
prefix
=
prefix
self
.
prefix
=
prefix
...
@@ -249,7 +252,7 @@ class Peer:
...
@@ -249,7 +252,7 @@ class Peer:
def
__lt__
(
self
,
other
):
def
__lt__
(
self
,
other
):
return
self
.
prefix
<
(
other
if
type
(
other
)
is
str
else
other
.
prefix
)
return
self
.
prefix
<
(
other
if
type
(
other
)
is
str
else
other
.
prefix
)
def
hello0
(
self
,
cert
)
:
def
hello0
(
self
,
cert
:
crypto
.
X509
)
->
bytes
:
if
self
.
_hello
<
time
.
time
():
if
self
.
_hello
<
time
.
time
():
try
:
try
:
# Always assume peer is not old, in case it has just upgraded,
# Always assume peer is not old, in case it has just upgraded,
...
@@ -264,7 +267,7 @@ class Peer:
...
@@ -264,7 +267,7 @@ class Peer:
def
hello0Sent
(
self
):
def
hello0Sent
(
self
):
self
.
_hello
=
time
.
time
()
+
60
self
.
_hello
=
time
.
time
()
+
60
def
hello
(
self
,
cert
,
protocol
)
:
def
hello
(
self
,
cert
:
Cert
,
protocol
:
int
)
->
bytes
:
key
=
self
.
_key
=
newHmacSecret
()
key
=
self
.
_key
=
newHmacSecret
()
h
=
encrypt
(
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
self
.
cert
),
h
=
encrypt
(
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
self
.
cert
),
key
)
key
)
...
@@ -274,10 +277,10 @@ class Peer:
...
@@ -274,10 +277,10 @@ class Peer:
return
b''
.
join
((
b'
\
0
\
0
\
0
\
2
'
,
PACKED_PROTOCOL
if
protocol
else
b''
,
return
b''
.
join
((
b'
\
0
\
0
\
0
\
2
'
,
PACKED_PROTOCOL
if
protocol
else
b''
,
h
,
cert
.
sign
(
h
)))
h
,
cert
.
sign
(
h
)))
def
_hmac
(
self
,
msg
)
:
def
_hmac
(
self
,
msg
:
bytes
)
->
bytes
:
return
hmac
.
HMAC
(
self
.
_key
,
msg
,
hashlib
.
sha1
).
digest
()
return
hmac
.
HMAC
(
self
.
_key
,
msg
,
hashlib
.
sha1
).
digest
()
def
newSession
(
self
,
key
:
bytes
,
protocol
):
def
newSession
(
self
,
key
:
bytes
,
protocol
:
int
):
if
key
<=
self
.
_key
:
if
key
<=
self
.
_key
:
raise
NewSessionError
(
self
.
_key
,
key
)
raise
NewSessionError
(
self
.
_key
,
key
)
self
.
_key
=
key
self
.
_key
=
key
...
@@ -285,12 +288,12 @@ class Peer:
...
@@ -285,12 +288,12 @@ class Peer:
self
.
_last
=
None
self
.
_last
=
None
self
.
protocol
=
protocol
self
.
protocol
=
protocol
def
verify
(
self
,
sign
,
data
):
def
verify
(
self
,
sign
:
bytes
,
data
:
bytes
):
crypto
.
verify
(
self
.
cert
,
sign
,
data
,
'sha512'
)
crypto
.
verify
(
self
.
cert
,
sign
,
data
,
'sha512'
)
seqno_struct
=
struct
.
Struct
(
"!L"
)
seqno_struct
=
struct
.
Struct
(
"!L"
)
def
decode
(
self
,
msg
:
bytes
,
_unpack
=
seqno_struct
.
unpack
)
->
bytes
:
def
decode
(
self
,
msg
:
bytes
,
_unpack
=
seqno_struct
.
unpack
)
->
tuple
[
int
,
bytes
,
int
|
None
]
|
bytes
:
assert
isinstance
(
msg
,
bytes
)
assert
isinstance
(
msg
,
bytes
)
seqno
,
=
_unpack
(
msg
[:
4
])
seqno
,
=
_unpack
(
msg
[:
4
])
if
seqno
<=
2
:
if
seqno
<=
2
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment