mirror of
https://github.com/xvzc/SpoofDPI.git
synced 2025-01-03 04:50:11 +00:00
commit
480427b2d4
@ -11,11 +11,18 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
p := proxy.New(config)
|
pxy := proxy.New(config)
|
||||||
if *config.Debug {
|
if *config.Debug {
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
} else {
|
} else {
|
||||||
@ -36,7 +43,7 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go p.Start()
|
go pxy.Start()
|
||||||
|
|
||||||
// Handle signals
|
// Handle signals
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
|
@ -48,7 +48,7 @@ func (d *DnsResolver) Lookup(domain string) (string, error) {
|
|||||||
|
|
||||||
response, _, err := c.Exchange(msg, dnsServer)
|
response, _, err := c.Exchange(msg, dnsServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New(" couldn't resolve the domain")
|
return "", errors.New("couldn not resolve the domain")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, answer := range response.Answer {
|
for _, answer := range response.Answer {
|
||||||
@ -58,7 +58,7 @@ func (d *DnsResolver) Lookup(domain string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("[DNS] couldn't resolve the domain")
|
return "", errors.New("couldn not resolve the domain")
|
||||||
}
|
}
|
||||||
|
|
||||||
func dohLookup(domain string) (string, error) {
|
func dohLookup(domain string) (string, error) {
|
||||||
@ -68,7 +68,7 @@ func dohLookup(domain string) (string, error) {
|
|||||||
|
|
||||||
rsp, err := c.Query(ctx, dohDns.Domain(domain), dohDns.TypeA)
|
rsp, err := c.Query(ctx, dohDns.Domain(domain), dohDns.TypeA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("[DOH] couldn't resolve the domain")
|
return "", errors.New("could not resolve the domain")
|
||||||
}
|
}
|
||||||
// doh dns answer
|
// doh dns answer
|
||||||
answer := rsp.Answer
|
answer := rsp.Answer
|
||||||
@ -86,5 +86,5 @@ func dohLookup(domain string) (string, error) {
|
|||||||
// close the client
|
// close the client
|
||||||
c.Close()
|
c.Close()
|
||||||
|
|
||||||
return "", errors.New("[DOH] couldn't resolve the domain")
|
return "", errors.New("couldn not resolve the domain")
|
||||||
}
|
}
|
||||||
|
195
net/conn.go
195
net/conn.go
@ -1,195 +0,0 @@
|
|||||||
package net
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/xvzc/SpoofDPI/dns"
|
|
||||||
"github.com/xvzc/SpoofDPI/packet"
|
|
||||||
)
|
|
||||||
|
|
||||||
const BUF_SIZE = 1024
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
net.TCPConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *Conn) WriteChunks(c [][]byte) (n int, err error) {
|
|
||||||
total := 0
|
|
||||||
for i := 0; i < len(c); i++ {
|
|
||||||
b, err := conn.Write(c[i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
total += b
|
|
||||||
}
|
|
||||||
|
|
||||||
return total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *Conn) ReadBytes() ([]byte, error) {
|
|
||||||
ret := make([]byte, 0)
|
|
||||||
buf := make([]byte, BUF_SIZE)
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
switch err.(type) {
|
|
||||||
case *net.OpError:
|
|
||||||
return nil, errors.New("timed out")
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = append(ret, buf[:n]...)
|
|
||||||
|
|
||||||
if n < BUF_SIZE {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lConn *Conn) HandleHttp(p *packet.HttpPacket, timeout int, resolver *dns.DnsResolver) {
|
|
||||||
p.Tidy()
|
|
||||||
|
|
||||||
ip, err := resolver.Lookup(p.Domain())
|
|
||||||
if err != nil {
|
|
||||||
log.Error("[HTTP] Error looking up for domain with ", p.Domain(), " ", err)
|
|
||||||
lConn.Write([]byte(p.Version() + " 502 Bad Gateway\r\n\r\n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create connection to server
|
|
||||||
var port = "80"
|
|
||||||
if p.Port() != "" {
|
|
||||||
port = p.Port()
|
|
||||||
}
|
|
||||||
|
|
||||||
rConn, err := DialTCP("tcp", ip, port)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("[HTTP] ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
lConn.Close()
|
|
||||||
log.Debug("[HTTP] Closing client Connection.. ", lConn.RemoteAddr())
|
|
||||||
|
|
||||||
rConn.Close()
|
|
||||||
log.Debug("[HTTP] Closing server Connection.. ", p.Domain(), " ", rConn.LocalAddr())
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Debug("[HTTP] New connection to the server ", p.Domain(), " ", rConn.LocalAddr())
|
|
||||||
|
|
||||||
go rConn.Serve(lConn, "[HTTP]", lConn.RemoteAddr().String(), p.Domain(), timeout)
|
|
||||||
|
|
||||||
_, err = rConn.Write(p.Raw())
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("[HTTP] Error sending request to ", p.Domain(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("[HTTP] Sent a request to ", p.Domain())
|
|
||||||
|
|
||||||
lConn.Serve(rConn, "[HTTP]", lConn.RemoteAddr().String(), p.Domain(), timeout)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lConn *Conn) HandleHttps(p *packet.HttpPacket, timeout int, resolver *dns.DnsResolver) {
|
|
||||||
ip, err := resolver.Lookup(p.Domain())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error("[HTTPS] Error looking up for domain: ", p.Domain(), " ", err)
|
|
||||||
lConn.Write([]byte(p.Version() + " 502 Bad Gateway\r\n\r\n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a connection to the requested server
|
|
||||||
var port = "443"
|
|
||||||
if p.Port() != "" {
|
|
||||||
port = p.Port()
|
|
||||||
}
|
|
||||||
|
|
||||||
rConn, err := DialTCP("tcp4", ip, port)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("[HTTPS] ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
lConn.Close()
|
|
||||||
log.Debug("[HTTPS] Closing client Connection.. ", lConn.RemoteAddr())
|
|
||||||
|
|
||||||
rConn.Close()
|
|
||||||
log.Debug("[HTTPS] Closing server Connection.. ", p.Domain(), " ", rConn.LocalAddr())
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Debug("[HTTPS] New connection to the server ", p.Domain(), " ", rConn.LocalAddr())
|
|
||||||
|
|
||||||
_, err = lConn.Write([]byte(p.Version() + " 200 Connection Established\r\n\r\n"))
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("[HTTPS] Error sending 200 Connection Established to the client", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("[HTTPS] Sent 200 Connection Estabalished to ", lConn.RemoteAddr())
|
|
||||||
|
|
||||||
// Read client hello
|
|
||||||
clientHello, err := lConn.ReadBytes()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("[HTTPS] Error reading client hello from the client", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("[HTTPS] Client sent hello ", len(clientHello), "bytes")
|
|
||||||
|
|
||||||
// Generate a go routine that reads from the server
|
|
||||||
|
|
||||||
pkt := packet.NewHttpsPacket(clientHello)
|
|
||||||
|
|
||||||
chunks := pkt.SplitInChunks()
|
|
||||||
|
|
||||||
go rConn.Serve(lConn, "[HTTPS]", rConn.RemoteAddr().String(), p.Domain(), timeout)
|
|
||||||
|
|
||||||
if _, err := rConn.WriteChunks(chunks); err != nil {
|
|
||||||
log.Debug("[HTTPS] Error writing client hello to ", p.Domain(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lConn.Serve(rConn, "[HTTPS]", lConn.RemoteAddr().String(), p.Domain(), timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (from *Conn) Serve(to *Conn, proto string, fd string, td string, timeout int) {
|
|
||||||
proto += " "
|
|
||||||
|
|
||||||
for {
|
|
||||||
from.SetReadDeadline(
|
|
||||||
time.Now().Add(time.Millisecond * time.Duration(timeout)),
|
|
||||||
)
|
|
||||||
|
|
||||||
buf, err := from.ReadBytes()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
log.Debug(proto, "Finished ", fd)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Debug(proto, "Error reading from ", fd, " ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := to.Write(buf); err != nil {
|
|
||||||
log.Debug(proto, "Error Writing to ", td)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
31
net/dial.go
31
net/dial.go
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
20
net/tcp.go
20
net/tcp.go
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,5 @@
|
|||||||
package packet
|
package packet
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/xvzc/SpoofDPI/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HttpsPacket struct {
|
type HttpsPacket struct {
|
||||||
raw []byte
|
raw []byte
|
||||||
}
|
}
|
||||||
@ -17,21 +13,3 @@ func NewHttpsPacket(raw []byte) HttpsPacket {
|
|||||||
func (p *HttpsPacket) Raw() []byte {
|
func (p *HttpsPacket) Raw() []byte {
|
||||||
return p.raw
|
return p.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HttpsPacket) SplitInChunks() [][]byte {
|
|
||||||
if len(p.Raw()) < 1 {
|
|
||||||
return [][]byte{p.Raw()}
|
|
||||||
}
|
|
||||||
config := util.GetConfig()
|
|
||||||
|
|
||||||
// If the packet matches the pattern or the URLs, we don't split it
|
|
||||||
if config.PatternExists() {
|
|
||||||
if (config.PatternMatches(p.Raw())) {
|
|
||||||
return [][]byte{(p.Raw())[:1], (p.Raw())[1:]}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [][]byte{p.Raw()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [][]byte{(p.Raw())[:1], (p.Raw())[1:]}
|
|
||||||
}
|
|
||||||
|
53
proxy/http.go
Normal file
53
proxy/http.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/xvzc/SpoofDPI/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pxy *Proxy) handleHttp(lConn *net.TCPConn, pkt *packet.HttpPacket, ip string) {
|
||||||
|
pkt.Tidy()
|
||||||
|
|
||||||
|
// Create a connection to the requested server
|
||||||
|
var port int = 80
|
||||||
|
var err error
|
||||||
|
if pkt.Port() != "" {
|
||||||
|
port, err = strconv.Atoi(pkt.Port())
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("[HTTP] Error while parsing port for ", pkt.Domain(), " aborting..")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: port})
|
||||||
|
if err != nil {
|
||||||
|
lConn.Close()
|
||||||
|
log.Debug("[HTTP] ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
lConn.Close()
|
||||||
|
log.Debug("[HTTP] Closing client Connection.. ", lConn.RemoteAddr())
|
||||||
|
|
||||||
|
rConn.Close()
|
||||||
|
log.Debug("[HTTP] Closing server Connection.. ", pkt.Domain(), " ", rConn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Debug("[HTTP] New connection to the server ", pkt.Domain(), " ", rConn.LocalAddr())
|
||||||
|
|
||||||
|
go Serve(rConn, lConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout)
|
||||||
|
|
||||||
|
_, err = rConn.Write(pkt.Raw())
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("[HTTP] Error sending request to ", pkt.Domain(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("[HTTP] Sent a request to ", pkt.Domain())
|
||||||
|
|
||||||
|
Serve(lConn, rConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout)
|
||||||
|
}
|
117
proxy/https.go
Normal file
117
proxy/https.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/xvzc/SpoofDPI/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pxy *Proxy) handleHttps(lConn *net.TCPConn, initPkt *packet.HttpPacket, ip string) {
|
||||||
|
// Create a connection to the requested server
|
||||||
|
var port int = 443
|
||||||
|
var err error
|
||||||
|
if 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("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: port})
|
||||||
|
if err != nil {
|
||||||
|
lConn.Close()
|
||||||
|
log.Debug("[HTTPS] ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
lConn.Close()
|
||||||
|
log.Debug("[HTTPS] Closing client Connection.. ", lConn.RemoteAddr())
|
||||||
|
|
||||||
|
rConn.Close()
|
||||||
|
log.Debug("[HTTPS] Closing server Connection.. ", initPkt.Domain(), " ", rConn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Debug("[HTTPS] New connection to the server ", initPkt.Domain(), " ", rConn.LocalAddr())
|
||||||
|
|
||||||
|
_, err = lConn.Write([]byte(initPkt.Version() + " 200 Connection Established\r\n\r\n"))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("[HTTPS] Error sending 200 Connection Established to the client", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("[HTTPS] Sent 200 Connection Estabalished to ", lConn.RemoteAddr())
|
||||||
|
|
||||||
|
// Read client hello
|
||||||
|
clientHello, err := ReadBytes(lConn)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("[HTTPS] Error reading client hello from the client", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("[HTTPS] Client sent hello ", len(clientHello), "bytes")
|
||||||
|
|
||||||
|
// Generate a go routine that reads from the server
|
||||||
|
|
||||||
|
chPkt := packet.NewHttpsPacket(clientHello)
|
||||||
|
|
||||||
|
// 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())) {
|
||||||
|
log.Debug("[HTTPS] Writing plain client hello to ", initPkt.Domain())
|
||||||
|
if _, err := rConn.Write(chPkt.Raw()); err != nil {
|
||||||
|
log.Debug("[HTTPS] Error writing plain client hello to ", initPkt.Domain(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("[HTTPS] Writing chunked client hello to ", initPkt.Domain())
|
||||||
|
chunks := pxy.splitInChunks(chPkt.Raw(), pxy.windowSize)
|
||||||
|
if _, err := WriteChunks(rConn, chunks); err != nil {
|
||||||
|
log.Debug("[HTTPS] Error writing chunked client hello to ", initPkt.Domain(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serve(lConn, rConn, "[HTTPS]", lConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *Proxy) splitInChunks(bytes []byte, size int) [][]byte {
|
||||||
|
// If the packet matches the pattern or the URLs, we don't split it
|
||||||
|
if pxy.patternExists() && !pxy.patternMatches(bytes) {
|
||||||
|
return [][]byte{bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunks [][]byte
|
||||||
|
var raw []byte = bytes
|
||||||
|
|
||||||
|
for {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// necessary check to avoid slicing beyond
|
||||||
|
// slice capacity
|
||||||
|
if len(raw) < size {
|
||||||
|
size = len(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks = append(chunks, raw[0:size])
|
||||||
|
raw = raw[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *Proxy) patternExists() bool {
|
||||||
|
return pxy.allowedPattern != nil || pxy.allowedUrls != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *Proxy) patternMatches(bytes []byte) bool {
|
||||||
|
return (pxy.allowedPattern != nil && pxy.allowedPattern.Match(bytes)) ||
|
||||||
|
(pxy.allowedUrls != nil && pxy.allowedUrls.Match(bytes))
|
||||||
|
}
|
79
proxy/io.go
Normal file
79
proxy/io.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BUF_SIZE = 1024
|
||||||
|
|
||||||
|
func WriteChunks(conn *net.TCPConn, c [][]byte) (n int, err error) {
|
||||||
|
total := 0
|
||||||
|
for i := 0; i < len(c); i++ {
|
||||||
|
b, err := conn.Write(c[i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
total += b
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadBytes(conn *net.TCPConn) ([]byte, error) {
|
||||||
|
ret := make([]byte, 0)
|
||||||
|
buf := make([]byte, BUF_SIZE)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *net.OpError:
|
||||||
|
return nil, errors.New("timed out")
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = append(ret, buf[:n]...)
|
||||||
|
|
||||||
|
if n < BUF_SIZE {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serve(from *net.TCPConn, to *net.TCPConn, proto string, fd string, td string, timeout int) {
|
||||||
|
proto += " "
|
||||||
|
|
||||||
|
for {
|
||||||
|
from.SetReadDeadline(
|
||||||
|
time.Now().Add(time.Millisecond * time.Duration(timeout)),
|
||||||
|
)
|
||||||
|
|
||||||
|
buf, err := ReadBytes(from)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
log.Debug(proto, "Finished ", fd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug(proto, "Error reading from ", fd, " ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := to.Write(buf); err != nil {
|
||||||
|
log.Debug(proto, "Error Writing to ", td)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
proxy/proxy.go
106
proxy/proxy.go
@ -2,59 +2,59 @@ package proxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
addr string
|
addr string
|
||||||
port int
|
port int
|
||||||
timeout int
|
timeout int
|
||||||
resolver *dns.DnsResolver
|
resolver *dns.DnsResolver
|
||||||
|
windowSize int
|
||||||
|
allowedPattern *regexp.Regexp
|
||||||
|
allowedUrls *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *util.Config) *Proxy {
|
func New(config *util.Config) *Proxy {
|
||||||
return &Proxy{
|
return &Proxy{
|
||||||
addr: *config.Addr,
|
addr: *config.Addr,
|
||||||
port: *config.Port,
|
port: *config.Port,
|
||||||
timeout: *config.Timeout,
|
timeout: *config.Timeout,
|
||||||
resolver: dns.NewResolver(config),
|
windowSize: *config.WindowSize,
|
||||||
|
allowedPattern: config.AllowedPattern,
|
||||||
|
allowedUrls: config.AllowedUrls,
|
||||||
|
resolver: dns.NewResolver(config),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) TcpAddr() *net.TCPAddr {
|
func (pxy *Proxy) Start() {
|
||||||
return net.TcpAddr(p.addr, p.port)
|
l, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.ParseIP(pxy.addr), Port: pxy.port})
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) Port() int {
|
|
||||||
return p.port
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) Start() {
|
|
||||||
l, err := net.ListenTCP("tcp4", p.TcpAddr())
|
|
||||||
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", p.timeout))
|
log.Println(fmt.Sprintf("[PROXY] Connection timeout is set to %dms", pxy.timeout))
|
||||||
|
|
||||||
log.Println("Created a listener on port", p.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
|
||||||
}
|
}
|
||||||
@ -63,22 +63,68 @@ func (p *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()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pkt.IsValidMethod() {
|
if !pkt.IsValidMethod() {
|
||||||
log.Debug("Unsupported method: ", pkt.Method())
|
log.Debug("[PROXY] Unsupported method: ", pkt.Method())
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := pxy.resolver.Lookup(pkt.Domain())
|
||||||
|
if err != nil {
|
||||||
|
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.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid recursively querying self
|
||||||
|
if pkt.Port() == strconv.Itoa(pxy.port) && isLoopedRequest(net.ParseIP(ip)) {
|
||||||
|
log.Error("[PROXY] Looped request has been detected. aborting.")
|
||||||
|
conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if pkt.IsConnectMethod() {
|
if pkt.IsConnectMethod() {
|
||||||
log.Debug("[HTTPS] Start")
|
log.Debug("[PROXY] Start HTTPS")
|
||||||
conn.HandleHttps(pkt, p.timeout, p.resolver)
|
pxy.handleHttps(conn.(*net.TCPConn), pkt, ip)
|
||||||
} else {
|
} else {
|
||||||
log.Debug("[HTTP] Start")
|
log.Debug("[PROXY] Start HTTP")
|
||||||
conn.HandleHttp(pkt, p.timeout, p.resolver)
|
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
|
||||||
|
}
|
||||||
|
@ -22,6 +22,8 @@ type Config struct {
|
|||||||
Timeout *int
|
Timeout *int
|
||||||
AllowedPattern *regexp.Regexp
|
AllowedPattern *regexp.Regexp
|
||||||
AllowedUrls *regexp.Regexp
|
AllowedUrls *regexp.Regexp
|
||||||
|
WindowSize *int
|
||||||
|
Version *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArrayFlags []string
|
type ArrayFlags []string
|
||||||
@ -43,15 +45,6 @@ func GetConfig() *Config {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) PatternExists() bool {
|
|
||||||
return c.AllowedPattern != nil || c.AllowedUrls != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) PatternMatches(bytes []byte) bool {
|
|
||||||
return (c.AllowedPattern != nil && c.AllowedPattern.Match(bytes)) ||
|
|
||||||
(c.AllowedUrls != nil && c.AllowedUrls.Match(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseArgs() {
|
func ParseArgs() {
|
||||||
config = &Config{}
|
config = &Config{}
|
||||||
config.Addr = flag.String("addr", "127.0.0.1", "Listen addr")
|
config.Addr = flag.String("addr", "127.0.0.1", "Listen addr")
|
||||||
@ -62,6 +55,8 @@ func ParseArgs() {
|
|||||||
config.Debug = flag.Bool("debug", false, "Enable debug output")
|
config.Debug = flag.Bool("debug", false, "Enable debug output")
|
||||||
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.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(
|
||||||
@ -88,8 +83,8 @@ func ParseArgs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PrintColoredBanner() {
|
func PrintColoredBanner() {
|
||||||
cyan := putils.LettersFromStringWithStyle("Spoof", pterm.NewStyle(pterm.FgCyan))
|
cyan := putils.LettersFromStringWithStyle("Spoof", pterm.NewStyle(pterm.FgCyan))
|
||||||
purple := putils.LettersFromStringWithStyle("DPI", pterm.NewStyle(pterm.FgLightMagenta))
|
purple := putils.LettersFromStringWithStyle("DPI", pterm.NewStyle(pterm.FgLightMagenta))
|
||||||
pterm.DefaultBigText.WithLetters(cyan, purple).Render()
|
pterm.DefaultBigText.WithLetters(cyan, purple).Render()
|
||||||
|
|
||||||
pterm.DefaultBulletList.WithItems([]pterm.BulletListItem{
|
pterm.DefaultBulletList.WithItems([]pterm.BulletListItem{
|
||||||
|
Loading…
Reference in New Issue
Block a user