Mise à jour effectuée, merci de nous signaler tout dysfonctionnement ! | Upgrade done, please let us know about any dysfunction!

network_tools.py 6.15 KB
Newer Older
Moul's avatar
Moul committed
1
from __future__ import unicode_literals
2
3
from ipaddress import ip_address
from json import loads
Moul's avatar
Moul committed
4
5
import socket
import urllib.request
6
import logging
7
from sys import exit, stderr
8
from commandlines import Command
Moul's avatar
Moul committed
9

Moul's avatar
Moul committed
10
11
12
13
14
15
from silkaj.constants import (
    G1_DEFAULT_ENDPOINT,
    G1_TEST_DEFAULT_ENDPOINT,
    CONNECTION_TIMEOUT,
)

Moul's avatar
Moul committed
16

17
def discover_peers(discover):
18
19
20
21
22
23
    """
    From first node, discover his known nodes.
    Remove from know nodes if nodes are down.
    If discover option: scan all network to know all nodes.
        display percentage discovering.
    """
24
    endpoints = parse_endpoints(get_request("network/peers")["peers"])
Moul's avatar
Moul committed
25
26
    if discover:
        print("Discovering network")
27
    for i, endpoint in enumerate(endpoints):
Moul's avatar
Moul committed
28
29
        if discover:
            print("{0:.0f}%".format(i / len(endpoints) * 100))
30
31
        if best_node(endpoint, False) is None:
            endpoints.remove(endpoint)
Moul's avatar
Moul committed
32
        elif discover:
33
            endpoints = recursive_discovering(endpoints, endpoint)
34
    return endpoints
35

Moul's avatar
Moul committed
36

37
def recursive_discovering(endpoints):
38
39
40
41
    """
    Discover recursively new nodes.
    If new node found add it and try to found new node from his known nodes.
    """
42
    news = parse_endpoints(get_request("network/peers")["peers"])
43
    for new in news:
Moul's avatar
Moul committed
44
        if best_node(new, False) is not None and new not in endpoints:
45
46
            endpoints.append(new)
            recursive_discovering(endpoints, new)
47
    return endpoints
Moul's avatar
Moul committed
48

Moul's avatar
Moul committed
49
50
51
52
53
54
55
56

def parse_endpoints(rep):
    """
    endpoints[]{"domain", "ip4", "ip6", "pubkey"}
    list of dictionaries
    reps: raw endpoints
    """
    i, j, endpoints = 0, 0, []
Moul's avatar
Moul committed
57
58
    while i < len(rep):
        if rep[i]["status"] == "UP":
Moul's avatar
Moul committed
59
60
            while j < len(rep[i]["endpoints"]):
                ep = parse_endpoint(rep[i]["endpoints"][j])
Moul's avatar
Moul committed
61
                j += 1
62
63
64
                if ep is None:
                    break
                ep["pubkey"] = rep[i]["pubkey"]
Moul's avatar
Moul committed
65
                endpoints.append(ep)
Moul's avatar
Moul committed
66
67
        i += 1
        j = 0
68
    return endpoints
Moul's avatar
Moul committed
69

Moul's avatar
Moul committed
70

71
72
73
74
75
76
77
78
79
80
81
82
class EndPoint(object):
    __instance = None

    # Try to inheritate this part for all singleton classes
    def __new__(cls):
        if EndPoint.__instance is None:
            EndPoint.__instance = object.__new__(cls)
        return EndPoint.__instance

    def __init__(self):
        cli_args = Command()
        ep = dict()
Moul's avatar
Moul committed
83
        if cli_args.contains_switches("p"):
84
85
86
87
88
            peer = cli_args.get_definition("p")
            if ":" in peer:
                ep["domain"], ep["port"] = peer.rsplit(":", 1)
            else:
                ep["domain"], ep["port"] = peer, "443"
Moul's avatar
Moul committed
89
        else:
Moul's avatar
Moul committed
90
91
92
93
94
95
            ep["domain"], ep["port"] = (
                G1_TEST_DEFAULT_ENDPOINT
                if cli_args.contains_switches("gtest")
                else G1_DEFAULT_ENDPOINT
            )
        if ep["domain"].startswith("[") and ep["domain"].endswith("]"):
96
            ep["domain"] = ep["domain"][1:-1]
97
98
99
        self.ep = ep


Moul's avatar
Moul committed
100
101
102
103
104
105
106
def parse_endpoint(rep):
    """
    rep: raw endpoint, sep: split endpoint
    domain, ip4 or ip6 could miss on raw endpoint
    """
    ep, sep = {}, rep.split(" ")
    if sep[0] == "BASIC_MERKLED_API":
Moul's avatar
Moul committed
107
108
        if check_port(sep[-1]):
            ep["port"] = sep[-1]
