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

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