blob: 03a12024075a2f298dc059978ae7486fbdea58d3 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 main
import (
"log"
"syscall/zx"
"syscall/zx/mxerror"
"netstack/util"
"fidl/fuchsia/net"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/network/ipv4"
"github.com/google/netstack/tcpip/network/ipv6"
"github.com/google/netstack/tcpip/transport/ping"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
"github.com/google/netstack/waiter"
)
// #cgo CFLAGS: -I${SRCDIR}/../../../../zircon/system/ulib/zxs/include
// #include <lib/zxs/protocol.h>
import "C"
type socketProviderImpl struct {
ns *Netstack
}
func sockProto(typ net.SocketType, protocol net.SocketProtocol) (tcpip.TransportProtocolNumber, error) {
switch typ {
case net.SocketTypeStream:
switch protocol {
case net.SocketProtocolIp, net.SocketProtocolTcp:
return tcp.ProtocolNumber, nil
default:
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported SOCK_STREAM protocol: %d", protocol)
}
case net.SocketTypeDgram:
switch protocol {
case net.SocketProtocolIp, net.SocketProtocolUdp:
return udp.ProtocolNumber, nil
case net.SocketProtocolIcmp:
return ping.ProtocolNumber4, nil
default:
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported SOCK_DGRAM protocol: %d", protocol)
}
}
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported protocol: %d/%d", typ, protocol)
}
func (sp *socketProviderImpl) OpenSocket(d net.SocketDomain, t net.SocketType, p net.SocketProtocol) (zx.Socket, int32, error) {
var netProto tcpip.NetworkProtocolNumber
switch d {
case net.SocketDomainInet:
netProto = ipv4.ProtocolNumber
case net.SocketDomainInet6:
netProto = ipv6.ProtocolNumber
default:
return zx.Socket(zx.HandleInvalid), int32(zx.ErrNotSupported), nil
}
transProto, err := sockProto(t, p)
if err != nil {
return zx.Socket(zx.HandleInvalid), int32(errStatus(err)), nil
}
{
wq := new(waiter.Queue)
ep, err := sp.ns.mu.stack.NewEndpoint(transProto, netProto, wq)
if err != nil {
if debug {
log.Printf("socket: new endpoint: %v", err)
}
return zx.Socket(zx.HandleInvalid), int32(zx.ErrInternal), nil
}
{
_, peerS, err := newIostate(sp.ns, netProto, transProto, wq, ep, false)
if err != nil {
if debug {
log.Printf("socket: new iostate: %v", err)
}
return zx.Socket(zx.HandleInvalid), int32(errStatus(err)), nil
}
return peerS, 0, nil
}
}
}
func (sp *socketProviderImpl) GetAddrInfo(node *string, service *string, hints *net.AddrInfoHints) (net.AddrInfoStatus, uint32, [4]net.AddrInfo, error) {
if hints == nil {
hints = &net.AddrInfoHints{}
}
if hints.SockType == 0 {
hints.SockType = C.SOCK_STREAM
}
if hints.Protocol == 0 {
switch hints.SockType {
case C.SOCK_STREAM:
hints.Protocol = C.IPPROTO_TCP
case C.SOCK_DGRAM:
hints.Protocol = C.IPPROTO_UDP
}
}
transProto, err := sockProto(net.SocketType(hints.SockType), net.SocketProtocol(hints.Protocol))
if err != nil {
if debug {
log.Printf("getaddrinfo: sockProto: %v", err)
}
return net.AddrInfoStatusSystemError, 0, [4]net.AddrInfo{}, nil
}
var port uint16
if service != nil && *service != "" {
if port, err = serviceLookup(*service, transProto); err != nil {
if debug {
log.Printf("getaddrinfo: serviceLookup: %v", err)
}
return net.AddrInfoStatusSystemError, 0, [4]net.AddrInfo{}, nil
}
}
var addrs []tcpip.Address
switch {
case node == nil || *node == "":
addrs = append(addrs, "\x00\x00\x00\x00")
case *node == "localhost" || *node == sp.ns.getNodeName():
switch hints.Family {
case C.AF_UNSPEC:
addrs = append(addrs, ipv4Loopback, ipv6Loopback)
case C.AF_INET:
addrs = append(addrs, ipv4Loopback)
case C.AF_INET6:
addrs = append(addrs, ipv6Loopback)
default:
return net.AddrInfoStatusSystemError, 0, [4]net.AddrInfo{}, nil
}
default:
if addrs, err = sp.ns.dnsClient.LookupIP(*node); err != nil {
addrs = append(addrs, util.Parse(*node))
if debug {
log.Printf("getaddrinfo: addr=%v, err=%v", addrs, err)
}
}
}
if len(addrs) == 0 || len(addrs[0]) == 0 {
return net.AddrInfoStatusNoName, 0, [4]net.AddrInfo{}, nil
}
// Reply up to 4 addresses.
num := uint32(0)
var results [4]net.AddrInfo
for _, addr := range addrs {
ai := net.AddrInfo{
Flags: 0,
SockType: hints.SockType,
Protocol: hints.Protocol,
Port: port,
}
switch len(addr) {
case 4:
if hints.Family != C.AF_UNSPEC && hints.Family != C.AF_INET {
continue
}
ai.Family = C.AF_INET
ai.Addr.Len = uint32(copy(ai.Addr.Val[:], addr))
case 16:
if hints.Family != C.AF_UNSPEC && hints.Family != C.AF_INET6 {
continue
}
ai.Family = C.AF_INET6
ai.Addr.Len = uint32(copy(ai.Addr.Val[:], addr))
default:
if debug {
log.Printf("getaddrinfo: len(addr)=%d, wrong size", len(addr))
}
// TODO: failing to resolve is a valid reply. fill out retval
return net.AddrInfoStatusSystemError, 0, results, nil
}
results[num] = ai
num++
if int(num) == len(results) {
break
}
}
return net.AddrInfoStatusOk, num, results, nil
}