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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -96,29 +97,26 @@ func (p *HttpRequest) IsConnectMethod() bool {
|
|||||||
func (p *HttpRequest) Tidy() {
|
func (p *HttpRequest) Tidy() {
|
||||||
s := string(p.raw)
|
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++ {
|
var buf bytes.Buffer
|
||||||
if strings.HasPrefix(lines[i], "Proxy-Connection") {
|
buf.Grow(len(p.raw))
|
||||||
lines[i] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := ""
|
crLF := []byte{0xD, 0xA}
|
||||||
|
for _, m := range meta {
|
||||||
for i := 0; i < len(lines); i++ {
|
if strings.HasPrefix(m, "Proxy-Connection") {
|
||||||
if lines[i] == "" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
buf.WriteString(m)
|
||||||
result += lines[i] + "\r\n"
|
buf.Write(crLF)
|
||||||
}
|
}
|
||||||
|
buf.Write(crLF)
|
||||||
|
buf.WriteString(parts[1])
|
||||||
|
|
||||||
result += "\r\n"
|
p.raw = buf.Bytes()
|
||||||
|
|
||||||
p.raw = []byte(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(rdr io.Reader) (*HttpRequest, error) {
|
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/dns"
|
||||||
"github.com/xvzc/SpoofDPI/packet"
|
"github.com/xvzc/SpoofDPI/packet"
|
||||||
|
"github.com/xvzc/SpoofDPI/proxy/handler"
|
||||||
"github.com/xvzc/SpoofDPI/util"
|
"github.com/xvzc/SpoofDPI/util"
|
||||||
"github.com/xvzc/SpoofDPI/util/log"
|
"github.com/xvzc/SpoofDPI/util/log"
|
||||||
)
|
)
|
||||||
@ -25,6 +26,10 @@ type Proxy struct {
|
|||||||
allowedPattern []*regexp.Regexp
|
allowedPattern []*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Serve(ctx context.Context, lConn *net.TCPConn, pkt *packet.HttpRequest, ip string)
|
||||||
|
}
|
||||||
|
|
||||||
func New(config *util.Config) *Proxy {
|
func New(config *util.Config) *Proxy {
|
||||||
return &Proxy{
|
return &Proxy{
|
||||||
addr: config.Addr,
|
addr: config.Addr,
|
||||||
@ -74,7 +79,9 @@ func (pxy *Proxy) Start(ctx context.Context) {
|
|||||||
return
|
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() {
|
if !pkt.IsValidMethod() {
|
||||||
logger.Debug().Msgf("unsupported method: %s", pkt.Method())
|
logger.Debug().Msgf("unsupported method: %s", pkt.Method())
|
||||||
@ -100,11 +107,14 @@ func (pxy *Proxy) Start(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var h Handler
|
||||||
if pkt.IsConnectMethod() {
|
if pkt.IsConnectMethod() {
|
||||||
pxy.handleHttps(ctx, conn.(*net.TCPConn), matched, pkt, ip)
|
h = handler.NewHttpsHandler(pxy.timeout, pxy.windowSize, pxy.allowedPattern, matched)
|
||||||
} else {
|
} 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