-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathaddrs.go
175 lines (152 loc) · 3.82 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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package websocket
import (
"fmt"
"net"
"net/url"
"strconv"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// Addr is an implementation of net.Addr for WebSocket.
type Addr struct {
*url.URL
}
var _ net.Addr = (*Addr)(nil)
// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string {
return "websocket"
}
// NewAddr creates an Addr with `ws` scheme (insecure).
//
// Deprecated. Use NewAddrWithScheme.
func NewAddr(host string) *Addr {
// Older versions of the transport only supported insecure connections (i.e.
// WS instead of WSS). Assume that is the case here.
return NewAddrWithScheme(host, false)
}
// NewAddrWithScheme creates a new Addr using the given host string. isSecure
// should be true for WSS connections and false for WS.
func NewAddrWithScheme(host string, isSecure bool) *Addr {
scheme := "ws"
if isSecure {
scheme = "wss"
}
return &Addr{
URL: &url.URL{
Scheme: scheme,
Host: host,
},
}
}
func ConvertWebsocketMultiaddrToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
url, err := parseMultiaddr(maddr)
if err != nil {
return nil, err
}
return &Addr{URL: url}, nil
}
func ParseWebsocketNetAddr(a net.Addr) (ma.Multiaddr, error) {
wsa, ok := a.(*Addr)
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
}