blob: 3f67a1ddd5f44e6a24e3fd82cf9a38cf717fd382 [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"context"
"errors"
"sync"
"syscall"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/fidl"
"syscall/zx/mxnet"
zxnet "syscall/zx/net"
"syscall/zx/zxsocket"
"time"
)
func (p *ipStackCapabilities) probe() {
p.ipv4Enabled = true
p.ipv6Enabled = true
p.ipv4MappedIPv6Enabled = true
}
// A sockaddr represents a TCP, UDP, IP or Unix network endpoint
// address that can be converted into an fdio sockaddr message.
type sockaddr interface {
Addr
family() int
isWildcard() bool
sockaddr(family int) (addr mxnet.Addr, port uint16, zone uint32, err error)
}
func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
switch net[len(net)-1] {
case '4':
return syscall.AF_INET, false
case '6':
return syscall.AF_INET6, true
}
if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
if supportsIPv4map() || !supportsIPv4() {
return syscall.AF_INET6, false
}
if laddr == nil {
return syscall.AF_INET, false
}
return laddr.family(), false
}
if (laddr == nil || laddr.family() == syscall.AF_INET) &&
(raddr == nil || raddr.family() == syscall.AF_INET) {
return syscall.AF_INET, false
}
return syscall.AF_INET6, false
}
var socketProvider struct {
mu sync.Mutex
h zx.Handle
}
func getProvider() zx.Handle {
socketProvider.mu.Lock()
defer socketProvider.mu.Unlock()
if socketProvider.h == zx.HandleInvalid {
if c0, c1, err := zx.NewChannel(0); err == nil {
if err := fdio.ServiceConnect("/svc/"+zxnet.ProviderName, zx.Handle(c0)); err == nil {
socketProvider.h = zx.Handle(c1)
}
}
}
return socketProvider.h
}
func dialFuchsia(ctx context.Context, net string, laddr, raddr sockaddr) (fd *netFD, err error) {
family, _ := favoriteAddrFamily(net, laddr, raddr, "dial")
sotype := syscall.SOCK_STREAM
if stringsHasPrefix(net, "udp") {
sotype = syscall.SOCK_DGRAM
}
var sp zxnet.ProviderInterface
// Wait for the network stack to publish the socket device.
// See similar logic in zircon/system/ulib/fdio/bsdsocket.c.
for i := 0; i < 40; i++ {
if h := getProvider(); h.IsValid() {
sp = zxnet.ProviderInterface(fidl.ChannelProxy{Channel: zx.Channel(h)})
break
}
time.Sleep(250 * time.Millisecond)
}
code, control, err := sp.Socket(int16(family), int16(sotype), syscall.IPPROTO_IP)
if err != nil {
return nil, err
}
if code != 0 {
return nil, syscall.Errno(code)
}
sock, err := zxsocket.NewSocket(control)
if err != nil {
return nil, err
}
fd = newFD(sock, family, sotype, net)
if laddr != nil {
addr, port, zone, err := laddr.sockaddr(family)
if err != nil {
return nil, err
}
if addr != "" || port != 0 {
b := make([]byte, mxnet.SockaddrLen)
addrlen, err := mxnet.EncodeSockaddr(b, addr, port, zone)
if err != nil {
return nil, err
}
b = b[:addrlen]
if code, err := fd.sock.Bind(b); err != nil {
return nil, err
} else if code != 0 {
return nil, syscall.Errno(code)
}
}
if raddr == nil {
switch sotype {
case syscall.SOCK_STREAM:
code, err := fd.sock.Listen(int16(listenerBacklog()))
if err != nil {
return nil, err
}
if code != 0 {
return nil, syscall.Errno(code)
}
case syscall.SOCK_DGRAM:
return nil, errors.New("net: TODO listen datagram")
}
}
}
if raddr != nil {
addr, port, zone, err := raddr.sockaddr(family)
if err != nil {
return nil, err
}
b := make([]byte, mxnet.SockaddrLen)
addrlen, err := mxnet.EncodeSockaddr(b, addr, port, zone)
if err != nil {
return nil, err
}
b = b[:addrlen]
code, err := fd.sock.Connect(b)
if code == syscall.EINPROGRESS {
if obs, err := fd.sock.Wait(mxnet.MXSIO_SIGNAL_OUTGOING|zx.SignalSocketPeerClosed, zx.TimensecInfinite); err != nil {
return nil, err
} else if obs&zx.SignalSocketPeerClosed != 0 {
return nil, &zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket"}
} else if obs&mxnet.MXSIO_SIGNAL_OUTGOING != 0 {
// fallthrough
} else {
panic("unreachable")
}
// Call connect again to learn the result.
code, err = fd.sock.Connect(b)
}
if err != nil {
return nil, err
}
if code != 0 {
return nil, syscall.Errno(code)
}
fd.isConnected = true
}
fd.setAddr()
return fd, nil
}
func ipToSockaddr(family int, ip IP, port int, zone_str string) (addr mxnet.Addr, portres uint16, zone uint32, err error) {
switch family {
case syscall.AF_INET:
if len(ip) == 0 {
ip = IPv4zero
}
ip4 := ip.To4()
if ip4 == nil {
return "", 0, 0, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
}
return mxnet.Addr(ip4)[:4], uint16(port), 0, nil
case syscall.AF_INET6:
if len(ip) == 0 || ip.Equal(IPv4zero) {
ip = IPv6zero
}
ip6 := ip.To16()
if ip6 == nil {
return "", 0, 0, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
}
return mxnet.Addr(ip6), uint16(port), uint32(zoneCache.index(zone_str)), nil
}
return "", 0, 0, &AddrError{Err: "invalid address family", Addr: ip.String()}
}