Skip to content

Commit 2da13ec

Browse files
committed
Merge go-multiaddr-net
2 parents 192ac0f + 0ae1c1c commit 2da13ec

File tree

10 files changed

+2092
-0
lines changed

10 files changed

+2092
-0
lines changed

net/convert.go

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package manet
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"path/filepath"
7+
"runtime"
8+
"strings"
9+
10+
ma "github.com/multiformats/go-multiaddr"
11+
)
12+
13+
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")
14+
var errNotIP = fmt.Errorf("multiaddr does not start with an IP address")
15+
16+
// FromNetAddr converts a net.Addr type to a Multiaddr.
17+
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
18+
return defaultCodecs.FromNetAddr(a)
19+
}
20+
21+
// FromNetAddr converts a net.Addr to Multiaddress.
22+
func (cm *CodecMap) FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
23+
if a == nil {
24+
return nil, fmt.Errorf("nil multiaddr")
25+
}
26+
p, err := cm.getAddrParser(a.Network())
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return p(a)
32+
}
33+
34+
// ToNetAddr converts a Multiaddr to a net.Addr
35+
// Must be ThinWaist. acceptable protocol stacks are:
36+
// /ip{4,6}/{tcp, udp}
37+
func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
38+
return defaultCodecs.ToNetAddr(maddr)
39+
}
40+
41+
// ToNetAddr converts a Multiaddress to a standard net.Addr.
42+
func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
43+
protos := maddr.Protocols()
44+
final := protos[len(protos)-1]
45+
46+
p, err := cm.getMaddrParser(final.Name)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
return p(maddr)
52+
}
53+
54+
func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
55+
network, host, err := DialArgs(maddr)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
switch network {
61+
case "tcp", "tcp4", "tcp6":
62+
return net.ResolveTCPAddr(network, host)
63+
case "udp", "udp4", "udp6":
64+
return net.ResolveUDPAddr(network, host)
65+
case "ip", "ip4", "ip6":
66+
return net.ResolveIPAddr(network, host)
67+
case "unix":
68+
return net.ResolveUnixAddr(network, host)
69+
}
70+
71+
return nil, fmt.Errorf("network not supported: %s", network)
72+
}
73+
74+
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
75+
switch {
76+
case ip.To4() != nil:
77+
return ma.NewComponent("ip4", ip.String())
78+
case ip.To16() != nil:
79+
ip6, err := ma.NewComponent("ip6", ip.String())
80+
if err != nil {
81+
return nil, err
82+
}
83+
if zone == "" {
84+
return ip6, nil
85+
} else {
86+
zone, err := ma.NewComponent("ip6zone", zone)
87+
if err != nil {
88+
return nil, err
89+
}
90+
return zone.Encapsulate(ip6), nil
91+
}
92+
default:
93+
return nil, errIncorrectNetAddr
94+
}
95+
}
96+
97+
// FromIP converts a net.IP type to a Multiaddr.
98+
func FromIP(ip net.IP) (ma.Multiaddr, error) {
99+
return FromIPAndZone(ip, "")
100+
}
101+
102+
// ToIP converts a Multiaddr to a net.IP when possible
103+
func ToIP(addr ma.Multiaddr) (net.IP, error) {
104+
var ip net.IP
105+
ma.ForEach(addr, func(c ma.Component) bool {
106+
switch c.Protocol().Code {
107+
case ma.P_IP6ZONE:
108+
// we can't return these anyways.
109+
return true
110+
case ma.P_IP6, ma.P_IP4:
111+
ip = net.IP(c.RawValue())
112+
return false
113+
}
114+
return false
115+
})
116+
if ip == nil {
117+
return nil, errNotIP
118+
}
119+
return ip, nil
120+
}
121+
122+
// DialArgs is a convenience function that returns network and address as
123+
// expected by net.Dial. See https://godoc.org/net#Dial for an overview of
124+
// possible return values (we do not support the unixpacket ones yet). Unix
125+
// addresses do not, at present, compose.
126+
func DialArgs(m ma.Multiaddr) (string, string, error) {
127+
zone, network, ip, port, hostname, err := dialArgComponents(m)
128+
if err != nil {
129+
return "", "", err
130+
}
131+
132+
// If we have a hostname (dns*), we don't want any fancy ipv6 formatting
133+
// logic (zone, brackets, etc.).
134+
if hostname {
135+
switch network {
136+
case "ip", "ip4", "ip6":
137+
return network, ip, nil
138+
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
139+
return network, ip + ":" + port, nil
140+
}
141+
// Hostname is only true when network is one of the above.
142+
panic("unreachable")
143+
}
144+
145+
switch network {
146+
case "ip6":
147+
if zone != "" {
148+
ip += "%" + zone
149+
}
150+
fallthrough
151+
case "ip4":
152+
return network, ip, nil
153+
case "tcp4", "udp4":
154+
return network, ip + ":" + port, nil
155+
case "tcp6", "udp6":
156+
if zone != "" {
157+
ip += "%" + zone
158+
}
159+
return network, "[" + ip + "]" + ":" + port, nil
160+
case "unix":
161+
if runtime.GOOS == "windows" {
162+
// convert /c:/... to c:\...
163+
ip = filepath.FromSlash(strings.TrimLeft(ip, "/"))
164+
}
165+
return network, ip, nil
166+
default:
167+
return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
168+
}
169+
}
170+
171+
// dialArgComponents extracts the raw pieces used in dialing a Multiaddr
172+
func dialArgComponents(m ma.Multiaddr) (zone, network, ip, port string, hostname bool, err error) {
173+
ma.ForEach(m, func(c ma.Component) bool {
174+
switch network {
175+
case "":
176+
switch c.Protocol().Code {
177+
case ma.P_IP6ZONE:
178+
if zone != "" {
179+
err = fmt.Errorf("%s has multiple zones", m)
180+
return false
181+
}
182+
zone = c.Value()
183+
return true
184+
case ma.P_IP6:
185+
network = "ip6"
186+
ip = c.Value()
187+
return true
188+
case ma.P_IP4:
189+
if zone != "" {
190+
err = fmt.Errorf("%s has ip4 with zone", m)
191+
return false
192+
}
193+
network = "ip4"
194+
ip = c.Value()
195+
return true
196+
case ma.P_DNS:
197+
network = "ip"
198+
hostname = true
199+
ip = c.Value()
200+
return true
201+
case ma.P_DNS4:
202+
network = "ip4"
203+
hostname = true
204+
ip = c.Value()
205+
return true
206+
case ma.P_DNS6:
207+
network = "ip6"
208+
hostname = true
209+
ip = c.Value()
210+
return true
211+
case ma.P_UNIX:
212+
network = "unix"
213+
ip = c.Value()
214+
return false
215+
}
216+
case "ip":
217+
switch c.Protocol().Code {
218+
case ma.P_UDP:
219+
network = "udp"
220+
case ma.P_TCP:
221+
network = "tcp"
222+
default:
223+
return false
224+
}
225+
port = c.Value()
226+
case "ip4":
227+
switch c.Protocol().Code {
228+
case ma.P_UDP:
229+
network = "udp4"
230+
case ma.P_TCP:
231+
network = "tcp4"
232+
default:
233+
return false
234+
}
235+
port = c.Value()
236+
case "ip6":
237+
switch c.Protocol().Code {
238+
case ma.P_UDP:
239+
network = "udp6"
240+
case ma.P_TCP:
241+
network = "tcp6"
242+
default:
243+
return false
244+
}
245+
port = c.Value()
246+
}
247+
// Done.
248+
return false
249+
})
250+
return
251+
}
252+
253+
func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) {
254+
ac, ok := a.(*net.TCPAddr)
255+
if !ok {
256+
return nil, errIncorrectNetAddr
257+
}
258+
259+
// Get IP Addr
260+
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
261+
if err != nil {
262+
return nil, errIncorrectNetAddr
263+
}
264+
265+
// Get TCP Addr
266+
tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
267+
if err != nil {
268+
return nil, errIncorrectNetAddr
269+
}
270+
271+
// Encapsulate
272+
return ipm.Encapsulate(tcpm), nil
273+
}
274+
275+
func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) {
276+
ac, ok := a.(*net.UDPAddr)
277+
if !ok {
278+
return nil, errIncorrectNetAddr
279+
}
280+
281+
// Get IP Addr
282+
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
283+
if err != nil {
284+
return nil, errIncorrectNetAddr
285+
}
286+
287+
// Get UDP Addr
288+
udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
289+
if err != nil {
290+
return nil, errIncorrectNetAddr
291+
}
292+
293+
// Encapsulate
294+
return ipm.Encapsulate(udpm), nil
295+
}
296+
297+
func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) {
298+
ac, ok := a.(*net.IPAddr)
299+
if !ok {
300+
return nil, errIncorrectNetAddr
301+
}
302+
return FromIPAndZone(ac.IP, ac.Zone)
303+
}
304+
305+
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
306+
ac, ok := a.(*net.IPNet)
307+
if !ok {
308+
return nil, errIncorrectNetAddr
309+
}
310+
return FromIP(ac.IP)
311+
}
312+
313+
func parseUnixNetAddr(a net.Addr) (ma.Multiaddr, error) {
314+
ac, ok := a.(*net.UnixAddr)
315+
if !ok {
316+
return nil, errIncorrectNetAddr
317+
}
318+
319+
path := ac.Name
320+
if runtime.GOOS == "windows" {
321+
// Convert c:\foobar\... to c:/foobar/...
322+
path = filepath.ToSlash(path)
323+
}
324+
if len(path) == 0 || path[0] != '/' {
325+
// convert "" and "c:/..." to "/..."
326+
path = "/" + path
327+
}
328+
329+
return ma.NewComponent("unix", path)
330+
}

0 commit comments

Comments
 (0)