1 // Copyright 2009 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.
13 // Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
16 // Plan 9 uses IPv6 natively, see ip(3).
17 func (p
*ipStackCapabilities
) probe() {
18 p
.ipv4Enabled
= probe(netdir
+"/iproute", "4i")
19 p
.ipv6Enabled
= probe(netdir
+"/iproute", "6i")
20 if p
.ipv4Enabled
&& p
.ipv6Enabled
{
21 p
.ipv4MappedIPv6Enabled
= true
25 func probe(filename
, query
string) bool {
28 if file
, err
= open(filename
); err
!= nil {
34 for line
, ok
:= file
.readLine(); ok
&& !r
; line
, ok
= file
.readLine() {
39 for i
:= 0; i
< len(f
); i
++ {
49 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
50 func parsePlan9Addr(s
string) (ip IP
, iport
int, err error
) {
51 addr
:= IPv4zero
// address contains port only
52 i
:= byteIndex(s
, '!')
56 return nil, 0, &ParseError
{Type
: "IP address", Text
: s
}
59 p
, _
, ok
:= dtoi(s
[i
+1:])
61 return nil, 0, &ParseError
{Type
: "port", Text
: s
}
63 if p
< 0 || p
> 0xFFFF {
64 return nil, 0, &AddrError
{Err
: "invalid port", Addr
: string(p
)}
69 func readPlan9Addr(proto
, filename
string) (addr Addr
, err error
) {
72 f
, err
:= os
.Open(filename
)
77 n
, err
:= f
.Read(buf
[:])
81 ip
, port
, err
:= parsePlan9Addr(string(buf
[:n
]))
87 addr
= &TCPAddr
{IP
: ip
, Port
: port
}
89 addr
= &UDPAddr
{IP
: ip
, Port
: port
}
91 return nil, UnknownNetworkError(proto
)
96 func startPlan9(ctx context
.Context
, net
string, addr Addr
) (ctl
*os
.File
, dest
, proto
, name
string, err error
) {
101 switch a
:= addr
.(type) {
111 err
= UnknownNetworkError(net
)
116 err
= InvalidAddrError("port should be < 65536")
120 clone
, dest
, err
:= queryCS1(ctx
, proto
, ip
, port
)
124 f
, err
:= os
.OpenFile(clone
, os
.O_RDWR
, 0)
129 n
, err
:= f
.Read(buf
[:])
134 return f
, dest
, proto
, string(buf
[:n
]), nil
137 func fixErr(err error
) {
138 oe
, ok
:= err
.(*OpError
)
142 nonNilInterface
:= func(a Addr
) bool {
143 switch a
:= a
.(type) {
154 if nonNilInterface(oe
.Source
) {
157 if nonNilInterface(oe
.Addr
) {
160 if pe
, ok
:= oe
.Err
.(*os
.PathError
); ok
{
161 if _
, ok
= pe
.Err
.(syscall
.ErrorString
); ok
{
167 func dialPlan9(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
168 defer func() { fixErr(err
) }()
173 resc
:= make(chan res
)
175 testHookDialChannel()
176 fd
, err
:= dialPlan9Blocking(ctx
, net
, laddr
, raddr
)
178 case resc
<- res
{fd
, err
}:
187 return res
.fd
, res
.err
189 return nil, mapErr(ctx
.Err())
193 func dialPlan9Blocking(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
194 if isWildcard(raddr
) {
195 raddr
= toLocal(raddr
, net
)
197 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, raddr
)
201 _
, err
= f
.WriteString("connect " + dest
)
206 data
, err
:= os
.OpenFile(netdir
+"/"+proto
+"/"+name
+"/data", os
.O_RDWR
, 0)
211 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
217 return newFD(proto
, name
, nil, f
, data
, laddr
, raddr
)
220 func listenPlan9(ctx context
.Context
, net
string, laddr Addr
) (fd
*netFD
, err error
) {
221 defer func() { fixErr(err
) }()
222 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, laddr
)
226 _
, err
= f
.WriteString("announce " + dest
)
231 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
236 return newFD(proto
, name
, nil, f
, nil, laddr
, nil)
239 func (fd
*netFD
) netFD() (*netFD
, error
) {
240 return newFD(fd
.net
, fd
.n
, fd
.listen
, fd
.ctl
, fd
.data
, fd
.laddr
, fd
.raddr
)
243 func (fd
*netFD
) acceptPlan9() (nfd
*netFD
, err error
) {
244 defer func() { fixErr(err
) }()
245 if err
:= fd
.pfd
.ReadLock(); err
!= nil {
248 defer fd
.pfd
.ReadUnlock()
249 listen
, err
:= os
.Open(fd
.dir
+ "/listen")
254 n
, err
:= listen
.Read(buf
[:])
259 name
:= string(buf
[:n
])
260 ctl
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/ctl", os
.O_RDWR
, 0)
265 data
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/data", os
.O_RDWR
, 0)
271 raddr
, err
:= readPlan9Addr(fd
.net
, netdir
+"/"+fd
.net
+"/"+name
+"/remote")
278 return newFD(fd
.net
, name
, listen
, ctl
, data
, fd
.laddr
, raddr
)
281 func isWildcard(a Addr
) bool {
283 switch a
:= a
.(type) {
285 wildcard
= a
.isWildcard()
287 wildcard
= a
.isWildcard()
289 wildcard
= a
.isWildcard()
294 func toLocal(a Addr
, net
string) Addr
{
295 switch a
:= a
.(type) {
297 a
.IP
= loopbackIP(net
)
299 a
.IP
= loopbackIP(net
)
301 a
.IP
= loopbackIP(net
)