chore: embed hysteria, clean irrelevant codes, code from https://github.com/HyNetwork/hysteria

This commit is contained in:
Skyxim
2022-07-03 18:22:56 +08:00
parent 8ce9737f3d
commit 3cc1870aee
28 changed files with 3251 additions and 375 deletions

View File

@ -0,0 +1 @@
Grabbed from https://github.com/xtaci/tcpraw with modifications

View File

@ -0,0 +1,102 @@
package faketcp
import (
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/hysteria/obfs"
"net"
"sync"
"syscall"
"time"
)
const udpBufferSize = 65535
type ObfsFakeTCPConn struct {
orig *TCPConn
obfs obfs.Obfuscator
closed bool
readBuf []byte
readMutex sync.Mutex
writeBuf []byte
writeMutex sync.Mutex
}
func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn {
return &ObfsFakeTCPConn{
orig: orig,
obfs: obfs,
readBuf: make([]byte, udpBufferSize),
writeBuf: make([]byte, udpBufferSize),
}
}
func (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) {
for {
c.readMutex.Lock()
if c.closed {
log.Infoln("read faketcp obfs before")
}
n, addr, err := c.orig.ReadFrom(c.readBuf)
if c.closed {
log.Infoln("read faketcp obfs after")
}
if n <= 0 {
c.readMutex.Unlock()
return 0, addr, err
}
newN := c.obfs.Deobfuscate(c.readBuf[:n], p)
c.readMutex.Unlock()
if newN > 0 {
// Valid packet
return newN, addr, err
} else if err != nil {
// Not valid and orig.ReadFrom had some error
return 0, addr, err
}
}
}
func (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
c.writeMutex.Lock()
bn := c.obfs.Obfuscate(p, c.writeBuf)
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
c.writeMutex.Unlock()
if err != nil {
return 0, err
} else {
return len(p), nil
}
}
func (c *ObfsFakeTCPConn) Close() error {
c.closed = true
return c.orig.Close()
}
func (c *ObfsFakeTCPConn) LocalAddr() net.Addr {
return c.orig.LocalAddr()
}
func (c *ObfsFakeTCPConn) SetDeadline(t time.Time) error {
return c.orig.SetDeadline(t)
}
func (c *ObfsFakeTCPConn) SetReadDeadline(t time.Time) error {
return c.orig.SetReadDeadline(t)
}
func (c *ObfsFakeTCPConn) SetWriteDeadline(t time.Time) error {
return c.orig.SetWriteDeadline(t)
}
func (c *ObfsFakeTCPConn) SetReadBuffer(bytes int) error {
return c.orig.SetReadBuffer(bytes)
}
func (c *ObfsFakeTCPConn) SetWriteBuffer(bytes int) error {
return c.orig.SetWriteBuffer(bytes)
}
func (c *ObfsFakeTCPConn) SyscallConn() (syscall.RawConn, error) {
return c.orig.SyscallConn()
}

View File

@ -0,0 +1,616 @@
//go:build linux
// +build linux
package faketcp
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
}
func (conn *TCPConn) SyscallConn() (syscall.RawConn, error) {
if len(conn.handles) == 0 {
return nil, errors.New("no handles")
// How is it possible?
}
return conn.handles[0].SyscallConn()
}
// 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
}

View File

@ -0,0 +1,21 @@
//go:build !linux
// +build !linux
package faketcp
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("faketcp is not supported on this platform")
}
func Listen(network, address string) (*TCPConn, error) {
return nil, errors.New("faketcp is not supported on this platform")
}

View File

@ -0,0 +1,196 @@
//go:build linux
// +build linux
package faketcp
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)
}
}
}

View File

