|
| 1 | +// Copyright 2011 The Go Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +package net |
| 6 | + |
| 7 | +/* |
| 8 | +#include <sys/types.h> |
| 9 | +#include <sys/socket.h> |
| 10 | +#include <netinet/in.h> |
| 11 | +#include <netdb.h> |
| 12 | +#include <stdlib.h> |
| 13 | +#include <unistd.h> |
| 14 | +#include <string.h> |
| 15 | +*/ |
| 16 | +import "C" |
| 17 | + |
| 18 | +import ( |
| 19 | + "os" |
| 20 | + "syscall" |
| 21 | + "unsafe" |
| 22 | +) |
| 23 | + |
| 24 | +func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) { |
| 25 | + ip, err, completed := cgoLookupIP(name) |
| 26 | + for _, p := range ip { |
| 27 | + addrs = append(addrs, p.String()) |
| 28 | + } |
| 29 | + return |
| 30 | +} |
| 31 | + |
| 32 | +func cgoLookupPort(net, service string) (port int, err os.Error, completed bool) { |
| 33 | + var res *C.struct_addrinfo |
| 34 | + var hints C.struct_addrinfo |
| 35 | + |
| 36 | + switch net { |
| 37 | + case "": |
| 38 | + // no hints |
| 39 | + case "tcp", "tcp4", "tcp6": |
| 40 | + hints.ai_socktype = C.SOCK_STREAM |
| 41 | + hints.ai_protocol = C.IPPROTO_TCP |
| 42 | + case "udp", "udp4", "udp6": |
| 43 | + hints.ai_socktype = C.SOCK_DGRAM |
| 44 | + hints.ai_protocol = C.IPPROTO_UDP |
| 45 | + default: |
| 46 | + return 0, UnknownNetworkError(net), true |
| 47 | + } |
| 48 | + if len(net) >= 4 { |
| 49 | + switch net[3] { |
| 50 | + case '4': |
| 51 | + hints.ai_family = C.AF_INET |
| 52 | + case '6': |
| 53 | + hints.ai_family = C.AF_INET6 |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + s := C.CString(service) |
| 58 | + defer C.free(unsafe.Pointer(s)) |
| 59 | + if C.getaddrinfo(nil, s, &hints, &res) == 0 { |
| 60 | + defer C.freeaddrinfo(res) |
| 61 | + for r := res; r != nil; r = r.ai_next { |
| 62 | + switch r.ai_family { |
| 63 | + default: |
| 64 | + continue |
| 65 | + case C.AF_INET: |
| 66 | + sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) |
| 67 | + p := (*[2]byte)(unsafe.Pointer(&sa.Port)) |
| 68 | + return int(p[0])<<8 | int(p[1]), nil, true |
| 69 | + case C.AF_INET6: |
| 70 | + sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) |
| 71 | + p := (*[2]byte)(unsafe.Pointer(&sa.Port)) |
| 72 | + return int(p[0])<<8 | int(p[1]), nil, true |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + return 0, &AddrError{"unknown port", net + "/" + service}, true |
| 77 | +} |
| 78 | + |
| 79 | +func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err os.Error, completed bool) { |
| 80 | + var res *C.struct_addrinfo |
| 81 | + var hints C.struct_addrinfo |
| 82 | + |
| 83 | + // NOTE(rsc): In theory there are approximately balanced |
| 84 | + // arguments for and against including AI_ADDRCONFIG |
| 85 | + // in the flags (it includes IPv4 results only on IPv4 systems, |
| 86 | + // and similarly for IPv6), but in practice setting it causes |
| 87 | + // getaddrinfo to return the wrong canonical name on Linux. |
| 88 | + // So definitely leave it out. |
| 89 | + hints.ai_flags = C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME |
| 90 | + |
| 91 | + h := C.CString(name) |
| 92 | + defer C.free(unsafe.Pointer(h)) |
| 93 | + gerrno, err := C.getaddrinfo(h, nil, &hints, &res) |
| 94 | + if gerrno != 0 { |
| 95 | + var str string |
| 96 | + if gerrno == C.EAI_NONAME { |
| 97 | + str = noSuchHost |
| 98 | + } else if gerrno == C.EAI_SYSTEM { |
| 99 | + str = err.String() |
| 100 | + } else { |
| 101 | + str = C.GoString(C.gai_strerror(gerrno)) |
| 102 | + } |
| 103 | + return nil, "", &DNSError{Error: str, Name: name}, true |
| 104 | + } |
| 105 | + defer C.freeaddrinfo(res) |
| 106 | + if res != nil { |
| 107 | + cname = C.GoString(res.ai_canonname) |
| 108 | + if cname == "" { |
| 109 | + cname = name |
| 110 | + } |
| 111 | + if len(cname) > 0 && cname[len(cname)-1] != '.' { |
| 112 | + cname += "." |
| 113 | + } |
| 114 | + } |
| 115 | + for r := res; r != nil; r = r.ai_next { |
| 116 | + // Everything comes back twice, once for UDP and once for TCP. |
| 117 | + if r.ai_socktype != C.SOCK_STREAM { |
| 118 | + continue |
| 119 | + } |
| 120 | + switch r.ai_family { |
| 121 | + default: |
| 122 | + continue |
| 123 | + case C.AF_INET: |
| 124 | + sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) |
| 125 | + addrs = append(addrs, copyIP(sa.Addr[:])) |
| 126 | + case C.AF_INET6: |
| 127 | + sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) |
| 128 | + addrs = append(addrs, copyIP(sa.Addr[:])) |
| 129 | + } |
| 130 | + } |
| 131 | + return addrs, cname, nil, true |
| 132 | +} |
| 133 | + |
| 134 | +func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) { |
| 135 | + addrs, _, err, completed = cgoLookupIPCNAME(name) |
| 136 | + return |
| 137 | +} |
| 138 | + |
| 139 | +func cgoLookupCNAME(name string) (cname string, err os.Error, completed bool) { |
| 140 | + _, cname, err, completed = cgoLookupIPCNAME(name) |
| 141 | + return |
| 142 | +} |
| 143 | + |
| 144 | +func copyIP(x IP) IP { |
| 145 | + y := make(IP, len(x)) |
| 146 | + copy(y, x) |
| 147 | + return y |
| 148 | +} |
0 commit comments