diff options
Diffstat (limited to 'src/net/sock_posix.go')
-rw-r--r-- | src/net/sock_posix.go | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go new file mode 100644 index 0000000..4431c3a --- /dev/null +++ b/src/net/sock_posix.go @@ -0,0 +1,254 @@ +// 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. + +//go:build unix || windows + +package net + +import ( + "context" + "internal/poll" + "os" + "syscall" +) + +// socket returns a network file descriptor that is ready for +// asynchronous I/O using the network poller. +func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (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 { + poll.CloseFunc(s) + return nil, err + } + if fd, err = newFD(s, family, sotype, net); err != nil { + poll.CloseFunc(s) + return nil, err + } + + // This function makes a network file descriptor for the + // following applications: + // + // - An endpoint holder that opens a passive stream + // connection, 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(), ctrlFn); err != nil { + fd.Close() + return nil, err + } + return fd, nil + case syscall.SOCK_DGRAM: + if err := fd.listenDatagram(laddr, ctrlFn); err != nil { + fd.Close() + return nil, err + } + return fd, nil + } + } + if err := fd.dial(ctx, laddr, raddr, ctrlFn); err != nil { + fd.Close() + return nil, err + } + return fd, nil +} + +func (fd *netFD) ctrlNetwork() string { + switch fd.net { + case "unix", "unixgram", "unixpacket": + return fd.net + } + switch fd.net[len(fd.net)-1] { + case '4', '6': + return fd.net + } + if fd.family == syscall.AF_INET { + return fd.net + "4" + } + return fd.net + "6" +} + +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(ctx context.Context, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) error { + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err + } + var ctrlAddr string + if raddr != nil { + ctrlAddr = raddr.String() + } else if laddr != nil { + ctrlAddr = laddr.String() + } + if err := ctrlFn(fd.ctrlNetwork(), ctrlAddr, c); err != nil { + return err + } + } + 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.pfd.Sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } + } + } + var rsa syscall.Sockaddr // remote address from the user + var crsa syscall.Sockaddr // remote address we actually connected to + if raddr != nil { + if rsa, err = raddr.sockaddr(fd.family); err != nil { + return err + } + if crsa, err = fd.connect(ctx, lsa, rsa); err != nil { + return err + } + fd.isConnected = true + } else { + if err := fd.init(); err != nil { + return err + } + } + // Record the local and remote addresses from the actual socket. + // Get the local address by calling Getsockname. + // For the remote address, use + // 1) the one returned by the connect method, if any; or + // 2) the one from Getpeername, if it succeeds; or + // 3) the one passed to us as the raddr parameter. + lsa, _ = syscall.Getsockname(fd.pfd.Sysfd) + if crsa != nil { + fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(crsa)) + } else if rsa, _ = syscall.Getpeername(fd.pfd.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, ctrlFn func(string, string, syscall.RawConn) error) error { + var err error + if err = setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil { + return err + } + var lsa syscall.Sockaddr + if lsa, err = laddr.sockaddr(fd.family); err != nil { + return err + } + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err + } + if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil { + return err + } + } + if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } + if err = listenFunc(fd.pfd.Sysfd, backlog); err != nil { + return os.NewSyscallError("listen", err) + } + if err = fd.init(); err != nil { + return err + } + lsa, _ = syscall.Getsockname(fd.pfd.Sysfd) + fd.setAddr(fd.addrFunc()(lsa), nil) + return nil +} + +func (fd *netFD) listenDatagram(laddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) 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.pfd.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 + } + } + var err error + var lsa syscall.Sockaddr + if lsa, err = laddr.sockaddr(fd.family); err != nil { + return err + } + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err + } + if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil { + return err + } + } + if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } + if err = fd.init(); err != nil { + return err + } + lsa, _ = syscall.Getsockname(fd.pfd.Sysfd) + fd.setAddr(fd.addrFunc()(lsa), nil) + return nil +} |