added reachabilty check

This commit is contained in:
Nico Schottelius 2019-12-16 23:48:48 +01:00
parent 5407ba8987
commit 1f91fd2334

256
server.py
View file

@ -1,11 +1,16 @@
from flask import Flask, request, redirect, url_for from flask import Flask, request, redirect, url_for
import os import os
import sys import sys
import ipaddress
import random
import subprocess
app = Flask(__name__) app = Flask(__name__)
# where player are stored # where player are stored
dir= "./players" DB_DIR= "./db"
PLAYER_DIR = os.path.join(DB_DIR, "players")
NET_DIR = os.path.join(DB_DIR, "networks")
REGULAR_PAGE = """ REGULAR_PAGE = """
<html> <html>
@ -20,17 +25,20 @@ IPv6 games?
If you want to register, simply submit your name below. If you want to register, simply submit your name below.
Obviously, your name and IPv6 address will be publicly recorded. Obviously, your name and IPv6 address will be publicly recorded.
<p>You are player: {player}
<h2>Register</h2> <h2>Register</h2>
<form action="/" method=post> <form action="/" method=post>
Your name: <input type="text" name="name"> Your name: <input type="text" name="name">
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
<p>Afterwards <a href="/net">register your network</a>
and then <a href="/check_addr">make your random ip reachable</a>.
<h2>Interested in playing IPv6 games are... </h2> <h2>Interested in playing IPv6 games are... </h2>
<ul> <ul>
{} {player_list}
</ul> </ul>
<h2>Open Source</h2> <h2>Open Source</h2>
@ -41,49 +49,273 @@ you can find the source code on
</html> </html>
""" """
NET_PAGE = """
<html>
<head>
<title>Register your network</title>
</head>
<body>
<h1>Register your network</h1>
For playing IPv6 games, you should have a free /64 network to play around with.
Use the format <b>2001:db8::/64</b>.
<h2>Register</h2>
<form action="/net" method=post>
Your network: <input type="text" name="network">
<input type="submit" value="Submit">
</form>
"""
ADDR_CHECK_PAGE = """
<html>
<head>
<title>Check your network</title>
</head>
<body>
<h1>Check your network</h1>
To be able to prove that you control a network, we selected a random IP in it.
Only if you can make that IPv6 address <b>ping6-able</b>, then we believe that
you really control it.
<p>You are: {player}
<p>
Your registered random IPv6 address in your network is {random_ip}.
The result of the check is: {returncode} (0 mean s reachable, everything else is an error)
and the output of the ping command is:
<pre>
{output}
</pre>
(we should really convert to jinja2 now)
"""
NO_NETWORK = """Sorry, this is not an IPv6 network"""
NO_SLASH_64 = """Sorry, this is not a /64 IPv6 network"""
NO_USER = """Sorry, you are not a user, you cannot check your IPv6 address. Register first"""
def player_exists_at_address(address):
fname = os.path.join(PLAYER_DIR, address)
exists = False
if os.path.exists(fname):
exists = True
return exists
def addresses(): def addresses():
address_list = [] address_list = []
if os.path.isdir(dir): if os.path.isdir(PLAYER_DIR):
address_list = os.listdir(dir) address_list = os.listdir(PLAYER_DIR)
return address_list return address_list
def networks():
network_list = []
if os.path.isdir(NET_DIR):
network_list = os.listdir(NET_DIR)
return network_list
def get_player_from_network(network):
fname = os.path.join(NET_DIR, network, "address")
with open(fname,"r") as fd:
address = fd.readlines()[0].strip()
return address
def get_player_network(address):
network = None
for net in networks():
fname = os.path.join(NET_DIR, net, "address")
with open(fname,"r") as fd:
net_address = fd.readlines()[0].strip()
if net_address == address:
network = net
return network
def get_player_ip(address):
random_ip = None
network = get_player_network(address)
if not network:
return None
fname = os.path.join(NET_DIR, network, "random_ip")
if os.path.exists(fname):
with open(fname,"r") as fd:
random_ip = fd.readlines()[0].strip()
return random_ip
def get_player_name(address): def get_player_name(address):
fname = os.path.join(dir, address) fname = os.path.join(PLAYER_DIR, address)
with open(fname, "r") as fd: with open(fname, "r") as fd:
name = fd.readlines() name = fd.readlines()
return name[0].strip() return name[0].strip()
def save_player(name, address): def save_player(name, address):
fname = os.path.join(dir, address) fname = os.path.join(PLAYER_DIR, address)
with open(fname, "w+") as fd: with open(fname, "w+") as fd:
fd.write("{}\n".format(name)) fd.write("{}\n".format(name))
def is_v6_network(network):
is_network = True
try:
net = ipaddress.IPv6Network(network)
except:
is_network = False
return is_network
def is_v6_slash_64(network):
is_64 = True
try:
net = ipaddress.IPv6Network(network)
print("{} {}".format(net, network))
if not net.prefixlen == 64:
is_64 = False
except:
is_64 = False
return is_64
def get_random_ip(network):
net = ipaddress.IPv6Network(network)
addr_offset = random.randrange(2**64)
addr = net[0] + addr_offset
return addr
def save_network(address, network):
if not is_v6_network(network):
return NO_NETWORK
if not is_v6_slash_64(network):
return NO_SLASH_64
if not os.path.isdir(NET_DIR):
os.mkdir(NET_DIR)
# get filesystem friendly name
net_name = str(ipaddress.IPv6Network(network)[0])
dirname = os.path.join(NET_DIR, net_name)
if not os.path.isdir(dirname):
os.mkdir(dirname)
fname = os.path.join(dirname, "address")
with open(fname, "w+") as fd:
fd.write("{}\n".format(address))
fname = os.path.join(dirname, "network")
with open(fname, "w+") as fd:
fd.write("{}\n".format(network))
random_ip = get_random_ip(network)
print("ip={}".format(random_ip))
fname = os.path.join(dirname, "random_ip")
with open(fname, "w+") as fd:
fd.write("{}\n".format(random_ip))
return redirect(url_for('net'))
def identify_user(address):
""" Find user by connecting IPv6 address """
player = None
# found the player
if player_exists_at_address(address):
return address
# check whether player is connecting from a registered network
v6_addr = ipaddress.IPv6Address(address)
for net in networks():
v6_net = ipaddress.IPv6Network("{}/64".format(net))
if v6_addr in v6_net:
player = get_player_from_network(net)
break
return player
@app.route('/check_addr', methods=['GET'])
def check_addr():
address = request.remote_addr
player = identify_user(address)
if not player:
return NO_USER
random_ip = get_player_ip(player)
s = subprocess.run(["ping", "-c1", random_ip], capture_output=True)
return ADDR_CHECK_PAGE.format(player=player,
random_ip=random_ip,
returncode=s.returncode,
output=s.stdout.decode("utf-8"))
@app.route('/net', methods=['GET', 'POST' ])
def net():
if request.method == 'GET':
return NET_PAGE
else:
network = request.form['network']
address = request.remote_addr
return save_network(address, network)
@app.route('/', methods=['GET', 'POST' ]) @app.route('/', methods=['GET', 'POST' ])
def main(): def main():
address = request.remote_addr
if request.method == 'GET': if request.method == 'GET':
html = [] html = []
player = identify_user(address)
for address in addresses(): for address in addresses():
name = get_player_name(address) name = get_player_name(address)
html.append("<li> {}@{}".format(name,address)) network = get_player_network(address)
random_ip = get_player_ip(address)
return REGULAR_PAGE.format("\n".join(html)) html.append("<li> {}@{}: net: {} random_ip: {}".format(name,address, network, random_ip))
return REGULAR_PAGE.format(player_list="\n".join(html), player=player)
else: else:
name = request.form['name'] name = request.form['name']
address = request.remote_addr
save_player(name, address) save_player(name, address)
return redirect(url_for('main')) return redirect(url_for('main'))
if __name__ == '__main__': if __name__ == '__main__':
if not os.path.isdir(dir): if not os.path.isdir(DB_DIR):
print("Please create player directory {} before starting".format(dir)) os.mkdir(DB_DIR)
sys.exit(1)
app.run(host="::", debug=True) app.run(host="::", debug=True)