Imported Upstream version 4.7.3
[platform/upstream/gcc48.git] / libgo / go / net / dial.go
1 // Copyright 2010 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 import (
8         "time"
9 )
10
11 func parseDialNetwork(net string) (afnet string, proto int, err error) {
12         i := last(net, ':')
13         if i < 0 { // no colon
14                 switch net {
15                 case "tcp", "tcp4", "tcp6":
16                 case "udp", "udp4", "udp6":
17                 case "unix", "unixgram", "unixpacket":
18                 default:
19                         return "", 0, UnknownNetworkError(net)
20                 }
21                 return net, 0, nil
22         }
23         afnet = net[:i]
24         switch afnet {
25         case "ip", "ip4", "ip6":
26                 protostr := net[i+1:]
27                 proto, i, ok := dtoi(protostr, 0)
28                 if !ok || i != len(protostr) {
29                         proto, err = lookupProtocol(protostr)
30                         if err != nil {
31                                 return "", 0, err
32                         }
33                 }
34                 return afnet, proto, nil
35         }
36         return "", 0, UnknownNetworkError(net)
37 }
38
39 func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
40         afnet, _, err = parseDialNetwork(net)
41         if err != nil {
42                 return "", nil, &OpError{op, net, nil, err}
43         }
44         if op == "dial" && addr == "" {
45                 return "", nil, &OpError{op, net, nil, errMissingAddress}
46         }
47         switch afnet {
48         case "tcp", "tcp4", "tcp6":
49                 if addr != "" {
50                         a, err = ResolveTCPAddr(afnet, addr)
51                 }
52         case "udp", "udp4", "udp6":
53                 if addr != "" {
54                         a, err = ResolveUDPAddr(afnet, addr)
55                 }
56         case "ip", "ip4", "ip6":
57                 if addr != "" {
58                         a, err = ResolveIPAddr(afnet, addr)
59                 }
60         case "unix", "unixgram", "unixpacket":
61                 if addr != "" {
62                         a, err = ResolveUnixAddr(afnet, addr)
63                 }
64         }
65         return
66 }
67
68 // Dial connects to the address addr on the network net.
69 //
70 // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
71 // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
72 // (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket".
73 //
74 // For TCP and UDP networks, addresses have the form host:port.
75 // If host is a literal IPv6 address, it must be enclosed
76 // in square brackets.  The functions JoinHostPort and SplitHostPort
77 // manipulate addresses in this form.
78 //
79 // Examples:
80 //      Dial("tcp", "12.34.56.78:80")
81 //      Dial("tcp", "google.com:80")
82 //      Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
83 //
84 // For IP networks, addr must be "ip", "ip4" or "ip6" followed
85 // by a colon and a protocol number or name.
86 //
87 // Examples:
88 //      Dial("ip4:1", "127.0.0.1")
89 //      Dial("ip6:ospf", "::1")
90 //
91 func Dial(net, addr string) (Conn, error) {
92         _, addri, err := resolveNetAddr("dial", net, addr)
93         if err != nil {
94                 return nil, err
95         }
96         return dialAddr(net, addr, addri)
97 }
98
99 func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
100         switch ra := addri.(type) {
101         case *TCPAddr:
102                 c, err = DialTCP(net, nil, ra)
103         case *UDPAddr:
104                 c, err = DialUDP(net, nil, ra)
105         case *IPAddr:
106                 c, err = DialIP(net, nil, ra)
107         case *UnixAddr:
108                 c, err = DialUnix(net, nil, ra)
109         default:
110                 err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
111         }
112         if err != nil {
113                 return nil, err
114         }
115         return
116 }
117
118 // DialTimeout acts like Dial but takes a timeout.
119 // The timeout includes name resolution, if required.
120 func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
121         // TODO(bradfitz): the timeout should be pushed down into the
122         // net package's event loop, so on timeout to dead hosts we
123         // don't have a goroutine sticking around for the default of
124         // ~3 minutes.
125         t := time.NewTimer(timeout)
126         defer t.Stop()
127         type pair struct {
128                 Conn
129                 error
130         }
131         ch := make(chan pair, 1)
132         resolvedAddr := make(chan Addr, 1)
133         go func() {
134                 _, addri, err := resolveNetAddr("dial", net, addr)
135                 if err != nil {
136                         ch <- pair{nil, err}
137                         return
138                 }
139                 resolvedAddr <- addri // in case we need it for OpError
140                 c, err := dialAddr(net, addr, addri)
141                 ch <- pair{c, err}
142         }()
143         select {
144         case <-t.C:
145                 // Try to use the real Addr in our OpError, if we resolved it
146                 // before the timeout. Otherwise we just use stringAddr.
147                 var addri Addr
148                 select {
149                 case a := <-resolvedAddr:
150                         addri = a
151                 default:
152                         addri = &stringAddr{net, addr}
153                 }
154                 err := &OpError{
155                         Op:   "dial",
156                         Net:  net,
157                         Addr: addri,
158                         Err:  &timeoutError{},
159                 }
160                 return nil, err
161         case p := <-ch:
162                 return p.Conn, p.error
163         }
164         panic("unreachable")
165 }
166
167 type stringAddr struct {
168         net, addr string
169 }
170
171 func (a stringAddr) Network() string { return a.net }
172 func (a stringAddr) String() string  { return a.addr }
173
174 // Listen announces on the local network address laddr.
175 // The network string net must be a stream-oriented network:
176 // "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
177 func Listen(net, laddr string) (Listener, error) {
178         afnet, a, err := resolveNetAddr("listen", net, laddr)
179         if err != nil {
180                 return nil, err
181         }
182         switch afnet {
183         case "tcp", "tcp4", "tcp6":
184                 var la *TCPAddr
185                 if a != nil {
186                         la = a.(*TCPAddr)
187                 }
188                 return ListenTCP(net, la)
189         case "unix", "unixpacket":
190                 var la *UnixAddr
191                 if a != nil {
192                         la = a.(*UnixAddr)
193                 }
194                 return ListenUnix(net, la)
195         }
196         return nil, UnknownNetworkError(net)
197 }
198
199 // ListenPacket announces on the local network address laddr.
200 // The network string net must be a packet-oriented network:
201 // "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
202 func ListenPacket(net, addr string) (PacketConn, error) {
203         afnet, a, err := resolveNetAddr("listen", net, addr)
204         if err != nil {
205                 return nil, err
206         }
207         switch afnet {
208         case "udp", "udp4", "udp6":
209                 var la *UDPAddr
210                 if a != nil {
211                         la = a.(*UDPAddr)
212                 }
213                 return ListenUDP(net, la)
214         case "ip", "ip4", "ip6":
215                 var la *IPAddr
216                 if a != nil {
217                         la = a.(*IPAddr)
218                 }
219                 return ListenIP(net, la)
220         case "unixgram":
221                 var la *UnixAddr
222                 if a != nil {
223                         la = a.(*UnixAddr)
224                 }
225                 return DialUnix(net, la, nil)
226         }
227         return nil, UnknownNetworkError(net)
228 }