Moul's avatar
Moul committed
109
110
111
112
113
114
        if (
            len(sep) == 5
            and check_ip(sep[1]) == 0
            and check_ip(sep[2]) == 4
            and check_ip(sep[3]) == 6
        ):
Moul's avatar
Moul committed
115
116
117
118
119
120
            ep["domain"], ep["ip4"], ep["ip6"] = sep[1], sep[2], sep[3]
        if len(sep) == 4:
            ep = endpoint_type(sep[1], ep)
            ep = endpoint_type(sep[2], ep)
        if len(sep) == 3:
            ep = endpoint_type(sep[1], ep)
121
        return ep
122
    else:
123
        return None
Moul's avatar
Moul committed
124

Moul's avatar
Moul committed
125

Moul's avatar
Moul committed
126
127
def endpoint_type(sep, ep):
    typ = check_ip(sep)
Moul's avatar
Moul committed
128
129
130
131
132
133
    if typ == 0:
        ep["domain"] = sep
    elif typ == 4:
        ep["ip4"] = sep
    elif typ == 6:
        ep["ip6"] = sep
134
    return ep
Moul's avatar
Moul committed
135

Moul's avatar
Moul committed
136

Moul's avatar
Moul committed
137
def check_ip(address):
Moul's avatar
Moul committed
138
    try:
139
        return ip_address(address).version
Moul's avatar
Moul committed
140
141
142
    except:
        return 0

Moul's avatar
Moul committed
143

144
def get_request(path, ep=EndPoint().ep):
Moul's avatar
Moul committed
145
    address = best_node(ep, False)
Moul's avatar
Moul committed
146
147
    if address is None:
        return address
148
    url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
149
150
    if ep["port"] == "443":
        url = "https://" + ep[address] + "/" + path
Moul's avatar
Moul committed
151
    request = urllib.request.Request(url)
152
    response = urllib.request.urlopen(request, timeout=CONNECTION_TIMEOUT)
Moul's avatar
Moul committed
153
    encoding = response.info().get_content_charset("utf8")
154
    return loads(response.read().decode(encoding))
Moul's avatar
Moul committed
155

Moul's avatar
Moul committed
156

157
def post_request(path, postdata, ep=EndPoint().ep):
Moul's avatar
Moul committed
158
    address = best_node(ep, False)
Moul's avatar
Moul committed
159
160
    if address is None:
        return address
Tortue95's avatar
Tortue95 committed
161
162
163
    url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
    if ep["port"] == "443":
        url = "https://" + ep[address] + "/" + path
Moul's avatar
Moul committed
164
    request = urllib.request.Request(url, bytes(postdata, "utf-8"))
Tortue95's avatar
Tortue95 committed
165
    try:
166
        response = urllib.request.urlopen(request, timeout=CONNECTION_TIMEOUT)
Tortue95's avatar
Tortue95 committed
167
    except urllib.error.URLError as e:
168
169
        print(e, file=stderr)
        exit(1)
Moul's avatar
Moul committed
170
    encoding = response.info().get_content_charset("utf8")
171
    return loads(response.read().decode(encoding))
Tortue95's avatar
Tortue95 committed
172

Moul's avatar
Moul committed
173

Moul's avatar
Moul committed
174
175
def best_node(ep, main):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
176
    s.settimeout(CONNECTION_TIMEOUT)
177
    addresses, port = ("domain", "ip6", "ip4"), int(ep["port"])
Moul's avatar
Moul committed
178
179
180
181
    for address in addresses:
        if address in ep:
            try:
                s.connect((ep[address], port))
182
                s.close()
183
                return address
184
            except Exception as e:
Moul's avatar
Moul committed
185
186
187
                logging.debug(
                    "Connection to endpoint %s (%s) failled (%s)" % (ep, address, e)
                )
Moul's avatar
Moul committed
188
    if main:
vincentux's avatar
vincentux committed
189
        print("Wrong node given as argument", file=stderr)
190
        exit(1)
191
    return None
Moul's avatar
Moul committed
192
193


Moul's avatar
Moul committed
194
195
196
197
def check_port(port):
    try:
        port = int(port)
    except:
198
        print("Port must be an integer", file=stderr)
199
        return False
Moul's avatar
Moul committed
200
    if port < 0 or port > 65536:
201
        print("Wrong port number", file=stderr)
202
203
        return False
    return True
204

205

206
207
class HeadBlock(object):
    __instance = None
208

209
    def __new__(cls):
210
211
212
        if HeadBlock.__instance is None:
            HeadBlock.__instance = object.__new__(cls)
        return HeadBlock.__instance
213

214
215
    def __init__(self):
        self.head_block = get_request("blockchain/current")