blob: e2503afe4408714cb42a1d06d5ce90d1a1aa965a [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"
"strings"
"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, 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 getSocketProvider() zx.Handle {
socketProvider.mu.Lock()
defer socketProvider.mu.Unlock()
if socketProvider.h == zx.HandleInvalid {
c0, c1, err := zx.NewChannel(0)
if err == nil {
err = fdio.ServiceConnect("/svc/"+zxnet.SocketProviderName, zx.Handle(c0))
if 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 strings.HasPrefix(net, "udp") {
sotype = syscall.SOCK_DGRAM
}
var sp zxnet.SocketProviderInterface
// 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 := getSocketProvider(); h.IsValid() {
sp = zxnet.SocketProviderInterface(fidl.ChannelProxy{Channel: zx.Channel(h)})
break
}
time.Sleep(250 * time.Millisecond)
}
code, s, 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 := zxsocket.NewSocket(s)
if sotype == syscall.SOCK_DGRAM {
sock.SetDgram()
}
fd = newFD(sock, family, sotype, net)
if laddr != nil {
addr, port, 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)
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, err := raddr.sockaddr(family)
if err != nil {
return nil, err
}
b := make([]byte, mxnet.SockaddrLen)
addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
if err != nil {
return nil, err
}
b = b[:addrlen]
code, err := fd.sock.Connect(b)
if code == syscall.EINPROGRESS {
obs, err := fd.sock.Wait(mxnet.MXSIO_SIGNAL_OUTGOING|zx.SignalSocketPeerClosed, zx.TimensecInfinite)
if err != nil {
return nil, err
}
if obs&zx.SignalSocketPeerClosed != 0 {
return nil, &zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket"}
}
if obs&mxnet.MXSIO_SIGNAL_OUTGOING != 0 {
// Call connect again to learn the result.
code, err = fd.sock.Connect(b)
} else {
panic("unreachable")
}
}
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 string) (addr mxnet.Addr, portres uint16, err error) {
switch family {
case syscall.AF_INET:
if len(ip) == 0 {
ip = IPv4zero
}
ip4 := ip.To4()
if ip4 == nil {
return "", 0, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
}
return mxnet.Addr(ip4)[:4], uint16(port), nil
case syscall.AF_INET6:
if len(ip) == 0 || ip.Equal(IPv4zero) {
ip = IPv6zero
}
ip6 := ip.To16()
if ip6 == nil {
return "", 0, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
}
return mxnet.Addr(ip6), uint16(port), nil
}
return "", 0, &AddrError{Err: "invalid address family", Addr: ip.String()}
}