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 }