-
Notifications
You must be signed in to change notification settings - Fork 244
/
tcp_ping.go
106 lines (91 loc) · 2.18 KB
/
tcp_ping.go
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
package mailservers
import (
"context"
"fmt"
"net"
"time"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/status-im/status-go/rtt"
)
type PingQuery struct {
Addresses []string `json:"addresses"`
TimeoutMs int `json:"timeoutMs"`
}
type PingResult struct {
ENode string `json:"address"`
RTTMs *int `json:"rttMs"`
Err *string `json:"error"`
}
func (pr *PingResult) Update(rttMs int, err error) {
if err != nil {
errStr := err.Error()
pr.Err = &errStr
}
if rttMs > 0 {
pr.RTTMs = &rttMs
} else {
pr.RTTMs = nil
}
}
func enodeToAddr(enodeAddr string) (string, error) {
node, err := enode.ParseV4(enodeAddr)
if err != nil {
return "", err
}
var ip4 enr.IPv4
err = node.Load(&ip4)
if err != nil {
return "", err
}
var tcp enr.TCP
err = node.Load(&tcp)
if err != nil {
return "", err
}
return fmt.Sprintf("%s:%d", net.IP(ip4).String(), tcp), nil
}
func parseEnodes(enodes []string) (map[string]*PingResult, []string) {
// parse enode addreses into normal host + port addresses
results := make(map[string]*PingResult, len(enodes))
var toPing []string
for i := range enodes {
addr, err := enodeToAddr(enodes[i])
if err != nil {
// using enode since it's irrelevant but needs to be unique
errStr := err.Error()
results[enodes[i]] = &PingResult{ENode: enodes[i], Err: &errStr}
continue
}
results[addr] = &PingResult{ENode: enodes[i]}
toPing = append(toPing, addr)
}
return results, toPing
}
func mapValues(m map[string]*PingResult) []*PingResult {
rval := make([]*PingResult, 0, len(m))
for _, value := range m {
rval = append(rval, value)
}
return rval
}
func (a *API) Ping(ctx context.Context, pq PingQuery) ([]*PingResult, error) {
timeout := time.Duration(pq.TimeoutMs) * time.Millisecond
// parse enodes into pingable addresses
resultsMap, toPing := parseEnodes(pq.Addresses)
// run the checks concurrently
results, err := rtt.CheckHosts(toPing, timeout)
if err != nil {
return nil, err
}
// set ping results
for i := range results {
r := results[i]
pr := resultsMap[r.Addr]
if pr == nil {
continue
}
pr.Update(r.RTTMs, r.Err)
}
return mapValues(resultsMap), nil
}