pax_global_header00006660000000000000000000000064135230151410014505gustar00rootroot0000000000000052 comment=8dad6ae5a8380aa19816999ecaec7bf4a7d00d6c tcpraw-1.2.25/000077500000000000000000000000001352301514100130745ustar00rootroot00000000000000tcpraw-1.2.25/.gitignore000066400000000000000000000003001352301514100150550ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out tcpraw-1.2.25/.travis.yml000066400000000000000000000005051352301514100152050ustar00rootroot00000000000000language: go sudo: required go: - 1.10.x - 1.11.x - 1.12.x before_install: - go get -t -v ./... install: - go get github.com/xtaci/tcpraw script: - sudo -E env "PATH=$PATH" go test -coverprofile=coverage.txt -covermode=atomic -bench . after_success: - bash <(curl -s https://codecov.io/bash) tcpraw-1.2.25/LICENSE000066400000000000000000000020461352301514100141030ustar00rootroot00000000000000MIT License Copyright (c) 2019 xtaci Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tcpraw-1.2.25/README.md000066400000000000000000000023571352301514100143620ustar00rootroot00000000000000# tcpraw [![GoDoc][1]][2] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Coverage Statusd][7]][8] [![MIT licensed][9]][10] [1]: https://godoc.org/github.com/xtaci/tcpraw?status.svg [2]: https://godoc.org/github.com/xtaci/tcpraw [3]: https://travis-ci.org/xtaci/tcpraw.svg?branch=master [4]: https://travis-ci.org/xtaci/tcpraw [5]: https://goreportcard.com/badge/github.com/xtaci/tcpraw [6]: https://goreportcard.com/report/github.com/xtaci/tcpraw [7]: https://codecov.io/gh/xtaci/tcpraw/branch/master/graph/badge.svg [8]: https://codecov.io/gh/xtaci/tcpraw [9]: https://img.shields.io/badge/license-MIT-blue.svg [10]: LICENSE # Introduction A packet-oriented connection by simulating TCP protocol ## Features 0. Tiny 1. Support IPv4 and IPv6. 2. Realistic sliding window, NAT friendly. 3. Pure golang without cgo, available on all architecture. ## Documentation For complete documentation, see the associated [Godoc](https://godoc.org/github.com/xtaci/tcpraw). ## Benchmark ``` goos: linux goarch: amd64 pkg: github.com/xtaci/tcpraw BenchmarkEcho-2 20000 93036 ns/op 11.01 MB/s 6200 B/op 62 allocs/op PASS ok github.com/xtaci/tcpraw 2.758s ``` ## Status Stable ## Who is using this https://github.com/xtaci/kcptun tcpraw-1.2.25/tcp_linux.go000066400000000000000000000360551352301514100154410ustar00rootroot00000000000000// +build linux package tcpraw import ( "crypto/rand" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "net" "sync" "sync/atomic" "syscall" "time" "github.com/coreos/go-iptables/iptables" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) var ( errOpNotImplemented = errors.New("operation not implemented") errTimeout = errors.New("timeout") expire = time.Minute ) // a message from NIC type message struct { bts []byte addr net.Addr } // a tcp flow information of a connection pair type tcpFlow struct { conn *net.TCPConn // the related system TCP connection of this flow handle *net.IPConn // the handle to send packets seq uint32 // TCP sequence number ack uint32 // TCP acknowledge number networkLayer gopacket.SerializableLayer // network layer header for tx ts time.Time // last packet incoming time buf gopacket.SerializeBuffer // a buffer for write tcpHeader layers.TCP } // TCPConn defines a TCP-packet oriented connection type TCPConn struct { die chan struct{} dieOnce sync.Once // the main golang sockets tcpconn *net.TCPConn // from net.Dial listener *net.TCPListener // from net.Listen // handles handles []*net.IPConn // packets captured from all related NICs will be delivered to this channel chMessage chan message // all TCP flows flowTable map[string]*tcpFlow flowsLock sync.Mutex // iptables iptables *iptables.IPTables iprule []string ip6tables *iptables.IPTables ip6rule []string // deadlines readDeadline atomic.Value writeDeadline atomic.Value // serialization opts gopacket.SerializeOptions } // lockflow locks the flow table and apply function `f` to the entry, and create one if not exist func (conn *TCPConn) lockflow(addr net.Addr, f func(e *tcpFlow)) { key := addr.String() conn.flowsLock.Lock() e := conn.flowTable[key] if e == nil { // entry first visit e = new(tcpFlow) e.ts = time.Now() e.buf = gopacket.NewSerializeBuffer() } f(e) conn.flowTable[key] = e conn.flowsLock.Unlock() } // clean expired flows func (conn *TCPConn) cleaner() { ticker := time.NewTicker(time.Minute) select { case <-conn.die: return case <-ticker.C: conn.flowsLock.Lock() for k, v := range conn.flowTable { if time.Now().Sub(v.ts) > expire { if v.conn != nil { setTTL(v.conn, 64) v.conn.Close() } delete(conn.flowTable, k) } } conn.flowsLock.Unlock() } } // captureFlow capture every inbound packets based on rules of BPF func (conn *TCPConn) captureFlow(handle *net.IPConn, port int) { buf := make([]byte, 2048) opt := gopacket.DecodeOptions{NoCopy: true, Lazy: true} for { n, addr, err := handle.ReadFromIP(buf) if err != nil { return } // try decoding TCP frame from buf[:n] packet := gopacket.NewPacket(buf[:n], layers.LayerTypeTCP, opt) transport := packet.TransportLayer() tcp, ok := transport.(*layers.TCP) if !ok { continue } // port filtering if int(tcp.DstPort) != port { continue } // address building var src net.TCPAddr src.IP = addr.IP src.Port = int(tcp.SrcPort) var orphan bool // flow maintaince conn.lockflow(&src, func(e *tcpFlow) { if e.conn == nil { // make sure it's related to net.TCPConn orphan = true // mark as orphan if it's not related net.TCPConn } // to keep track of TCP header related to this source e.ts = time.Now() if tcp.ACK { e.seq = tcp.Ack } if tcp.SYN { e.ack = tcp.Seq + 1 } if tcp.PSH { if e.ack == tcp.Seq { e.ack = tcp.Seq + uint32(len(tcp.Payload)) } } e.handle = handle }) // push data if it's not orphan if !orphan && tcp.PSH { payload := make([]byte, len(tcp.Payload)) copy(payload, tcp.Payload) select { case conn.chMessage <- message{payload, &src}: case <-conn.die: return } } } } // ReadFrom implements the PacketConn ReadFrom method. func (conn *TCPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { var timer *time.Timer var deadline <-chan time.Time if d, ok := conn.readDeadline.Load().(time.Time); ok && !d.IsZero() { timer = time.NewTimer(time.Until(d)) defer timer.Stop() deadline = timer.C } select { case <-deadline: return 0, nil, errTimeout case <-conn.die: return 0, nil, io.EOF case packet := <-conn.chMessage: n = copy(p, packet.bts) return n, packet.addr, nil } } // WriteTo implements the PacketConn WriteTo method. func (conn *TCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { var deadline <-chan time.Time if d, ok := conn.writeDeadline.Load().(time.Time); ok && !d.IsZero() { timer := time.NewTimer(time.Until(d)) defer timer.Stop() deadline = timer.C } select { case <-deadline: return 0, errTimeout case <-conn.die: return 0, io.EOF default: raddr, err := net.ResolveTCPAddr("tcp", addr.String()) if err != nil { return 0, err } var lport int if conn.tcpconn != nil { lport = conn.tcpconn.LocalAddr().(*net.TCPAddr).Port } else { lport = conn.listener.Addr().(*net.TCPAddr).Port } conn.lockflow(addr, func(e *tcpFlow) { // if the flow doesn't have handle , assume this packet has lost, without notification if e.handle == nil { n = len(p) return } // build tcp header with local and remote port e.tcpHeader.SrcPort = layers.TCPPort(lport) e.tcpHeader.DstPort = layers.TCPPort(raddr.Port) binary.Read(rand.Reader, binary.LittleEndian, &e.tcpHeader.Window) e.tcpHeader.Window |= 0x8000 // make sure it's larger than 32768 e.tcpHeader.Ack = e.ack e.tcpHeader.Seq = e.seq e.tcpHeader.PSH = true e.tcpHeader.ACK = true // build IP header with src & dst ip for TCP checksum if raddr.IP.To4() != nil { ip := &layers.IPv4{ Protocol: layers.IPProtocolTCP, SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To4(), DstIP: raddr.IP.To4(), } e.tcpHeader.SetNetworkLayerForChecksum(ip) } else { ip := &layers.IPv6{ NextHeader: layers.IPProtocolTCP, SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To16(), DstIP: raddr.IP.To16(), } e.tcpHeader.SetNetworkLayerForChecksum(ip) } e.buf.Clear() gopacket.SerializeLayers(e.buf, conn.opts, &e.tcpHeader, gopacket.Payload(p)) if conn.tcpconn != nil { _, err = e.handle.Write(e.buf.Bytes()) } else { _, err = e.handle.WriteToIP(e.buf.Bytes(), &net.IPAddr{IP: raddr.IP}) } // increase seq in flow e.seq += uint32(len(p)) n = len(p) }) } return } // Close closes the connection. func (conn *TCPConn) Close() error { var err error conn.dieOnce.Do(func() { // signal closing close(conn.die) // close all established tcp connections if conn.tcpconn != nil { // client setTTL(conn.tcpconn, 64) err = conn.tcpconn.Close() } else if conn.listener != nil { err = conn.listener.Close() // server conn.flowsLock.Lock() for k, v := range conn.flowTable { if v.conn != nil { setTTL(v.conn, 64) v.conn.Close() } delete(conn.flowTable, k) } conn.flowsLock.Unlock() } // close handles for k := range conn.handles { conn.handles[k].Close() } // delete iptable if conn.iptables != nil { conn.iptables.Delete("filter", "OUTPUT", conn.iprule...) } if conn.ip6tables != nil { conn.ip6tables.Delete("filter", "OUTPUT", conn.ip6rule...) } }) return err } // LocalAddr returns the local network address. func (conn *TCPConn) LocalAddr() net.Addr { if conn.tcpconn != nil { return conn.tcpconn.LocalAddr() } else if conn.listener != nil { return conn.listener.Addr() } return nil } // SetDeadline implements the Conn SetDeadline method. func (conn *TCPConn) SetDeadline(t time.Time) error { if err := conn.SetReadDeadline(t); err != nil { return err } if err := conn.SetWriteDeadline(t); err != nil { return err } return nil } // SetReadDeadline implements the Conn SetReadDeadline method. func (conn *TCPConn) SetReadDeadline(t time.Time) error { conn.readDeadline.Store(t) return nil } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (conn *TCPConn) SetWriteDeadline(t time.Time) error { conn.writeDeadline.Store(t) return nil } // SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. func (conn *TCPConn) SetDSCP(dscp int) error { for k := range conn.handles { if err := setDSCP(conn.handles[k], dscp); err != nil { return err } } return nil } // SetReadBuffer sets the size of the operating system's receive buffer associated with the connection. func (conn *TCPConn) SetReadBuffer(bytes int) error { var err error for k := range conn.handles { if err := conn.handles[k].SetReadBuffer(bytes); err != nil { return err } } return err } // SetWriteBuffer sets the size of the operating system's transmit buffer associated with the connection. func (conn *TCPConn) SetWriteBuffer(bytes int) error { var err error for k := range conn.handles { if err := conn.handles[k].SetWriteBuffer(bytes); err != nil { return err } } return err } // Dial connects to the remote TCP port, // and returns a single packet-oriented connection func Dial(network, address string) (*TCPConn, error) { // remote address resolve raddr, err := net.ResolveTCPAddr(network, address) if err != nil { return nil, err } // AF_INET handle, err := net.DialIP("ip:tcp", nil, &net.IPAddr{IP: raddr.IP}) if err != nil { return nil, err } // create an established tcp connection // will hack this tcp connection for packet transmission tcpconn, err := net.DialTCP(network, nil, raddr) if err != nil { return nil, err } // fields conn := new(TCPConn) conn.die = make(chan struct{}) conn.flowTable = make(map[string]*tcpFlow) conn.tcpconn = tcpconn conn.chMessage = make(chan message) conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) conn.handles = append(conn.handles, handle) conn.opts = gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, } go conn.captureFlow(handle, tcpconn.LocalAddr().(*net.TCPAddr).Port) go conn.cleaner() // iptables err = setTTL(tcpconn, 1) if err != nil { return nil, err } if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { if !exists { if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { conn.iprule = rule conn.iptables = ipt } } } } if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { if !exists { if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { conn.ip6rule = rule conn.ip6tables = ipt } } } } // discard everything go io.Copy(ioutil.Discard, tcpconn) return conn, nil } // Listen acts like net.ListenTCP, // and returns a single packet-oriented connection func Listen(network, address string) (*TCPConn, error) { // fields conn := new(TCPConn) conn.flowTable = make(map[string]*tcpFlow) conn.die = make(chan struct{}) conn.chMessage = make(chan message) conn.opts = gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, } // resolve address laddr, err := net.ResolveTCPAddr(network, address) if err != nil { return nil, err } // AF_INET ifaces, err := net.Interfaces() if err != nil { return nil, err } if laddr.IP == nil || laddr.IP.IsUnspecified() { // if address is not specified, capture on all ifaces var lasterr error for _, iface := range ifaces { if addrs, err := iface.Addrs(); err == nil { for _, addr := range addrs { if ipaddr, ok := addr.(*net.IPNet); ok { if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: ipaddr.IP}); err == nil { conn.handles = append(conn.handles, handle) go conn.captureFlow(handle, laddr.Port) } else { lasterr = err } } } } } if len(conn.handles) == 0 { return nil, lasterr } } else { if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: laddr.IP}); err == nil { conn.handles = append(conn.handles, handle) go conn.captureFlow(handle, laddr.Port) } else { return nil, err } } // start listening l, err := net.ListenTCP(network, laddr) if err != nil { return nil, err } conn.listener = l // start cleaner go conn.cleaner() // iptables drop packets marked with TTL = 1 // TODO: what if iptables is not available, the next hop will send back ICMP Time Exceeded, // is this still an acceptable behavior? if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { if !exists { if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { conn.iprule = rule conn.iptables = ipt } } } } if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { if !exists { if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { conn.ip6rule = rule conn.ip6tables = ipt } } } } // discard everything in original connection go func() { for { tcpconn, err := l.AcceptTCP() if err != nil { return } // if we cannot set TTL = 1, the only thing reasonable is panic if err := setTTL(tcpconn, 1); err != nil { panic(err) } // record net.Conn conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) // discard everything go io.Copy(ioutil.Discard, tcpconn) } }() return conn, nil } // setTTL sets the Time-To-Live field on a given connection func setTTL(c *net.TCPConn, ttl int) error { raw, err := c.SyscallConn() if err != nil { return err } addr := c.LocalAddr().(*net.TCPAddr) if addr.IP.To4() == nil { raw.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, ttl) }) } else { raw.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, ttl) }) } return err } // setDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. func setDSCP(c *net.IPConn, dscp int) error { raw, err := c.SyscallConn() if err != nil { return err } addr := c.LocalAddr().(*net.IPAddr) if addr.IP.To4() == nil { raw.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, dscp) }) } else { raw.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, dscp<<2) }) } return err } tcpraw-1.2.25/tcp_stub.go000066400000000000000000000006151352301514100152500ustar00rootroot00000000000000// +build !linux package tcpraw import ( "errors" "net" ) type TCPConn struct{ *net.UDPConn } // Dial connects to the remote TCP port, // and returns a single packet-oriented connection func Dial(network, address string) (*TCPConn, error) { return nil, errors.New("os not supported") } func Listen(network, address string) (*TCPConn, error) { return nil, errors.New("os not supported") } tcpraw-1.2.25/tcp_test.go000066400000000000000000000066711352301514100152620ustar00rootroot00000000000000package tcpraw import ( "log" "net" "net/http" _ "net/http/pprof" "testing" ) //const testPortStream = "127.0.0.1:3456" //const testPortPacket = "127.0.0.1:3457" const testPortStream = "127.0.0.1:3456" const portServerPacket = "[::]:3457" const portRemotePacket = "127.0.0.1:3457" func init() { startTCPServer() startTCPRawServer() go func() { log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) }() } func startTCPServer() net.Listener { l, err := net.Listen("tcp", testPortStream) if err != nil { log.Panicln(err) } go func() { defer l.Close() for { conn, err := l.Accept() if err != nil { log.Println(err) return } go handleRequest(conn) } }() return l } func startTCPRawServer() *TCPConn { conn, err := Listen("tcp", portServerPacket) if err != nil { log.Panicln(err) } err = conn.SetReadBuffer(1024 * 1024) if err != nil { log.Println(err) } err = conn.SetWriteBuffer(1024 * 1024) if err != nil { log.Println(err) } go func() { defer conn.Close() buf := make([]byte, 1024) for { n, addr, err := conn.ReadFrom(buf) if err != nil { log.Println("server readfrom:", err) return } //echo n, err = conn.WriteTo(buf[:n], addr) if err != nil { log.Println("server writeTo:", err) return } } }() return conn } func handleRequest(conn net.Conn) { defer conn.Close() for { buf := make([]byte, 1024) size, err := conn.Read(buf) if err != nil { log.Println("handleRequest:", err) return } data := buf[:size] conn.Write(data) } } func TestDialTCPStream(t *testing.T) { conn, err := Dial("tcp", testPortStream) if err != nil { t.Fatal(err) } defer conn.Close() addr, err := net.ResolveTCPAddr("tcp", testPortStream) if err != nil { t.Fatal(err) } n, err := conn.WriteTo([]byte("abc"), addr) if err != nil { t.Fatal(n, err) } buf := make([]byte, 1024) if n, addr, err := conn.ReadFrom(buf); err != nil { t.Fatal(n, addr, err) } else { log.Println(string(buf[:n]), "from:", addr) } } func TestDialToTCPPacket(t *testing.T) { conn, err := Dial("tcp", portRemotePacket) if err != nil { t.Fatal(err) } defer conn.Close() addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) if err != nil { t.Fatal(err) } n, err := conn.WriteTo([]byte("abc"), addr) if err != nil { t.Fatal(n, err) } log.Println("written") buf := make([]byte, 1024) log.Println("readfrom buf") if n, addr, err := conn.ReadFrom(buf); err != nil { log.Println(err) t.Fatal(n, addr, err) } else { log.Println(string(buf[:n]), "from:", addr) } log.Println("complete") } func TestSettings(t *testing.T) { conn, err := Dial("tcp", portRemotePacket) if err != nil { t.Fatal(err) } defer conn.Close() if err := conn.SetDSCP(46); err != nil { log.Fatal("SetDSCP:", err) } if err := conn.SetReadBuffer(4096); err != nil { log.Fatal("SetReaderBuffer:", err) } if err := conn.SetWriteBuffer(4096); err != nil { log.Fatal("SetWriteBuffer:", err) } } func BenchmarkEcho(b *testing.B) { conn, err := Dial("tcp", portRemotePacket) if err != nil { b.Fatal(err) } defer conn.Close() addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) if err != nil { b.Fatal(err) } buf := make([]byte, 1024) b.ReportAllocs() b.SetBytes(int64(len(buf))) for i := 0; i < b.N; i++ { n, err := conn.WriteTo(buf, addr) if err != nil { b.Fatal(n, err) } if n, addr, err := conn.ReadFrom(buf); err != nil { b.Fatal(n, addr, err) } } }