f8d9fa9e80
This upgrades all of libgo other than the runtime package to the Go 1.4 release. In Go 1.4 much of the runtime was rewritten into Go. Merging that code will take more time and will not change the API, so I'm putting it off for now. There are a few runtime changes anyhow, to accomodate other packages that rely on minor modifications to the runtime support. The compiler changes slightly to add a one-bit flag to each type descriptor kind that is stored directly in an interface, which for gccgo is currently only pointer types. Another one-bit flag (gcprog) is reserved because it is used by the gc compiler, but gccgo does not currently use it. There is another error check in the compiler since I ran across it during testing. gotools/: * Makefile.am (go_cmd_go_files): Sort entries. Add generate.go. * Makefile.in: Rebuild. From-SVN: r219627
382 lines
10 KiB
Go
382 lines
10 KiB
Go
// 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.
|
|
|
|
package net
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
lookupPort = oldLookupPort
|
|
lookupIP = oldLookupIP
|
|
)
|
|
|
|
func getprotobyname(name string) (proto int, err error) {
|
|
p, err := syscall.GetProtoByName(name)
|
|
if err != nil {
|
|
return 0, os.NewSyscallError("GetProtoByName", err)
|
|
}
|
|
return int(p.Proto), nil
|
|
}
|
|
|
|
// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
|
|
func lookupProtocol(name string) (proto int, err error) {
|
|
// GetProtoByName return value is stored in thread local storage.
|
|
// Start new os thread before the call to prevent races.
|
|
type result struct {
|
|
proto int
|
|
err error
|
|
}
|
|
ch := make(chan result)
|
|
go func() {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
proto, err := getprotobyname(name)
|
|
ch <- result{proto: proto, err: err}
|
|
}()
|
|
r := <-ch
|
|
if r.err != nil {
|
|
if proto, ok := protocols[name]; ok {
|
|
return proto, nil
|
|
}
|
|
}
|
|
return r.proto, r.err
|
|
}
|
|
|
|
func lookupHost(name string) (addrs []string, err error) {
|
|
ips, err := LookupIP(name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
addrs = make([]string, 0, len(ips))
|
|
for _, ip := range ips {
|
|
addrs = append(addrs, ip.String())
|
|
}
|
|
return
|
|
}
|
|
|
|
func gethostbyname(name string) (addrs []IP, err error) {
|
|
// caller already acquired thread
|
|
h, err := syscall.GetHostByName(name)
|
|
if err != nil {
|
|
return nil, os.NewSyscallError("GetHostByName", err)
|
|
}
|
|
switch h.AddrType {
|
|
case syscall.AF_INET:
|
|
i := 0
|
|
addrs = make([]IP, 100) // plenty of room to grow
|
|
for p := (*[100](*[4]byte))(unsafe.Pointer(h.AddrList)); i < cap(addrs) && p[i] != nil; i++ {
|
|
addrs[i] = IPv4(p[i][0], p[i][1], p[i][2], p[i][3])
|
|
}
|
|
addrs = addrs[0:i]
|
|
default: // TODO(vcc): Implement non IPv4 address lookups.
|
|
return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
|
|
}
|
|
return addrs, nil
|
|
}
|
|
|
|
func oldLookupIP(name string) (addrs []IP, err error) {
|
|
// GetHostByName return value is stored in thread local storage.
|
|
// Start new os thread before the call to prevent races.
|
|
type result struct {
|
|
addrs []IP
|
|
err error
|
|
}
|
|
ch := make(chan result)
|
|
go func() {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
addrs, err := gethostbyname(name)
|
|
ch <- result{addrs: addrs, err: err}
|
|
}()
|
|
r := <-ch
|
|
return r.addrs, r.err
|
|
}
|
|
|
|
func newLookupIP(name string) (addrs []IP, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
hints := syscall.AddrinfoW{
|
|
Family: syscall.AF_UNSPEC,
|
|
Socktype: syscall.SOCK_STREAM,
|
|
Protocol: syscall.IPPROTO_IP,
|
|
}
|
|
var result *syscall.AddrinfoW
|
|
e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
|
|
if e != nil {
|
|
return nil, os.NewSyscallError("GetAddrInfoW", e)
|
|
}
|
|
defer syscall.FreeAddrInfoW(result)
|
|
addrs = make([]IP, 0, 5)
|
|
for ; result != nil; result = result.Next {
|
|
addr := unsafe.Pointer(result.Addr)
|
|
switch result.Family {
|
|
case syscall.AF_INET:
|
|
a := (*syscall.RawSockaddrInet4)(addr).Addr
|
|
addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3]))
|
|
case syscall.AF_INET6:
|
|
a := (*syscall.RawSockaddrInet6)(addr).Addr
|
|
addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]})
|
|
default:
|
|
return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
|
|
}
|
|
}
|
|
return addrs, nil
|
|
}
|
|
|
|
func getservbyname(network, service string) (port int, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
switch network {
|
|
case "tcp4", "tcp6":
|
|
network = "tcp"
|
|
case "udp4", "udp6":
|
|
network = "udp"
|
|
}
|
|
s, err := syscall.GetServByName(service, network)
|
|
if err != nil {
|
|
return 0, os.NewSyscallError("GetServByName", err)
|
|
}
|
|
return int(syscall.Ntohs(s.Port)), nil
|
|
}
|
|
|
|
func oldLookupPort(network, service string) (port int, err error) {
|
|
// GetServByName return value is stored in thread local storage.
|
|
// Start new os thread before the call to prevent races.
|
|
type result struct {
|
|
port int
|
|
err error
|
|
}
|
|
ch := make(chan result)
|
|
go func() {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
port, err := getservbyname(network, service)
|
|
ch <- result{port: port, err: err}
|
|
}()
|
|
r := <-ch
|
|
return r.port, r.err
|
|
}
|
|
|
|
func newLookupPort(network, service string) (port int, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var stype int32
|
|
switch network {
|
|
case "tcp4", "tcp6":
|
|
stype = syscall.SOCK_STREAM
|
|
case "udp4", "udp6":
|
|
stype = syscall.SOCK_DGRAM
|
|
}
|
|
hints := syscall.AddrinfoW{
|
|
Family: syscall.AF_UNSPEC,
|
|
Socktype: stype,
|
|
Protocol: syscall.IPPROTO_IP,
|
|
}
|
|
var result *syscall.AddrinfoW
|
|
e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
|
|
if e != nil {
|
|
return 0, os.NewSyscallError("GetAddrInfoW", e)
|
|
}
|
|
defer syscall.FreeAddrInfoW(result)
|
|
if result == nil {
|
|
return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
|
|
}
|
|
addr := unsafe.Pointer(result.Addr)
|
|
switch result.Family {
|
|
case syscall.AF_INET:
|
|
a := (*syscall.RawSockaddrInet4)(addr)
|
|
return int(syscall.Ntohs(a.Port)), nil
|
|
case syscall.AF_INET6:
|
|
a := (*syscall.RawSockaddrInet6)(addr)
|
|
return int(syscall.Ntohs(a.Port)), nil
|
|
}
|
|
return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
|
|
}
|
|
|
|
func lookupCNAME(name string) (cname string, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
|
|
// windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
|
|
if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
|
|
// if there are no aliases, the canonical name is the input name
|
|
if name == "" || name[len(name)-1] != '.' {
|
|
return name + ".", nil
|
|
}
|
|
return name, nil
|
|
}
|
|
if e != nil {
|
|
return "", os.NewSyscallError("LookupCNAME", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
|
|
cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:]) + "."
|
|
return
|
|
}
|
|
|
|
func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var target string
|
|
if service == "" && proto == "" {
|
|
target = name
|
|
} else {
|
|
target = "_" + service + "._" + proto + "." + name
|
|
}
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
|
|
if e != nil {
|
|
return "", nil, os.NewSyscallError("LookupSRV", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
addrs = make([]*SRV, 0, 10)
|
|
for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
|
|
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
|
|
addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
|
|
}
|
|
byPriorityWeight(addrs).sort()
|
|
return name, addrs, nil
|
|
}
|
|
|
|
func lookupMX(name string) (mx []*MX, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
|
|
if e != nil {
|
|
return nil, os.NewSyscallError("LookupMX", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
mx = make([]*MX, 0, 10)
|
|
for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
|
|
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
|
|
mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
|
|
}
|
|
byPref(mx).sort()
|
|
return mx, nil
|
|
}
|
|
|
|
func lookupNS(name string) (ns []*NS, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
|
|
if e != nil {
|
|
return nil, os.NewSyscallError("LookupNS", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
ns = make([]*NS, 0, 10)
|
|
for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
|
|
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
|
|
ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
|
|
}
|
|
return ns, nil
|
|
}
|
|
|
|
func lookupTXT(name string) (txt []string, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
|
|
if e != nil {
|
|
return nil, os.NewSyscallError("LookupTXT", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
txt = make([]string, 0, 10)
|
|
for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
|
|
d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
|
|
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
|
|
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
|
|
txt = append(txt, s)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func lookupAddr(addr string) (name []string, err error) {
|
|
acquireThread()
|
|
defer releaseThread()
|
|
arpa, err := reverseaddr(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var r *syscall.DNSRecord
|
|
e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
|
|
if e != nil {
|
|
return nil, os.NewSyscallError("LookupAddr", e)
|
|
}
|
|
defer syscall.DnsRecordListFree(r, 1)
|
|
|
|
name = make([]string, 0, 10)
|
|
for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
|
|
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
|
|
name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
|
|
}
|
|
return name, nil
|
|
}
|
|
|
|
const dnsSectionMask = 0x0003
|
|
|
|
// returns only results applicable to name and resolves CNAME entries
|
|
func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {
|
|
cname := syscall.StringToUTF16Ptr(name)
|
|
if dnstype != syscall.DNS_TYPE_CNAME {
|
|
cname = resolveCNAME(cname, r)
|
|
}
|
|
rec := make([]*syscall.DNSRecord, 0, 10)
|
|
for p := r; p != nil; p = p.Next {
|
|
if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
|
|
continue
|
|
}
|
|
if p.Type != dnstype {
|
|
continue
|
|
}
|
|
if !syscall.DnsNameCompare(cname, p.Name) {
|
|
continue
|
|
}
|
|
rec = append(rec, p)
|
|
}
|
|
return rec
|
|
}
|
|
|
|
// returns the last CNAME in chain
|
|
func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {
|
|
// limit cname resolving to 10 in case of a infinite CNAME loop
|
|
Cname:
|
|
for cnameloop := 0; cnameloop < 10; cnameloop++ {
|
|
for p := r; p != nil; p = p.Next {
|
|
if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
|
|
continue
|
|
}
|
|
if p.Type != syscall.DNS_TYPE_CNAME {
|
|
continue
|
|
}
|
|
if !syscall.DnsNameCompare(name, p.Name) {
|
|
continue
|
|
}
|
|
name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host
|
|
continue Cname
|
|
}
|
|
break
|
|
}
|
|
return name
|
|
}
|