SpoofDPI/proxy/proxy.go

161 lines
3.6 KiB
Go
Raw Normal View History

2021-12-29 17:08:30 +00:00
package proxy
import (
2024-08-22 08:49:05 +00:00
"context"
2024-07-22 10:59:11 +00:00
"net"
2021-12-29 17:08:30 +00:00
"os"
2024-07-22 04:49:18 +00:00
"regexp"
2024-07-22 10:59:11 +00:00
"strconv"
2022-01-03 07:24:39 +00:00
2024-07-21 07:57:47 +00:00
"github.com/xvzc/SpoofDPI/dns"
2022-01-08 06:35:32 +00:00
"github.com/xvzc/SpoofDPI/packet"
"github.com/xvzc/SpoofDPI/proxy/handler"
2023-09-08 08:35:41 +00:00
"github.com/xvzc/SpoofDPI/util"
2024-08-22 08:49:05 +00:00
"github.com/xvzc/SpoofDPI/util/log"
2021-12-29 17:08:30 +00:00
)
2024-08-22 08:49:05 +00:00
const scopeProxy = "PROXY"
2022-01-08 15:48:19 +00:00
type Proxy struct {
2024-07-22 04:49:18 +00:00
addr string
port int
timeout int
2024-08-18 06:55:47 +00:00
resolver *dns.Dns
2024-07-22 04:49:18 +00:00
windowSize int
2024-08-18 06:55:47 +00:00
enableDoh bool
allowedPattern []*regexp.Regexp
2022-01-08 15:48:19 +00:00
}
type Handler interface {
Serve(ctx context.Context, lConn *net.TCPConn, pkt *packet.HttpRequest, ip string)
}
2023-09-08 08:35:41 +00:00
func New(config *util.Config) *Proxy {
2022-01-08 15:48:19 +00:00
return &Proxy{
addr: config.Addr,
port: config.Port,
timeout: config.Timeout,
windowSize: config.WindowSize,
enableDoh: config.EnableDoh,
2024-08-15 10:12:29 +00:00
allowedPattern: config.AllowedPatterns,
2024-08-19 02:00:26 +00:00
resolver: dns.NewDns(config),
2022-01-08 15:48:19 +00:00
}
}
2024-08-22 08:49:05 +00:00
func (pxy *Proxy) Start(ctx context.Context) {
ctx = util.GetCtxWithScope(ctx, scopeProxy)
logger := log.GetCtxLogger(ctx)
l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(pxy.addr), Port: pxy.port})
2021-12-29 17:08:30 +00:00
if err != nil {
2024-08-22 08:49:05 +00:00
logger.Fatal().Msgf("error creating listener: %s", err)
2022-01-04 16:47:18 +00:00
os.Exit(1)
2021-12-29 17:08:30 +00:00
}
if pxy.timeout > 0 {
2024-08-23 12:19:40 +00:00
logger.Info().Msgf("connection timeout is set to %d ms", pxy.timeout)
}
2023-09-08 08:35:41 +00:00
2024-08-22 08:49:05 +00:00
logger.Info().Msgf("created a listener on port %d", pxy.port)
if len(pxy.allowedPattern) > 0 {
2024-08-23 12:19:40 +00:00
logger.Info().Msgf("number of white-listed pattern: %d", len(pxy.allowedPattern))
}
2021-12-29 17:08:30 +00:00
2022-01-04 16:47:18 +00:00
for {
2022-01-10 19:27:12 +00:00
conn, err := l.Accept()
2021-12-29 17:08:30 +00:00
if err != nil {
2024-08-22 08:49:05 +00:00
logger.Fatal().Msgf("error accepting connection: %s", err)
2021-12-29 17:08:30 +00:00
continue
}
2022-01-04 16:47:18 +00:00
go func() {
2024-08-22 08:49:05 +00:00
ctx := util.GetCtxWithTraceId(ctx)
logger := log.GetCtxLogger(ctx)
2024-08-22 08:49:05 +00:00
pkt, err := packet.ReadHttpRequest(conn)
2022-11-29 07:54:28 +00:00
if err != nil {
2024-08-22 08:49:05 +00:00
logger.Debug().Msgf("error while parsing request: %s", err)
2024-07-22 10:59:11 +00:00
conn.Close()
2022-11-29 07:54:28 +00:00
return
}
2022-01-07 15:39:58 +00:00
pkt.Tidy()
logger.Debug().Msgf("request from %s\n\n%s", conn.RemoteAddr(), string(pkt.Raw()))
2024-08-14 08:01:14 +00:00
2022-01-11 17:15:45 +00:00
if !pkt.IsValidMethod() {
2024-08-22 08:49:05 +00:00
logger.Debug().Msgf("unsupported method: %s", pkt.Method())
2024-07-22 10:59:11 +00:00
conn.Close()
2024-07-22 04:49:18 +00:00
return
}
matched := pxy.patternMatches([]byte(pkt.Domain()))
useSystemDns := !matched
2024-08-22 08:49:05 +00:00
ip, err := pxy.resolver.ResolveHost(ctx, pkt.Domain(), pxy.enableDoh, useSystemDns)
2024-07-22 04:49:18 +00:00
if err != nil {
2024-08-22 08:49:05 +00:00
logger.Debug().Msgf("error while dns lookup: %s %s", pkt.Domain(), err)
2024-07-22 04:49:18 +00:00
conn.Write([]byte(pkt.Version() + " 502 Bad Gateway\r\n\r\n"))
2024-07-22 10:59:11 +00:00
conn.Close()
return
}
// Avoid recursively querying self
2024-08-22 08:49:05 +00:00
if pkt.Port() == strconv.Itoa(pxy.port) && isLoopedRequest(ctx, net.ParseIP(ip)) {
logger.Error().Msg("looped request has been detected. aborting.")
2024-07-22 10:59:11 +00:00
conn.Close()
2022-01-07 14:04:09 +00:00
return
}
var h Handler
2022-01-11 17:15:45 +00:00
if pkt.IsConnectMethod() {
h = handler.NewHttpsHandler(pxy.timeout, pxy.windowSize, pxy.allowedPattern, matched)
2022-01-04 16:47:18 +00:00
} else {
h = handler.NewHttpHandler(pxy.timeout)
2022-01-04 16:47:18 +00:00
}
h.Serve(ctx, conn.(*net.TCPConn), pkt, ip)
2022-01-04 16:47:18 +00:00
}()
}
2021-12-29 17:08:30 +00:00
}
2024-07-22 10:59:11 +00:00
func (pxy *Proxy) patternMatches(bytes []byte) bool {
if pxy.allowedPattern == nil {
return true
}
for _, pattern := range pxy.allowedPattern {
if pattern.Match(bytes) {
return true
}
}
return false
}
2024-08-22 08:49:05 +00:00
func isLoopedRequest(ctx context.Context, ip net.IP) bool {
2024-07-22 10:59:11 +00:00
if ip.IsLoopback() {
return true
}
2024-08-22 08:49:05 +00:00
logger := log.GetCtxLogger(ctx)
2024-07-22 10:59:11 +00:00
// Get list of available addresses
// See `ip -4 addr show`
addr, err := net.InterfaceAddrs() // needs AF_NETLINK on linux
if err != nil {
2024-08-22 08:49:05 +00:00
logger.Error().Msgf("error while getting addresses of our network interfaces: %s", err)
2024-07-22 10:59:11 +00:00
return false
}
for _, addr := range addr {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(ip) {
2024-07-22 10:59:11 +00:00
return true
}
}
}
return false
}