mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
split ipsock.go, sock.go, and unixsock.go out of net.go
prior to cleanup. no changes, only moving. remove dependencies on strconv and strings R=r http://go/go-review/1017010
This commit is contained in:
parent
fd1add2768
commit
35ace1d1f5
9 changed files with 874 additions and 819 deletions
|
|
@ -5,8 +5,8 @@ bignum.install: fmt.install
|
|||
bufio.install: io.install os.install strconv.install utf8.install
|
||||
bytes.install: os.install unicode.install utf8.install
|
||||
compress/flate.install: bufio.install bytes.install io.install math.install os.install sort.install strconv.install
|
||||
compress/gzip.install: bufio.install compress/flate.install hash/crc32.install hash.install io.install os.install
|
||||
compress/zlib.install: bufio.install compress/flate.install hash/adler32.install hash.install io.install os.install
|
||||
compress/gzip.install: bufio.install compress/flate.install hash.install hash/crc32.install io.install os.install
|
||||
compress/zlib.install: bufio.install compress/flate.install hash.install hash/adler32.install io.install os.install
|
||||
container/heap.install: sort.install
|
||||
container/list.install:
|
||||
container/ring.install:
|
||||
|
|
@ -19,7 +19,7 @@ crypto/rc4.install: os.install strconv.install
|
|||
crypto/sha1.install: hash.install os.install
|
||||
debug/dwarf.install: encoding/binary.install os.install strconv.install
|
||||
debug/macho.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
|
||||
debug/elf.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
|
||||
debug/elf.install: debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
|
||||
debug/gosym.install: encoding/binary.install fmt.install os.install strconv.install strings.install
|
||||
debug/proc.install: container/vector.install fmt.install io.install os.install runtime.install strconv.install strings.install sync.install syscall.install
|
||||
ebnf.install: container/vector.install go/scanner.install go/token.install os.install strconv.install unicode.install utf8.install
|
||||
|
|
@ -47,13 +47,13 @@ hash/adler32.install: hash.install os.install
|
|||
hash/crc32.install: hash.install os.install
|
||||
http.install: bufio.install bytes.install container/vector.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install
|
||||
image.install:
|
||||
image/png.install: bufio.install compress/zlib.install hash/crc32.install hash.install image.install io.install os.install strconv.install
|
||||
image/png.install: bufio.install compress/zlib.install hash.install hash/crc32.install image.install io.install os.install strconv.install
|
||||
io.install: bytes.install os.install sort.install strings.install sync.install
|
||||
json.install: bytes.install container/vector.install fmt.install math.install reflect.install strconv.install strings.install utf8.install
|
||||
log.install: fmt.install io.install os.install runtime.install time.install
|
||||
malloc.install:
|
||||
math.install:
|
||||
net.install: fmt.install io.install once.install os.install reflect.install strconv.install strings.install sync.install syscall.install
|
||||
net.install: fmt.install io.install once.install os.install reflect.install sync.install syscall.install
|
||||
once.install: sync.install
|
||||
os.install: once.install syscall.install
|
||||
path.install: io.install os.install strings.install
|
||||
|
|
@ -63,14 +63,13 @@ regexp.install: bytes.install container/vector.install io.install os.install run
|
|||
rpc.install: bufio.install fmt.install gob.install http.install io.install log.install net.install os.install reflect.install sort.install strings.install sync.install template.install unicode.install utf8.install
|
||||
runtime.install:
|
||||
sort.install:
|
||||
strconv.install: bytes.install math.install os.install unicode.install utf8.install
|
||||
strconv.install: bytes.install math.install os.install strings.install unicode.install utf8.install
|
||||
strings.install: os.install unicode.install utf8.install
|
||||
sync.install: runtime.install
|
||||
syscall.install: sync.install
|
||||
tabwriter.install: bytes.install container/vector.install io.install os.install utf8.install
|
||||
template.install: bytes.install container/vector.install fmt.install io.install os.install reflect.install runtime.install strings.install
|
||||
testing.install: flag.install fmt.install os.install runtime.install utf8.install
|
||||
testing/expect.install: fmt.install os.install rand.install reflect.install strings.install
|
||||
testing/iotest.install: bytes.install io.install log.install os.install
|
||||
testing/quick.install: flag.install fmt.install math.install os.install rand.install reflect.install strings.install
|
||||
time.install: io.install once.install os.install syscall.install
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ GOFILES=\
|
|||
fd.go\
|
||||
fd_$(GOOS).go\
|
||||
ip.go\
|
||||
ipsock.go\
|
||||
net.go\
|
||||
parse.go\
|
||||
port.go\
|
||||
sock.go\
|
||||
unixsock.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
|
|
|||
|
|
@ -11,14 +11,12 @@
|
|||
// Could have a small cache.
|
||||
// Random UDP source port (net.Dial should do that for us).
|
||||
// Random request IDs.
|
||||
// More substantial error reporting.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"once";
|
||||
"os";
|
||||
"strings";
|
||||
)
|
||||
|
||||
// DNSError represents a DNS lookup error.
|
||||
|
|
@ -240,7 +238,7 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
|
|||
// If name is rooted (trailing dot) or has enough dots,
|
||||
// try it by itself first.
|
||||
rooted := len(name) > 0 && name[len(name)-1] == '.';
|
||||
if rooted || strings.Count(name, ".") >= cfg.ndots {
|
||||
if rooted || count(name, '.') >= cfg.ndots {
|
||||
rname := name;
|
||||
if !rooted {
|
||||
rname += ".";
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
package net
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
// IP address lengths (bytes).
|
||||
const (
|
||||
IPv4len = 4;
|
||||
|
|
|
|||
362
src/pkg/net/ipsock.go
Normal file
362
src/pkg/net/ipsock.go
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// IP sockets
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"os";
|
||||
"syscall";
|
||||
)
|
||||
|
||||
// Should we try to use the IPv4 socket interface if we're
|
||||
// only dealing with IPv4 sockets? As long as the host system
|
||||
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
|
||||
// interface. That simplifies our code and is most general.
|
||||
// Unfortunately, we need to run on kernels built without IPv6 support too.
|
||||
// So probe the kernel to figure it out.
|
||||
func kernelSupportsIPv6() bool {
|
||||
fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP);
|
||||
if fd >= 0 {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
return e == 0
|
||||
}
|
||||
|
||||
var preferIPv4 = !kernelSupportsIPv6()
|
||||
|
||||
// TODO(rsc): if syscall.OS == "linux", we're supposd to read
|
||||
// /proc/sys/net/core/somaxconn,
|
||||
// to take advantage of kernels that have raised the limit.
|
||||
func listenBacklog() int {
|
||||
return syscall.SOMAXCONN
|
||||
}
|
||||
|
||||
// ListenerTCP is a TCP network listener.
|
||||
// Clients should typically use variables of type Listener
|
||||
// instead of assuming TCP.
|
||||
type ListenerTCP struct {
|
||||
fd *netFD;
|
||||
}
|
||||
|
||||
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
|
||||
// Net must be "tcp", "tcp4", or "tcp6".
|
||||
// If laddr has a port of 0, it means to listen on some available port.
|
||||
// The caller can use l.Addr() to retrieve the chosen address.
|
||||
func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) {
|
||||
fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
e1 := syscall.Listen(fd.fd, listenBacklog());
|
||||
if e1 != 0 {
|
||||
syscall.Close(fd.fd);
|
||||
return nil, &OpError{"listen", "tcp", laddr, os.Errno(e1)};
|
||||
}
|
||||
l = new(ListenerTCP);
|
||||
l.fd = fd;
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// AcceptTCP accepts the next incoming call and returns the new connection
|
||||
// and the remote address.
|
||||
func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) {
|
||||
if l == nil || l.fd == nil || l.fd.fd < 0 {
|
||||
return nil, "", os.EINVAL
|
||||
}
|
||||
fd, e := l.fd.accept();
|
||||
if e != nil {
|
||||
return nil, "", e
|
||||
}
|
||||
return newConnTCP(fd, fd.raddr), fd.raddr, nil
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface;
|
||||
// it waits for the next call and returns a generic Conn.
|
||||
func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) {
|
||||
c1, r1, e1 := l.AcceptTCP();
|
||||
if e1 != nil {
|
||||
return nil, "", e1
|
||||
}
|
||||
return c1, r1, nil
|
||||
}
|
||||
|
||||
// Close stops listening on the TCP address.
|
||||
// Already Accepted connections are not closed.
|
||||
func (l *ListenerTCP) Close() os.Error {
|
||||
if l == nil || l.fd == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return l.fd.Close()
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *ListenerTCP) Addr() string {
|
||||
return l.fd.addr();
|
||||
}
|
||||
|
||||
// Internet sockets (TCP, UDP)
|
||||
|
||||
func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) {
|
||||
// Parse addresses (unless they are empty).
|
||||
var lip, rip IP;
|
||||
var lport, rport int;
|
||||
|
||||
if laddr != "" {
|
||||
if lip, lport, err = hostPortToIP(net, laddr, mode); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if raddr != "" {
|
||||
if rip, rport, err = hostPortToIP(net, raddr, mode); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out IP version.
|
||||
// If network has a suffix like "tcp4", obey it.
|
||||
vers := 0;
|
||||
switch net[len(net)-1] {
|
||||
case '4':
|
||||
vers = 4;
|
||||
case '6':
|
||||
vers = 6;
|
||||
default:
|
||||
// Otherwise, guess.
|
||||
// If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
|
||||
if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) {
|
||||
vers = 4
|
||||
} else {
|
||||
vers = 6
|
||||
}
|
||||
}
|
||||
|
||||
var family int;
|
||||
if vers == 4 {
|
||||
family = syscall.AF_INET
|
||||
} else {
|
||||
family = syscall.AF_INET6
|
||||
}
|
||||
|
||||
var la, ra syscall.Sockaddr;
|
||||
if lip != nil {
|
||||
if la, err = ipToSockaddr(family, lip, lport); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if rip != nil {
|
||||
if ra, err = ipToSockaddr(family, rip, rport); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
return fd, nil;
|
||||
|
||||
Error:
|
||||
addr := raddr;
|
||||
if mode == "listen" {
|
||||
addr = laddr;
|
||||
}
|
||||
return nil, &OpError{mode, net, addr, err};
|
||||
}
|
||||
|
||||
|
||||
// TCP connections.
|
||||
|
||||
// ConnTCP is an implementation of the Conn interface
|
||||
// for TCP network connections.
|
||||
type ConnTCP struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error {
|
||||
if c == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay))
|
||||
}
|
||||
|
||||
func newConnTCP(fd *netFD, raddr string) *ConnTCP {
|
||||
c := new(ConnTCP);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
c.SetNoDelay(true);
|
||||
return c
|
||||
}
|
||||
|
||||
// DialTCP is like Dial but can only connect to TCP networks
|
||||
// and returns a ConnTCP structure.
|
||||
func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) {
|
||||
if raddr == "" {
|
||||
return nil, &OpError{"dial", "tcp", "", errMissingAddress}
|
||||
}
|
||||
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnTCP(fd, raddr), nil
|
||||
}
|
||||
|
||||
|
||||
// UDP connections.
|
||||
|
||||
// TODO(rsc): UDP headers mode
|
||||
|
||||
// ConnUDP is an implementation of the Conn interface
|
||||
// for UDP network connections.
|
||||
type ConnUDP struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func newConnUDP(fd *netFD, raddr string) *ConnUDP {
|
||||
c := new(ConnUDP);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
return c
|
||||
}
|
||||
|
||||
// DialUDP is like Dial but can only connect to UDP networks
|
||||
// and returns a ConnUDP structure.
|
||||
func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) {
|
||||
if raddr == "" {
|
||||
return nil, &OpError{"dial", "udp", "", errMissingAddress}
|
||||
}
|
||||
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnUDP(fd, raddr), nil
|
||||
}
|
||||
|
||||
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
if ip = ip.To4(); ip == nil {
|
||||
return nil, os.EINVAL
|
||||
}
|
||||
s := new(syscall.SockaddrInet4);
|
||||
for i := 0; i < IPv4len; i++ {
|
||||
s.Addr[i] = ip[i];
|
||||
}
|
||||
s.Port = port;
|
||||
return s, nil;
|
||||
case syscall.AF_INET6:
|
||||
// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
|
||||
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
|
||||
// which it refuses to do. Rewrite to the IPv6 all zeros.
|
||||
if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
|
||||
ip = IPzero;
|
||||
}
|
||||
if ip = ip.To16(); ip == nil {
|
||||
return nil, os.EINVAL
|
||||
}
|
||||
s := new(syscall.SockaddrInet6);
|
||||
for i := 0; i < IPv6len; i++ {
|
||||
s.Addr[i] = ip[i];
|
||||
}
|
||||
s.Port = port;
|
||||
return s, nil;
|
||||
}
|
||||
return nil, os.EINVAL;
|
||||
}
|
||||
|
||||
// Split "host:port" into "host" and "port".
|
||||
// Host cannot contain colons unless it is bracketed.
|
||||
func splitHostPort(hostport string) (host, port string, err os.Error) {
|
||||
// The port starts after the last colon.
|
||||
i := last(hostport, ':');
|
||||
if i < 0 {
|
||||
err = &AddrError{"missing port in address", hostport};
|
||||
return;
|
||||
}
|
||||
|
||||
host, port = hostport[0:i], hostport[i+1:len(hostport)];
|
||||
|
||||
// Can put brackets around host ...
|
||||
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
|
||||
host = host[1:len(host)-1]
|
||||
} else {
|
||||
// ... but if there are no brackets, no colons.
|
||||
if byteIndex(host, ':') >= 0 {
|
||||
err = &AddrError{"too many colons in address", hostport};
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Join "host" and "port" into "host:port".
|
||||
// If host contains colons, will join into "[host]:port".
|
||||
func joinHostPort(host, port string) string {
|
||||
// If host has colons, have to bracket it.
|
||||
if byteIndex(host, ':') >= 0 {
|
||||
return "[" + host + "]:" + port
|
||||
}
|
||||
return host + ":" + port
|
||||
}
|
||||
|
||||
// Convert "host:port" into IP address and port.
|
||||
// For now, host and port must be numeric literals.
|
||||
// Eventually, we'll have name resolution.
|
||||
func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) {
|
||||
host, port, err := splitHostPort(hostport);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
var addr IP;
|
||||
if host == "" {
|
||||
if mode != "listen" {
|
||||
err = &AddrError{"no host in address", hostport};
|
||||
goto Error;
|
||||
}
|
||||
if preferIPv4 {
|
||||
addr = IPv4zero;
|
||||
} else {
|
||||
addr = IPzero; // wildcard - listen to all
|
||||
}
|
||||
}
|
||||
|
||||
// Try as an IP address.
|
||||
if addr == nil {
|
||||
addr = ParseIP(host);
|
||||
}
|
||||
if addr == nil {
|
||||
// Not an IP address. Try as a DNS name.
|
||||
_, addrs, err1 := LookupHost(host);
|
||||
if err1 != nil {
|
||||
err = err1;
|
||||
goto Error;
|
||||
}
|
||||
addr = ParseIP(addrs[0]);
|
||||
if addr == nil {
|
||||
// should not happen
|
||||
err = &AddrError{"LookupHost returned invalid address", addrs[0]};
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
p, i, ok := dtoi(port, 0);
|
||||
if !ok || i != len(port) {
|
||||
p, err = LookupPort(net, port);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if p < 0 || p > 0xFFFF {
|
||||
err = &AddrError{"invalid port", port};
|
||||
goto Error;
|
||||
}
|
||||
|
||||
return addr, p, nil;
|
||||
|
||||
Error:
|
||||
return nil, 0, err;
|
||||
}
|
||||
|
||||
|
|
@ -4,52 +4,11 @@
|
|||
|
||||
package net
|
||||
|
||||
import (
|
||||
"os";
|
||||
"reflect";
|
||||
"strconv";
|
||||
"strings";
|
||||
"syscall";
|
||||
)
|
||||
// TODO(rsc):
|
||||
// support for raw IP sockets
|
||||
// support for raw ethernet sockets
|
||||
|
||||
var errMissingAddress = os.ErrorString("missing address")
|
||||
|
||||
type OpError struct {
|
||||
Op string;
|
||||
Net string;
|
||||
Addr string;
|
||||
Error os.Error;
|
||||
}
|
||||
|
||||
func (e *OpError) String() string {
|
||||
s := e.Op;
|
||||
if e.Net != "" {
|
||||
s += " " + e.Net;
|
||||
}
|
||||
if e.Addr != "" {
|
||||
s += " " + e.Addr;
|
||||
}
|
||||
s += ": " + e.Error.String();
|
||||
return s;
|
||||
}
|
||||
|
||||
type AddrError struct {
|
||||
Error string;
|
||||
Addr string;
|
||||
}
|
||||
|
||||
func (e *AddrError) String() string {
|
||||
s := e.Error;
|
||||
if e.Addr != "" {
|
||||
s += " " + e.Addr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
type UnknownNetworkError string
|
||||
func (e UnknownNetworkError) String() string {
|
||||
return "unknown network " + string(e);
|
||||
}
|
||||
import "os"
|
||||
|
||||
// Conn is a generic network connection.
|
||||
type Conn interface {
|
||||
|
|
@ -134,681 +93,12 @@ type Conn interface {
|
|||
BindToDevice(dev string) os.Error;
|
||||
}
|
||||
|
||||
// Should we try to use the IPv4 socket interface if we're
|
||||
// only dealing with IPv4 sockets? As long as the host system
|
||||
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
|
||||
// interface. That simplifies our code and is most general.
|
||||
// Unfortunately, we need to run on kernels built without IPv6 support too.
|
||||
// So probe the kernel to figure it out.
|
||||
func kernelSupportsIPv6() bool {
|
||||
fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP);
|
||||
if fd >= 0 {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
return e == 0
|
||||
}
|
||||
|
||||
var preferIPv4 = !kernelSupportsIPv6()
|
||||
|
||||
// TODO(rsc): if syscall.OS == "linux", we're supposd to read
|
||||
// /proc/sys/net/core/somaxconn,
|
||||
// to take advantage of kernels that have raised the limit.
|
||||
func listenBacklog() int {
|
||||
return syscall.SOMAXCONN
|
||||
}
|
||||
|
||||
// Split "host:port" into "host" and "port".
|
||||
// Host cannot contain colons unless it is bracketed.
|
||||
func splitHostPort(hostport string) (host, port string, err os.Error) {
|
||||
// The port starts after the last colon.
|
||||
i := strings.LastIndex(hostport, ":");
|
||||
if i < 0 {
|
||||
err = &AddrError{"missing port in address", hostport};
|
||||
return;
|
||||
}
|
||||
|
||||
host, port = hostport[0:i], hostport[i+1:len(hostport)];
|
||||
|
||||
// Can put brackets around host ...
|
||||
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
|
||||
host = host[1:len(host)-1]
|
||||
} else {
|
||||
// ... but if there are no brackets, no colons.
|
||||
if byteIndex(host, ':') >= 0 {
|
||||
err = &AddrError{"too many colons in address", hostport};
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Join "host" and "port" into "host:port".
|
||||
// If host contains colons, will join into "[host]:port".
|
||||
func joinHostPort(host, port string) string {
|
||||
// If host has colons, have to bracket it.
|
||||
if byteIndex(host, ':') >= 0 {
|
||||
return "[" + host + "]:" + port
|
||||
}
|
||||
return host + ":" + port
|
||||
}
|
||||
|
||||
// Convert "host:port" into IP address and port.
|
||||
// For now, host and port must be numeric literals.
|
||||
// Eventually, we'll have name resolution.
|
||||
func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) {
|
||||
host, port, err := splitHostPort(hostport);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
var addr IP;
|
||||
if host == "" {
|
||||
if mode != "listen" {
|
||||
err = &AddrError{"no host in address", hostport};
|
||||
goto Error;
|
||||
}
|
||||
if preferIPv4 {
|
||||
addr = IPv4zero;
|
||||
} else {
|
||||
addr = IPzero; // wildcard - listen to all
|
||||
}
|
||||
}
|
||||
|
||||
// Try as an IP address.
|
||||
if addr == nil {
|
||||
addr = ParseIP(host);
|
||||
}
|
||||
if addr == nil {
|
||||
// Not an IP address. Try as a DNS name.
|
||||
_, addrs, err1 := LookupHost(host);
|
||||
if err1 != nil {
|
||||
err = err1;
|
||||
goto Error;
|
||||
}
|
||||
addr = ParseIP(addrs[0]);
|
||||
if addr == nil {
|
||||
// should not happen
|
||||
err = &AddrError{"LookupHost returned invalid address", addrs[0]};
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
p, i, ok := dtoi(port, 0);
|
||||
if !ok || i != len(port) {
|
||||
p, err = LookupPort(net, port);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if p < 0 || p > 0xFFFF {
|
||||
err = &AddrError{"invalid port", port};
|
||||
goto Error;
|
||||
}
|
||||
|
||||
return addr, p, nil;
|
||||
|
||||
Error:
|
||||
return nil, 0, err;
|
||||
}
|
||||
|
||||
type UnknownSocketError struct {
|
||||
sa syscall.Sockaddr;
|
||||
}
|
||||
func (e *UnknownSocketError) String() string {
|
||||
return "unknown socket address type " + reflect.Typeof(e.sa).String()
|
||||
}
|
||||
|
||||
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
|
||||
switch a := sa.(type) {
|
||||
case *syscall.SockaddrInet4:
|
||||
return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil;
|
||||
case *syscall.SockaddrInet6:
|
||||
return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil;
|
||||
case *syscall.SockaddrUnix:
|
||||
return a.Name, nil;
|
||||
}
|
||||
|
||||
return "", &UnknownSocketError{sa};
|
||||
}
|
||||
|
||||
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
if ip = ip.To4(); ip == nil {
|
||||
return nil, os.EINVAL
|
||||
}
|
||||
s := new(syscall.SockaddrInet4);
|
||||
for i := 0; i < IPv4len; i++ {
|
||||
s.Addr[i] = ip[i];
|
||||
}
|
||||
s.Port = port;
|
||||
return s, nil;
|
||||
case syscall.AF_INET6:
|
||||
// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
|
||||
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
|
||||
// which it refuses to do. Rewrite to the IPv6 all zeros.
|
||||
if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
|
||||
ip = IPzero;
|
||||
}
|
||||
if ip = ip.To16(); ip == nil {
|
||||
return nil, os.EINVAL
|
||||
}
|
||||
s := new(syscall.SockaddrInet6);
|
||||
for i := 0; i < IPv6len; i++ {
|
||||
s.Addr[i] = ip[i];
|
||||
}
|
||||
s.Port = port;
|
||||
return s, nil;
|
||||
}
|
||||
return nil, os.EINVAL;
|
||||
}
|
||||
|
||||
// Boolean to int.
|
||||
func boolint(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Generic socket creation.
|
||||
func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) {
|
||||
// See ../syscall/exec.go for description of ForkLock.
|
||||
syscall.ForkLock.RLock();
|
||||
s, e := syscall.Socket(f, p, t);
|
||||
if e != 0 {
|
||||
syscall.ForkLock.RUnlock();
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
syscall.CloseOnExec(s);
|
||||
syscall.ForkLock.RUnlock();
|
||||
|
||||
// Allow reuse of recently-used addresses.
|
||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1);
|
||||
|
||||
if la != nil {
|
||||
e = syscall.Bind(s, la);
|
||||
if e != 0 {
|
||||
syscall.Close(s);
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
}
|
||||
|
||||
if ra != nil {
|
||||
e = syscall.Connect(s, ra);
|
||||
if e != 0 {
|
||||
syscall.Close(s);
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = newFD(s, net, laddr, raddr);
|
||||
if err != nil {
|
||||
syscall.Close(s);
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
|
||||
// Generic implementation of Conn interface; not exported.
|
||||
type connBase struct {
|
||||
fd *netFD;
|
||||
raddr string;
|
||||
}
|
||||
|
||||
func (c *connBase) LocalAddr() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.fd.addr();
|
||||
}
|
||||
|
||||
func (c *connBase) RemoteAddr() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.fd.remoteAddr();
|
||||
}
|
||||
|
||||
func (c *connBase) File() *os.File {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.fd.file;
|
||||
}
|
||||
|
||||
func (c *connBase) sysFD() int {
|
||||
if c == nil || c.fd == nil {
|
||||
return -1;
|
||||
}
|
||||
return c.fd.fd;
|
||||
}
|
||||
|
||||
func (c *connBase) Read(b []byte) (n int, err os.Error) {
|
||||
n, err = c.fd.Read(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) Write(b []byte) (n int, err os.Error) {
|
||||
n, err = c.fd.Write(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) {
|
||||
if c == nil {
|
||||
return -1, "", os.EINVAL
|
||||
}
|
||||
n, err = c.Read(b);
|
||||
return n, c.raddr, err
|
||||
}
|
||||
|
||||
func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) {
|
||||
if c == nil {
|
||||
return -1, os.EINVAL
|
||||
}
|
||||
if raddr != c.raddr {
|
||||
return -1, os.EINVAL
|
||||
}
|
||||
n, err = c.Write(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) Close() os.Error {
|
||||
if c == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return c.fd.Close()
|
||||
}
|
||||
|
||||
|
||||
func setsockoptInt(fd, level, opt int, value int) os.Error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value));
|
||||
}
|
||||
|
||||
func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
|
||||
var tv = syscall.NsecToTimeval(nsec);
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv));
|
||||
}
|
||||
|
||||
func (c *connBase) SetReadBuffer(bytes int) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes);
|
||||
}
|
||||
|
||||
func (c *connBase) SetWriteBuffer(bytes int) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes);
|
||||
}
|
||||
|
||||
func (c *connBase) SetReadTimeout(nsec int64) os.Error {
|
||||
c.fd.rdeadline_delta = nsec;
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (c *connBase) SetWriteTimeout(nsec int64) os.Error {
|
||||
c.fd.wdeadline_delta = nsec;
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (c *connBase) SetTimeout(nsec int64) os.Error {
|
||||
if e := c.SetReadTimeout(nsec); e != nil {
|
||||
return e
|
||||
}
|
||||
return c.SetWriteTimeout(nsec)
|
||||
}
|
||||
|
||||
func (c *connBase) SetReuseAddr(reuse bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse));
|
||||
}
|
||||
|
||||
func (c *connBase) BindToDevice(dev string) os.Error {
|
||||
// TODO(rsc): call setsockopt with null-terminated string pointer
|
||||
return os.EINVAL
|
||||
}
|
||||
|
||||
func (c *connBase) SetDontRoute(dontroute bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute));
|
||||
}
|
||||
|
||||
func (c *connBase) SetKeepAlive(keepalive bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive));
|
||||
}
|
||||
|
||||
func (c *connBase) SetLinger(sec int) os.Error {
|
||||
var l syscall.Linger;
|
||||
if sec >= 0 {
|
||||
l.Onoff = 1;
|
||||
l.Linger = int32(sec);
|
||||
} else {
|
||||
l.Onoff = 0;
|
||||
l.Linger = 0;
|
||||
}
|
||||
e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l);
|
||||
return os.NewSyscallError("setsockopt", e);
|
||||
}
|
||||
|
||||
|
||||
// Internet sockets (TCP, UDP)
|
||||
|
||||
func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) {
|
||||
// Parse addresses (unless they are empty).
|
||||
var lip, rip IP;
|
||||
var lport, rport int;
|
||||
|
||||
if laddr != "" {
|
||||
if lip, lport, err = hostPortToIP(net, laddr, mode); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if raddr != "" {
|
||||
if rip, rport, err = hostPortToIP(net, raddr, mode); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out IP version.
|
||||
// If network has a suffix like "tcp4", obey it.
|
||||
vers := 0;
|
||||
switch net[len(net)-1] {
|
||||
case '4':
|
||||
vers = 4;
|
||||
case '6':
|
||||
vers = 6;
|
||||
default:
|
||||
// Otherwise, guess.
|
||||
// If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
|
||||
if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) {
|
||||
vers = 4
|
||||
} else {
|
||||
vers = 6
|
||||
}
|
||||
}
|
||||
|
||||
var family int;
|
||||
if vers == 4 {
|
||||
family = syscall.AF_INET
|
||||
} else {
|
||||
family = syscall.AF_INET6
|
||||
}
|
||||
|
||||
var la, ra syscall.Sockaddr;
|
||||
if lip != nil {
|
||||
if la, err = ipToSockaddr(family, lip, lport); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
if rip != nil {
|
||||
if ra, err = ipToSockaddr(family, rip, rport); err != nil {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
return fd, nil;
|
||||
|
||||
Error:
|
||||
addr := raddr;
|
||||
if mode == "listen" {
|
||||
addr = laddr;
|
||||
}
|
||||
return nil, &OpError{mode, net, addr, err};
|
||||
}
|
||||
|
||||
|
||||
// TCP connections.
|
||||
|
||||
// ConnTCP is an implementation of the Conn interface
|
||||
// for TCP network connections.
|
||||
type ConnTCP struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error {
|
||||
if c == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay))
|
||||
}
|
||||
|
||||
func newConnTCP(fd *netFD, raddr string) *ConnTCP {
|
||||
c := new(ConnTCP);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
c.SetNoDelay(true);
|
||||
return c
|
||||
}
|
||||
|
||||
// DialTCP is like Dial but can only connect to TCP networks
|
||||
// and returns a ConnTCP structure.
|
||||
func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) {
|
||||
if raddr == "" {
|
||||
return nil, &OpError{"dial", "tcp", "", errMissingAddress}
|
||||
}
|
||||
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnTCP(fd, raddr), nil
|
||||
}
|
||||
|
||||
|
||||
// UDP connections.
|
||||
|
||||
// TODO(rsc): UDP headers mode
|
||||
|
||||
// ConnUDP is an implementation of the Conn interface
|
||||
// for UDP network connections.
|
||||
type ConnUDP struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func newConnUDP(fd *netFD, raddr string) *ConnUDP {
|
||||
c := new(ConnUDP);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
return c
|
||||
}
|
||||
|
||||
// DialUDP is like Dial but can only connect to UDP networks
|
||||
// and returns a ConnUDP structure.
|
||||
func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) {
|
||||
if raddr == "" {
|
||||
return nil, &OpError{"dial", "udp", "", errMissingAddress}
|
||||
}
|
||||
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnUDP(fd, raddr), nil
|
||||
}
|
||||
|
||||
|
||||
// TODO: raw IP connections
|
||||
|
||||
// TODO: raw ethernet connections
|
||||
|
||||
|
||||
// Unix domain sockets
|
||||
|
||||
func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) {
|
||||
var proto int;
|
||||
switch net {
|
||||
default:
|
||||
return nil, UnknownNetworkError(net);
|
||||
case "unix":
|
||||
proto = syscall.SOCK_STREAM;
|
||||
case "unix-dgram":
|
||||
proto = syscall.SOCK_DGRAM;
|
||||
}
|
||||
|
||||
var la, ra syscall.Sockaddr;
|
||||
switch mode {
|
||||
default:
|
||||
panic("unixSocket", mode);
|
||||
|
||||
case "dial":
|
||||
if laddr != "" {
|
||||
return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}}
|
||||
}
|
||||
if raddr == "" {
|
||||
return nil, &OpError{mode, net, "", errMissingAddress}
|
||||
}
|
||||
ra = &syscall.SockaddrUnix{Name: raddr};
|
||||
|
||||
case "listen":
|
||||
if laddr == "" {
|
||||
return nil, &OpError{mode, net, "", errMissingAddress}
|
||||
}
|
||||
la = &syscall.SockaddrUnix{Name: laddr};
|
||||
if raddr != "" {
|
||||
return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}}
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
return fd, nil;
|
||||
|
||||
Error:
|
||||
addr := raddr;
|
||||
if mode == "listen" {
|
||||
addr = laddr;
|
||||
}
|
||||
return nil, &OpError{mode, net, addr, err};
|
||||
}
|
||||
|
||||
// ConnUnix is an implementation of the Conn interface
|
||||
// for connections to Unix domain sockets.
|
||||
type ConnUnix struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func newConnUnix(fd *netFD, raddr string) *ConnUnix {
|
||||
c := new(ConnUnix);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
return c;
|
||||
}
|
||||
|
||||
// DialUnix is like Dial but can only connect to Unix domain sockets
|
||||
// and returns a ConnUnix structure. The laddr argument must be
|
||||
// the empty string; it is included only to match the signature of
|
||||
// the other dial routines.
|
||||
func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) {
|
||||
fd, e := unixSocket(net, laddr, raddr, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnUnix(fd, raddr), nil;
|
||||
}
|
||||
|
||||
// ListenerUnix is a Unix domain socket listener.
|
||||
// Clients should typically use variables of type Listener
|
||||
// instead of assuming Unix domain sockets.
|
||||
type ListenerUnix struct {
|
||||
fd *netFD;
|
||||
laddr string
|
||||
}
|
||||
|
||||
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
|
||||
// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets).
|
||||
func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) {
|
||||
fd, e := unixSocket(net, laddr, "", "listen");
|
||||
if e != nil {
|
||||
if pe, ok := e.(*os.PathError); ok {
|
||||
e = pe.Error;
|
||||
}
|
||||
// Check for socket ``in use'' but ``refusing connections,''
|
||||
// which means some program created it and exited
|
||||
// without unlinking it from the file system.
|
||||
// Clean up on that program's behalf and try again.
|
||||
// Don't do this for Linux's ``abstract'' sockets, which begin with @.
|
||||
if e != os.EADDRINUSE || laddr[0] == '@' {
|
||||
return nil, e;
|
||||
}
|
||||
fd1, e1 := unixSocket(net, "", laddr, "dial");
|
||||
if e1 == nil {
|
||||
fd1.Close();
|
||||
}
|
||||
if pe, ok := e1.(*os.PathError); ok {
|
||||
e1 = pe.Error;
|
||||
}
|
||||
if e1 != os.ECONNREFUSED {
|
||||
return nil, e;
|
||||
}
|
||||
syscall.Unlink(laddr);
|
||||
fd1, e1 = unixSocket(net, laddr, "", "listen");
|
||||
if e1 != nil {
|
||||
return nil, e;
|
||||
}
|
||||
fd = fd1;
|
||||
}
|
||||
e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
|
||||
if e1 != 0 {
|
||||
syscall.Close(fd.fd);
|
||||
return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)};
|
||||
}
|
||||
return &ListenerUnix{fd, laddr}, nil;
|
||||
}
|
||||
|
||||
// AcceptUnix accepts the next incoming call and returns the new connection
|
||||
// and the remote address.
|
||||
func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) {
|
||||
if l == nil || l.fd == nil || l.fd.fd < 0 {
|
||||
return nil, "", os.EINVAL
|
||||
}
|
||||
fd, e := l.fd.accept();
|
||||
if e != nil {
|
||||
return nil, "", e
|
||||
}
|
||||
return newConnUnix(fd, fd.raddr), raddr, nil
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface;
|
||||
// it waits for the next call and returns a generic Conn.
|
||||
func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) {
|
||||
// TODO(rsc): Should return l.AcceptUnix() be okay here?
|
||||
// There is a type conversion -- the first return arg of
|
||||
// l.AcceptUnix() is *ConnUnix and it gets converted to Conn
|
||||
// in the explicit assignment.
|
||||
c, raddr, err = l.AcceptUnix();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Close stops listening on the Unix address.
|
||||
// Already accepted connections are not closed.
|
||||
func (l *ListenerUnix) Close() os.Error {
|
||||
if l == nil || l.fd == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
|
||||
// The operating system doesn't clean up
|
||||
// the file that announcing created, so
|
||||
// we have to clean it up ourselves.
|
||||
// There's a race here--we can't know for
|
||||
// sure whether someone else has come along
|
||||
// and replaced our socket name already--
|
||||
// but this sequence (remove then close)
|
||||
// is at least compatible with the auto-remove
|
||||
// sequence in ListenUnix. It's only non-Go
|
||||
// programs that can mess us up.
|
||||
if l.laddr[0] != '@' {
|
||||
syscall.Unlink(l.laddr);
|
||||
}
|
||||
err := l.fd.Close();
|
||||
l.fd = nil;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *ListenerUnix) Addr() string {
|
||||
return l.fd.addr();
|
||||
// A Listener is a generic network listener.
|
||||
// Accept waits for the next connection and Close closes the connection.
|
||||
type Listener interface {
|
||||
Accept() (c Conn, raddr string, err os.Error);
|
||||
Close() os.Error;
|
||||
Addr() string; // Listener's network address
|
||||
}
|
||||
|
||||
// Dial connects to the remote address raddr on the network net.
|
||||
|
|
@ -840,92 +130,10 @@ func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
|
|||
case "unix", "unix-dgram":
|
||||
c, err := DialUnix(net, laddr, raddr);
|
||||
return c, err;
|
||||
/*
|
||||
case "ether":
|
||||
c, err := DialEther(net, laddr, raddr);
|
||||
return c, err;
|
||||
case "ipv4":
|
||||
c, err := DialIPv4(net, laddr, raddr);
|
||||
return c, err;
|
||||
case "ipv6":
|
||||
c, err := DialIPv6(net, laddr, raddr);
|
||||
return c, err
|
||||
*/
|
||||
}
|
||||
return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)};
|
||||
}
|
||||
|
||||
// A Listener is a generic network listener.
|
||||
// Accept waits for the next connection and Close closes the connection.
|
||||
type Listener interface {
|
||||
Accept() (c Conn, raddr string, err os.Error);
|
||||
Close() os.Error;
|
||||
Addr() string; // Listener's network address
|
||||
}
|
||||
|
||||
// ListenerTCP is a TCP network listener.
|
||||
// Clients should typically use variables of type Listener
|
||||
// instead of assuming TCP.
|
||||
type ListenerTCP struct {
|
||||
fd *netFD;
|
||||
}
|
||||
|
||||
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
|
||||
// Net must be "tcp", "tcp4", or "tcp6".
|
||||
// If laddr has a port of 0, it means to listen on some available port.
|
||||
// The caller can use l.Addr() to retrieve the chosen address.
|
||||
func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) {
|
||||
fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
e1 := syscall.Listen(fd.fd, listenBacklog());
|
||||
if e1 != 0 {
|
||||
syscall.Close(fd.fd);
|
||||
return nil, &OpError{"listen", "tcp", laddr, os.Errno(e1)};
|
||||
}
|
||||
l = new(ListenerTCP);
|
||||
l.fd = fd;
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// AcceptTCP accepts the next incoming call and returns the new connection
|
||||
// and the remote address.
|
||||
func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) {
|
||||
if l == nil || l.fd == nil || l.fd.fd < 0 {
|
||||
return nil, "", os.EINVAL
|
||||
}
|
||||
fd, e := l.fd.accept();
|
||||
if e != nil {
|
||||
return nil, "", e
|
||||
}
|
||||
return newConnTCP(fd, fd.raddr), fd.raddr, nil
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface;
|
||||
// it waits for the next call and returns a generic Conn.
|
||||
func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) {
|
||||
c1, r1, e1 := l.AcceptTCP();
|
||||
if e1 != nil {
|
||||
return nil, "", e1
|
||||
}
|
||||
return c1, r1, nil
|
||||
}
|
||||
|
||||
// Close stops listening on the TCP address.
|
||||
// Already Accepted connections are not closed.
|
||||
func (l *ListenerTCP) Close() os.Error {
|
||||
if l == nil || l.fd == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return l.fd.Close()
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *ListenerTCP) Addr() string {
|
||||
return l.fd.addr();
|
||||
}
|
||||
|
||||
// Listen announces on the local network address laddr.
|
||||
// The network string net must be "tcp", "tcp4", "tcp6",
|
||||
// "unix", or "unix-dgram".
|
||||
|
|
@ -943,11 +151,47 @@ func Listen(net, laddr string) (l Listener, err os.Error) {
|
|||
return nil, err;
|
||||
}
|
||||
return l, nil;
|
||||
/*
|
||||
more here
|
||||
*/
|
||||
// BUG(rsc): Listen should support UDP.
|
||||
}
|
||||
return nil, UnknownNetworkError(net);
|
||||
}
|
||||
|
||||
var errMissingAddress = os.ErrorString("missing address")
|
||||
|
||||
type OpError struct {
|
||||
Op string;
|
||||
Net string;
|
||||
Addr string;
|
||||
Error os.Error;
|
||||
}
|
||||
|
||||
func (e *OpError) String() string {
|
||||
s := e.Op;
|
||||
if e.Net != "" {
|
||||
s += " " + e.Net;
|
||||
}
|
||||
if e.Addr != "" {
|
||||
s += " " + e.Addr;
|
||||
}
|
||||
s += ": " + e.Error.String();
|
||||
return s;
|
||||
}
|
||||
|
||||
type AddrError struct {
|
||||
Error string;
|
||||
Addr string;
|
||||
}
|
||||
|
||||
func (e *AddrError) String() string {
|
||||
s := e.Error;
|
||||
if e.Addr != "" {
|
||||
s += " " + e.Addr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
type UnknownNetworkError string
|
||||
func (e UnknownNetworkError) String() string {
|
||||
return "unknown network " + string(e);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Simple file i/o and string manipulation, to avoid
|
||||
// depending on strconv and bufio.
|
||||
// depending on strconv and bufio and strings.
|
||||
|
||||
package net
|
||||
|
||||
|
|
@ -154,3 +154,46 @@ func xtoi(s string, i0 int) (n int, i int, ok bool) {
|
|||
return n, i, true
|
||||
}
|
||||
|
||||
// Integer to decimal.
|
||||
func itoa(i int) string {
|
||||
var buf [30]byte;
|
||||
n := len(buf);
|
||||
neg := false;
|
||||
if i < 0 {
|
||||
i = -i;
|
||||
neg = true;
|
||||
}
|
||||
ui := uint(i);
|
||||
for ui > 0 || n == len(buf) {
|
||||
n--;
|
||||
buf[n] = byte('0' + ui%10);
|
||||
ui /= 10;
|
||||
}
|
||||
if neg {
|
||||
n--;
|
||||
buf[n] = '-';
|
||||
}
|
||||
return string(buf[n:len(buf)]);
|
||||
}
|
||||
|
||||
// Number of occurrences of b in s.
|
||||
func count(s string, b byte) int {
|
||||
n := 0;
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == b {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Index of rightmost occurrence of b in s.
|
||||
func last(s string, b byte) int {
|
||||
i := len(s);
|
||||
for i--; i >= 0; i-- {
|
||||
if s[i] == b {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
|
|
|||
219
src/pkg/net/sock.go
Normal file
219
src/pkg/net/sock.go
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Sockets
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"os";
|
||||
"reflect";
|
||||
"syscall";
|
||||
)
|
||||
|
||||
// Boolean to int.
|
||||
func boolint(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Generic socket creation.
|
||||
func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) {
|
||||
// See ../syscall/exec.go for description of ForkLock.
|
||||
syscall.ForkLock.RLock();
|
||||
s, e := syscall.Socket(f, p, t);
|
||||
if e != 0 {
|
||||
syscall.ForkLock.RUnlock();
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
syscall.CloseOnExec(s);
|
||||
syscall.ForkLock.RUnlock();
|
||||
|
||||
// Allow reuse of recently-used addresses.
|
||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1);
|
||||
|
||||
if la != nil {
|
||||
e = syscall.Bind(s, la);
|
||||
if e != 0 {
|
||||
syscall.Close(s);
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
}
|
||||
|
||||
if ra != nil {
|
||||
e = syscall.Connect(s, ra);
|
||||
if e != 0 {
|
||||
syscall.Close(s);
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = newFD(s, net, laddr, raddr);
|
||||
if err != nil {
|
||||
syscall.Close(s);
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
|
||||
// Generic implementation of Conn interface; not exported.
|
||||
type connBase struct {
|
||||
fd *netFD;
|
||||
raddr string;
|
||||
}
|
||||
|
||||
func (c *connBase) LocalAddr() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.fd.addr();
|
||||
}
|
||||
|
||||
func (c *connBase) RemoteAddr() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.fd.remoteAddr();
|
||||
}
|
||||
|
||||
func (c *connBase) File() *os.File {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.fd.file;
|
||||
}
|
||||
|
||||
func (c *connBase) sysFD() int {
|
||||
if c == nil || c.fd == nil {
|
||||
return -1;
|
||||
}
|
||||
return c.fd.fd;
|
||||
}
|
||||
|
||||
func (c *connBase) Read(b []byte) (n int, err os.Error) {
|
||||
n, err = c.fd.Read(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) Write(b []byte) (n int, err os.Error) {
|
||||
n, err = c.fd.Write(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) {
|
||||
if c == nil {
|
||||
return -1, "", os.EINVAL
|
||||
}
|
||||
n, err = c.Read(b);
|
||||
return n, c.raddr, err
|
||||
}
|
||||
|
||||
func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) {
|
||||
if c == nil {
|
||||
return -1, os.EINVAL
|
||||
}
|
||||
if raddr != c.raddr {
|
||||
return -1, os.EINVAL
|
||||
}
|
||||
n, err = c.Write(b);
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connBase) Close() os.Error {
|
||||
if c == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
return c.fd.Close()
|
||||
}
|
||||
|
||||
|
||||
func setsockoptInt(fd, level, opt int, value int) os.Error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value));
|
||||
}
|
||||
|
||||
func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
|
||||
var tv = syscall.NsecToTimeval(nsec);
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv));
|
||||
}
|
||||
|
||||
func (c *connBase) SetReadBuffer(bytes int) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes);
|
||||
}
|
||||
|
||||
func (c *connBase) SetWriteBuffer(bytes int) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes);
|
||||
}
|
||||
|
||||
func (c *connBase) SetReadTimeout(nsec int64) os.Error {
|
||||
c.fd.rdeadline_delta = nsec;
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (c *connBase) SetWriteTimeout(nsec int64) os.Error {
|
||||
c.fd.wdeadline_delta = nsec;
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (c *connBase) SetTimeout(nsec int64) os.Error {
|
||||
if e := c.SetReadTimeout(nsec); e != nil {
|
||||
return e
|
||||
}
|
||||
return c.SetWriteTimeout(nsec)
|
||||
}
|
||||
|
||||
func (c *connBase) SetReuseAddr(reuse bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse));
|
||||
}
|
||||
|
||||
func (c *connBase) BindToDevice(dev string) os.Error {
|
||||
// TODO(rsc): call setsockopt with null-terminated string pointer
|
||||
return os.EINVAL
|
||||
}
|
||||
|
||||
func (c *connBase) SetDontRoute(dontroute bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute));
|
||||
}
|
||||
|
||||
func (c *connBase) SetKeepAlive(keepalive bool) os.Error {
|
||||
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive));
|
||||
}
|
||||
|
||||
func (c *connBase) SetLinger(sec int) os.Error {
|
||||
var l syscall.Linger;
|
||||
if sec >= 0 {
|
||||
l.Onoff = 1;
|
||||
l.Linger = int32(sec);
|
||||
} else {
|
||||
l.Onoff = 0;
|
||||
l.Linger = 0;
|
||||
}
|
||||
e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l);
|
||||
return os.NewSyscallError("setsockopt", e);
|
||||
}
|
||||
|
||||
|
||||
type UnknownSocketError struct {
|
||||
sa syscall.Sockaddr;
|
||||
}
|
||||
func (e *UnknownSocketError) String() string {
|
||||
return "unknown socket address type " + reflect.Typeof(e.sa).String()
|
||||
}
|
||||
|
||||
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
|
||||
switch a := sa.(type) {
|
||||
case *syscall.SockaddrInet4:
|
||||
return joinHostPort(IP(&a.Addr).String(), itoa(a.Port)), nil;
|
||||
case *syscall.SockaddrInet6:
|
||||
return joinHostPort(IP(&a.Addr).String(), itoa(a.Port)), nil;
|
||||
case *syscall.SockaddrUnix:
|
||||
return a.Name, nil;
|
||||
}
|
||||
|
||||
return "", &UnknownSocketError{sa};
|
||||
}
|
||||
|
||||
190
src/pkg/net/unixsock.go
Normal file
190
src/pkg/net/unixsock.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Unix domain sockets
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"os";
|
||||
"syscall";
|
||||
)
|
||||
|
||||
func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) {
|
||||
var proto int;
|
||||
switch net {
|
||||
default:
|
||||
return nil, UnknownNetworkError(net);
|
||||
case "unix":
|
||||
proto = syscall.SOCK_STREAM;
|
||||
case "unix-dgram":
|
||||
proto = syscall.SOCK_DGRAM;
|
||||
}
|
||||
|
||||
var la, ra syscall.Sockaddr;
|
||||
switch mode {
|
||||
default:
|
||||
panic("unixSocket", mode);
|
||||
|
||||
case "dial":
|
||||
if laddr != "" {
|
||||
return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}}
|
||||
}
|
||||
if raddr == "" {
|
||||
return nil, &OpError{mode, net, "", errMissingAddress}
|
||||
}
|
||||
ra = &syscall.SockaddrUnix{Name: raddr};
|
||||
|
||||
case "listen":
|
||||
if laddr == "" {
|
||||
return nil, &OpError{mode, net, "", errMissingAddress}
|
||||
}
|
||||
la = &syscall.SockaddrUnix{Name: laddr};
|
||||
if raddr != "" {
|
||||
return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}}
|
||||
}
|
||||
}
|
||||
|
||||
fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra);
|
||||
if err != nil {
|
||||
goto Error;
|
||||
}
|
||||
return fd, nil;
|
||||
|
||||
Error:
|
||||
addr := raddr;
|
||||
if mode == "listen" {
|
||||
addr = laddr;
|
||||
}
|
||||
return nil, &OpError{mode, net, addr, err};
|
||||
}
|
||||
|
||||
// ConnUnix is an implementation of the Conn interface
|
||||
// for connections to Unix domain sockets.
|
||||
type ConnUnix struct {
|
||||
connBase
|
||||
}
|
||||
|
||||
func newConnUnix(fd *netFD, raddr string) *ConnUnix {
|
||||
c := new(ConnUnix);
|
||||
c.fd = fd;
|
||||
c.raddr = raddr;
|
||||
return c;
|
||||
}
|
||||
|
||||
// DialUnix is like Dial but can only connect to Unix domain sockets
|
||||
// and returns a ConnUnix structure. The laddr argument must be
|
||||
// the empty string; it is included only to match the signature of
|
||||
// the other dial routines.
|
||||
func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) {
|
||||
fd, e := unixSocket(net, laddr, raddr, "dial");
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return newConnUnix(fd, raddr), nil;
|
||||
}
|
||||
|
||||
// ListenerUnix is a Unix domain socket listener.
|
||||
// Clients should typically use variables of type Listener
|
||||
// instead of assuming Unix domain sockets.
|
||||
type ListenerUnix struct {
|
||||
fd *netFD;
|
||||
laddr string
|
||||
}
|
||||
|
||||
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
|
||||
// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets).
|
||||
func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) {
|
||||
fd, e := unixSocket(net, laddr, "", "listen");
|
||||
if e != nil {
|
||||
if pe, ok := e.(*os.PathError); ok {
|
||||
e = pe.Error;
|
||||
}
|
||||
// Check for socket ``in use'' but ``refusing connections,''
|
||||
// which means some program created it and exited
|
||||
// without unlinking it from the file system.
|
||||
// Clean up on that program's behalf and try again.
|
||||
// Don't do this for Linux's ``abstract'' sockets, which begin with @.
|
||||
if e != os.EADDRINUSE || laddr[0] == '@' {
|
||||
return nil, e;
|
||||
}
|
||||
fd1, e1 := unixSocket(net, "", laddr, "dial");
|
||||
if e1 == nil {
|
||||
fd1.Close();
|
||||
}
|
||||
if pe, ok := e1.(*os.PathError); ok {
|
||||
e1 = pe.Error;
|
||||
}
|
||||
if e1 != os.ECONNREFUSED {
|
||||
return nil, e;
|
||||
}
|
||||
syscall.Unlink(laddr);
|
||||
fd1, e1 = unixSocket(net, laddr, "", "listen");
|
||||
if e1 != nil {
|
||||
return nil, e;
|
||||
}
|
||||
fd = fd1;
|
||||
}
|
||||
e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
|
||||
if e1 != 0 {
|
||||
syscall.Close(fd.fd);
|
||||
return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)};
|
||||
}
|
||||
return &ListenerUnix{fd, laddr}, nil;
|
||||
}
|
||||
|
||||
// AcceptUnix accepts the next incoming call and returns the new connection
|
||||
// and the remote address.
|
||||
func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) {
|
||||
if l == nil || l.fd == nil || l.fd.fd < 0 {
|
||||
return nil, "", os.EINVAL
|
||||
}
|
||||
fd, e := l.fd.accept();
|
||||
if e != nil {
|
||||
return nil, "", e
|
||||
}
|
||||
return newConnUnix(fd, fd.raddr), raddr, nil
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface;
|
||||
// it waits for the next call and returns a generic Conn.
|
||||
func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) {
|
||||
// TODO(rsc): Should return l.AcceptUnix() be okay here?
|
||||
// There is a type conversion -- the first return arg of
|
||||
// l.AcceptUnix() is *ConnUnix and it gets converted to Conn
|
||||
// in the explicit assignment.
|
||||
c, raddr, err = l.AcceptUnix();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Close stops listening on the Unix address.
|
||||
// Already accepted connections are not closed.
|
||||
func (l *ListenerUnix) Close() os.Error {
|
||||
if l == nil || l.fd == nil {
|
||||
return os.EINVAL
|
||||
}
|
||||
|
||||
// The operating system doesn't clean up
|
||||
// the file that announcing created, so
|
||||
// we have to clean it up ourselves.
|
||||
// There's a race here--we can't know for
|
||||
// sure whether someone else has come along
|
||||
// and replaced our socket name already--
|
||||
// but this sequence (remove then close)
|
||||
// is at least compatible with the auto-remove
|
||||
// sequence in ListenUnix. It's only non-Go
|
||||
// programs that can mess us up.
|
||||
if l.laddr[0] != '@' {
|
||||
syscall.Unlink(l.laddr);
|
||||
}
|
||||
err := l.fd.Close();
|
||||
l.fd = nil;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *ListenerUnix) Addr() string {
|
||||
return l.fd.addr();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue