forked from libp2p/go-libp2p
-
Notifications
You must be signed in to change notification settings - Fork 0
/
addrs.go
151 lines (130 loc) · 3.23 KB
/
addrs.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package websocket
import (
"fmt"
"net"
"net/url"
"strconv"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// addrWrapper is an implementation of net.Addr for WebSocket.
type addrWrapper struct {
*url.URL
}
var _ net.Addr = addrWrapper{}
// Network returns the network type for a WebSocket, "websocket".
func (a addrWrapper) Network() string {
return "websocket"
}
func ConvertWebsocketMultiaddrToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
url, err := parseMultiaddr(maddr)
if err != nil {
return nil, err
}
return addrWrapper{URL: url}, nil
}
func ParseWebsocketNetAddr(a net.Addr) (ma.Multiaddr, error) {
wsa, ok := a.(addrWrapper)
if !ok {
return nil, fmt.Errorf("not a websocket address")
}
var (
tcpma ma.Multiaddr
err error
port int
host = wsa.Hostname()
)
// Get the port
if portStr := wsa.Port(); portStr != "" {
port, err = strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("failed to parse port '%q': %s", portStr, err)
}
} else {
return nil, fmt.Errorf("invalid port in url: '%q'", wsa.URL)
}
// NOTE: Ignoring IPv6 zones...
// Detect if host is IP address or DNS
if ip := net.ParseIP(host); ip != nil {
// Assume IP address
tcpma, err = manet.FromNetAddr(&net.TCPAddr{
IP: ip,
Port: port,
})
if err != nil {
return nil, err
}
} else {
// Assume DNS name
tcpma, err = ma.NewMultiaddr(fmt.Sprintf("/dns/%s/tcp/%d", host, port))
if err != nil {
return nil, err
}
}
wsma, err := ma.NewMultiaddr("/" + wsa.Scheme)
if err != nil {
return nil, err
}
return tcpma.Encapsulate(wsma), nil
}
func parseMultiaddr(maddr ma.Multiaddr) (*url.URL, error) {
parsed, err := parseWebsocketMultiaddr(maddr)
if err != nil {
return nil, err
}
scheme := "ws"
if parsed.isWSS {
scheme = "wss"
}
network, host, err := manet.DialArgs(parsed.restMultiaddr)
if err != nil {
return nil, err
}
switch network {
case "tcp", "tcp4", "tcp6":
default:
return nil, fmt.Errorf("unsupported websocket network %s", network)
}
return &url.URL{
Scheme: scheme,
Host: host,
}, nil
}
type parsedWebsocketMultiaddr struct {
isWSS bool
// sni is the SNI value for the TLS handshake, and for setting HTTP Host header
sni *ma.Component
// the rest of the multiaddr before the /tls/sni/example.com/ws or /ws or /wss
restMultiaddr ma.Multiaddr
}
func parseWebsocketMultiaddr(a ma.Multiaddr) (parsedWebsocketMultiaddr, error) {
out := parsedWebsocketMultiaddr{}
// First check if we have a WSS component. If so we'll canonicalize it into a /tls/ws
withoutWss := a.Decapsulate(wssComponent)
if !withoutWss.Equal(a) {
a = withoutWss.Encapsulate(tlsWsComponent)
}
// Remove the ws component
withoutWs := a.Decapsulate(wsComponent)
if withoutWs.Equal(a) {
return out, fmt.Errorf("not a websocket multiaddr")
}
rest := withoutWs
// If this is not a wss then withoutWs is the rest of the multiaddr
out.restMultiaddr = withoutWs
for {
var head *ma.Component
rest, head = ma.SplitLast(rest)
if head == nil || rest == nil {
break
}
if head.Protocol().Code == ma.P_SNI {
out.sni = head
} else if head.Protocol().Code == ma.P_TLS {
out.isWSS = true
out.restMultiaddr = rest
break
}
}
return out, nil
}