diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 12:50:40 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 12:50:40 -0700 |
commit | 2a0db60599fdd75b1bc3e297180fbe1282763759 (patch) | |
tree | 68d43c3e30d9ab961ddf6b7365201ca6b675b253 /src/net/sock_posix.go | |
parent | ef33cba3c8de6c431df56503df51fcd3a473c89e (diff) | |
parent | f154da9e12608589e8d5f0508f908a0c3e88a1bb (diff) | |
download | golang-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.go | 216 |
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 +} |