github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/net_unix.go (about)

     1  // +build dragonfly freebsd linux nacl netbsd openbsd solaris
     2  
     3  package gotfo
     4  
     5  import (
     6  	"context"
     7  	"net"
     8  	"syscall"
     9  )
    10  import (
    11  	"os"
    12  )
    13  
    14  const (
    15  	TCP_FASTOPEN   = 23
    16  	LISTEN_BACKLOG = 23
    17  )
    18  
    19  type TFOListener struct {
    20  	*net.TCPListener
    21  	fd *netFD
    22  }
    23  
    24  func socket(family int, fastOpen bool) (int, error) {
    25  	fd, err := syscall.Socket(family, syscall.SOCK_STREAM, 0)
    26  	if err != nil {
    27  		return 0, err
    28  	}
    29  	if fastOpen {
    30  		if err := syscall.SetsockoptInt(fd, syscall.SOL_TCP, TCP_FASTOPEN, 1); err != nil {
    31  			return 0, err
    32  		}
    33  	}
    34  
    35  	if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
    36  		return 0, err
    37  	}
    38  
    39  	return fd, nil
    40  }
    41  
    42  func Listen(address string, fastOpen bool) (net.Listener, error) {
    43  	laddr, err := net.ResolveTCPAddr("tcp", address)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	fd, err := socket(syscall.AF_INET, fastOpen)
    49  	if err != nil {
    50  		syscall.Close(fd)
    51  		return nil, err
    52  	}
    53  
    54  	sa := tcpAddrToSockaddr(laddr)
    55  
    56  	if err := syscall.Bind(fd, sa); err != nil {
    57  		syscall.Close(fd)
    58  		return nil, err
    59  	}
    60  
    61  	if err := syscall.Listen(fd, LISTEN_BACKLOG); err != nil {
    62  		syscall.Close(fd)
    63  		return nil, err
    64  	}
    65  
    66  	nfd := newFD(fd)
    67  	if err := nfd.init(); err != nil {
    68  		syscall.Close(fd)
    69  		return nil, err
    70  	}
    71  
    72  	return newTCPListener(nfd, false), nil
    73  }
    74  
    75  func Dial(address string, fastOpen bool, data []byte) (*net.TCPConn, error) {
    76  	return DialContext(context.Background(), address, fastOpen, data)
    77  }
    78  
    79  var fdCallback func(int)
    80  
    81  func SetFdCallback(fn func(int)) {
    82  	fdCallback = fn
    83  }
    84  
    85  func DialContext(ctx context.Context, address string, fastOpen bool, data []byte) (*net.TCPConn, error) {
    86  	raddr, err := net.ResolveTCPAddr("tcp", address)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	fd, err := socket(syscall.AF_INET, fastOpen)
    92  	if err != nil {
    93  		syscall.Close(fd)
    94  		return nil, err
    95  	}
    96  
    97  	sa := tcpAddrToSockaddr(raddr)
    98  
    99  	nfd := newFD(fd)
   100  	if err := nfd.init(); err != nil {
   101  		syscall.Close(fd)
   102  		return nil, err
   103  	}
   104  
   105  	if fdCallback != nil {
   106  		fdCallback(nfd.sysfd)
   107  	}
   108  
   109  	for {
   110  		if fastOpen {
   111  			err = syscall.Sendto(nfd.sysfd, data, syscall.MSG_FASTOPEN, sa)
   112  		} else {
   113  			err = syscall.Connect(nfd.sysfd, sa)
   114  		}
   115  		if err == syscall.EAGAIN {
   116  			continue
   117  		}
   118  		break
   119  	}
   120  
   121  	if _, ok := err.(syscall.Errno); ok {
   122  		err = os.NewSyscallError("sendto", err)
   123  	}
   124  
   125  	return newTCPConn(nfd), err
   126  }