mirror of
https://github.com/xvzc/SpoofDPI.git
synced 2024-12-22 06:15:51 +00:00
fix: packet processing for http request (#230)
* fix: http proxy * chore: make a function for setting connection timeout * chore: rename parameters for consistency
This commit is contained in:
parent
19ec6980ba
commit
79d255719e
@ -2,6 +2,7 @@ package packet
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -96,29 +97,26 @@ func (p *HttpRequest) IsConnectMethod() bool {
|
||||
func (p *HttpRequest) Tidy() {
|
||||
s := string(p.raw)
|
||||
|
||||
lines := strings.Split(s, "\r\n")
|
||||
parts := strings.Split(s, "\r\n\r\n")
|
||||
meta := strings.Split(parts[0], "\r\n")
|
||||
|
||||
lines[0] = p.method + " " + p.path + " " + p.version
|
||||
meta[0] = p.method + " " + p.path + " " + p.version
|
||||
|
||||
for i := 0; i < len(lines); i++ {
|
||||
if strings.HasPrefix(lines[i], "Proxy-Connection") {
|
||||
lines[i] = ""
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(len(p.raw))
|
||||
|
||||
result := ""
|
||||
|
||||
for i := 0; i < len(lines); i++ {
|
||||
if lines[i] == "" {
|
||||
crLF := []byte{0xD, 0xA}
|
||||
for _, m := range meta {
|
||||
if strings.HasPrefix(m, "Proxy-Connection") {
|
||||
continue
|
||||
}
|
||||
|
||||
result += lines[i] + "\r\n"
|
||||
buf.WriteString(m)
|
||||
buf.Write(crLF)
|
||||
}
|
||||
buf.Write(crLF)
|
||||
buf.WriteString(parts[1])
|
||||
|
||||
result += "\r\n"
|
||||
|
||||
p.raw = []byte(result)
|
||||
p.raw = buf.Bytes()
|
||||
}
|
||||
|
||||
func parse(rdr io.Reader) (*HttpRequest, error) {
|
||||
|
14
proxy/handler/conn.go
Normal file
14
proxy/handler/conn.go
Normal file
@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func setConnectionTimeout(conn *net.TCPConn, timeout int) error {
|
||||
if timeout <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(timeout)))
|
||||
}
|
124
proxy/handler/http.go
Normal file
124
proxy/handler/http.go
Normal file
@ -0,0 +1,124 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/xvzc/SpoofDPI/packet"
|
||||
"github.com/xvzc/SpoofDPI/util"
|
||||
"github.com/xvzc/SpoofDPI/util/log"
|
||||
)
|
||||
|
||||
type HttpHandler struct {
|
||||
bufferSize int
|
||||
protocol string
|
||||
port int
|
||||
timeout int
|
||||
}
|
||||
|
||||
func NewHttpHandler(timeout int) *HttpHandler {
|
||||
return &HttpHandler{
|
||||
bufferSize: 1024,
|
||||
protocol: "HTTP",
|
||||
port: 80,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpHandler) Serve(ctx context.Context, lConn *net.TCPConn, pkt *packet.HttpRequest, ip string) {
|
||||
ctx = util.GetCtxWithScope(ctx, h.protocol)
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
// 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 {
|
||||
logger.Debug().Msgf("error while parsing port for %s aborting..", pkt.Domain())
|
||||
}
|
||||
}
|
||||
|
||||
rConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: port})
|
||||
if err != nil {
|
||||
lConn.Close()
|
||||
logger.Debug().Msgf("%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("new connection to the server %s -> %s", rConn.LocalAddr(), pkt.Domain())
|
||||
|
||||
go h.deliverResponse(ctx, rConn, lConn, pkt.Domain(), lConn.RemoteAddr().String())
|
||||
go h.deliverRequest(ctx, lConn, rConn, lConn.RemoteAddr().String(), pkt.Domain())
|
||||
|
||||
_, err = rConn.Write(pkt.Raw())
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error sending request to %s: %s", pkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpHandler) deliverRequest(ctx context.Context, from *net.TCPConn, to *net.TCPConn, fd string, td string) {
|
||||
ctx = util.GetCtxWithScope(ctx, h.protocol)
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
defer func() {
|
||||
from.Close()
|
||||
to.Close()
|
||||
|
||||
logger.Debug().Msgf("closing proxy connection: %s -> %s", fd, td)
|
||||
}()
|
||||
|
||||
for {
|
||||
err := setConnectionTimeout(from, h.timeout)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error while setting connection deadline for %s: %s", fd, err)
|
||||
}
|
||||
|
||||
pkt, err := packet.ReadHttpRequest(from)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error reading from %s: %s", fd, err)
|
||||
return
|
||||
}
|
||||
|
||||
pkt.Tidy()
|
||||
|
||||
if _, err := to.Write(pkt.Raw()); err != nil {
|
||||
logger.Debug().Msgf("error Writing to %s", td)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpHandler) deliverResponse(ctx context.Context, from *net.TCPConn, to *net.TCPConn, fd string, td string) {
|
||||
ctx = util.GetCtxWithScope(ctx, h.protocol)
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
defer func() {
|
||||
from.Close()
|
||||
to.Close()
|
||||
|
||||
logger.Debug().Msgf("closing proxy connection: %s -> %s", fd, td)
|
||||
}()
|
||||
|
||||
buf := make([]byte, h.bufferSize)
|
||||
for {
|
||||
err := setConnectionTimeout(from, h.timeout)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error while setting connection deadline for %s: %s", fd, err)
|
||||
}
|
||||
|
||||
bytesRead, err := ReadBytes(ctx, from, buf)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error reading from %s: %s", fd, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := to.Write(bytesRead); err != nil {
|
||||
logger.Debug().Msgf("error Writing to %s", td)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
178
proxy/handler/https.go
Normal file
178
proxy/handler/https.go
Normal file
@ -0,0 +1,178 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/xvzc/SpoofDPI/packet"
|
||||
"github.com/xvzc/SpoofDPI/util"
|
||||
"github.com/xvzc/SpoofDPI/util/log"
|
||||
)
|
||||
|
||||
type HttpsHandler struct {
|
||||
bufferSize int
|
||||
protocol string
|
||||
port int
|
||||
timeout int
|
||||
windowsize int
|
||||
exploit bool
|
||||
allowedPatterns []*regexp.Regexp
|
||||
}
|
||||
|
||||
func NewHttpsHandler(timeout int, windowSize int, allowedPatterns []*regexp.Regexp, exploit bool) *HttpsHandler {
|
||||
return &HttpsHandler{
|
||||
bufferSize: 1024,
|
||||
protocol: "HTTPS",
|
||||
port: 443,
|
||||
timeout: timeout,
|
||||
windowsize: windowSize,
|
||||
allowedPatterns: allowedPatterns,
|
||||
exploit: exploit,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpsHandler) Serve(ctx context.Context, lConn *net.TCPConn, initPkt *packet.HttpRequest, ip string) {
|
||||
ctx = util.GetCtxWithScope(ctx, h.protocol)
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
// Create a connection to the requested server
|
||||
var err error
|
||||
if initPkt.Port() != "" {
|
||||
h.port, err = strconv.Atoi(initPkt.Port())
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error parsing port for %s aborting..", initPkt.Domain())
|
||||
}
|
||||
}
|
||||
|
||||
rConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.ParseIP(ip), Port: h.port})
|
||||
if err != nil {
|
||||
lConn.Close()
|
||||
logger.Debug().Msgf("%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("new connection to the server %s -> %s", rConn.LocalAddr(), initPkt.Domain())
|
||||
|
||||
_, err = lConn.Write([]byte(initPkt.Version() + " 200 Connection Established\r\n\r\n"))
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error sending 200 connection established to the client: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("sent connection estabalished to %s", lConn.RemoteAddr())
|
||||
|
||||
// Read client hello
|
||||
m, err := packet.ReadTLSMessage(lConn)
|
||||
if err != nil || !m.IsClientHello() {
|
||||
logger.Debug().Msgf("error reading client hello from %s: %s", lConn.RemoteAddr().String(), err)
|
||||
return
|
||||
}
|
||||
clientHello := m.Raw
|
||||
|
||||
logger.Debug().Msgf("client sent hello %d bytes", len(clientHello))
|
||||
|
||||
// Generate a go routine that reads from the server
|
||||
go h.communicate(ctx, rConn, lConn, initPkt.Domain(), lConn.RemoteAddr().String())
|
||||
go h.communicate(ctx, lConn, rConn, lConn.RemoteAddr().String(), initPkt.Domain())
|
||||
|
||||
if h.exploit {
|
||||
logger.Debug().Msgf("writing chunked client hello to %s", initPkt.Domain())
|
||||
chunks := splitInChunks(ctx, clientHello, h.windowsize)
|
||||
if _, err := writeChunks(rConn, chunks); err != nil {
|
||||
logger.Debug().Msgf("error writing chunked client hello to %s: %s", initPkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.Debug().Msgf("writing plain client hello to %s", initPkt.Domain())
|
||||
if _, err := rConn.Write(clientHello); err != nil {
|
||||
logger.Debug().Msgf("error writing plain client hello to %s: %s", initPkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpsHandler) communicate(ctx context.Context, from *net.TCPConn, to *net.TCPConn, fd string, td string) {
|
||||
ctx = util.GetCtxWithScope(ctx, h.protocol)
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
defer func() {
|
||||
from.Close()
|
||||
to.Close()
|
||||
|
||||
logger.Debug().Msgf("closing proxy connection: %s -> %s", fd, td)
|
||||
}()
|
||||
|
||||
buf := make([]byte, h.bufferSize)
|
||||
for {
|
||||
err := setConnectionTimeout(from, h.timeout)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error while setting connection deadline for %s: %s", fd, err)
|
||||
}
|
||||
|
||||
bytesRead, err := ReadBytes(ctx, from, buf)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("error reading from %s: %s", fd, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := to.Write(bytesRead); err != nil {
|
||||
logger.Debug().Msgf("error Writing to %s", td)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitInChunks(ctx context.Context, bytes []byte, size int) [][]byte {
|
||||
logger := log.GetCtxLogger(ctx)
|
||||
|
||||
var chunks [][]byte
|
||||
var raw []byte = bytes
|
||||
|
||||
logger.Debug().Msgf("window-size: %d", size)
|
||||
|
||||
if size > 0 {
|
||||
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
|
||||
}
|
||||
|
||||
// When the given window-size <= 0
|
||||
|
||||
if len(raw) < 1 {
|
||||
return [][]byte{raw}
|
||||
}
|
||||
|
||||
logger.Debug().Msg("using legacy fragmentation")
|
||||
|
||||
return [][]byte{raw[:1], raw[1:]}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
26
proxy/handler/io.go
Normal file
26
proxy/handler/io.go
Normal file
@ -0,0 +1,26 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func ReadBytes(ctx context.Context, conn *net.TCPConn, dest []byte) ([]byte, error) {
|
||||
n, err := readBytesInternal(ctx, conn, dest)
|
||||
return dest[:n], err
|
||||
}
|
||||
|
||||
func readBytesInternal(ctx context.Context, conn *net.TCPConn, dest []byte) (int, error) {
|
||||
totalRead, err := conn.Read(dest)
|
||||
if err != nil {
|
||||
var opError *net.OpError
|
||||
switch {
|
||||
case errors.As(err, &opError) && opError.Timeout():
|
||||
return totalRead, errors.New("timed out")
|
||||
default:
|
||||
return totalRead, err
|
||||
}
|
||||
}
|
||||
return totalRead, nil
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/xvzc/SpoofDPI/dns"
|
||||
"github.com/xvzc/SpoofDPI/packet"
|
||||
"github.com/xvzc/SpoofDPI/proxy/handler"
|
||||
"github.com/xvzc/SpoofDPI/util"
|
||||
"github.com/xvzc/SpoofDPI/util/log"
|
||||
)
|
||||
@ -25,6 +26,10 @@ type Proxy struct {
|
||||
allowedPattern []*regexp.Regexp
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
Serve(ctx context.Context, lConn *net.TCPConn, pkt *packet.HttpRequest, ip string)
|
||||
}
|
||||
|
||||
func New(config *util.Config) *Proxy {
|
||||
return &Proxy{
|
||||
addr: config.Addr,
|
||||
@ -74,7 +79,9 @@ func (pxy *Proxy) Start(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("request from %s\n\n%s", conn.RemoteAddr(), pkt.Raw())
|
||||
pkt.Tidy()
|
||||
|
||||
logger.Debug().Msgf("request from %s\n\n%s", conn.RemoteAddr(), string(pkt.Raw()))
|
||||
|
||||
if !pkt.IsValidMethod() {
|
||||
logger.Debug().Msgf("unsupported method: %s", pkt.Method())
|
||||
@ -100,11 +107,14 @@ func (pxy *Proxy) Start(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var h Handler
|
||||
if pkt.IsConnectMethod() {
|
||||
pxy.handleHttps(ctx, conn.(*net.TCPConn), matched, pkt, ip)
|
||||
h = handler.NewHttpsHandler(pxy.timeout, pxy.windowSize, pxy.allowedPattern, matched)
|
||||
} else {
|
||||
pxy.handleHttp(ctx, conn.(*net.TCPConn), pkt, ip)
|
||||
h = handler.NewHttpHandler(pxy.timeout)
|
||||
}
|
||||
|
||||
h.Serve(ctx, conn.(*net.TCPConn), pkt, ip)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user