blob: 46d7c3813dd6ccaae7322bc3e704344ecf62de2e [file] [log] [blame]
// Copyright 2017 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.
// +build fuchsia
// Package mxnet contains types for talking to Fuchsia's fdio net interface.
package mxnet
import (
"errors"
"math"
"unsafe"
)
// Addr is an IP address.
// It has either len == 4 (if it is IPv4) or len == 16 (IPv6).
type Addr string
// TODO(crawshaw): consider cleaning up these interfaces by migrating to
// an address type that captures everything:
//
// type Address struct {
// Addr [16]byte
// IPv4 bool
// Port uint16
// }
const SockaddrLen = c_sockaddr_in6_len
func DecodeSockaddr(data []byte) (addr Addr, port uint16, err error) {
if len(data) < 2 {
return "", 0, errors.New("mxnet:DecodeSockaddr: data too short")
}
family := uint16(data[0]) | uint16(data[1])<<8
switch family {
case AF_INET:
if len(data) < int(unsafe.Sizeof(c_sockaddr_in{})) {
return "", 0, errors.New("mxnet:DecodeSockaddr: data too short for ipv4")
}
v := (*c_sockaddr_in)(unsafe.Pointer(&data[0]))
port = uint16(data[3]) | uint16(data[2])<<8
if !isZeros(v.sin_addr[:]) {
addr = Addr(v.sin_addr[:])
}
return addr, port, nil
case AF_INET6:
if len(data) < int(unsafe.Sizeof(c_sockaddr_in6{})) {
return "", 0, errors.New("mxnet:DecodeSockaddr: data too short for ipv6")
}
v := (*c_sockaddr_in6)(unsafe.Pointer(&data[0]))
port = uint16(data[3]) | uint16(data[2])<<8
if !isZeros(v.sin6_addr[:]) {
addr = Addr(v.sin6_addr[:])
}
return addr, port, nil
default:
return "", 0, errors.New("mxnet:DecodeSockaddr: unknown family")
}
}
func EncodeSockaddr(dst []byte, addr Addr, port int, zone uint32) (int, error) {
switch len(addr) {
case 0, 4:
if len(dst) < c_sockaddr_in_len {
return 0, errors.New("mxnet: dst too small for c_sockaddr_in")
}
sockaddr := c_sockaddr_in{sin_family: AF_INET}
if port > math.MaxUint16 {
return 0, errors.New("port overflow")
}
sockaddr.sin_port.setPort(uint16(port))
copy(sockaddr.sin_addr[:], addr)
srcb := (*[unsafe.Sizeof(sockaddr)]byte)(unsafe.Pointer(&sockaddr))[:]
return copy(dst, srcb), nil
case 16:
if len(dst) < c_sockaddr_in_len {
return 0, errors.New("mxnet: dst too small for c_sockaddr_in6")
}
sockaddr := c_sockaddr_in6{sin6_family: AF_INET6}
if port > math.MaxUint16 {
return 0, errors.New("port overflow")
}
sockaddr.sin6_port.setPort(uint16(port))
sockaddr.sin6_scope_id = zone
copy(sockaddr.sin6_addr[:], addr)
srcb := (*[unsafe.Sizeof(sockaddr)]byte)(unsafe.Pointer(&sockaddr))[:]
return copy(dst, srcb), nil
default:
return 0, errors.New("mxnet: bad address length")
}
}
const (
c_sockaddr_in_len = int(unsafe.Sizeof(c_sockaddr_in{}))
c_sockaddr_in6_len = int(unsafe.Sizeof(c_sockaddr_in6{}))
)
func (v c_in_port) port() uint16 {
return uint16(v[0])<<8 | uint16(v[1])
}
func (v *c_in_port) setPort(port uint16) {
v[0] = uint8(port >> 8)
v[1] = uint8(port)
}
func isZeros(buf []byte) bool {
for i := 0; i < len(buf); i++ {
if buf[i] != 0 {
return false
}
}
return true
}