Remove net package and refactor

This commit is contained in:
xvzc 2024-07-22 19:59:11 +09:00
parent 2d8b9103db
commit ed70bde7e7
9 changed files with 118 additions and 135 deletions

View File

@ -11,9 +11,16 @@ import (
"github.com/xvzc/SpoofDPI/util" "github.com/xvzc/SpoofDPI/util"
) )
var VERSION = "v0.0.0(dev)"
func main() { func main() {
util.ParseArgs() util.ParseArgs()
config := util.GetConfig() config := util.GetConfig()
if *config.Version {
println("spoog-dpi", VERSION)
println("\nA simple and fast anti-censorship tool written in Go.")
println("https://github.com/xvzc/SpoofDPI")
os.Exit(0)
}
pxy := proxy.New(config) pxy := proxy.New(config)
if *config.Debug { if *config.Debug {

View File

@ -1,31 +0,0 @@
package net
import (
"net"
"strconv"
)
func ListenTCP(network string, addr *TCPAddr) (Listener, error) {
l, err := net.ListenTCP(network, addr.Addr)
if err != nil {
return Listener{}, err
}
return Listener{listener: l}, nil
}
func DialTCP(network string, ip string, port string) (*Conn, error) {
p, _ := strconv.Atoi(port)
addr := &net.TCPAddr{
IP: net.ParseIP(ip),
Port: p,
}
conn, err := net.DialTCP(network, nil, addr)
if err != nil {
return &Conn{}, err
}
return &Conn{*conn}, nil
}

View File

@ -1,18 +0,0 @@
package net
import (
"net"
)
type Listener struct {
listener *net.TCPListener
}
func (l *Listener) Accept() (*Conn, error) {
conn, err := l.listener.AcceptTCP()
if err != nil {
return &Conn{}, err
}
return &Conn{*conn}, nil
}

View File

@ -1,20 +0,0 @@
package net
import (
"net"
)
type TCPAddr struct {
Addr *net.TCPAddr
}
func TcpAddr(ip string, port int) *TCPAddr {
addr := &net.TCPAddr{
IP: net.ParseIP(ip),
Port: port,
}
return &TCPAddr{
Addr: addr,
}
}

View File

@ -1,21 +1,28 @@
package proxy package proxy
import ( import (
"net"
"strconv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/xvzc/SpoofDPI/net"
"github.com/xvzc/SpoofDPI/packet" "github.com/xvzc/SpoofDPI/packet"
) )
func (pxy *Proxy) HandleHttp(lConn *net.Conn, pkt *packet.HttpPacket, ip string) { func (pxy *Proxy) HandleHttp(lConn *net.TCPConn, pkt *packet.HttpPacket, ip string) {
pkt.Tidy() pkt.Tidy()
// Create connection to server // Create a connection to the requested server
var port = "80" var port int = 80
var err error
if pkt.Port() != "" { if pkt.Port() != "" {
port = pkt.Port() port, err = strconv.Atoi(pkt.Port())
if err != nil {
log.Debug("[HTTPS] Error while parsing port for ", pkt.Domain(), " aborting..")
}
} }
rConn, err := net.DialTCP("tcp", ip, port) rConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: port})
if err != nil { if err != nil {
lConn.Close() lConn.Close()
log.Debug("[HTTP] ", err) log.Debug("[HTTP] ", err)
@ -32,7 +39,7 @@ func (pxy *Proxy) HandleHttp(lConn *net.Conn, pkt *packet.HttpPacket, ip string)
log.Debug("[HTTP] New connection to the server ", pkt.Domain(), " ", rConn.LocalAddr()) log.Debug("[HTTP] New connection to the server ", pkt.Domain(), " ", rConn.LocalAddr())
go rConn.Serve(lConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout) go Serve(rConn, lConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout)
_, err = rConn.Write(pkt.Raw()) _, err = rConn.Write(pkt.Raw())
if err != nil { if err != nil {
@ -42,5 +49,5 @@ func (pxy *Proxy) HandleHttp(lConn *net.Conn, pkt *packet.HttpPacket, ip string)
log.Debug("[HTTP] Sent a request to ", pkt.Domain()) log.Debug("[HTTP] Sent a request to ", pkt.Domain())
lConn.Serve(rConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout) Serve(lConn, rConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout)
} }

View File

@ -1,19 +1,25 @@
package proxy package proxy
import ( import (
"net"
"strconv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/xvzc/SpoofDPI/net"
"github.com/xvzc/SpoofDPI/packet" "github.com/xvzc/SpoofDPI/packet"
) )
func (pxy *Proxy) HandleHttps(lConn *net.Conn, initPkt *packet.HttpPacket, ip string) { func (pxy *Proxy) HandleHttps(lConn *net.TCPConn, initPkt *packet.HttpPacket, ip string) {
// Create a connection to the requested server // Create a connection to the requested server
var port = "443" var port int = 443
var err error
if initPkt.Port() != "" { if initPkt.Port() != "" {
port = initPkt.Port() port, err = strconv.Atoi(initPkt.Port())
if err != nil {
log.Debug("[HTTPS] Error while parsing port for ", initPkt.Domain(), " aborting..")
}
} }
rConn, err := net.DialTCP("tcp4", ip, port) rConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: port})
if err != nil { if err != nil {
lConn.Close() lConn.Close()
log.Debug("[HTTPS] ", err) log.Debug("[HTTPS] ", err)
@ -39,7 +45,7 @@ func (pxy *Proxy) HandleHttps(lConn *net.Conn, initPkt *packet.HttpPacket, ip st
log.Debug("[HTTPS] Sent 200 Connection Estabalished to ", lConn.RemoteAddr()) log.Debug("[HTTPS] Sent 200 Connection Estabalished to ", lConn.RemoteAddr())
// Read client hello // Read client hello
clientHello, err := lConn.ReadBytes() clientHello, err := ReadBytes(lConn)
if err != nil { if err != nil {
log.Debug("[HTTPS] Error reading client hello from the client", err) log.Debug("[HTTPS] Error reading client hello from the client", err)
return return
@ -51,22 +57,27 @@ func (pxy *Proxy) HandleHttps(lConn *net.Conn, initPkt *packet.HttpPacket, ip st
chPkt := packet.NewHttpsPacket(clientHello) chPkt := packet.NewHttpsPacket(clientHello)
go rConn.Serve(lConn, "[HTTPS]", rConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout) lConn.SetLinger(3)
rConn.SetLinger(3)
go Serve(rConn, lConn, "[HTTPS]", rConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout)
if pxy.patternExists() && !pxy.patternMatches([]byte(initPkt.Domain())) { if pxy.patternExists() && !pxy.patternMatches([]byte(initPkt.Domain())) {
log.Debug("[HTTPS] Writing plain client hello to ", initPkt.Domain())
if _, err := rConn.Write(chPkt.Raw()); err != nil { if _, err := rConn.Write(chPkt.Raw()); err != nil {
log.Debug("[HTTPS] Error writing client hello to ", initPkt.Domain(), err) log.Debug("[HTTPS] Error writing plain client hello to ", initPkt.Domain(), err)
return return
} }
} else { } else {
log.Debug("[HTTPS] Writing chunked client hello to ", initPkt.Domain())
chunks := pxy.splitInChunks(chPkt.Raw(), pxy.windowSize) chunks := pxy.splitInChunks(chPkt.Raw(), pxy.windowSize)
if _, err := rConn.WriteChunks(chunks); err != nil { if _, err := WriteChunks(rConn, chunks); err != nil {
log.Debug("[HTTPS] Error writing client hello to ", initPkt.Domain(), err) log.Debug("[HTTPS] Error writing chunked client hello to ", initPkt.Domain(), err)
return return
} }
} }
lConn.Serve(rConn, "[HTTPS]", lConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout) Serve(lConn, rConn, "[HTTPS]", lConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout)
} }
func (pxy *Proxy) splitInChunks(bytes []byte, size int) [][]byte { func (pxy *Proxy) splitInChunks(bytes []byte, size int) [][]byte {

View File

@ -1,4 +1,4 @@
package net package proxy
import ( import (
"errors" "errors"
@ -11,11 +11,7 @@ import (
const BUF_SIZE = 1024 const BUF_SIZE = 1024
type Conn struct { func WriteChunks(conn *net.TCPConn, c [][]byte) (n int, err error) {
net.TCPConn
}
func (conn *Conn) WriteChunks(c [][]byte) (n int, err error) {
total := 0 total := 0
for i := 0; i < len(c); i++ { for i := 0; i < len(c); i++ {
b, err := conn.Write(c[i]) b, err := conn.Write(c[i])
@ -29,7 +25,7 @@ func (conn *Conn) WriteChunks(c [][]byte) (n int, err error) {
return total, nil return total, nil
} }
func (conn *Conn) ReadBytes() ([]byte, error) { func ReadBytes(conn *net.TCPConn) ([]byte, error) {
ret := make([]byte, 0) ret := make([]byte, 0)
buf := make([]byte, BUF_SIZE) buf := make([]byte, BUF_SIZE)
@ -57,7 +53,7 @@ func (conn *Conn) ReadBytes() ([]byte, error) {
return ret, nil return ret, nil
} }
func (from *Conn) Serve(to *Conn, proto string, fd string, td string, timeout int) { func Serve(from *net.TCPConn, to *net.TCPConn, proto string, fd string, td string, timeout int) {
proto += " " proto += " "
for { for {
@ -65,7 +61,7 @@ func (from *Conn) Serve(to *Conn, proto string, fd string, td string, timeout in
time.Now().Add(time.Millisecond * time.Duration(timeout)), time.Now().Add(time.Millisecond * time.Duration(timeout)),
) )
buf, err := from.ReadBytes() buf, err := ReadBytes(from)
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
log.Debug(proto, "Finished ", fd) log.Debug(proto, "Finished ", fd)

View File

@ -2,12 +2,13 @@ package proxy
import ( import (
"fmt" "fmt"
"net"
"os" "os"
"regexp" "regexp"
"strconv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/xvzc/SpoofDPI/dns" "github.com/xvzc/SpoofDPI/dns"
"github.com/xvzc/SpoofDPI/net"
"github.com/xvzc/SpoofDPI/packet" "github.com/xvzc/SpoofDPI/packet"
"github.com/xvzc/SpoofDPI/util" "github.com/xvzc/SpoofDPI/util"
) )
@ -34,34 +35,26 @@ func New(config *util.Config) *Proxy {
} }
} }
func (pxy *Proxy) TcpAddr() *net.TCPAddr {
return net.TcpAddr(pxy.addr, pxy.port)
}
func (pxy *Proxy) Port() int {
return pxy.port
}
func (pxy *Proxy) Start() { func (pxy *Proxy) Start() {
l, err := net.ListenTCP("tcp4", pxy.TcpAddr()) l, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.ParseIP(pxy.addr), Port: pxy.port})
if err != nil { if err != nil {
log.Fatal("Error creating listener: ", err) log.Fatal("[PROXY] Error creating listener: ", err)
os.Exit(1) os.Exit(1)
} }
log.Println(fmt.Sprintf("Connection timeout is set to %dms", pxy.timeout)) log.Println(fmt.Sprintf("[PROXY] Connection timeout is set to %dms", pxy.timeout))
log.Println("Created a listener on port", pxy.Port()) log.Println("[PROXY] Created a listener on port", pxy.port)
for { for {
conn, err := l.Accept() conn, err := l.Accept()
if err != nil { if err != nil {
log.Fatal("Error accepting connection: ", err) log.Fatal("[PROXY] Error accepting connection: ", err)
continue continue
} }
go func() { go func() {
b, err := conn.ReadBytes() b, err := ReadBytes(conn.(*net.TCPConn))
if err != nil { if err != nil {
return return
} }
@ -70,32 +63,68 @@ func (pxy *Proxy) Start() {
pkt, err := packet.NewHttpPacket(b) pkt, err := packet.NewHttpPacket(b)
if err != nil { if err != nil {
log.Debug("Error while parsing request: ", string(b)) log.Debug("[PROXY] Error while parsing request: ", string(b))
conn.Close() conn.Close()
return return
} }
if !pkt.IsValidMethod() { if !pkt.IsValidMethod() {
log.Debug("Unsupported method: ", pkt.Method()) log.Debug("[PROXY] Unsupported method: ", pkt.Method())
conn.Close() conn.Close()
return return
} }
ip, err := pxy.resolver.Lookup(pkt.Domain()) ip, err := pxy.resolver.Lookup(pkt.Domain())
if err != nil { if err != nil {
log.Error("[HTTP] Error looking up for domain with ", pkt.Domain(), " ", err) log.Error("[PROXY] Error looking up for domain with ", pkt.Domain(), " ", err)
conn.Write([]byte(pkt.Version() + " 502 Bad Gateway\r\n\r\n")) conn.Write([]byte(pkt.Version() + " 502 Bad Gateway\r\n\r\n"))
conn.Close() conn.Close()
return return
} }
// Avoid recursively querying self
if pkt.Port() == strconv.Itoa(pxy.port) && isLoopedRequest(net.ParseIP(ip)) {
log.Error("[HTTP] Invalid request: final target has the same IP and port as our proxy")
conn.Close()
return
}
if pkt.IsConnectMethod() { if pkt.IsConnectMethod() {
log.Debug("[HTTPS] Start") log.Debug("[PROXY] Start HTTPS")
pxy.HandleHttps(conn, pkt, ip) pxy.HandleHttps(conn.(*net.TCPConn), pkt, ip)
} else { } else {
log.Debug("[HTTP] Start") log.Debug("[PROXY] Start HTTP")
pxy.HandleHttp(conn, pkt, ip) pxy.HandleHttp(conn.(*net.TCPConn), pkt, ip)
} }
}() }()
} }
} }
func isLoopedRequest(ip net.IP) bool {
// we don't handle IPv6 at all it seems
if ip.To4() == nil {
return false
}
if ip.IsLoopback() {
return true
}
// Get list of available addresses
// See `ip -4 addr show`
addr, err := net.InterfaceAddrs() // needs AF_NETLINK on linux
if err != nil {
log.Error("[PROXY] Error while getting addresses of our network interfaces: ", err)
return false
}
for _, addr := range addr {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.To4() != nil && ipnet.IP.To4().Equal(ip) {
return true
}
}
}
return false
}

View File

@ -23,6 +23,7 @@ type Config struct {
AllowedPattern *regexp.Regexp AllowedPattern *regexp.Regexp
AllowedUrls *regexp.Regexp AllowedUrls *regexp.Regexp
WindowSize *int WindowSize *int
Version *bool
} }
type ArrayFlags []string type ArrayFlags []string
@ -55,6 +56,7 @@ func ParseArgs() {
config.NoBanner = flag.Bool("no-banner", false, "Disable banner") config.NoBanner = flag.Bool("no-banner", false, "Disable banner")
config.Timeout = flag.Int("timeout", 2000, "timeout in milliseconds") config.Timeout = flag.Int("timeout", 2000, "timeout in milliseconds")
config.WindowSize = flag.Int("window-size", 50, "window-size for fragmented client hello") config.WindowSize = flag.Int("window-size", 50, "window-size for fragmented client hello")
config.Version = flag.Bool("v", false, "print version")
flag.Var(&allowedHosts, "url", "Bypass DPI only on this url, can be passed multiple times") flag.Var(&allowedHosts, "url", "Bypass DPI only on this url, can be passed multiple times")
allowedPattern = flag.String( allowedPattern = flag.String(