Skip to content

Commit 7641d2d

Browse files
authored
Merge pull request #1 from stealthrocket/getaddrinfo
Name resolution via sock_getaddrinfo
2 parents 0ba7399 + 61adfa5 commit 7641d2d

File tree

5 files changed

+277
-53
lines changed

5 files changed

+277
-53
lines changed

README.md

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,39 @@ func main() {
8484

8585
## Name Resolution
8686

87-
Go has a built-in name resolver that sidesteps CGO (e.g. `getaddrinfo(3)`)
88-
calls.
87+
There are two methods available for resolving a set of IP addresses
88+
for a hostname.
8989

90-
This library will automatically configure the `net.DefaultResolver`
91-
from the standard library to use the `Dial` function from this library.
92-
You just need the following import somewhere:
90+
### getaddrinfo
91+
92+
The `sock_getaddrinfo` host function is used to implement name resolution.
93+
This requires WasmEdge, or a WasmEdge compatible WASI layer
94+
(e.g. [wasi-go](http://github.com/stealthrocket/wasi-go)).
95+
96+
When using this method, the standard library resolver **will not work**. You
97+
_cannot_ use `net.DefaultResolver`, `net.LookupIP`, etc. with this approach
98+
because the standard library does not allow us to patch it with an alternative
99+
implementation.
100+
101+
Note that `sock_getaddrinfo` may block!
102+
103+
### Pure Go Resolver
104+
105+
The pure Go name resolver is not currently enabled for GOOS=wasip1.
106+
107+
The following series of CLs will change this: https://go-review.googlesource.com/c/go/+/500576.
108+
This will hopefully land in Go v1.22 in ~February 2024.
109+
110+
If you're using a version of Go that has the CL's included, you can
111+
instruct this library to use the pure Go resolver by including the
112+
`purego` build tag.
113+
114+
The library will then automatically configure the `net.DefaultResolver`.
115+
All you need is the following import somewhere in your application:
93116

94117
```go
95118
import _ "github.com/stealthrocket/net"
96119
```
97120

98121
You should then be able to use the lookup functions from the standard
99122
library (e.g. `net.LookupIP(host)`).
100-
101-
Note that name resolution currently depends on the following series of CLs:
102-
https://go-review.googlesource.com/c/go/+/500576

dial_wasip1.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
)
1212

1313
func init() {
14-
net.DefaultResolver.Dial = DialContext
15-
1614
if t, ok := http.DefaultTransport.(*http.Transport); ok {
1715
t.DialContext = DialContext
1816
}

lookup_wasip1.go

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,63 @@ package net
33
import (
44
"fmt"
55
"net"
6+
7+
"github.com/stealthrocket/net/syscall"
68
)
79

810
func lookupAddr(context, network, address string) (net.Addr, error) {
11+
var hints syscall.AddrInfo
912
switch network {
1013
case "tcp", "tcp4", "tcp6":
14+
hints.SocketType = syscall.SOCK_STREAM
15+
hints.Protocol = syscall.IPPROTO_TCP
1116
case "udp", "udp4", "udp6":
17+
hints.SocketType = syscall.SOCK_DGRAM
18+
hints.Protocol = syscall.IPPROTO_UDP
1219
case "unix", "unixgram":
1320
return &net.UnixAddr{Name: address, Net: network}, nil
1421
default:
1522
return nil, fmt.Errorf("not implemented: %s", network)
1623
}
17-
host, portstr, err := net.SplitHostPort(address)
18-
if err != nil {
19-
return nil, err
24+
switch network {
25+
case "tcp", "udp":
26+
hints.Family = syscall.AF_UNSPEC
27+
case "tcp4", "udp4":
28+
hints.Family = syscall.AF_INET
29+
case "tcp6", "udp6":
30+
hints.Family = syscall.AF_INET6
2031
}
21-
port, err := net.LookupPort(network, portstr)
32+
hostname, service, err := net.SplitHostPort(address)
2233
if err != nil {
2334
return nil, err
2435
}
25-
if host == "" {
26-
if context == "listen" {
27-
switch network {
28-
case "tcp", "tcp4":
29-
return &net.TCPAddr{IP: net.IPv4zero, Port: port}, nil
30-
case "tcp6":
31-
return &net.TCPAddr{IP: net.IPv6zero, Port: port}, nil
32-
}
33-
}
34-
return nil, fmt.Errorf("invalid address %q for %s", address, context)
36+
if context == "listen" && hostname == "" {
37+
hints.Flags |= syscall.AI_PASSIVE
3538
}
36-
ips, err := net.LookupIP(host)
39+
40+
results := make([]syscall.AddrInfo, 16)
41+
n, err := syscall.Getaddrinfo(hostname, service, hints, results)
3742
if err != nil {
3843
return nil, err
3944
}
40-
if network == "tcp" || network == "tcp4" {
41-
for _, ip := range ips {
42-
if len(ip) == net.IPv4len {
43-
return &net.TCPAddr{IP: ip, Port: port}, nil
44-
}
45+
results = results[:n]
46+
for _, r := range results {
47+
var ip net.IP
48+
var port int
49+
switch a := r.Address.(type) {
50+
case *syscall.SockaddrInet4:
51+
ip = a.Addr[:]
52+
port = a.Port
53+
case *syscall.SockaddrInet6:
54+
ip = a.Addr[:]
55+
port = a.Port
4556
}
46-
}
47-
if network == "tcp" || network == "tcp6" {
48-
for _, ip := range ips {
49-
if len(ip) == net.IPv6len {
50-
return &net.TCPAddr{IP: ip, Port: port}, nil
51-
}
52-
}
53-
}
54-
if network == "udp" || network == "udp4" {
55-
for _, ip := range ips {
56-
if len(ip) == net.IPv4len {
57-
return &net.UDPAddr{IP: ip, Port: port}, nil
58-
}
59-
}
60-
}
61-
if network == "udp" || network == "udp6" {
62-
for _, ip := range ips {
63-
if len(ip) == net.IPv6len {
64-
return &net.UDPAddr{IP: ip, Port: port}, nil
65-
}
57+
switch network {
58+
case "tcp", "tcp4", "tcp6":
59+
return &net.TCPAddr{IP: ip, Port: port}, nil
60+
case "udp", "udp4", "udp6":
61+
return &net.UDPAddr{IP: ip, Port: port}, nil
6662
}
6763
}
68-
return nil, fmt.Errorf("cannot listen on %q", host)
64+
return nil, fmt.Errorf("lookup failed: %q", address)
6965
}

lookup_wasip1_purego.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//go:build wasip1 && purego
2+
3+
package net
4+
5+
import (
6+
"fmt"
7+
"net"
8+
)
9+
10+
func init() {
11+
net.DefaultResolver.Dial = DialContext
12+
}
13+
14+
func lookupAddr(context, network, address string) (net.Addr, error) {
15+
switch network {
16+
case "tcp", "tcp4", "tcp6":
17+
case "udp", "udp4", "udp6":
18+
case "unix", "unixgram":
19+
return &net.UnixAddr{Name: address, Net: network}, nil
20+
default:
21+
return nil, fmt.Errorf("not implemented: %s", network)
22+
}
23+
hostname, service, err := net.SplitHostPort(address)
24+
if err != nil {
25+
return nil, err
26+
}
27+
port, err := net.LookupPort(network, service)
28+
if err != nil {
29+
return nil, err
30+
}
31+
if hostname == "" {
32+
if context == "listen" {
33+
switch network {
34+
case "tcp", "tcp4":
35+
return &net.TCPAddr{IP: net.IPv4zero, Port: port}, nil
36+
case "tcp6":
37+
return &net.TCPAddr{IP: net.IPv6zero, Port: port}, nil
38+
}
39+
}
40+
return nil, fmt.Errorf("invalid address %q for %s", address, context)
41+
}
42+
ips, err := net.LookupIP(hostname)
43+
if err != nil {
44+
return nil, err
45+
}
46+
if network == "tcp" || network == "tcp4" {
47+
for _, ip := range ips {
48+
if len(ip) == net.IPv4len {
49+
return &net.TCPAddr{IP: ip, Port: port}, nil
50+
}
51+
}
52+
}
53+
if network == "tcp" || network == "tcp6" {
54+
for _, ip := range ips {
55+
if len(ip) == net.IPv6len {
56+
return &net.TCPAddr{IP: ip, Port: port}, nil
57+
}
58+
}
59+
}
60+
if network == "udp" || network == "udp4" {
61+
for _, ip := range ips {
62+
if len(ip) == net.IPv4len {
63+
return &net.UDPAddr{IP: ip, Port: port}, nil
64+
}
65+
}
66+
}
67+
if network == "udp" || network == "udp6" {
68+
for _, ip := range ips {
69+
if len(ip) == net.IPv6len {
70+
return &net.UDPAddr{IP: ip, Port: port}, nil
71+
}
72+
}
73+
}
74+
return nil, fmt.Errorf("lookup failed: %q", address)
75+
}

0 commit comments

Comments
 (0)