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.
5 // Internet protocol family sockets for Plan 9
15 func probe(filename
, query
string) bool {
18 if file
, err
= open(filename
); err
!= nil {
23 for line
, ok
:= file
.readLine(); ok
&& !r
; line
, ok
= file
.readLine() {
28 for i
:= 0; i
< len(f
); i
++ {
39 func probeIPv4Stack() bool {
40 return probe(netdir
+"/iproute", "4i")
43 // probeIPv6Stack returns two boolean values. If the first boolean
44 // value is true, kernel supports basic IPv6 functionality. If the
45 // second boolean value is true, kernel supports IPv6 IPv4-mapping.
46 func probeIPv6Stack() (supportsIPv6
, supportsIPv4map
bool) {
47 // Plan 9 uses IPv6 natively, see ip(3).
48 r
:= probe(netdir
+"/iproute", "6i")
51 v
= probe(netdir
+"/iproute", "4i")
56 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
57 func parsePlan9Addr(s
string) (ip IP
, iport
int, err error
) {
58 addr
:= IPv4zero
// address contains port only
59 i
:= byteIndex(s
, '!')
63 return nil, 0, &ParseError
{Type
: "IP address", Text
: s
}
66 p
, _
, ok
:= dtoi(s
[i
+1:])
68 return nil, 0, &ParseError
{Type
: "port", Text
: s
}
70 if p
< 0 || p
> 0xFFFF {
71 return nil, 0, &AddrError
{Err
: "invalid port", Addr
: string(p
)}
76 func readPlan9Addr(proto
, filename
string) (addr Addr
, err error
) {
79 f
, err
:= os
.Open(filename
)
84 n
, err
:= f
.Read(buf
[:])
88 ip
, port
, err
:= parsePlan9Addr(string(buf
[:n
]))
94 addr
= &TCPAddr
{IP
: ip
, Port
: port
}
96 addr
= &UDPAddr
{IP
: ip
, Port
: port
}
98 return nil, UnknownNetworkError(proto
)
103 func startPlan9(ctx context
.Context
, net
string, addr Addr
) (ctl
*os
.File
, dest
, proto
, name
string, err error
) {
108 switch a
:= addr
.(type) {
118 err
= UnknownNetworkError(net
)
123 err
= InvalidAddrError("port should be < 65536")
127 clone
, dest
, err
:= queryCS1(ctx
, proto
, ip
, port
)
131 f
, err
:= os
.OpenFile(clone
, os
.O_RDWR
, 0)
136 n
, err
:= f
.Read(buf
[:])
141 return f
, dest
, proto
, string(buf
[:n
]), nil
144 func fixErr(err error
) {
145 oe
, ok
:= err
.(*OpError
)
149 nonNilInterface
:= func(a Addr
) bool {
150 switch a
:= a
.(type) {
161 if nonNilInterface(oe
.Source
) {
164 if nonNilInterface(oe
.Addr
) {
167 if pe
, ok
:= oe
.Err
.(*os
.PathError
); ok
{
168 if _
, ok
= pe
.Err
.(syscall
.ErrorString
); ok
{
174 func dialPlan9(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
175 defer func() { fixErr(err
) }()
180 resc
:= make(chan res
)
182 testHookDialChannel()
183 fd
, err
:= dialPlan9Blocking(ctx
, net
, laddr
, raddr
)
185 case resc
<- res
{fd
, err
}:
194 return res
.fd
, res
.err
196 return nil, mapErr(ctx
.Err())
200 func dialPlan9Blocking(ctx context
.Context
, net
string, laddr
, raddr Addr
) (fd
*netFD
, err error
) {
201 if isWildcard(raddr
) {
202 raddr
= toLocal(raddr
, net
)
204 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, raddr
)
208 _
, err
= f
.WriteString("connect " + dest
)
213 data
, err
:= os
.OpenFile(netdir
+"/"+proto
+"/"+name
+"/data", os
.O_RDWR
, 0)
218 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
224 return newFD(proto
, name
, nil, f
, data
, laddr
, raddr
)
227 func listenPlan9(ctx context
.Context
, net
string, laddr Addr
) (fd
*netFD
, err error
) {
228 defer func() { fixErr(err
) }()
229 f
, dest
, proto
, name
, err
:= startPlan9(ctx
, net
, laddr
)
233 _
, err
= f
.WriteString("announce " + dest
)
238 laddr
, err
= readPlan9Addr(proto
, netdir
+"/"+proto
+"/"+name
+"/local")
243 return newFD(proto
, name
, nil, f
, nil, laddr
, nil)
246 func (fd
*netFD
) netFD() (*netFD
, error
) {
247 return newFD(fd
.net
, fd
.n
, fd
.listen
, fd
.ctl
, fd
.data
, fd
.laddr
, fd
.raddr
)
250 func (fd
*netFD
) acceptPlan9() (nfd
*netFD
, err error
) {
251 defer func() { fixErr(err
) }()
252 if err
:= fd
.readLock(); err
!= nil {
255 defer fd
.readUnlock()
256 listen
, err
:= os
.Open(fd
.dir
+ "/listen")
261 n
, err
:= listen
.Read(buf
[:])
266 name
:= string(buf
[:n
])
267 ctl
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/ctl", os
.O_RDWR
, 0)
272 data
, err
:= os
.OpenFile(netdir
+"/"+fd
.net
+"/"+name
+"/data", os
.O_RDWR
, 0)
278 raddr
, err
:= readPlan9Addr(fd
.net
, netdir
+"/"+fd
.net
+"/"+name
+"/remote")
285 return newFD(fd
.net
, name
, listen
, ctl
, data
, fd
.laddr
, raddr
)
288 func isWildcard(a Addr
) bool {
290 switch a
:= a
.(type) {
292 wildcard
= a
.isWildcard()
294 wildcard
= a
.isWildcard()
296 wildcard
= a
.isWildcard()
301 func toLocal(a Addr
, net
string) Addr
{
302 switch a
:= a
.(type) {
304 a
.IP
= loopbackIP(net
)
306 a
.IP
= loopbackIP(net
)
308 a
.IP
= loopbackIP(net
)