diff --git a/README.md b/README.md index fe09a27..1067e46 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Forked from the code for http://www.fc00.org (http://h.fc00.org on Hyperboria). ## Sending your view of the network -This code reads a map of known nodes from `y.yakamo.org:3000/current` (reachable over yggdrasil). In order to display an accurate map of the network, we need your help. If you run a yggdrasil node, plase send your network view using the [send-view.py](https://github.com/yakamok/Niflheim-api/blob/master/send-view.py) script. +This code reads a map of known nodes from `y.yakamo.org:3000/current` (reachable over yggdrasil). You may alternatively generate your own view of the network by running [a crawler script](scripts/crawl-dht.py), but this may take some time (figuring out how to run it and use the results is left as an exercise to the user). ## Web server ```bash diff --git a/scripts/crawl-dht.py b/scripts/crawl-dht.py new file mode 100644 index 0000000..b5e9306 --- /dev/null +++ b/scripts/crawl-dht.py @@ -0,0 +1,75 @@ +import json +import socket +import sys +import time + +#gives the option to get data from an external server instead and send that +#if no options given it will default to localhost instead +if len(sys.argv) == 3: + host_port = (sys.argv[1], int(sys.argv[2])) +else: + host_port = ('localhost', 9001) + +def getDHTPingRequest(key, coords, target=None): + if target: + return '{{"keepalive":true, "request":"dhtPing", "box_pub_key":"{}", "coords":"{}", "target":"{}"}}'.format(key, coords, target) + else: + return '{{"keepalive":true, "request":"dhtPing", "box_pub_key":"{}", "coords":"{}"}}'.format(key, coords) + +def doRequest(req): + try: + ygg = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ygg.connect(host_port) + ygg.send(req) + data = json.loads(ygg.recv(1024*15)) + return data + except: + return None + +visited = dict() # Add nodes after a successful lookup response +rumored = dict() # Add rumors about nodes to ping +timedout = dict() +def handleResponse(address, info, data): + global visited + global rumored + global timedout + timedout[str(address)] = {'box_pub_key':str(info['box_pub_key']), 'coords':str(info['coords'])} + if not data: return + if 'response' not in data: return + if 'nodes' not in data['response']: return + for addr,rumor in data['response']['nodes'].iteritems(): + if addr in visited: continue + rumored[addr] = rumor + if address not in visited: + # TODO? remove this, it's debug output that happens to be in the same format as yakamo's "current" json file + now = time.time() + visited[str(address)] = {'box_pub_key':str(info['box_pub_key']), 'coords':str(info['coords']), 'time':now} + if address in timedout: del timedout[address] + if len(visited) > 1: sys.stdout.write(",\n") + sys.stdout.write('"{}": ["{}", {}]'.format(address, info['coords'], int(now))) + sys.stdout.flush() +# End handleResponse + +# Get self info +selfInfo = doRequest('{"keepalive":true, "request":"getSelf"}') + +# Initialize dicts of visited/rumored nodes +for k,v in selfInfo['response']['self'].iteritems(): rumored[k] = v + +# Loop over rumored nodes and ping them, adding to visited if they respond +print '{"yggnodes": {' +while len(rumored) > 0: + for k,v in rumored.iteritems(): + handleResponse(k, v, doRequest(getDHTPingRequest(v['box_pub_key'], v['coords']))) + # These next two are imperfect workarounds to deal with old kad nodes + handleResponse(k, v, doRequest(getDHTPingRequest(v['box_pub_key'], v['coords'], '0'*128))) + handleResponse(k, v, doRequest(getDHTPingRequest(v['box_pub_key'], v['coords'], 'f'*128))) + break + del rumored[k] +print '\n}}' +#End + +# TODO do something with the results + +#print visited +#print timedout diff --git a/web/graphPlotter.py b/web/graphPlotter.py index 53c882d..078c99b 100644 --- a/web/graphPlotter.py +++ b/web/graphPlotter.py @@ -8,10 +8,10 @@ def position_nodes(nodes, edges): G = pgv.AGraph(strict=True, directed=False, size='10!') for n in nodes.values(): - G.add_node(n.coords, label=n.ip, version=n.version) + G.add_node(n.ip, label=n.label, coords=n.coords) for e in edges: - G.add_edge(e.a.coords, e.b.coords, len=1.0) + G.add_edge(e.a.ip, e.b.ip, len=1.0) G.layout(prog='neato', args='-Gepsilon=0.0001 -Gmaxiter=100000') @@ -66,7 +66,7 @@ def get_graph_json(G): 'id': n.name, 'label': name if name else n.attr['label'], 'name': name, - 'version': n.attr['version'], + 'coords': n.attr['coords'], 'x': float(pos[0]), 'y': float(pos[1]), 'color': _gradient_color(neighbor_ratio, [(100, 100, 100), (0, 0, 0)]), diff --git a/web/static/network.js b/web/static/network.js index 235f430..21568a9 100644 --- a/web/static/network.js +++ b/web/static/network.js @@ -174,7 +174,7 @@ function showNodeInfo(node) { '

' + node.label + '

' + '' + node.id + '
' + '
' + - 'Version: ' + node.version + '
' + + 'Coords: ' + node.coords + '
' + 'Peers: ' + node.peers.length + '
' + 'Centrality: ' + node.centrality + '
' + '' + diff --git a/web/templates/about.html b/web/templates/about.html index f6f4b4c..d179e57 100644 --- a/web/templates/about.html +++ b/web/templates/about.html @@ -8,7 +8,7 @@

Network map

-

The network page has a map of Yggdrasil's spanning tree as it is now. The map is not complete since it is hard/impossible to get a full picture of the network, and it only includes the minimum subset of links needed to construct the spanning tree. The known nodes and tree coordinates are taken from Yakamo's API. You can submit your node's view of the network by periodically running send-view.py.

+

The network page has a map of Yggdrasil's spanning tree as it is now. The map is not complete since it is hard/impossible to get a full picture of the network, and it only includes the minimum subset of links needed to construct the spanning tree. The known nodes and tree coordinates are taken from Yakamo's API.