#!/usr/bin/env python3 USERLENGTH = 50 import ipaddress import random import sys import etcd import ungleichapi import json import datetime from flask import Flask, abort, request, Response from flask_restful import Resource, Api from flask_restful import reqparse app = Flask(__name__) api = Api(app) def get_random_ip(network): net = ipaddress.IPv6Network(network) addr_offset = random.randrange(2**64) addr = net[0] + addr_offset return addr class Level(Resource): points = 0 def test(self): pass class Ping6(Level): points = 10 def test(self): """ ping6 -c3 """ class Game(object): def __init__(self, name, etcdclient, etcbase="/ungleichgame/v1"): self.client = etcdclient self.app = Flask(name) self.app.add_url_rule('/', 'highscore', self.highscore) self.app.add_url_rule('/highscore', 'highscore', self.highscore) self.app.add_url_rule("/register", 'register', self.register, methods=['POST']) # etcd paths are below here self.etcbase = etcbase self.userbase = "{}/user".format(self.etcbase) def read_etcd(self, path, recursive=False): try: data = self.client.read(path, recursive=recursive) except etcd.EtcdKeyNotFound: return None except Exception: abort(Response(status=400, response="Error connecting to etcd")) return data def get_highscore(self, username=None): """ Returns a dict['username'] = points """ all_users = {} highscore = {} print("getting high") if username: path = "{}/{}".format(self.userbase, username) user = self.read_etcd(path) if user: all_users[username] = user else: path = "{}/".format(self.userbase) users = self.read_etcd(path, recursive=True) print("reading from {}".format(path)) if users: for child in users.children: print("adding user {} {} = {}".format(child, child.key, child.value)) all_users[child.key] = child.value for k, v in all_users.items(): # Ignore all kind of errors - just add the ones that work try: highscore[k] = json.loads(v)['points'] print("f?") except Exception as e: print(e) return highscore def highscore(self): point_list = self.get_highscore() res = [] if not point_list: return Response("No winners yet!") for k, v in point_list.items(): res.append("

{} has {} points

".format(k, v)) return Response("\n".join(res)) def require_args(self, *args): parser = reqparse.RequestParser() for arg in args: parser.add_argument(arg, required=True) return parser.parse_args() def register(self): args = self.require_args("network", "user") # Needs to be fixed with ungleich-otp username=args['user'] try: net = ipaddress.IPv6Network(args['network']) network = args['network'] except Exception as e: return Response(status=400, response="Cannot register network {}: {}".format(network, e)) if not net.prefixlen == 64: return Response(status=400, response="{} mask is not /64 - please use a /64 network".format(net)) self.client.write("/ungleichgame/v1/{}/network".format(username), network) data = self.client.read("/ungleichgame/v1/{}/network".format(username)) return json.dumps("All good, go to /level/1 to start with level 1! - {}".format(data.value)) @app.route("/level/1", methods=['GET', 'POST']) # post for username def get_ip_address(): parser = reqparse.RequestParser() parser.add_argument('user', required=True) args = parser.parse_args() # Needs to be fixed with ungleich-otp username=args['user'] if request.method == 'GET': return Response(""" This is an easy level - just register any /64 network that you fully control. After submission the game server will generate a random IPv6 address in this network. """) client = etcd.Client(port=2379) try: data = client.read("/ungleichgame/v1/{}/network".format(username)) # FIXME: differentiate keynotfound and other errors except Exception as e: return Response(status=400, response="Cannot read your network, try registering first (error: {})".format(e)) return Response("data={}".format(data.value)) address = get_random_ip(data.value) # FIXME: catch errors client.write("/ungleichgame/v1/{}/address".format(username), address) return Response("Your IPv6 address for this game is {}. Make it pingable and post to /level/1/result".format(address)) @app.route("/level/2", methods=['GET', 'POST']) # post for username def pingme(): parser = reqparse.RequestParser() parser.add_argument('user', required=True) args = parser.parse_args() # Needs to be fixed with ungleich-otp username=args['user'] if request.method == 'GET': return Response(""" Proof that you can really control the network that you submitted: - Setup the IPv6 address to be ping6 able globally - POST to this address when it is configured """) if request.method == 'POST': try: data = client.read("/ungleichgame/v1/{}/address".format(username), address) except Exception as e: return Response(status=400, response=""" You need to register a network before trying to be reachable. Please go back to Level 1 for registering your network. """) return Response("something good") if __name__ == '__main__': net_base = "2a0a:e5c1:{:x}::/64" net_offset = random.randrange(0xffff) net = ipaddress.IPv6Network(net_base.format(net_offset)) username = 'nico{}'.format(net_offset) print("{} has {}".format(username, net)) g = Game(__name__, etcd.Client(port=2379)) g.app.run(port='5002') # app.run(port='5002')