Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,39 @@ func main() {

## Name Resolution

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

This library will automatically configure the `net.DefaultResolver`
from the standard library to use the `Dial` function from this library.
You just need the following import somewhere:
### getaddrinfo

The `sock_getaddrinfo` host function is used to implement name resolution.
This requires WasmEdge, or a WasmEdge compatible WASI layer
(e.g. [wasi-go](http://github.com/stealthrocket/wasi-go)).

When using this method, the standard library resolver **will not work**. You
_cannot_ use `net.DefaultResolver`, `net.LookupIP`, etc. with this approach
because the standard library does not allow us to patch it with an alternative
implementation.

Note that `sock_getaddrinfo` may block!

### Pure Go Resolver

The pure Go name resolver is not currently enabled for GOOS=wasip1.

The following series of CLs will change this: https://go-review.googlesource.com/c/go/+/500576.
This will hopefully land in Go v1.22 in ~February 2024.

If you're using a version of Go that has the CL's included, you can
instruct this library to use the pure Go resolver by including the
`purego` build tag.

The library will then automatically configure the `net.DefaultResolver`.
All you need is the following import somewhere in your application:

```go
import _ "github.com/stealthrocket/net"
```

You should then be able to use the lookup functions from the standard
library (e.g. `net.LookupIP(host)`).

Note that name resolution currently depends on the following series of CLs:
https://go-review.googlesource.com/c/go/+/500576
2 changes: 0 additions & 2 deletions dial_wasip1.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
)

func init() {
net.DefaultResolver.Dial = DialContext

if t, ok := http.DefaultTransport.(*http.Transport); ok {
t.DialContext = DialContext
}
Expand Down
78 changes: 37 additions & 41 deletions lookup_wasip1.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,63 @@ package net
import (
"fmt"
"net"

"github.com/stealthrocket/net/syscall"
)

func lookupAddr(context, network, address string) (net.Addr, error) {
var hints syscall.AddrInfo
switch network {
case "tcp", "tcp4", "tcp6":
hints.SocketType = syscall.SOCK_STREAM
hints.Protocol = syscall.IPPROTO_TCP
case "udp", "udp4", "udp6":
hints.SocketType = syscall.SOCK_DGRAM
hints.Protocol = syscall.IPPROTO_UDP
case "unix", "unixgram":
return &net.UnixAddr{Name: address, Net: network}, nil
default:
return nil, fmt.Errorf("not implemented: %s", network)
}
host, portstr, err := net.SplitHostPort(address)
if err != nil {
return nil, err
switch network {
case "tcp", "udp":
hints.Family = syscall.AF_UNSPEC
case "tcp4", "udp4":
hints.Family = syscall.AF_INET
case "tcp6", "udp6":
hints.Family = syscall.AF_INET6
}
port, err := net.LookupPort(network, portstr)
hostname, service, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
if host == "" {
if context == "listen" {
switch network {
case "tcp", "tcp4":
return &net.TCPAddr{IP: net.IPv4zero, Port: port}, nil
case "tcp6":
return &net.TCPAddr{IP: net.IPv6zero, Port: port}, nil
}
}
return nil, fmt.Errorf("invalid address %q for %s", address, context)
if context == "listen" && hostname == "" {
hints.Flags |= syscall.AI_PASSIVE
}
ips, err := net.LookupIP(host)

results := make([]syscall.AddrInfo, 16)
n, err := syscall.Getaddrinfo(hostname, service, hints, results)
if err != nil {
return nil, err
}
if network == "tcp" || network == "tcp4" {
for _, ip := range ips {
if len(ip) == net.IPv4len {
return &net.TCPAddr{IP: ip, Port: port}, nil
}
results = results[:n]
for _, r := range results {
var ip net.IP
var port int
switch a := r.Address.(type) {
case *syscall.SockaddrInet4:
ip = a.Addr[:]
port = a.Port
case *syscall.SockaddrInet6:
ip = a.Addr[:]
port = a.Port
}
}
if network == "tcp" || network == "tcp6" {
for _, ip := range ips {
if len(ip) == net.IPv6len {
return &net.TCPAddr{IP: ip, Port: port}, nil
}
}
}
if network == "udp" || network == "udp4" {
for _, ip := range ips {
if len(ip) == net.IPv4len {
return &net.UDPAddr{IP: ip, Port: port}, nil
}
}
}
if network == "udp" || network == "udp6" {
for _, ip := range ips {
if len(ip) == net.IPv6len {
return &net.UDPAddr{IP: ip, Port: port}, nil
}
switch network {
case "tcp", "tcp4", "tcp6":
return &net.TCPAddr{IP: ip, Port: port}, nil
case "udp", "udp4", "udp6":
return &net.UDPAddr{IP: ip, Port: port}, nil
}
}
return nil, fmt.Errorf("cannot listen on %q", host)
return nil, fmt.Errorf("lookup failed: %q", address)
}
75 changes: 75 additions & 0 deletions lookup_wasip1_purego.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//go:build wasip1 && purego

package net

import (
"fmt"
"net"
)

func init() {
net.DefaultResolver.Dial = DialContext
}

func lookupAddr(context, network, address string) (net.Addr, error) {
switch network {
case "tcp", "tcp4", "tcp6":
case "udp", "udp4", "udp6":
case "unix", "unixgram":
return &net.UnixAddr{Name: address, Net: network}, nil
default:
return nil, fmt.Errorf("not implemented: %s", network)
}
hostname, service, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
port, err := net.LookupPort(network, service)
if err != nil {
return nil, err
}
if hostname == "" {
if context == "listen" {
switch network {
case "tcp", "tcp4":
return &net.TCPAddr{IP: net.IPv4zero, Port: port}, nil
case "tcp6":
return &net.TCPAddr{IP: net.IPv6zero, Port: port}, nil
}
}
return nil, fmt.Errorf("invalid address %q for %s", address, context)
}
ips, err := net.LookupIP(hostname)
if err != nil {
return nil, err
}
if network == "tcp" || network == "tcp4" {
for _, ip := range ips {
if len(ip) == net.IPv4len {
return &net.TCPAddr{IP: ip, Port: port}, nil
}
}
}
if network == "tcp" || network == "tcp6" {
for _, ip := range ips {
if len(ip) == net.IPv6len {
return &net.TCPAddr{IP: ip, Port: port}, nil
}
}
}
if network == "udp" || network == "udp4" {
for _, ip := range ips {
if len(ip) == net.IPv4len {
return &net.UDPAddr{IP: ip, Port: port}, nil
}
}
}
if network == "udp" || network == "udp6" {
for _, ip := range ips {
if len(ip) == net.IPv6len {
return &net.UDPAddr{IP: ip, Port: port}, nil
}
}
}
return nil, fmt.Errorf("lookup failed: %q", address)
}
Loading