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.
16 // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
19 // Plan 9 uses IPv6 natively, see ip(3).
20 func (p
*ipStackCapabilities
) probe() {
21 p
.ipv4Enabled
= probe(netdir
+"/iproute", "4i")
22 p
.ipv6Enabled
= probe(netdir
+"/iproute", "6i")
23 if p
.ipv4Enabled
&& p
.ipv6Enabled
{
24 p
.ipv4MappedIPv6Enabled
= true
28 func probe(filename
, query
string) bool {
31 if file
, err
= open(filename
); err
!= nil {
37 for line
, ok
:= file
.readLine(); ok
&& !r
; line
, ok
= file
.readLine() {
42 for i
:= 0; i
< len(f
); i
++ {
52 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
53 func parsePlan9Addr(s
string) (ip IP
, iport
int, err error
) {
54 addr
:= IPv4zero
// address contains port only
55 i
:= bytealg
.IndexByteString(s
, '!')
59 return nil, 0, &ParseError
{Type
: "IP address", Text
: s
}
62 p
, plen
, ok
:= dtoi(s
[i
+1:])
64 return nil, 0, &ParseError
{Type
: "port", Text
: s
}
66 if p
< 0 || p
> 0xFFFF {
67 return nil, 0, &AddrError
{Err
: "invalid port", Addr
: s
[i
+1 : i
+1+plen
]}
72 func readPlan9Addr(net
, filename
string) (addr Addr
, err error
) {
75 f
, err
:= os
.Open(filename
)
80 n
, err
:= f
.Read(buf
[:])
84 ip
, port
, err
:= parsePlan9Addr(string(buf
[:n
]))
90 if ip
.Equal(IPv6zero
) {
95 case "tcp", "tcp4", "tcp6":
96 addr
= &TCPAddr
{IP
: ip
, Port
: port
}
97 case "udp", "udp4", "udp6":
98 addr
= &UDPAddr
{IP
: ip
, Port
: port
}
100 return nil, UnknownNetworkError(net
)
105 func startPlan9(ctx context
.Context
, net
string, addr Addr
) (ctl
*os
.File
, dest
, proto
, name
string, err error
) {
110 switch a
:= addr
.(type) {
120 err
= UnknownNetworkError(net
)
125 err
= InvalidAddrError("port should be < 65536")
129 clone
, dest
, err
:= queryCS1(ctx
, proto
, ip
, port
)
133 f
, err
:= os
.OpenFile(clone
, os
.O_RDWR
, 0)
138 n
, err
:= f
.Read(buf
[:])
143 return f
, dest
, proto
, string(buf
[:n
]), nil
146 func fixErr(err error
) {
147 oe
, ok
:= err
.(*OpError
)
151 nonNilInterface
:= func(a Addr
) bool {
152 switch a
:= a
.(type) {
163 if nonNilInterface(oe
.Source
) {
166 if nonNilInterface(oe
.Addr
) {
169 if pe
, ok
:= oe
.Err
.(*fs
.PathError
); ok
{
170 if _
, ok
= pe
.Err
.(syscall
.ErrorString
); ok
{
176 func dialPlan9(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
177 defer func() { fixErr(err
) }()
182 resc
:= make(chan res
)
184 testHookDialChannel()
185 fd
, err
:= dialPlan9Blocking(ctx
, net
, laddr
, raddr
)
187 case resc
<- res
{fd
, err
}:
196 return res
.fd
, res
.err
198 return nil, mapErr(ctx
.Err())
202 func dialPlan9Blocking(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
203 if isWildcard(raddr
) {
204 raddr
= toLocal(raddr
, net
)
206 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, raddr
)
210 if la
:= plan9LocalAddr(laddr
); la
== "" {
211 err
= hangupCtlWrite(ctx
, proto
, f
, "connect "+dest
)
213 err
= hangupCtlWrite(ctx
, proto
, f
, "connect "+dest
+" "+la
)
219 data
, err
:= os
.OpenFile(netdir
+"/"+proto
+"/"+name
+"/data", os
.O_RDWR
, 0)
224 laddr
, err
= readPlan9Addr(net
, netdir
+"/"+proto
+"/"+name
+"/local")
230 return newFD(proto
, name
, nil, f
, data
, laddr
, raddr
)
233 func listenPlan9(ctx context
.Context
, net
string, laddr Addr
) (fd
*netFD
, err error
) {
234 defer func() { fixErr(err
) }()
235 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, laddr
)
239 _
, err
= f
.WriteString("announce " + dest
)
242 return nil, &OpError
{Op
: "announce", Net
: net
, Source
: laddr
, Addr
: nil, Err
: err
}
244 laddr
, err
= readPlan9Addr(net
, netdir
+"/"+proto
+"/"+name
+"/local")
249 return newFD(proto
, name
, nil, f
, nil, laddr
, nil)
252 func (fd
*netFD
) netFD() (*netFD
, error
) {
253 return newFD(fd
.net
, fd
.n
, fd
.listen
, fd
.ctl
, fd
.data
, fd
.laddr
, fd
.raddr
)
256 func (fd
*netFD
) acceptPlan9() (nfd
*netFD
, err error
) {
257 defer func() { fixErr(err
) }()
258 if err
:= fd
.pfd
.ReadLock(); err
!= nil {
261 defer fd
.pfd
.ReadUnlock()
262 listen
, err
:= os
.Open(fd
.dir
+ "/listen")
267 n
, err
:= listen
.Read(buf
[:])
272 name
:= string(buf
[:n
])
273 ctl
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/ctl", os
.O_RDWR
, 0)
278 data
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/data", os
.O_RDWR
, 0)
284 raddr
, err
:= readPlan9Addr(fd
.net
, netdir
+"/"+fd
.net
+"/"+name
+"/remote")
291 return newFD(fd
.net
, name
, listen
, ctl
, data
, fd
.laddr
, raddr
)
294 func isWildcard(a Addr
) bool {
296 switch a
:= a
.(type) {
298 wildcard
= a
.isWildcard()
300 wildcard
= a
.isWildcard()
302 wildcard
= a
.isWildcard()
307 func toLocal(a Addr
, net
string) Addr
{
308 switch a
:= a
.(type) {
310 a
.IP
= loopbackIP(net
)
312 a
.IP
= loopbackIP(net
)
314 a
.IP
= loopbackIP(net
)
319 // plan9LocalAddr returns a Plan 9 local address string.
320 // See setladdrport at https://9p.io/sources/plan9/sys/src/9/ip/devip.c.
321 func plan9LocalAddr(addr Addr
) string {
324 switch a
:= addr
.(type) {
336 if len(ip
) == 0 || ip
.IsUnspecified() {
340 return itoa
.Itoa(port
)
342 return ip
.String() + "!" + itoa
.Itoa(port
)
345 func hangupCtlWrite(ctx context
.Context
, proto
string, ctl
*os
.File
, msg
string) error
{
347 _
, err
:= ctl
.WriteString(msg
)
350 written
:= make(chan struct{})
351 errc
:= make(chan error
)
355 ctl
.WriteString("hangup")
356 errc
<- mapErr(ctx
.Err())
361 _
, err
:= ctl
.WriteString(msg
)
363 if e
:= <-errc
; err
== nil && e
!= nil { // we hung up