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
5package net
6
7import (
8	"internal/poll"
9	"io"
10	"os"
11	"syscall"
12)
13
14// Network file descriptor.
15type netFD struct {
16	pfd poll.FD
17
18	// immutable until Close
19	net               string
20	n                 string
21	dir               string
22	listen, ctl, data *os.File
23	laddr, raddr      Addr
24	isStream          bool
25}
26
27var netdir = "/net" // default network
28
29func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
30	ret := &netFD{
31		net:    net,
32		n:      name,
33		dir:    netdir + "/" + net + "/" + name,
34		listen: listen,
35		ctl:    ctl, data: data,
36		laddr: laddr,
37		raddr: raddr,
38	}
39	ret.pfd.Destroy = ret.destroy
40	return ret, nil
41}
42
43func (fd *netFD) init() error {
44	// stub for future fd.pd.Init(fd)
45	return nil
46}
47
48func (fd *netFD) name() string {
49	var ls, rs string
50	if fd.laddr != nil {
51		ls = fd.laddr.String()
52	}
53	if fd.raddr != nil {
54		rs = fd.raddr.String()
55	}
56	return fd.net + ":" + ls + "->" + rs
57}
58
59func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
60
61func (fd *netFD) destroy() {
62	if !fd.ok() {
63		return
64	}
65	err := fd.ctl.Close()
66	if fd.data != nil {
67		if err1 := fd.data.Close(); err1 != nil && err == nil {
68			err = err1
69		}
70	}
71	if fd.listen != nil {
72		if err1 := fd.listen.Close(); err1 != nil && err == nil {
73			err = err1
74		}
75	}
76	fd.ctl = nil
77	fd.data = nil
78	fd.listen = nil
79}
80
81func (fd *netFD) Read(b []byte) (n int, err error) {
82	if !fd.ok() || fd.data == nil {
83		return 0, syscall.EINVAL
84	}
85	n, err = fd.pfd.Read(fd.data.Read, b)
86	if fd.net == "udp" && err == io.EOF {
87		n = 0
88		err = nil
89	}
90	return
91}
92
93func (fd *netFD) Write(b []byte) (n int, err error) {
94	if !fd.ok() || fd.data == nil {
95		return 0, syscall.EINVAL
96	}
97	return fd.pfd.Write(fd.data.Write, b)
98}
99
100func (fd *netFD) closeRead() error {
101	if !fd.ok() {
102		return syscall.EINVAL
103	}
104	return syscall.EPLAN9
105}
106
107func (fd *netFD) closeWrite() error {
108	if !fd.ok() {
109		return syscall.EINVAL
110	}
111	return syscall.EPLAN9
112}
113
114func (fd *netFD) Close() error {
115	if err := fd.pfd.Close(); err != nil {
116		return err
117	}
118	if !fd.ok() {
119		return syscall.EINVAL
120	}
121	if fd.net == "tcp" {
122		// The following line is required to unblock Reads.
123		_, err := fd.ctl.WriteString("close")
124		if err != nil {
125			return err
126		}
127	}
128	err := fd.ctl.Close()
129	if fd.data != nil {
130		if err1 := fd.data.Close(); err1 != nil && err == nil {
131			err = err1
132		}
133	}
134	if fd.listen != nil {
135		if err1 := fd.listen.Close(); err1 != nil && err == nil {
136			err = err1
137		}
138	}
139	fd.ctl = nil
140	fd.data = nil
141	fd.listen = nil
142	return err
143}
144
145// This method is only called via Conn.
146func (fd *netFD) dup() (*os.File, error) {
147	if !fd.ok() || fd.data == nil {
148		return nil, syscall.EINVAL
149	}
150	return fd.file(fd.data, fd.dir+"/data")
151}
152
153func (l *TCPListener) dup() (*os.File, error) {
154	if !l.fd.ok() {
155		return nil, syscall.EINVAL
156	}
157	return l.fd.file(l.fd.ctl, l.fd.dir+"/ctl")
158}
159
160func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
161	dfd, err := syscall.Dup(int(f.Fd()), -1)
162	if err != nil {
163		return nil, os.NewSyscallError("dup", err)
164	}
165	return os.NewFile(uintptr(dfd), s), nil
166}
167
168func setReadBuffer(fd *netFD, bytes int) error {
169	return syscall.EPLAN9
170}
171
172func setWriteBuffer(fd *netFD, bytes int) error {
173	return syscall.EPLAN9
174}
175