blob: 4af25bb23a400259cdff5cc0880b8ffa1ca4fe9e [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, 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
}
type socketProviderInfo struct {
mu sync.Mutex
h zx.Handle
}
var socketProvider socketProviderInfo
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/fuchsia.net.LegacySocketProvider", 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 len(net) >= 3 && net[:3] == "udp" {
sotype = syscall.SOCK_DGRAM
}
proto := syscall.IPPROTO_IP
var sock *zxsocket.Socket
// 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++ {
c := zx.Channel(getSocketProvider())
if c.Handle().IsValid() {
sp := zxnet.LegacySocketProviderInterface(fidl.Proxy{Channel: c})
s, _, err := sp.OpenSocket(zxnet.SocketDomain(family), zxnet.SocketType(sotype), zxnet.SocketProtocol(proto))
if err == nil {
sock = zxsocket.NewSocket(s)
break
}
}
time.Sleep(250 * time.Millisecond)
}
if err != nil {
return nil, err
}
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 {
if err := fd.pfd.Handle.(*zxsocket.Socket).Bind(addr, port); err != nil {
return nil, err
}
}
if raddr == nil {
switch sotype {
case syscall.SOCK_STREAM:
if err := fd.pfd.Handle.(*zxsocket.Socket).ListenStream(listenerBacklog); err != nil {
return nil, err
}
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
}
if err := fd.pfd.Handle.(*zxsocket.Socket).Connect(addr, port); err != nil {
return nil, err
}
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()}
}