func sendfile(c *net.TCPConn, f *os.File, fi os.FileInfo) { sockFile, err := c.File() if err != nil { fmt.Fprint(c, Error(fmt.Sprintf("couldn't get file sock: %x", err))) } syscall.Sendfile(int(sockFile.Fd()), int(f.Fd()), nil, int(fi.Size())) }
func SetKeepAlive(c *net.TCPConn, cfg *KeepAliveConfig) error { if err := c.SetKeepAlive(cfg.KeepAlive); err != nil { return err } file, err := c.File() if err != nil { return err } fd := int(file.Fd()) if cfg.KeepAliveIdle != 0 { if err := setIdle(fd, secs(cfg.KeepAliveIdle)); err != nil { return err } } if cfg.KeepAliveCount != 0 { if err := setCount(fd, cfg.KeepAliveCount); err != nil { return err } } if cfg.KeepAliveInterval != 0 { if err := setInterval(fd, secs(cfg.KeepAliveInterval)); err != nil { return nil } } return nil }
func LessDelayTcpConn(conn *net.TCPConn) (connOut net.Conn, err error) { //err = conn.SetKeepAlive(true) //if err!=nil{ // kmgErr.LogErrorWithStack(err) // return nil,err //} //err = conn.SetKeepAlivePeriod(5*time.Second) //5s太小,耗流量非常凶残. //if err!=nil{ // kmgErr.LogErrorWithStack(err) // return nil,err //} fd, err := conn.File() if err != nil { kmgErr.LogErrorWithStack(err) return } conn1, err := net.FileConn(fd) if err != nil { fd.Close() kmgErr.LogErrorWithStack(err) return } conn.Close() //尝试将连接重新设置回 block 模式,减少cpu占用,此方案不稳定,并且不知道如何解决不稳定的问题. //err = unix.SetNonblock(int(fd.Fd()),true) //if err!=nil{ // fd.Close() // kmgErr.LogErrorWithStack(err) // return nil,err //} //return NewDebugConn(fasterTcpConn{TCPConn: conn, fd: fd},conn.LocalAddr().String()+"_"+conn.RemoteAddr().String()), nil return &fasterTcpConn{TCPConn: conn1.(*net.TCPConn), fd: fd}, nil }
func snd(what *net.TCPConn, on *net.UnixConn) error { f, err := what.File() if err != nil { return err } defer f.Close() return fd.Put(on, f) }
func SysfdByTcpConn(tcpConn *net.TCPConn) (int, error) { file, err := tcpConn.File() if err != nil { return -1, err } defer file.Close() return int(file.Fd()), nil }
// setTCPUserTimeout sets TCP_RXT_CONNDROPTIME on darwin func setTCPUserTimeout(conn *net.TCPConn, uto time.Duration) error { f, err := conn.File() if err != nil { return err } defer f.Close() secs := int(uto.Nanoseconds() / 1e9) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(f.Fd()), syscall.IPPROTO_TCP, unix.TCP_RXT_CONNDROPTIME, secs)) }
// SetTCPUserTimeout sets TCP_USER_TIMEOUT according to RFC5842 func SetTCPUserTimeout(conn *net.TCPConn, uto time.Duration) error { f, err := conn.File() if err != nil { return err } defer f.Close() msecs := int(uto.Nanoseconds() / 1e6) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(f.Fd()), syscall.SOL_TCP, C.TCP_USER_TIMEOUT, msecs)) }
// GetOriginalDST retrieves the original destination address from // NATed connection. Currently, only Linux iptables using DNAT/REDIRECT // is supported. For other operating systems, this will just return // conn.LocalAddr(). // // Note that this function only works when nf_conntrack_ipv4 and/or // nf_conntrack_ipv6 is loaded in the kernel. func GetOriginalDST(conn *net.TCPConn) (*net.TCPAddr, error) { f, err := conn.File() if err != nil { return nil, err } defer f.Close() fd := int(f.Fd()) // revert to non-blocking mode. // see http://stackoverflow.com/a/28968431/1493661 if err = syscall.SetNonblock(fd, true); err != nil { return nil, os.NewSyscallError("setnonblock", err) } v6 := conn.LocalAddr().(*net.TCPAddr).IP.To4() == nil if v6 { var addr syscall.RawSockaddrInet6 var len uint32 len = uint32(unsafe.Sizeof(addr)) err = getsockopt(fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, unsafe.Pointer(&addr), &len) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } ip := make([]byte, 16) for i, b := range addr.Addr { ip[i] = b } pb := *(*[2]byte)(unsafe.Pointer(&addr.Port)) return &net.TCPAddr{ IP: ip, Port: int(pb[0])*256 + int(pb[1]), }, nil } // IPv4 var addr syscall.RawSockaddrInet4 var len uint32 len = uint32(unsafe.Sizeof(addr)) err = getsockopt(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, unsafe.Pointer(&addr), &len) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } ip := make([]byte, 4) for i, b := range addr.Addr { ip[i] = b } pb := *(*[2]byte)(unsafe.Pointer(&addr.Port)) return &net.TCPAddr{ IP: ip, Port: int(pb[0])*256 + int(pb[1]), }, nil }
// SetTCPUserTimeout sets TCP_MAXRT in Windows func SetTCPUserTimeout(conn *net.TCPConn, uto time.Duration) error { f, err := conn.File() if err != nil { return err } defer f.Close() // TCP_MAXRT in Windows is set as seconds secs := int(uto.Nanoseconds() / 1e9) // from MSDN, TCP_MAXRT is supported since Windows Vista return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(f.Fd()), syscall.SOL_TCP, C.TCP_MAXRT, secs)) }
// Not used anymore but kept for now. Trying to get keepalive in linux going // Above in the source code is a link to a blog post. Stolen from there. func linuxEnableKeepAlive(tcp *net.TCPConn) { file, err := tcp.File() if err == nil { // LINUX ONLY!! // If we error we just don't set these options. No harm. fd := int(file.Fd()) os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, TCP_KEEP_ALIVE)) os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, 9)) // _probes os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, 75)) // _intvl } }
// setTCPUserTimeout sets TCP_USER_TIMEOUT according to RFC5842 func setTCPUserTimeout(conn *net.TCPConn, uto time.Duration) error { f, err := conn.File() if err != nil { return err } defer f.Close() msecs := int(uto.Nanoseconds() / 1e6) // TCP_USER_TIMEOUT is a relatively new feature to detect dead peer from sender side. // Linux supports it since kernel 2.6.37. It's among Golang experimental under // golang.org/x/sys/unix but it doesn't support all Linux platforms yet. // we explicitly define it here until it becomes official in golang. // TODO: replace it with proper package when TCP_USER_TIMEOUT is supported in golang. const tcpUserTimeout = 0x12 return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(f.Fd()), syscall.IPPROTO_TCP, tcpUserTimeout, msecs)) }
func SetTcpTTLSockopts(conn *net.TCPConn, ttl int) error { level := syscall.IPPROTO_IP name := syscall.IP_TTL if strings.Contains(conn.RemoteAddr().String(), "[") { level = syscall.IPPROTO_IPV6 name = syscall.IPV6_UNICAST_HOPS } fi, err := conn.File() defer fi.Close() if err != nil { return err } if conn, err := net.FileConn(fi); err == nil { defer conn.Close() } return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fi.Fd()), level, name, ttl)) }
func sendFile(conn *net.TCPConn, fn string, offset int64) { f, err := os.Open(fn) if err != nil { fmt.Println(err.Error()) return } nf, err := conn.File() if err != nil { fmt.Println(err.Error()) return } dst := nf.Fd() for i := 0; i < 1000000; i++ { _, err = syscall.Sendfile(int(dst), int(f.Fd()), &offset, 1033) if err != nil { fmt.Println(err.Error()) break } } conn.CloseWrite() }
func NewTCPPipe(id uint, sourceConn net.TCPConn) (pipe TCPPipe, err error) { tcppipe := new(TCPPipe) f, err := sourceConn.File() if err != nil { log.Println("[DEBUG] Failed to read connection file descriptor.") sourceConn.Close() return *tcppipe, err } //TODO: Investigate this more. This seems arbitrary. If a linux machine: syscall.SOL_IP originalAddrBytes, err := syscall.GetsockoptIPv6Mreq(int(f.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST) if err != nil { log.Println("[DEBUG] Getsockopt failed.") sourceConn.Close() return *tcppipe, err } destConn, err := net.Dial("tcp", ByteToConnString(originalAddrBytes.Multiaddr)) if err != nil { log.Printf("[ERR] Unable to connect to destination. Closing connection %v.\n", id) sourceConn.Close() return *tcppipe, err } tcppipe = &TCPPipe{id: id, source: sourceConn, destination: destConn} return *tcppipe, nil }
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) { if clientConn == nil { log.Debugf("copy(): oops, dst is nil!") err = errors.New("ERR: clientConn is nil") return } // test if the underlying fd is nil remoteAddr := clientConn.RemoteAddr() if remoteAddr == nil { log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!") err = errors.New("ERR: clientConn.fd is nil") return } srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr()) newTCPConn = nil // net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode. // The workaround is to take the File returned by .File(), do getsockopt() to get the original // destination, then create a new *net.TCPConn by calling net.Conn.FileConn(). The new TCPConn // will be in non-blocking mode. What a pain. clientConnFile, err := clientConn.File() if err != nil { log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport) return } else { clientConn.Close() } // Get original destination // this is the only syscall in the Golang libs that I can find that returns 16 bytes // Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0} // port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080) // IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45) addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST) log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+v\n", addr) if err != nil { log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err) return } newConn, err := net.FileConn(clientConnFile) if err != nil { log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err) return } if _, ok := newConn.(*net.TCPConn); ok { newTCPConn = newConn.(*net.TCPConn) clientConnFile.Close() } else { errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn) log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg) err = errors.New(errmsg) return } ipv4 = itod(uint(addr.Multiaddr[4])) + "." + itod(uint(addr.Multiaddr[5])) + "." + itod(uint(addr.Multiaddr[6])) + "." + itod(uint(addr.Multiaddr[7])) port = uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3]) return }