mirror of
https://github.com/xvzc/SpoofDPI.git
synced 2024-12-22 06:15:51 +00:00
use system-dns when patterns are not matched
This commit is contained in:
parent
24c48c6c9a
commit
641ded49d8
@ -41,7 +41,7 @@ func main() {
|
||||
|
||||
if *config.SystemProxy {
|
||||
if err := util.SetOsProxy(*config.Port); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatal("Error while changing proxy settings")
|
||||
}
|
||||
}
|
||||
|
||||
|
42
dns/dns.go
42
dns/dns.go
@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -28,18 +29,30 @@ func NewResolver(config *util.Config) *DnsResolver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DnsResolver) Lookup(domain string) (string, error) {
|
||||
func (d *DnsResolver) Lookup(domain string, useSystemDns bool) (string, error) {
|
||||
ipRegex := "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
|
||||
|
||||
if r, _ := regexp.MatchString(ipRegex, domain); r {
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
if useSystemDns {
|
||||
log.Debug("[DNS] ", domain, " resolving with system dns")
|
||||
return systemLookup(domain)
|
||||
}
|
||||
|
||||
if d.enableDoh {
|
||||
log.Debug("[DNS] ", domain, " resolving with dns over https")
|
||||
return dohLookup(domain)
|
||||
}
|
||||
|
||||
dnsServer := d.host + ":" + d.port
|
||||
log.Debug("[DNS] ", domain, " resolving with custom dns")
|
||||
return customLookup(d.host, d.port, domain)
|
||||
}
|
||||
|
||||
func customLookup(host string, port string, domain string) (string, error) {
|
||||
|
||||
dnsServer := host + ":" + port
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)
|
||||
@ -48,17 +61,31 @@ func (d *DnsResolver) Lookup(domain string) (string, error) {
|
||||
|
||||
response, _, err := c.Exchange(msg, dnsServer)
|
||||
if err != nil {
|
||||
return "", errors.New("couldn not resolve the domain")
|
||||
return "", errors.New("couldn not resolve the domain(custom)")
|
||||
}
|
||||
|
||||
for _, answer := range response.Answer {
|
||||
if record, ok := answer.(*dns.A); ok {
|
||||
log.Debug("[DNS] resolved ", domain, ": ", record.A.String())
|
||||
return record.A.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("no record found")
|
||||
return "", errors.New("no record found(custom)")
|
||||
|
||||
}
|
||||
|
||||
func systemLookup(domain string) (string, error) {
|
||||
systemResolver := net.Resolver{PreferGo: true}
|
||||
ips, err := systemResolver.LookupIPAddr(context.Background(), domain)
|
||||
if err != nil {
|
||||
return "", errors.New("couldn not resolve the domain(system)")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
return ip.String(), nil
|
||||
}
|
||||
|
||||
return "", errors.New("no record found(system)")
|
||||
}
|
||||
|
||||
func dohLookup(domain string) (string, error) {
|
||||
@ -68,7 +95,7 @@ func dohLookup(domain string) (string, error) {
|
||||
|
||||
rsp, err := c.Query(ctx, dohDns.Domain(domain), dohDns.TypeA)
|
||||
if err != nil {
|
||||
return "", errors.New("could not resolve the domain")
|
||||
return "", errors.New("could not resolve the domain(doh)")
|
||||
}
|
||||
// doh dns answer
|
||||
answer := rsp.Answer
|
||||
@ -79,12 +106,11 @@ func dohLookup(domain string) (string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("[DOH] resolved ", domain, ": ", a.Data)
|
||||
return a.Data, nil
|
||||
}
|
||||
|
||||
// close the client
|
||||
c.Close()
|
||||
|
||||
return "", errors.New("no record found")
|
||||
return "", errors.New("no record found(doh)")
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION="v0.10.5"
|
||||
VERSION="v0.10.6"
|
||||
|
||||
for osarch in 'darwin/amd64' 'darwin/arm64' 'linux/amd64' 'linux/arm' 'linux/arm64' 'linux/mips' 'linux/mipsle'; do
|
||||
GOOS=${osarch%/*} GOARCH=${osarch#*/} go build -ldflags="-w -s -X main.VERSION=${VERSION}" github.com/xvzc/SpoofDPI/cmd/spoof-dpi &&
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/xvzc/SpoofDPI/packet"
|
||||
)
|
||||
|
||||
func (pxy *Proxy) handleHttps(lConn *net.TCPConn, initPkt *packet.HttpPacket, ip string) {
|
||||
func (pxy *Proxy) handleHttps(lConn *net.TCPConn, exploit bool, initPkt *packet.HttpPacket, ip string) {
|
||||
// Create a connection to the requested server
|
||||
var port int = 443
|
||||
var err error
|
||||
@ -62,17 +62,17 @@ func (pxy *Proxy) handleHttps(lConn *net.TCPConn, initPkt *packet.HttpPacket, ip
|
||||
|
||||
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)
|
||||
if exploit {
|
||||
log.Debug("[HTTPS] Writing chunked client hello to ", initPkt.Domain())
|
||||
chunks := 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
|
||||
}
|
||||
} else {
|
||||
log.Debug("[HTTPS] Writing chunked client hello to ", initPkt.Domain())
|
||||
chunks := pxy.splitInChunks(chPkt.Raw())
|
||||
if _, err := WriteChunks(rConn, chunks); err != nil {
|
||||
log.Debug("[HTTPS] Error writing chunked client hello to ", initPkt.Domain(), err)
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -80,17 +80,11 @@ func (pxy *Proxy) handleHttps(lConn *net.TCPConn, initPkt *packet.HttpPacket, ip
|
||||
Serve(lConn, rConn, "[HTTPS]", lConn.RemoteAddr().String(), initPkt.Domain(), pxy.timeout)
|
||||
}
|
||||
|
||||
func (pxy *Proxy) splitInChunks(bytes []byte) [][]byte {
|
||||
// If the packet matches the pattern or the URLs, we don't split it
|
||||
if pxy.patternExists() && !pxy.patternMatches(bytes) {
|
||||
return [][]byte{bytes}
|
||||
}
|
||||
|
||||
func splitInChunks(bytes []byte, size int) [][]byte {
|
||||
var chunks [][]byte
|
||||
var raw []byte = bytes
|
||||
var size = pxy.windowSize
|
||||
|
||||
log.Debug("[HTTPS] window-size: ", size)
|
||||
log.Debug("[HTTPS] window-size: ", size)
|
||||
|
||||
if size > 0 {
|
||||
for {
|
||||
@ -111,22 +105,13 @@ func (pxy *Proxy) splitInChunks(bytes []byte) [][]byte {
|
||||
return chunks
|
||||
}
|
||||
|
||||
// When the given window-size <= 0
|
||||
// When the given window-size <= 0
|
||||
|
||||
if len(raw) < 1 {
|
||||
return [][]byte{raw}
|
||||
}
|
||||
|
||||
log.Debug("[HTTPS] Using legacy fragmentation.")
|
||||
log.Debug("[HTTPS] Using legacy fragmentation.")
|
||||
|
||||
return [][]byte{raw[:1], raw[1:]}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ type Proxy struct {
|
||||
timeout int
|
||||
resolver *dns.DnsResolver
|
||||
windowSize int
|
||||
allowedPattern *regexp.Regexp
|
||||
allowedUrls *regexp.Regexp
|
||||
allowedPattern []*regexp.Regexp
|
||||
}
|
||||
|
||||
func New(config *util.Config) *Proxy {
|
||||
@ -30,7 +29,6 @@ func New(config *util.Config) *Proxy {
|
||||
timeout: *config.Timeout,
|
||||
windowSize: *config.WindowSize,
|
||||
allowedPattern: config.AllowedPattern,
|
||||
allowedUrls: config.AllowedUrls,
|
||||
resolver: dns.NewResolver(config),
|
||||
}
|
||||
}
|
||||
@ -42,11 +40,14 @@ func (pxy *Proxy) Start() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if pxy.timeout > 0 {
|
||||
log.Println(fmt.Sprintf("[PROXY] Connection timeout is set to %dms", pxy.timeout))
|
||||
}
|
||||
if pxy.timeout > 0 {
|
||||
log.Println(fmt.Sprintf("[PROXY] Connection timeout is set to %dms", pxy.timeout))
|
||||
}
|
||||
|
||||
log.Println("[PROXY] Created a listener on port", pxy.port)
|
||||
if len(pxy.allowedPattern) > 0 {
|
||||
log.Println("[PROXY] Number of white-listed pattern:", len(pxy.allowedPattern))
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
@ -76,9 +77,12 @@ func (pxy *Proxy) Start() {
|
||||
return
|
||||
}
|
||||
|
||||
ip, err := pxy.resolver.Lookup(pkt.Domain())
|
||||
matched := pxy.patternMatches([]byte(pkt.Domain()))
|
||||
useSystemDns := !matched
|
||||
|
||||
ip, err := pxy.resolver.Lookup(pkt.Domain(), useSystemDns)
|
||||
if err != nil {
|
||||
log.Debug("[PROXY] Error while dns lookup: ", pkt.Domain(), " ", err)
|
||||
log.Debug("[PROXY] Error while dns lookup: ", pkt.Domain(), " ", err)
|
||||
conn.Write([]byte(pkt.Version() + " 502 Bad Gateway\r\n\r\n"))
|
||||
conn.Close()
|
||||
return
|
||||
@ -93,7 +97,7 @@ func (pxy *Proxy) Start() {
|
||||
|
||||
if pkt.IsConnectMethod() {
|
||||
log.Debug("[PROXY] Start HTTPS")
|
||||
pxy.handleHttps(conn.(*net.TCPConn), pkt, ip)
|
||||
pxy.handleHttps(conn.(*net.TCPConn), matched, pkt, ip)
|
||||
} else {
|
||||
log.Debug("[PROXY] Start HTTP")
|
||||
pxy.handleHttp(conn.(*net.TCPConn), pkt, ip)
|
||||
@ -102,6 +106,20 @@ func (pxy *Proxy) Start() {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func isLoopedRequest(ip net.IP) bool {
|
||||
// we don't handle IPv6 at all it seems
|
||||
if ip.To4() == nil {
|
||||
|
@ -4,11 +4,9 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/pterm/pterm/putils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@ -21,26 +19,23 @@ type Config struct {
|
||||
NoBanner *bool
|
||||
SystemProxy *bool
|
||||
Timeout *int
|
||||
AllowedPattern *regexp.Regexp
|
||||
AllowedUrls *regexp.Regexp
|
||||
AllowedPattern []*regexp.Regexp
|
||||
WindowSize *int
|
||||
Version *bool
|
||||
}
|
||||
|
||||
type ArrayFlags []string
|
||||
type StringArray []string
|
||||
|
||||
func (i *ArrayFlags) String() string {
|
||||
return "my string representation"
|
||||
func (arr *StringArray) String() string {
|
||||
return fmt.Sprintf("%s", *arr)
|
||||
}
|
||||
|
||||
func (i *ArrayFlags) Set(value string) error {
|
||||
*i = append(*i, value)
|
||||
func (arr *StringArray) Set(value string) error {
|
||||
*arr = append(*arr, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
var config *Config
|
||||
var allowedHosts ArrayFlags
|
||||
var allowedPattern *string
|
||||
|
||||
func GetConfig() *Config {
|
||||
return config
|
||||
@ -52,38 +47,28 @@ func ParseArgs() {
|
||||
config.Port = flag.Int("port", 8080, "port")
|
||||
config.DnsAddr = flag.String("dns-addr", "8.8.8.8", "dns address")
|
||||
config.DnsPort = flag.Int("dns-port", 53, "port number for dns")
|
||||
config.EnableDoh = flag.Bool("enable-doh", false, "enable 'dns over https'")
|
||||
config.EnableDoh = flag.Bool("enable-doh", false, "enable 'dns-over-https'")
|
||||
config.Debug = flag.Bool("debug", false, "enable debug output")
|
||||
config.NoBanner = flag.Bool("no-banner", false, "disable banner")
|
||||
config.SystemProxy = flag.Bool("system-proxy", true, "enable system-wide proxy")
|
||||
config.Timeout = flag.Int("timeout", 0, "timeout in milliseconds. no timeout when not given")
|
||||
config.Timeout = flag.Int("timeout", 0, "timeout in milliseconds; no timeout when not given")
|
||||
config.WindowSize = flag.Int("window-size", 0, `chunk size, in number of bytes, for fragmented client hello,
|
||||
try lower values if the default value doesn't bypass the DPI;
|
||||
when not given, the client hello packet will be sent in two parts:
|
||||
fragmentation for the first data packet and the rest
|
||||
`)
|
||||
flag.Var(&allowedHosts, "url", "Bypass DPI only on this url, can be passed multiple times")
|
||||
allowedPattern = flag.String(
|
||||
"pattern",
|
||||
"",
|
||||
"bypass DPI only on packets matching this regex pattern",
|
||||
)
|
||||
config.Version = flag.Bool("v", false, "print spoof-dpi's version. this may contain some other relevant information")
|
||||
config.Version = flag.Bool("v", false, "print spoof-dpi's version; this may contain some other relevant information")
|
||||
|
||||
var allowedPattern StringArray
|
||||
flag.Var(
|
||||
&allowedPattern,
|
||||
"pattern",
|
||||
"bypass DPI only on packets matching this regex pattern; can be given multiple times",
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
if len(allowedHosts) > 0 {
|
||||
var escapedUrls []string
|
||||
for _, host := range allowedHosts {
|
||||
escapedUrls = append(escapedUrls, regexp.QuoteMeta(host))
|
||||
}
|
||||
|
||||
allowedHostsRegex := strings.Join(escapedUrls, "|")
|
||||
config.AllowedUrls = regexp.MustCompile(allowedHostsRegex)
|
||||
}
|
||||
|
||||
if *allowedPattern != "" {
|
||||
config.AllowedPattern = regexp.MustCompile(*allowedPattern)
|
||||
for _, pattern := range allowedPattern {
|
||||
config.AllowedPattern = append(config.AllowedPattern, regexp.MustCompile(pattern))
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,14 +83,6 @@ func PrintColoredBanner() {
|
||||
{Level: 0, Text: "DNS : " + fmt.Sprint(*config.DnsAddr)},
|
||||
{Level: 0, Text: "DEBUG : " + fmt.Sprint(*config.Debug)},
|
||||
}).Render()
|
||||
|
||||
if allowedHosts != nil && len(allowedHosts) > 0 {
|
||||
log.Info("White listed urls: ", allowedHosts)
|
||||
}
|
||||
|
||||
if *allowedPattern != "" {
|
||||
log.Info("Regex Pattern: ", *allowedPattern)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintSimpleInfo() {
|
||||
|
Loading…
Reference in New Issue
Block a user