Update Go library to last weekly.
[platform/upstream/gcc.git] / libgo / go / net / cgo_unix.go
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 // +build darwin freebsd linux
6
7 package net
8
9 /*
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <netdb.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 */
18
19 import (
20         "os"
21         "syscall"
22         "unsafe"
23 )
24
25 func libc_getaddrinfo(node *byte, service *byte, hints *syscall.Addrinfo, res **syscall.Addrinfo) int __asm__ ("getaddrinfo")
26 func libc_freeaddrinfo(res *syscall.Addrinfo) __asm__ ("freeaddrinfo")
27 func libc_gai_strerror(errcode int) *byte __asm__ ("gai_strerror")
28
29 // bytePtrToString takes a NUL-terminated array of bytes and convert
30 // it to a Go string.
31 func bytePtrToString(p *byte) string {
32         a := (*[10000]byte)(unsafe.Pointer(p))
33         i := 0
34         for a[i] != 0 {
35                 i++
36         }
37         return string(a[:i])
38 }
39
40 func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
41         ip, err, completed := cgoLookupIP(name)
42         for _, p := range ip {
43                 addrs = append(addrs, p.String())
44         }
45         return
46 }
47
48 func cgoLookupPort(net, service string) (port int, err os.Error, completed bool) {
49         var res *syscall.Addrinfo
50         var hints syscall.Addrinfo
51
52         switch net {
53         case "":
54                 // no hints
55         case "tcp", "tcp4", "tcp6":
56                 hints.Ai_socktype = syscall.SOCK_STREAM
57                 hints.Ai_protocol = syscall.IPPROTO_TCP
58         case "udp", "udp4", "udp6":
59                 hints.Ai_socktype = syscall.SOCK_DGRAM
60                 hints.Ai_protocol = syscall.IPPROTO_UDP
61         default:
62                 return 0, UnknownNetworkError(net), true
63         }
64         if len(net) >= 4 {
65                 switch net[3] {
66                 case '4':
67                         hints.Ai_family = syscall.AF_INET
68                 case '6':
69                         hints.Ai_family = syscall.AF_INET6
70                 }
71         }
72
73         s := syscall.StringBytePtr(service)
74         if libc_getaddrinfo(nil, s, &hints, &res) == 0 {
75                 defer libc_freeaddrinfo(res)
76                 for r := res; r != nil; r = r.Ai_next {
77                         switch r.Ai_family {
78                         default:
79                                 continue
80                         case syscall.AF_INET:
81                                 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
82                                 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
83                                 return int(p[0])<<8 | int(p[1]), nil, true
84                         case syscall.AF_INET6:
85                                 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
86                                 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
87                                 return int(p[0])<<8 | int(p[1]), nil, true
88                         }
89                 }
90         }
91         return 0, &AddrError{"unknown port", net + "/" + service}, true
92 }
93
94 func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err os.Error, completed bool) {
95         var res *syscall.Addrinfo
96         var hints syscall.Addrinfo
97
98         // NOTE(rsc): In theory there are approximately balanced
99         // arguments for and against including AI_ADDRCONFIG
100         // in the flags (it includes IPv4 results only on IPv4 systems,
101         // and similarly for IPv6), but in practice setting it causes
102         // getaddrinfo to return the wrong canonical name on Linux.
103         // So definitely leave it out.
104         hints.Ai_flags = int32((syscall.AI_ALL | syscall.AI_V4MAPPED | syscall.AI_CANONNAME) & cgoAddrInfoMask())
105
106         h := syscall.StringBytePtr(name)
107         gerrno := libc_getaddrinfo(h, nil, &hints, &res)
108         if gerrno != 0 {
109                 var str string
110                 if gerrno == syscall.EAI_NONAME {
111                         str = noSuchHost
112                 } else if gerrno == syscall.EAI_SYSTEM {
113                         str = syscall.Errstr(syscall.GetErrno())
114                 } else {
115                         str = bytePtrToString(libc_gai_strerror(gerrno))
116                 }
117                 return nil, "", &DNSError{Error: str, Name: name}, true
118         }
119         defer libc_freeaddrinfo(res)
120         if res != nil {
121                 cname = bytePtrToString((*byte)(unsafe.Pointer(res.Ai_canonname)))
122                 if cname == "" {
123                         cname = name
124                 }
125                 if len(cname) > 0 && cname[len(cname)-1] != '.' {
126                         cname += "."
127                 }
128         }
129         for r := res; r != nil; r = r.Ai_next {
130                 // Everything comes back twice, once for UDP and once for TCP.
131                 if r.Ai_socktype != syscall.SOCK_STREAM {
132                         continue
133                 }
134                 switch r.Ai_family {
135                 default:
136                         continue
137                 case syscall.AF_INET:
138                         sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
139                         addrs = append(addrs, copyIP(sa.Addr[:]))
140                 case syscall.AF_INET6:
141                         sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
142                         addrs = append(addrs, copyIP(sa.Addr[:]))
143                 }
144         }
145         return addrs, cname, nil, true
146 }
147
148 func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
149         addrs, _, err, completed = cgoLookupIPCNAME(name)
150         return
151 }
152
153 func cgoLookupCNAME(name string) (cname string, err os.Error, completed bool) {
154         _, cname, err, completed = cgoLookupIPCNAME(name)
155         return
156 }
157
158 func copyIP(x IP) IP {
159         y := make(IP, len(x))
160         copy(y, x)
161         return y
162 }