summaryrefslogtreecommitdiff
path: root/generate_tox_bootstrap.py
blob: 3198cd41259d1954f69283b4e13ce279b4e04c46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/python3
# pip3 install jinja2 requests

import datetime
import jinja2
import json
import requests
import socket

json_url = 'https://nodes.tox.chat/json'

tox_bootstrap_template = """
/*
 * Generated with generate_tox_bootstrap.py by GDR!
 * from {{ json_url }} on {{ now }}
 */
struct bootstrap_node {
    char *address;
    uint16_t port;
    uint8_t key[32];
} bootstrap_nodes[] = {
{% for node in nodes %}
    {
        "{{ node.ipv4 }}",
        {{ node.port }},
        {
            {{ node.public_key|toxtoc }}
        }
    },
{% endfor %}
};

struct bootstrap_node tcp_relays[] = {
{% for node in relays %}
    {
        "{{ node.ipv4 }}",
        {{ node.port }},
        {
            {{ node.public_key|toxtoc }}
        }
    },
{% endfor %}
};
"""

def toxtoc(value):
    """
    A Jinja2 filter to turn a ToxID into two lines of C bytes
    """
    def get_16_bytes(value):
        """
        Generate 1 line of C code - 16 bytes
        @param value a hex string of length 32 (32 hex chars)
        """
        if len(value) != 32:
            raise ValueError('%r is not a 32-char string')

        rv = ""

        for i in range(16):
            rv += "0x%s" % value[2*i : 2*i+2]
            if i < 15:
                rv += ", "

        return rv

    rv = get_16_bytes(value[:32]) + \
         ",\n" + (12*' ') + \
         get_16_bytes(value[32:])

    return rv

class Loader(jinja2.BaseLoader):
    def get_source(self, environment, template):
        return tox_bootstrap_template, 'tox_bootstrap_template', True

if __name__ == "__main__":
    r = requests.get(json_url)
    data = r.json()
    if 'nodes' not in data:
        raise ValueError('nodes element not in JSON')

    nodes = []
    tcp_relays = []

    for elem in data['nodes']:
        node = {}
        if 'ipv4' not in elem or 'port' not in elem or 'public_key' not in elem:
            print("SKIPPING", elem)
            continue
        
        if len(elem['public_key']) != 64:
            print("Bad public key %s, skipping!" % elem['public_key'])
            continue

        node['port'] = int(elem['port'])
        node['public_key'] = elem['public_key']

        try:
            socket.inet_aton(elem['ipv4'])
            node['ipv4'] = elem['ipv4']
        except socket.error:
            # IPv4 is not numeric, let's try resolving
            try:
                print("RESOLVING", elem['ipv4'])
                node['ipv4'] = socket.gethostbyname(elem['ipv4'])
            except socket.error:
                print("Could not resolve ipv4: %s, skipping!" % elem['ipv4'])
                continue

        if 'status_udp' in elem and elem['status_udp']:
            nodes.append(node)

        if 'tcp_ports' in elem and elem['tcp_ports'] and \
           'status_tcp' in elem and elem['status_tcp']:
            for port in elem['tcp_ports']:
                relay = dict(node)
                try:
                    relay['port'] = int(port)
                except ValueError:
                    continue

                tcp_relays.append(relay)

    env = jinja2.Environment(loader=Loader())
    env.filters['toxtoc'] = toxtoc
    template = env.get_template('tox_bootstrap_template')
    tox_bootstrap_h = template.render(nodes=nodes, now=datetime.datetime.now(), json_url=json_url, relays=tcp_relays)
    open('tox_bootstrap.h', 'w').write(tox_bootstrap_h)