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.
14 // Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
17 // Plan 9 uses IPv6 natively, see ip(3).
18 func (p
*ipStackCapabilities
) probe() {
19 p
.ipv4Enabled
= probe(netdir
+"/iproute", "4i")
20 p
.ipv6Enabled
= probe(netdir
+"/iproute", "6i")
21 if p
.ipv4Enabled
&& p
.ipv6Enabled
{
22 p
.ipv4MappedIPv6Enabled
= true
26 func probe(filename
, query
string) bool {
29 if file
, err
= open(filename
); err
!= nil {
35 for line
, ok
:= file
.readLine(); ok
&& !r
; line
, ok
= file
.readLine() {
40 for i
:= 0; i
< len(f
); i
++ {
50 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
51 func parsePlan9Addr(s
string) (ip IP
, iport
int, err error
) {
52 addr
:= IPv4zero
// address contains port only
53 i
:= bytealg
.IndexByteString(s
, '!')
57 return nil, 0, &ParseError
{Type
: "IP address", Text
: s
}
60 p
, _
, ok
:= dtoi(s
[i
+1:])
62 return nil, 0, &ParseError
{Type
: "port", Text
: s
}
64 if p
< 0 || p
> 0xFFFF {
65 return nil, 0, &AddrError
{Err
: "invalid port", Addr
: string(p
)}
70 func readPlan9Addr(proto
, filename
string) (addr Addr
, err error
) {
73 f
, err
:= os
.Open(filename
)
78 n
, err
:= f
.Read(buf
[:])
82 ip
, port
, err
:= parsePlan9Addr(string(buf
[:n
]))
88 addr
= &TCPAddr
{IP
: ip
, Port
: port
}
90 addr
= &UDPAddr
{IP
: ip
, Port
: port
}
92 return nil, UnknownNetworkError(proto
)
97 func startPlan9(ctx context
.Context
, net
string, addr Addr
) (ctl
*os
.File
, dest
, proto
, name
string, err error
) {
102 switch a
:= addr
.(type) {
112 err
= UnknownNetworkError(net
)
117 err
= InvalidAddrError("port should be < 65536")
121 clone
, dest
, err
:= queryCS1(ctx
, proto
, ip
, port
)
125 f
, err
:= os
.OpenFile(clone
, os
.O_RDWR
, 0)
130 n
, err
:= f
.Read(buf
[:])
135 return f
, dest
, proto
, string(buf
[:n
]), nil
138 func fixErr(err error
) {
139 oe
, ok
:= err
.(*OpError
)
143 nonNilInterface
:= func(a Addr
) bool {
144 switch a
:= a
.(type) {
155 if nonNilInterface(oe
.Source
) {
158 if nonNilInterface(oe
.Addr
) {
161 if pe
, ok
:= oe
.Err
.(*os
.PathError
); ok
{
162 if _
, ok
= pe
.Err
.(syscall
.ErrorString
); ok
{
168 func dialPlan9(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
169 defer func() { fixErr(err
) }()
174 resc
:= make(chan res
)
176 testHookDialChannel()
177 fd
, err
:= dialPlan9Blocking(ctx
, net
, laddr
, raddr
)
179 case resc
<- res
{fd
, err
}:
188 return res
.fd
, res
.err
190 return nil, mapErr(ctx
.Err())
194 func dialPlan9Blocking(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
195 if isWildcard(raddr
) {
196 raddr
= toLocal(raddr
, net
)
198 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, raddr
)
202 _
, err
= f
.WriteString("connect " + dest
)
207 data
, err
:= os
.OpenFile(netdir
+"/"+proto
+"/"+name
+"/data", os
.O_RDWR
, 0)
212 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
218 return newFD(proto
, name
, nil, f
, data
, laddr
, raddr
)
221 func listenPlan9(ctx context
.Context
, net
string, laddr Addr
) (fd
*netFD
, err error
) {
222 defer func() { fixErr(err
) }()
223 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, laddr
)
227 _
, err
= f
.WriteString("announce " + dest
)
230 return nil, &OpError
{Op
: "announce", Net
: net
, Source
: laddr
, Addr
: nil, Err
: err
}
232 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
237 return newFD(proto
, name
, nil, f
, nil, laddr
, nil)
240 func (fd
*netFD
) netFD() (*netFD
, error
) {
241 return newFD(fd
.net
, fd
.n
, fd
.listen
, fd
.ctl
, fd
.data
, fd
.laddr
, fd
.raddr
)
244 func (fd
*netFD
) acceptPlan9() (nfd
*netFD
, err error
) {
245 defer func() { fixErr(err
) }()
246 if err
:= fd
.pfd
.ReadLock(); err
!= nil {
249 defer fd
.pfd
.ReadUnlock()
250 listen
, err
:= os
.Open(fd
.dir
+ "/listen")
255 n
, err
:= listen
.Read(buf
[:])
260 name
:= string(buf
[:n
])
261 ctl
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/ctl", os
.O_RDWR
, 0)
266 data
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/data", os
.O_RDWR
, 0)
272 raddr
, err
:= readPlan9Addr(fd
.net
, netdir
+"/"+fd
.net
+"/"+name
+"/remote")
279 return newFD(fd
.net
, name
, listen
, ctl
, data
, fd
.laddr
, raddr
)
282 func isWildcard(a Addr
) bool {
284 switch a
:= a
.(type) {
286 wildcard
= a
.isWildcard()
288 wildcard
= a
.isWildcard()
290 wildcard
= a
.isWildcard()
295 func toLocal(a Addr
, net
string) Addr
{
296 switch a
:= a
.(type) {
298 a
.IP
= loopbackIP(net
)
300 a
.IP
= loopbackIP(net
)
302 a
.IP
= loopbackIP(net
)