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.
4
5// Internet protocol family sockets for Plan 9
6
7package net
8
9import (
10	"os"
11	"syscall"
12)
13
14func probe(filename, query string) bool {
15	var file *file
16	var err error
17	if file, err = open(filename); err != nil {
18		return false
19	}
20
21	r := false
22	for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
23		f := getFields(line)
24		if len(f) < 3 {
25			continue
26		}
27		for i := 0; i < len(f); i++ {
28			if query == f[i] {
29				r = true
30				break
31			}
32		}
33	}
34	file.close()
35	return r
36}
37
38func probeIPv4Stack() bool {
39	return probe(netdir+"/iproute", "4i")
40}
41
42// probeIPv6Stack returns two boolean values.  If the first boolean
43// value is true, kernel supports basic IPv6 functionality.  If the
44// second boolean value is true, kernel supports IPv6 IPv4-mapping.
45func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
46	// Plan 9 uses IPv6 natively, see ip(3).
47	r := probe(netdir+"/iproute", "6i")
48	v := false
49	if r {
50		v = probe(netdir+"/iproute", "4i")
51	}
52	return r, v
53}
54
55// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
56func parsePlan9Addr(s string) (ip IP, iport int, err error) {
57	addr := IPv4zero // address contains port only
58	i := byteIndex(s, '!')
59	if i >= 0 {
60		addr = ParseIP(s[:i])
61		if addr == nil {
62			return nil, 0, &ParseError{Type: "IP address", Text: s}
63		}
64	}
65	p, _, ok := dtoi(s[i+1:], 0)
66	if !ok {
67		return nil, 0, &ParseError{Type: "port", Text: s}
68	}
69	if p < 0 || p > 0xFFFF {
70		return nil, 0, &AddrError{Err: "invalid port", Addr: string(p)}
71	}
72	return addr, p, nil
73}
74
75func readPlan9Addr(proto, filename string) (addr Addr, err error) {
76	var buf [128]byte
77
78	f, err := os.Open(filename)
79	if err != nil {
80		return
81	}
82	defer f.Close()
83	n, err := f.Read(buf[:])
84	if err != nil {
85		return
86	}
87	ip, port, err := parsePlan9Addr(string(buf[:n]))
88	if err != nil {
89		return
90	}
91	switch proto {
92	case "tcp":
93		addr = &TCPAddr{IP: ip, Port: port}
94	case "udp":
95		addr = &UDPAddr{IP: ip, Port: port}
96	default:
97		return nil, UnknownNetworkError(proto)
98	}
99	return addr, nil
100}
101
102func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
103	var (
104		ip   IP
105		port int
106	)
107	switch a := addr.(type) {
108	case *TCPAddr:
109		proto = "tcp"
110		ip = a.IP
111		port = a.Port
112	case *UDPAddr:
113		proto = "udp"
114		ip = a.IP
115		port = a.Port
116	default:
117		err = UnknownNetworkError(net)
118		return
119	}
120
121	clone, dest, err := queryCS1(proto, ip, port)
122	if err != nil {
123		return
124	}
125	f, err := os.OpenFile(clone, os.O_RDWR, 0)
126	if err != nil {
127		return
128	}
129	var buf [16]byte
130	n, err := f.Read(buf[:])
131	if err != nil {
132		f.Close()
133		return
134	}
135	return f, dest, proto, string(buf[:n]), nil
136}
137
138func netErr(e error) {
139	oe, ok := e.(*OpError)
140	if !ok {
141		return
142	}
143	nonNilInterface := func(a Addr) bool {
144		switch a := a.(type) {
145		case *TCPAddr:
146			return a == nil
147		case *UDPAddr:
148			return a == nil
149		case *IPAddr:
150			return a == nil
151		default:
152			return false
153		}
154	}
155	if nonNilInterface(oe.Source) {
156		oe.Source = nil
157	}
158	if nonNilInterface(oe.Addr) {
159		oe.Addr = nil
160	}
161	if pe, ok := oe.Err.(*os.PathError); ok {
162		if _, ok = pe.Err.(syscall.ErrorString); ok {
163			oe.Err = pe.Err
164		}
165	}
166}
167
168func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
169	defer func() { netErr(err) }()
170	f, dest, proto, name, err := startPlan9(net, raddr)
171	if err != nil {
172		return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
173	}
174	_, err = f.WriteString("connect " + dest)
175	if err != nil {
176		f.Close()
177		return nil, &OpError{Op: "dial", Net: f.Name(), Source: laddr, Addr: raddr, Err: err}
178	}
179	data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
180	if err != nil {
181		f.Close()
182		return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
183	}
184	laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
185	if err != nil {
186		data.Close()
187		f.Close()
188		return nil, &OpError{Op: "dial", Net: proto, Source: laddr, Addr: raddr, Err: err}
189	}
190	return newFD(proto, name, f, data, laddr, raddr)
191}
192
193func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
194	defer func() { netErr(err) }()
195	f, dest, proto, name, err := startPlan9(net, laddr)
196	if err != nil {
197		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
198	}
199	_, err = f.WriteString("announce " + dest)
200	if err != nil {
201		f.Close()
202		return nil, &OpError{Op: "announce", Net: proto, Source: nil, Addr: laddr, Err: err}
203	}
204	laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
205	if err != nil {
206		f.Close()
207		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
208	}
209	return newFD(proto, name, f, nil, laddr, nil)
210}
211
212func (fd *netFD) netFD() (*netFD, error) {
213	return newFD(fd.net, fd.n, fd.ctl, fd.data, fd.laddr, fd.raddr)
214}
215
216func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
217	defer func() { netErr(err) }()
218	if err := fd.readLock(); err != nil {
219		return nil, err
220	}
221	defer fd.readUnlock()
222	f, err := os.Open(fd.dir + "/listen")
223	if err != nil {
224		return nil, &OpError{Op: "accept", Net: fd.dir + "/listen", Source: nil, Addr: fd.laddr, Err: err}
225	}
226	var buf [16]byte
227	n, err := f.Read(buf[:])
228	if err != nil {
229		f.Close()
230		return nil, &OpError{Op: "accept", Net: fd.dir + "/listen", Source: nil, Addr: fd.laddr, Err: err}
231	}
232	name := string(buf[:n])
233	data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
234	if err != nil {
235		f.Close()
236		return nil, &OpError{Op: "accept", Net: fd.net, Source: nil, Addr: fd.laddr, Err: err}
237	}
238	raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
239	if err != nil {
240		data.Close()
241		f.Close()
242		return nil, &OpError{Op: "accept", Net: fd.net, Source: nil, Addr: fd.laddr, Err: err}
243	}
244	return newFD(fd.net, name, f, data, fd.laddr, raddr)
245}
246