blob: 48eb1b0a88817e28b2ae20c0914143b30747243b [file] [log] [blame]
// Copyright 2009 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.
// netpkg.IP, TCP, UDP socket address lookup
package dns
import (
netpkg "net"
"strconv"
)
// These are probed by the real package net.
const (
supportsIPv6 = true
supportsIPv4map = true
)
func firstFavoriteAddr(filter func(netpkg.IP) netpkg.IP, addrs []string) (addr netpkg.IP) {
if filter == nil {
// We'll take any netpkg.IP address, but since the dialing code
// does not yet try multiple addresses, prefer to use
// an netpkg.IPv4 address if possible. This is especially relevant
// if localhost resolves to [ipv6-localhost, ipv4-localhost].
// Too much code assumes localhost == ipv4-localhost.
addr = firstSupportedAddr(ipv4only, addrs)
if addr == nil {
addr = firstSupportedAddr(anyaddr, addrs)
}
} else {
addr = firstSupportedAddr(filter, addrs)
}
return
}
func firstSupportedAddr(filter func(netpkg.IP) netpkg.IP, addrs []string) netpkg.IP {
for _, s := range addrs {
if addr := filter(netpkg.ParseIP(s)); addr != nil {
return addr
}
}
return nil
}
func anyaddr(x netpkg.IP) netpkg.IP {
if x4 := x.To4(); x4 != nil {
return x4
}
if supportsIPv6 {
return x
}
return nil
}
func ipv4only(x netpkg.IP) netpkg.IP { return x.To4() }
func ipv6only(x netpkg.IP) netpkg.IP {
// Only return addresses that we can use
// with the kernel's netpkg.IPv6 addressing modes.
if len(x) == netpkg.IPv6len && x.To4() == nil && supportsIPv6 {
return x
}
return nil
}
// Convert "host:port" into netpkg.IP address and port.
func hostPortToIP(net, hostport string) (ip netpkg.IP, iport int, err error) {
host, port, err := netpkg.SplitHostPort(hostport)
if err != nil {
return nil, 0, err
}
var addr netpkg.IP
if host != "" {
// Try as an netpkg.IP address.
addr = netpkg.ParseIP(host)
if addr == nil {
var filter func(netpkg.IP) netpkg.IP
if net != "" && net[len(net)-1] == '4' {
filter = ipv4only
}
if net != "" && net[len(net)-1] == '6' {
filter = ipv6only
}
// Not an netpkg.IP address. Try as a DNS name.
addrs, err := LookupHost(host)
if err != nil {
return nil, 0, err
}
addr = firstFavoriteAddr(filter, addrs)
if addr == nil {
// should not happen
return nil, 0, &netpkg.AddrError{"LookupHost returned no suitable address", addrs[0]}
}
}
}
p, err := strconv.Atoi(port)
if err != nil {
p, err = netpkg.LookupPort(net, port)
if err != nil {
return nil, 0, err
}
}
if p < 0 || p > 0xFFFF {
return nil, 0, &netpkg.AddrError{"invalid port", port}
}
return addr, p, nil
}
// Convert "host" into IP address.
func hostToIP(net, host string) (ip netpkg.IP, err error) {
var addr netpkg.IP
// Try as an IP address.
addr = netpkg.ParseIP(host)
if addr == nil {
filter := anyaddr
if net != "" && net[len(net)-1] == '4' {
filter = ipv4only
}
if net != "" && net[len(net)-1] == '6' {
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
addrs, err1 := LookupHost(host)
if err1 != nil {
err = err1
goto Error
}
addr = firstFavoriteAddr(filter, addrs)
if addr == nil {
// should not happen
err = &netpkg.AddrError{"LookupHost returned no suitable address", addrs[0]}
goto Error
}
}
return addr, nil
Error:
return nil, err
}
// ResolveTCPAddr parses addr as a TCP address of the form
// host:port and resolves domain names or port names to
// numeric addresses on the network net, which must be "tcp",
// "tcp4" or "tcp6". A literal netpkg.IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(net, addr string) (*netpkg.TCPAddr, error) {
ip, port, err := hostPortToIP(net, addr)
if err != nil {
return nil, err
}
return &netpkg.TCPAddr{IP: ip, Port: port}, nil
}
// ResolveUDPAddr parses addr as a UDP address of the form
// host:port and resolves domain names or port names to
// numeric addresses on the network net, which must be "udp",
// "udp4" or "udp6". A literal netpkg.IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(net, addr string) (*netpkg.UDPAddr, error) {
ip, port, err := hostPortToIP(net, addr)
if err != nil {
return nil, err
}
return &netpkg.UDPAddr{IP: ip, Port: port}, nil
}
// ResolveIPAddr parses addr as a IP address and resolves domain
// names to numeric addresses on the network net, which must be
// "ip", "ip4" or "ip6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]".
func ResolveIPAddr(net, addr string) (*netpkg.IPAddr, error) {
ip, err := hostToIP(net, addr)
if err != nil {
return nil, err
}
return &netpkg.IPAddr{IP: ip}, nil
}