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 }