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

network_tools.py 4.66 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
from sys import exit, stderr
Moul's avatar
Moul committed
7

8
CONNECTION_TIMEOUT = 10
Moul's avatar
Moul committed
9

10
11
12
13
14
15
16
def discover_peers(ep, discover):
    """
    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.
    """
17
    endpoints = parse_endpoints(get_request(ep, "network/peers")["peers"])
Moul's avatar
Moul committed
18
19
    if discover:
        print("Discovering network")
20
    for i, ep in enumerate(endpoints):
Moul's avatar
Moul committed
21
22
23
24
25
26
        if discover:
            print("{0:.0f}%".format(i / len(endpoints) * 100))
        if best_node(ep, 0) is None:
            endpoints.remove(ep)
        elif discover:
            endpoints = recursive_discovering(endpoints, ep)
27
    return endpoints
28

Moul's avatar
Moul committed
29

30
31
32
33
34
def recursive_discovering(endpoints, ep):
    """
    Discover recursively new nodes.
    If new node found add it and try to found new node from his known nodes.
    """
35
    news = parse_endpoints(get_request(ep, "network/peers")["peers"])
36
37
38
39
    for new in news:
        if best_node(new, 0) is not None and new not in endpoints:
            endpoints.append(new)
            recursive_discovering(endpoints, new)
40
    return endpoints
Moul's avatar
Moul committed
41

Moul's avatar
Moul committed
42
43
44
45
46
47
48
49
50
51
52
53

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

Moul's avatar
Moul committed
63

Moul's avatar
Moul committed
64
65
66
67
68
69
70
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
71
72
        if check_port(sep[-1]):
            ep["port"] = sep[-1]
Moul's avatar
Moul committed
73
74
75
76
77
78
79
        if len(sep) == 5 and check_ip(sep[1]) == 0 and check_ip(sep[2]) == 4 and check_ip(sep[3]) == 6:
            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)
80
        return ep
81
    else:
82
        return None
Moul's avatar
Moul committed
83

Moul's avatar
Moul committed
84

Moul's avatar
Moul committed
85
86
def endpoint_type(sep, ep):
    typ = check_ip(sep)
Moul's avatar
Moul committed
87
88
89
90
91
92
    if typ == 0:
        ep["domain"] = sep
    elif typ == 4:
        ep["ip4"] = sep
    elif typ == 6:
        ep["ip6"] = sep
93
    return ep
Moul's avatar
Moul committed
94

Moul's avatar
Moul committed
95

Moul's avatar
Moul committed
96
def check_ip(address):
Moul's avatar
Moul committed
97
    try:
98
        return ip_address(address).version
Moul's avatar
Moul committed
99
100
101
    except:
        return 0

Moul's avatar
Moul committed
102

103
def get_request(ep, path):
104
    address = best_node(ep, 0)
Moul's avatar
Moul committed
105
106
    if address is None:
        return address
107
    url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
108
109
    if ep["port"] == "443":
        url = "https://" + ep[address] + "/" + path
Moul's avatar
Moul committed
110
    request = urllib.request.Request(url)
111
    response = urllib.request.urlopen(request, timeout=CONNECTION_TIMEOUT)
Moul's avatar
Moul committed
112
    encoding = response.info().get_content_charset('utf8')
113
    return loads(response.read().decode(encoding))
Moul's avatar
Moul committed
114

Moul's avatar
Moul committed
115

Tortue95's avatar
Tortue95 committed
116
117
def post_request(ep, path, postdata):
    address = best_node(ep, 0)
Moul's avatar
Moul committed
118
119
    if address is None:
        return address
Tortue95's avatar
Tortue95 committed
120
121
122
    url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
    if ep["port"] == "443":
        url = "https://" + ep[address] + "/" + path
123
    request = urllib.request.Request(url, bytes(postdata, 'utf-8'), timeout=CONNECTION_TIMEOUT)
Tortue95's avatar
Tortue95 committed
124
125
126
    try:
        response = urllib.request.urlopen(request)
    except urllib.error.URLError as e:
127
128
        print(e, file=stderr)
        exit(1)
Tortue95's avatar
Tortue95 committed
129
    encoding = response.info().get_content_charset('utf8')
130
    return loads(response.read().decode(encoding))
Tortue95's avatar
Tortue95 committed
131

Moul's avatar
Moul committed
132

Moul's avatar
Moul committed
133
134
def best_node(ep, main):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
135
    s.settimeout(CONNECTION_TIMEOUT)
Moul's avatar
Moul committed
136
137
138
139
140
    addresses, port = {"ip6", "ip4", "domain"}, int(ep["port"])
    for address in addresses:
        if address in ep:
            try:
                s.connect((ep[address], port))
141
                s.close()
142
                return address
Moul's avatar
Moul committed
143
144
145
            except:
                pass
    if main:
vincentux's avatar
vincentux committed
146
        print("Wrong node given as argument", file=stderr)
147
        exit(1)
148
    return None
Moul's avatar
Moul committed
149
150


Moul's avatar
Moul committed
151
152
153
154
def check_port(port):
    try:
        port = int(port)
    except:
155
156
        print("Port must be an integer", file=stderr)
        exit(1)
Moul's avatar
Moul committed
157
    if (port < 0 or port > 65536):
158
159
        print("Wrong port number", file=stderr)
        exit(1)
160
    return 1
161

162

163
164
def get_current_block(ep):
    return get_request(ep, "blockchain/current")