@ -0,0 +1,89 @@
package udp
import (
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/hysteria/obfs"
"net"
"sync"
"time"
)
const udpBufferSize = 65535
type ObfsUDPConn struct {
orig net.PacketConn
obfs obfs.Obfuscator
readBuf []byte
readMutex sync.Mutex
writeBuf []byte
writeMutex sync.Mutex
closed bool
}
func NewObfsUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsUDPConn {
return &ObfsUDPConn{
orig: orig,
obfs: obfs,
readBuf: make([]byte, udpBufferSize),
writeBuf: make([]byte, udpBufferSize),
}
}
func (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
for {
c.readMutex.Lock()
if c.closed {
log.Infoln("read udp obfs before")
}
n, addr, err := c.orig.ReadFrom(c.readBuf)
if c.closed {
log.Infoln("read udp obfs after")
}
if n <= 0 {
c.readMutex.Unlock()
return 0, addr, err
}
newN := c.obfs.Deobfuscate(c.readBuf[:n], p)
c.readMutex.Unlock()
if newN > 0 {
// Valid packet
return newN, addr, err
} else if err != nil {
// Not valid and orig.ReadFrom had some error
return 0, addr, err
}
}
}
func (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
c.writeMutex.Lock()
bn := c.obfs.Obfuscate(p, c.writeBuf)
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
c.writeMutex.Unlock()
if err != nil {
return 0, err
} else {
return len(p), nil
}
}
func (c *ObfsUDPConn) Close() error {
c.closed = true
return c.orig.Close()
}
func (c *ObfsUDPConn) LocalAddr() net.Addr {
return c.orig.LocalAddr()
}
func (c *ObfsUDPConn) SetDeadline(t time.Time) error {
return c.orig.SetDeadline(t)
}
func (c *ObfsUDPConn) SetReadDeadline(t time.Time) error {
return c.orig.SetReadDeadline(t)
}
func (c *ObfsUDPConn) SetWriteDeadline(t time.Time) error {
return c.orig.SetWriteDeadline(t)
}

View File

@ -0,0 +1,105 @@
package wechat
import (
"encoding/binary"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/hysteria/obfs"
"math/rand"
"net"
"sync"
"time"
)
const udpBufferSize = 65535
type ObfsWeChatUDPConn struct {
orig net.PacketConn
obfs obfs.Obfuscator
closed bool
readBuf []byte
readMutex sync.Mutex
writeBuf []byte
writeMutex sync.Mutex
sn uint32
}
func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChatUDPConn {
log.Infoln("new wechat")
return &ObfsWeChatUDPConn{
orig: orig,
obfs: obfs,
readBuf: make([]byte, udpBufferSize),
writeBuf: make([]byte, udpBufferSize),
sn: rand.Uint32() & 0xFFFF,
}
}
func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
for {
c.readMutex.Lock()
if c.closed {
log.Infoln("read wechat obfs before")
}
n, addr, err := c.orig.ReadFrom(c.readBuf)
if c.closed {
log.Infoln("read wechat obfs after")
}
if n <= 13 {
c.readMutex.Unlock()
return 0, addr, err
}
newN := c.obfs.Deobfuscate(c.readBuf[13:n], p)
c.readMutex.Unlock()
if newN > 0 {
// Valid packet
return newN, addr, err
} else if err != nil {
// Not valid and orig.ReadFrom had some error
return 0, addr, err
}
}
}
func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
c.writeMutex.Lock()
c.writeBuf[0] = 0xa1
c.writeBuf[1] = 0x08
binary.BigEndian.PutUint32(c.writeBuf[2:], c.sn)
c.sn++
c.writeBuf[6] = 0x00
c.writeBuf[7] = 0x10
c.writeBuf[8] = 0x11
c.writeBuf[9] = 0x18
c.writeBuf[10] = 0x30
c.writeBuf[11] = 0x22
c.writeBuf[12] = 0x30
bn := c.obfs.Obfuscate(p, c.writeBuf[13:])
_, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr)
c.writeMutex.Unlock()
if err != nil {
return 0, err
} else {
return len(p), nil
}
}
func (c *ObfsWeChatUDPConn) Close() error {
c.closed = true
return c.orig.Close()
}
func (c *ObfsWeChatUDPConn) LocalAddr() net.Addr {
return c.orig.LocalAddr()
}
func (c *ObfsWeChatUDPConn) SetDeadline(t time.Time) error {
return c.orig.SetDeadline(t)
}
func (c *ObfsWeChatUDPConn) SetReadDeadline(t time.Time) error {
return c.orig.SetReadDeadline(t)
}
func (c *ObfsWeChatUDPConn) SetWriteDeadline(t time.Time) error {
return c.orig.SetWriteDeadline(t)
}