summaryrefslogtreecommitdiff
path: root/src/net/sock_posix.go
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 12:50:40 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 12:50:40 -0700
commit2a0db60599fdd75b1bc3e297180fbe1282763759 (patch)
tree68d43c3e30d9ab961ddf6b7365201ca6b675b253 /src/net/sock_posix.go
parentef33cba3c8de6c431df56503df51fcd3a473c89e (diff)
parentf154da9e12608589e8d5f0508f908a0c3e88a1bb (diff)
downloadgolang-2a0db60599fdd75b1bc3e297180fbe1282763759.tar.gz
Merge tag 'upstream/1.4' into debian-experimental
* tag 'upstream/1.4': Imported Upstream version 1.4
Diffstat (limited to 'src/net/sock_posix.go')
-rw-r--r--src/net/sock_posix.go216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
new file mode 100644
index 000000000..3f956df65
--- /dev/null
+++ b/src/net/sock_posix.go
@@ -0,0 +1,216 @@
+// 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.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+// A sockaddr represents a TCP, UDP, IP or Unix network endpoint
+// address that can be converted into a syscall.Sockaddr.
+type sockaddr interface {
+ Addr
+
+ netaddr
+
+ // family returns the platform-dependent address family
+ // identifier.
+ family() int
+
+ // isWildcard reports whether the address is a wildcard
+ // address.
+ isWildcard() bool
+
+ // sockaddr returns the address converted into a syscall
+ // sockaddr type that implements syscall.Sockaddr
+ // interface. It returns a nil interface when the address is
+ // nil.
+ sockaddr(family int) (syscall.Sockaddr, error)
+}
+
+// socket returns a network file descriptor that is ready for
+// asynchronous I/O using the network poller.
+func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time) (fd *netFD, err error) {
+ s, err := sysSocket(family, sotype, proto)
+ if err != nil {
+ return nil, err
+ }
+ if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+ if fd, err = newFD(s, family, sotype, net); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+
+ // This function makes a network file descriptor for the
+ // following applications:
+ //
+ // - An endpoint holder that opens a passive stream
+ // connenction, known as a stream listener
+ //
+ // - An endpoint holder that opens a destination-unspecific
+ // datagram connection, known as a datagram listener
+ //
+ // - An endpoint holder that opens an active stream or a
+ // destination-specific datagram connection, known as a
+ // dialer
+ //
+ // - An endpoint holder that opens the other connection, such
+ // as talking to the protocol stack inside the kernel
+ //
+ // For stream and datagram listeners, they will only require
+ // named sockets, so we can assume that it's just a request
+ // from stream or datagram listeners when laddr is not nil but
+ // raddr is nil. Otherwise we assume it's just for dialers or
+ // the other connection holders.
+
+ if laddr != nil && raddr == nil {
+ switch sotype {
+ case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
+ if err := fd.listenStream(laddr, listenerBacklog); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ return fd, nil
+ case syscall.SOCK_DGRAM:
+ if err := fd.listenDatagram(laddr); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ return fd, nil
+ }
+ }
+ if err := fd.dial(laddr, raddr, deadline); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ return fd, nil
+}
+
+func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr {
+ switch fd.family {
+ case syscall.AF_INET, syscall.AF_INET6:
+ switch fd.sotype {
+ case syscall.SOCK_STREAM:
+ return sockaddrToTCP
+ case syscall.SOCK_DGRAM:
+ return sockaddrToUDP
+ case syscall.SOCK_RAW:
+ return sockaddrToIP
+ }
+ case syscall.AF_UNIX:
+ switch fd.sotype {
+ case syscall.SOCK_STREAM:
+ return sockaddrToUnix
+ case syscall.SOCK_DGRAM:
+ return sockaddrToUnixgram
+ case syscall.SOCK_SEQPACKET:
+ return sockaddrToUnixpacket
+ }
+ }
+ return func(syscall.Sockaddr) Addr { return nil }
+}
+
+func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time) error {
+ var err error
+ var lsa syscall.Sockaddr
+ if laddr != nil {
+ if lsa, err = laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ }
+ }
+ var rsa syscall.Sockaddr
+ if raddr != nil {
+ if rsa, err = raddr.sockaddr(fd.family); err != nil {
+ return err
+ }
+ if err := fd.connect(lsa, rsa, deadline); err != nil {
+ return err
+ }
+ fd.isConnected = true
+ } else {
+ if err := fd.init(); err != nil {
+ return err
+ }
+ }
+ lsa, _ = syscall.Getsockname(fd.sysfd)
+ if rsa, _ = syscall.Getpeername(fd.sysfd); rsa != nil {
+ fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(rsa))
+ } else {
+ fd.setAddr(fd.addrFunc()(lsa), raddr)
+ }
+ return nil
+}
+
+func (fd *netFD) listenStream(laddr sockaddr, backlog int) error {
+ if err := setDefaultListenerSockopts(fd.sysfd); err != nil {
+ return err
+ }
+ if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ }
+ if err := syscall.Listen(fd.sysfd, backlog); err != nil {
+ return os.NewSyscallError("listen", err)
+ }
+ if err := fd.init(); err != nil {
+ return err
+ }
+ lsa, _ := syscall.Getsockname(fd.sysfd)
+ fd.setAddr(fd.addrFunc()(lsa), nil)
+ return nil
+}
+
+func (fd *netFD) listenDatagram(laddr sockaddr) error {
+ switch addr := laddr.(type) {
+ case *UDPAddr:
+ // We provide a socket that listens to a wildcard
+ // address with reusable UDP port when the given laddr
+ // is an appropriate UDP multicast address prefix.
+ // This makes it possible for a single UDP listener to
+ // join multiple different group addresses, for
+ // multiple UDP listeners that listen on the same UDP
+ // port to join the same group address.
+ if addr.IP != nil && addr.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(fd.sysfd); err != nil {
+ return err
+ }
+ addr := *addr
+ switch fd.family {
+ case syscall.AF_INET:
+ addr.IP = IPv4zero
+ case syscall.AF_INET6:
+ addr.IP = IPv6unspecified
+ }
+ laddr = &addr
+ }
+ }
+ if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ }
+ if err := fd.init(); err != nil {
+ return err
+ }
+ lsa, _ := syscall.Getsockname(fd.sysfd)
+ fd.setAddr(fd.addrFunc()(lsa), nil)
+ return nil
+}