From 1f91fd23341f7da6ccf7f9547815321497fb12ac Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Mon, 16 Dec 2019 23:48:48 +0100 Subject: [PATCH] added reachabilty check --- server.py | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 244 insertions(+), 12 deletions(-) diff --git a/server.py b/server.py index cb37d9c..cd7f1e1 100644 --- a/server.py +++ b/server.py @@ -1,11 +1,16 @@ from flask import Flask, request, redirect, url_for import os import sys +import ipaddress +import random +import subprocess app = Flask(__name__) # 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 = """ @@ -20,17 +25,20 @@ IPv6 games? If you want to register, simply submit your name below. Obviously, your name and IPv6 address will be publicly recorded. +

You are player: {player}

Register

Your name:
+

Afterwards register your network +and then make your random ip reachable.

Interested in playing IPv6 games are...

Open Source

@@ -41,49 +49,273 @@ you can find the source code on """ +NET_PAGE = """ + + +Register your network + + +

Register your network

+For playing IPv6 games, you should have a free /64 network to play around with. +Use the format 2001:db8::/64. + +

Register

+
+Your network: + +
+ +""" + +ADDR_CHECK_PAGE = """ + + +Check your network + + +

Check your network

+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 ping6-able, then we believe that +you really control it. + +

You are: {player} + +

+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: + +

+{output}
+
+ +(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(): address_list = [] - if os.path.isdir(dir): - address_list = os.listdir(dir) + if os.path.isdir(PLAYER_DIR): + address_list = os.listdir(PLAYER_DIR) 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): - fname = os.path.join(dir, address) + fname = os.path.join(PLAYER_DIR, address) with open(fname, "r") as fd: name = fd.readlines() return name[0].strip() def save_player(name, address): - fname = os.path.join(dir, address) + fname = os.path.join(PLAYER_DIR, address) with open(fname, "w+") as fd: 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' ]) def main(): + address = request.remote_addr + if request.method == 'GET': html = [] + player = identify_user(address) + for address in addresses(): name = get_player_name(address) - html.append("
  • {}@{}".format(name,address)) + network = get_player_network(address) + random_ip = get_player_ip(address) - return REGULAR_PAGE.format("\n".join(html)) + html.append("
  • {}@{}: net: {} random_ip: {}".format(name,address, network, random_ip)) + + return REGULAR_PAGE.format(player_list="\n".join(html), player=player) else: name = request.form['name'] - address = request.remote_addr save_player(name, address) return redirect(url_for('main')) if __name__ == '__main__': - if not os.path.isdir(dir): - print("Please create player directory {} before starting".format(dir)) - sys.exit(1) + if not os.path.isdir(DB_DIR): + os.mkdir(DB_DIR) app.run(host="::", debug=True)