mirror of
https://github.com/xvzc/SpoofDPI.git
synced 2025-01-03 04:50:11 +00:00
Merge branch 'refs/heads/main' into integration-test/main
This commit is contained in:
commit
52887b5435
3
.github/release-drafter.yml
vendored
3
.github/release-drafter.yml
vendored
@ -2,6 +2,3 @@ name-template: '$RESOLVED_VERSION'
|
||||
template: |
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
|
||||
$CONTRIBUTORS
|
||||
|
1
.github/workflows/release.yaml
vendored
1
.github/workflows/release.yaml
vendored
@ -41,6 +41,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG_NAME: ${{ needs.draft_release.outputs.tag_name }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,5 +2,7 @@ spoof-dpi
|
||||
spoof-dpi-*
|
||||
spoof-dpi.*
|
||||
!*/spoof-dpi/
|
||||
.DS_Store
|
||||
out/**
|
||||
|
||||
.DS_Store
|
||||
.idea/
|
||||
|
8
build
8
build
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
docker run --rm \
|
||||
-it \
|
||||
--workdir /app/out \
|
||||
-v ./:/app \
|
||||
golang:1.21-alpine \
|
||||
sh /app/make-releases.sh
|
27
dns/addrselect/LICENSE
Normal file
27
dns/addrselect/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
377
dns/addrselect/addrselect.go
Normal file
377
dns/addrselect/addrselect.go
Normal file
@ -0,0 +1,377 @@
|
||||
package addrselect
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Minimal RFC 6724 address selection.
|
||||
|
||||
func SortByRFC6724(addrs []net.IPAddr) {
|
||||
if len(addrs) < 2 {
|
||||
return
|
||||
}
|
||||
sortByRFC6724withSrcs(addrs, srcAddrs(addrs))
|
||||
}
|
||||
|
||||
func sortByRFC6724withSrcs(addrs []net.IPAddr, srcs []netip.Addr) {
|
||||
if len(addrs) != len(srcs) {
|
||||
panic("internal error")
|
||||
}
|
||||
addrAttr := make([]ipAttr, len(addrs))
|
||||
srcAttr := make([]ipAttr, len(srcs))
|
||||
for i, v := range addrs {
|
||||
addrAttrIP, _ := netip.AddrFromSlice(v.IP)
|
||||
addrAttr[i] = ipAttrOf(addrAttrIP)
|
||||
srcAttr[i] = ipAttrOf(srcs[i])
|
||||
}
|
||||
sort.Stable(&byRFC6724{
|
||||
addrs: addrs,
|
||||
addrAttr: addrAttr,
|
||||
srcs: srcs,
|
||||
srcAttr: srcAttr,
|
||||
})
|
||||
}
|
||||
|
||||
// srcAddrs tries to UDP-connect to each address to see if it has a
|
||||
// route. (This doesn't send any packets). The destination port
|
||||
// number is irrelevant.
|
||||
func srcAddrs(addrs []net.IPAddr) []netip.Addr {
|
||||
srcs := make([]netip.Addr, len(addrs))
|
||||
dst := net.UDPAddr{Port: 9}
|
||||
for i := range addrs {
|
||||
dst.IP = addrs[i].IP
|
||||
dst.Zone = addrs[i].Zone
|
||||
c, err := net.DialUDP("udp", nil, &dst)
|
||||
if err == nil {
|
||||
if src, ok := c.LocalAddr().(*net.UDPAddr); ok {
|
||||
srcs[i], _ = netip.AddrFromSlice(src.IP)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
return srcs
|
||||
}
|
||||
|
||||
type ipAttr struct {
|
||||
Scope scope
|
||||
Precedence uint8
|
||||
Label uint8
|
||||
}
|
||||
|
||||
func ipAttrOf(ip netip.Addr) ipAttr {
|
||||
if !ip.IsValid() {
|
||||
return ipAttr{}
|
||||
}
|
||||
match := rfc6724policyTable.Classify(ip)
|
||||
return ipAttr{
|
||||
Scope: classifyScope(ip),
|
||||
Precedence: match.Precedence,
|
||||
Label: match.Label,
|
||||
}
|
||||
}
|
||||
|
||||
type byRFC6724 struct {
|
||||
addrs []net.IPAddr // addrs to sort
|
||||
addrAttr []ipAttr
|
||||
srcs []netip.Addr // or not valid addr if unreachable
|
||||
srcAttr []ipAttr
|
||||
}
|
||||
|
||||
func (s *byRFC6724) Len() int { return len(s.addrs) }
|
||||
|
||||
func (s *byRFC6724) Swap(i, j int) {
|
||||
s.addrs[i], s.addrs[j] = s.addrs[j], s.addrs[i]
|
||||
s.srcs[i], s.srcs[j] = s.srcs[j], s.srcs[i]
|
||||
s.addrAttr[i], s.addrAttr[j] = s.addrAttr[j], s.addrAttr[i]
|
||||
s.srcAttr[i], s.srcAttr[j] = s.srcAttr[j], s.srcAttr[i]
|
||||
}
|
||||
|
||||
// Less reports whether i is a better destination address for this
|
||||
// host than j.
|
||||
//
|
||||
// The algorithm and variable names comes from RFC 6724 section 6.
|
||||
func (s *byRFC6724) Less(i, j int) bool {
|
||||
DA := s.addrs[i].IP
|
||||
DB := s.addrs[j].IP
|
||||
SourceDA := s.srcs[i]
|
||||
SourceDB := s.srcs[j]
|
||||
attrDA := &s.addrAttr[i]
|
||||
attrDB := &s.addrAttr[j]
|
||||
attrSourceDA := &s.srcAttr[i]
|
||||
attrSourceDB := &s.srcAttr[j]
|
||||
|
||||
const preferDA = true
|
||||
const preferDB = false
|
||||
|
||||
// Rule 1: Avoid unusable destinations.
|
||||
// If DB is known to be unreachable or if Source(DB) is undefined, then
|
||||
// prefer DA. Similarly, if DA is known to be unreachable or if
|
||||
// Source(DA) is undefined, then prefer DB.
|
||||
if !SourceDA.IsValid() && !SourceDB.IsValid() {
|
||||
return false // "equal"
|
||||
}
|
||||
if !SourceDB.IsValid() {
|
||||
return preferDA
|
||||
}
|
||||
if !SourceDA.IsValid() {
|
||||
return preferDB
|
||||
}
|
||||
|
||||
// Rule 2: Prefer matching scope.
|
||||
// If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
|
||||
// then prefer DA. Similarly, if Scope(DA) <> Scope(Source(DA)) and
|
||||
// Scope(DB) = Scope(Source(DB)), then prefer DB.
|
||||
if attrDA.Scope == attrSourceDA.Scope && attrDB.Scope != attrSourceDB.Scope {
|
||||
return preferDA
|
||||
}
|
||||
if attrDA.Scope != attrSourceDA.Scope && attrDB.Scope == attrSourceDB.Scope {
|
||||
return preferDB
|
||||
}
|
||||
|
||||
// Rule 3: Avoid deprecated addresses.
|
||||
// If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
|
||||
// Similarly, if Source(DA) is not deprecated and Source(DB) is
|
||||
// deprecated, then prefer DA.
|
||||
|
||||
// TODO(bradfitz): implement? low priority for now.
|
||||
|
||||
// Rule 4: Prefer home addresses.
|
||||
// If Source(DA) is simultaneously a home address and care-of address
|
||||
// and Source(DB) is not, then prefer DA. Similarly, if Source(DB) is
|
||||
// simultaneously a home address and care-of address and Source(DA) is
|
||||
// not, then prefer DB.
|
||||
|
||||
// TODO(bradfitz): implement? low priority for now.
|
||||
|
||||
// Rule 5: Prefer matching label.
|
||||
// If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
|
||||
// then prefer DA. Similarly, if Label(Source(DA)) <> Label(DA) and
|
||||
// Label(Source(DB)) = Label(DB), then prefer DB.
|
||||
if attrSourceDA.Label == attrDA.Label &&
|
||||
attrSourceDB.Label != attrDB.Label {
|
||||
return preferDA
|
||||
}
|
||||
if attrSourceDA.Label != attrDA.Label &&
|
||||
attrSourceDB.Label == attrDB.Label {
|
||||
return preferDB
|
||||
}
|
||||
|
||||
// Rule 6: Prefer higher precedence.
|
||||
// If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if
|
||||
// Precedence(DA) < Precedence(DB), then prefer DB.
|
||||
if attrDA.Precedence > attrDB.Precedence {
|
||||
return preferDA
|
||||
}
|
||||
if attrDA.Precedence < attrDB.Precedence {
|
||||
return preferDB
|
||||
}
|
||||
|
||||
// Rule 7: Prefer native transport.
|
||||
// If DA is reached via an encapsulating transition mechanism (e.g.,
|
||||
// IPv6 in IPv4) and DB is not, then prefer DB. Similarly, if DB is
|
||||
// reached via encapsulation and DA is not, then prefer DA.
|
||||
|
||||
// TODO(bradfitz): implement? low priority for now.
|
||||
|
||||
// Rule 8: Prefer smaller scope.
|
||||
// If Scope(DA) < Scope(DB), then prefer DA. Similarly, if Scope(DA) >
|
||||
// Scope(DB), then prefer DB.
|
||||
if attrDA.Scope < attrDB.Scope {
|
||||
return preferDA
|
||||
}
|
||||
if attrDA.Scope > attrDB.Scope {
|
||||
return preferDB
|
||||
}
|
||||
|
||||
// Rule 9: Use the longest matching prefix.
|
||||
// When DA and DB belong to the same address family (both are IPv6 or
|
||||
// both are IPv4 [but see below]): If CommonPrefixLen(Source(DA), DA) >
|
||||
// CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if
|
||||
// CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
|
||||
// then prefer DB.
|
||||
//
|
||||
// However, applying this rule to IPv4 addresses causes
|
||||
// problems (see issues 13283 and 18518), so limit to IPv6.
|
||||
if DA.To4() == nil && DB.To4() == nil {
|
||||
commonA := commonPrefixLen(SourceDA, DA)
|
||||
commonB := commonPrefixLen(SourceDB, DB)
|
||||
|
||||
if commonA > commonB {
|
||||
return preferDA
|
||||
}
|
||||
if commonA < commonB {
|
||||
return preferDB
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 10: Otherwise, leave the order unchanged.
|
||||
// If DA preceded DB in the original list, prefer DA.
|
||||
// Otherwise, prefer DB.
|
||||
return false // "equal"
|
||||
}
|
||||
|
||||
type policyTableEntry struct {
|
||||
Prefix netip.Prefix
|
||||
Precedence uint8
|
||||
Label uint8
|
||||
}
|
||||
|
||||
type policyTable []policyTableEntry
|
||||
|
||||
// RFC 6724 section 2.1.
|
||||
// Items are sorted by the size of their Prefix.Mask.Size,
|
||||
var rfc6724policyTable = policyTable{
|
||||
{
|
||||
// "::1/128"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128),
|
||||
Precedence: 50,
|
||||
Label: 0,
|
||||
},
|
||||
{
|
||||
// "::ffff:0:0/96"
|
||||
// IPv4-compatible, etc.
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96),
|
||||
Precedence: 35,
|
||||
Label: 4,
|
||||
},
|
||||
{
|
||||
// "::/96"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96),
|
||||
Precedence: 1,
|
||||
Label: 3,
|
||||
},
|
||||
{
|
||||
// "2001::/32"
|
||||
// Teredo
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32),
|
||||
Precedence: 5,
|
||||
Label: 5,
|
||||
},
|
||||
{
|
||||
// "2002::/16"
|
||||
// 6to4
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16),
|
||||
Precedence: 30,
|
||||
Label: 2,
|
||||
},
|
||||
{
|
||||
// "3ffe::/16"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16),
|
||||
Precedence: 1,
|
||||
Label: 12,
|
||||
},
|
||||
{
|
||||
// "fec0::/10"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10),
|
||||
Precedence: 1,
|
||||
Label: 11,
|
||||
},
|
||||
{
|
||||
// "fc00::/7"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7),
|
||||
Precedence: 3,
|
||||
Label: 13,
|
||||
},
|
||||
{
|
||||
// "::/0"
|
||||
Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
|
||||
Precedence: 40,
|
||||
Label: 1,
|
||||
},
|
||||
}
|
||||
|
||||
// Classify returns the policyTableEntry of the entry with the longest
|
||||
// matching prefix that contains ip.
|
||||
// The table t must be sorted from largest mask size to smallest.
|
||||
func (t policyTable) Classify(ip netip.Addr) policyTableEntry {
|
||||
// Prefix.Contains() will not match an IPv6 prefix for an IPv4 address.
|
||||
if ip.Is4() {
|
||||
ip = netip.AddrFrom16(ip.As16())
|
||||
}
|
||||
for _, ent := range t {
|
||||
if ent.Prefix.Contains(ip) {
|
||||
return ent
|
||||
}
|
||||
}
|
||||
return policyTableEntry{}
|
||||
}
|
||||
|
||||
// RFC 6724 section 3.1.
|
||||
type scope uint8
|
||||
|
||||
const (
|
||||
scopeInterfaceLocal scope = 0x1
|
||||
scopeLinkLocal scope = 0x2
|
||||
scopeAdminLocal scope = 0x4
|
||||
scopeSiteLocal scope = 0x5
|
||||
scopeOrgLocal scope = 0x8
|
||||
scopeGlobal scope = 0xe
|
||||
)
|
||||
|
||||
func classifyScope(ip netip.Addr) scope {
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
|
||||
return scopeLinkLocal
|
||||
}
|
||||
ipv6 := ip.Is6() && !ip.Is4In6()
|
||||
ipv6AsBytes := ip.As16()
|
||||
if ipv6 && ip.IsMulticast() {
|
||||
return scope(ipv6AsBytes[1] & 0xf)
|
||||
}
|
||||
// Site-local addresses are defined in RFC 3513 section 2.5.6
|
||||
// (and deprecated in RFC 3879).
|
||||
if ipv6 && ipv6AsBytes[0] == 0xfe && ipv6AsBytes[1]&0xc0 == 0xc0 {
|
||||
return scopeSiteLocal
|
||||
}
|
||||
return scopeGlobal
|
||||
}
|
||||
|
||||
// commonPrefixLen reports the length of the longest prefix (looking
|
||||
// at the most significant, or leftmost, bits) that the
|
||||
// two addresses have in common, up to the length of a's prefix (i.e.,
|
||||
// the portion of the address not including the interface ID).
|
||||
//
|
||||
// If a or b is an IPv4 address as an IPv6 address, the IPv4 addresses
|
||||
// are compared (with max common prefix length of 32).
|
||||
// If a and b are different IP versions, 0 is returned.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc6724#section-2.2
|
||||
func commonPrefixLen(a netip.Addr, b net.IP) (cpl int) {
|
||||
if b4 := b.To4(); b4 != nil {
|
||||
b = b4
|
||||
}
|
||||
aAsSlice := a.AsSlice()
|
||||
if len(aAsSlice) != len(b) {
|
||||
return 0
|
||||
}
|
||||
// If IPv6, only up to the prefix (first 64 bits)
|
||||
if len(aAsSlice) > 8 {
|
||||
aAsSlice = aAsSlice[:8]
|
||||
b = b[:8]
|
||||
}
|
||||
for len(aAsSlice) > 0 {
|
||||
if aAsSlice[0] == b[0] {
|
||||
cpl += 8
|
||||
aAsSlice = aAsSlice[1:]
|
||||
b = b[1:]
|
||||
continue
|
||||
}
|
||||
bits := 8
|
||||
ab, bb := aAsSlice[0], b[0]
|
||||
for {
|
||||
ab >>= 1
|
||||
bb >>= 1
|
||||
bits--
|
||||
if ab == bb {
|
||||
cpl += bits
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
134
dns/dns.go
134
dns/dns.go
@ -2,109 +2,91 @@ package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/xvzc/SpoofDPI/dns/resolver"
|
||||
"github.com/xvzc/SpoofDPI/util"
|
||||
)
|
||||
|
||||
type DnsResolver struct {
|
||||
type Resolver interface {
|
||||
Resolve(ctx context.Context, host string, qTypes []uint16) ([]net.IPAddr, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
type Dns struct {
|
||||
host string
|
||||
port string
|
||||
enableDoh bool
|
||||
systemClient Resolver
|
||||
generalClient Resolver
|
||||
dohClient Resolver
|
||||
}
|
||||
|
||||
func NewResolver(config *util.Config) *DnsResolver {
|
||||
return &DnsResolver{
|
||||
func NewDns(config *util.Config) *Dns {
|
||||
addr := *config.DnsAddr
|
||||
port := strconv.Itoa(*config.DnsPort)
|
||||
|
||||
return &Dns{
|
||||
host: *config.DnsAddr,
|
||||
port: strconv.Itoa(*config.DnsPort),
|
||||
enableDoh: *config.EnableDoh,
|
||||
port: port,
|
||||
systemClient: resolver.NewSystemResolver(),
|
||||
generalClient: resolver.NewGeneralResolver(net.JoinHostPort(addr, port)),
|
||||
dohClient: resolver.NewDOHResolver(addr),
|
||||
}
|
||||
}
|
||||
|
||||
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(d.host, domain)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
c := new(dns.Client)
|
||||
|
||||
response, _, err := c.Exchange(msg, dnsServer)
|
||||
if err != nil {
|
||||
return "", errors.New("could not resolve the domain(custom)")
|
||||
}
|
||||
|
||||
for _, answer := range response.Answer {
|
||||
if record, ok := answer.(*dns.A); ok {
|
||||
return record.A.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
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("could not resolve the domain(system)")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
func (d *Dns) ResolveHost(host string, enableDoh bool, useSystemDns bool) (string, error) {
|
||||
if ip, err := parseIpAddr(host); err == nil {
|
||||
return ip.String(), nil
|
||||
}
|
||||
|
||||
return "", errors.New("no record found(system)")
|
||||
}
|
||||
|
||||
func dohLookup(host string, domain string) (string, error) {
|
||||
clt := d.clientFactory(enableDoh, useSystemDns)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := getDOHClient(host)
|
||||
log.Debugf("[DNS] resolving %s using %s", host, clt)
|
||||
t := time.Now()
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)
|
||||
|
||||
response, err := client.dohExchange(ctx, msg)
|
||||
addrs, err := clt.Resolve(ctx, host, []uint16{dns.TypeAAAA, dns.TypeA})
|
||||
// addrs, err := clt.Resolve(ctx, host, []uint16{dns.TypeAAAA})
|
||||
if err != nil {
|
||||
return "", errors.New("could not resolve the domain(doh)")
|
||||
return "", fmt.Errorf("%s: %w", clt, err)
|
||||
}
|
||||
|
||||
for _, answer := range response.Answer {
|
||||
if record, ok := answer.(*dns.A); ok {
|
||||
return record.A.String(), nil
|
||||
}
|
||||
if len(addrs) > 0 {
|
||||
d := time.Since(t).Milliseconds()
|
||||
log.Debugf("[DNS] resolved %s from %s in %d ms", addrs[0].String(), host, d)
|
||||
return addrs[0].String(), nil
|
||||
}
|
||||
|
||||
return "", errors.New("no record found(doh)")
|
||||
return "", fmt.Errorf("could not resolve %s using %s", host, clt)
|
||||
}
|
||||
|
||||
func (d *Dns) clientFactory(enableDoh bool, useSystemDns bool) Resolver {
|
||||
if useSystemDns {
|
||||
return d.systemClient
|
||||
}
|
||||
|
||||
if enableDoh {
|
||||
return d.dohClient
|
||||
}
|
||||
|
||||
return d.generalClient
|
||||
}
|
||||
|
||||
func parseIpAddr(addr string) (*net.IPAddr, error) {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("%s is not an ip address", addr)
|
||||
}
|
||||
|
||||
ipAddr := &net.IPAddr{
|
||||
IP: ip,
|
||||
}
|
||||
|
||||
return ipAddr, nil
|
||||
}
|
||||
|
106
dns/doh.go
106
dns/doh.go
@ -1,106 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type DOHClient struct {
|
||||
upstream string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
var dohClient *DOHClient
|
||||
var clientOnce sync.Once
|
||||
|
||||
func getDOHClient(host string) *DOHClient {
|
||||
if dohClient != nil {
|
||||
return dohClient
|
||||
}
|
||||
|
||||
clientOnce.Do(func() {
|
||||
h := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 3 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
MaxIdleConns: 100,
|
||||
},
|
||||
}
|
||||
|
||||
host = regexp.MustCompile(`^https:\/\/|\/dns-query$`).ReplaceAllString(host, "")
|
||||
dohClient = &DOHClient{
|
||||
upstream: "https://" + host + "/dns-query",
|
||||
httpClient: h,
|
||||
}
|
||||
})
|
||||
|
||||
return dohClient
|
||||
}
|
||||
|
||||
func (d *DOHClient) dohQuery(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
||||
pack, err := msg.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s?dns=%s", d.upstream, base64.RawStdEncoding.EncodeToString(pack))
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("doh status error")
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultMsg := new(dns.Msg)
|
||||
err = resultMsg.Unpack(buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resultMsg, nil
|
||||
}
|
||||
|
||||
func (d *DOHClient) dohExchange(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
||||
res, err := d.dohQuery(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.Rcode != dns.RcodeSuccess {
|
||||
return nil, errors.New("doh rcode wasn't successful")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
95
dns/resolver/doh.go
Normal file
95
dns/resolver/doh.go
Normal file
@ -0,0 +1,95 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type DOHResolver struct {
|
||||
upstream string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewDOHResolver(host string) *DOHResolver {
|
||||
c := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 3 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
MaxIdleConns: 100,
|
||||
},
|
||||
}
|
||||
|
||||
host = regexp.MustCompile(`^https:\/\/|\/dns-query$`).ReplaceAllString(host, "")
|
||||
return &DOHResolver{
|
||||
upstream: "https://" + host + "/dns-query",
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *DOHResolver) Resolve(ctx context.Context, host string, qTypes []uint16) ([]net.IPAddr, error) {
|
||||
resultCh := lookupAllTypes(ctx, host, qTypes, r.exchange)
|
||||
addrs, err := processResults(ctx, resultCh)
|
||||
return addrs, err
|
||||
}
|
||||
|
||||
func (r *DOHResolver) String() string {
|
||||
return fmt.Sprintf("doh resolver(%s)", r.upstream)
|
||||
}
|
||||
|
||||
func (r *DOHResolver) exchange(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
||||
pack, err := msg.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s?dns=%s", r.upstream, base64.RawStdEncoding.EncodeToString(pack))
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
|
||||
resp, err := r.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("doh status error")
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultMsg := new(dns.Msg)
|
||||
err = resultMsg.Unpack(buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resultMsg.Rcode != dns.RcodeSuccess {
|
||||
return nil, errors.New("doh rcode wasn't successful")
|
||||
}
|
||||
|
||||
return resultMsg, nil
|
||||
}
|
36
dns/resolver/general.go
Normal file
36
dns/resolver/general.go
Normal file
@ -0,0 +1,36 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type GeneralResolver struct {
|
||||
client *dns.Client
|
||||
server string
|
||||
}
|
||||
|
||||
func NewGeneralResolver(server string) *GeneralResolver {
|
||||
return &GeneralResolver{
|
||||
client: &dns.Client{},
|
||||
server: server,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GeneralResolver) Resolve(ctx context.Context, host string, qTypes []uint16) ([]net.IPAddr, error) {
|
||||
resultCh := lookupAllTypes(ctx, host, qTypes, r.exchange)
|
||||
addrs, err := processResults(ctx, resultCh)
|
||||
return addrs, err
|
||||
}
|
||||
|
||||
func (r *GeneralResolver) String() string {
|
||||
return fmt.Sprintf("general resolver(%s)", r.server)
|
||||
}
|
||||
|
||||
func (r *GeneralResolver) exchange(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
||||
resp, _, err := r.client.Exchange(msg, r.server)
|
||||
return resp, err
|
||||
}
|
114
dns/resolver/resolver.go
Normal file
114
dns/resolver/resolver.go
Normal file
@ -0,0 +1,114 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/xvzc/SpoofDPI/dns/addrselect"
|
||||
)
|
||||
|
||||
type exchangeFunc = func(ctx context.Context, msg *dns.Msg) (*dns.Msg, error)
|
||||
|
||||
type DNSResult struct {
|
||||
msg *dns.Msg
|
||||
err error
|
||||
}
|
||||
|
||||
func recordTypeIDToName(id uint16) string {
|
||||
switch id {
|
||||
case 1:
|
||||
return "A"
|
||||
case 28:
|
||||
return "AAAA"
|
||||
}
|
||||
return strconv.FormatUint(uint64(id), 10)
|
||||
}
|
||||
|
||||
func parseAddrsFromMsg(msg *dns.Msg) []net.IPAddr {
|
||||
var addrs []net.IPAddr
|
||||
|
||||
for _, record := range msg.Answer {
|
||||
switch ipRecord := record.(type) {
|
||||
case *dns.A:
|
||||
addrs = append(addrs, net.IPAddr{IP: ipRecord.A})
|
||||
case *dns.AAAA:
|
||||
addrs = append(addrs, net.IPAddr{IP: ipRecord.AAAA})
|
||||
}
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func sortAddrs(addrs []net.IPAddr) {
|
||||
addrselect.SortByRFC6724(addrs)
|
||||
}
|
||||
|
||||
func lookupAllTypes(ctx context.Context, host string, qTypes []uint16, exchange exchangeFunc) <-chan *DNSResult {
|
||||
var wg sync.WaitGroup
|
||||
resCh := make(chan *DNSResult)
|
||||
|
||||
for _, qType := range qTypes {
|
||||
wg.Add(1)
|
||||
go func(qType uint16) {
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resCh <- lookupType(ctx, host, qType, exchange):
|
||||
}
|
||||
}(qType)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resCh)
|
||||
}()
|
||||
|
||||
return resCh
|
||||
}
|
||||
|
||||
func lookupType(ctx context.Context, host string, queryType uint16, exchange exchangeFunc) *DNSResult {
|
||||
msg := newMsg(host, queryType)
|
||||
resp, err := exchange(ctx, msg)
|
||||
if err != nil {
|
||||
queryName := recordTypeIDToName(queryType)
|
||||
err = fmt.Errorf("resolving %s, query type %s: %w", host, queryName, err)
|
||||
return &DNSResult{err: err}
|
||||
}
|
||||
return &DNSResult{msg: resp}
|
||||
}
|
||||
|
||||
func newMsg(host string, qType uint16) *dns.Msg {
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(dns.Fqdn(host), qType)
|
||||
return msg
|
||||
}
|
||||
|
||||
func processResults(ctx context.Context, resCh <-chan *DNSResult) ([]net.IPAddr, error) {
|
||||
var errs []error
|
||||
var addrs []net.IPAddr
|
||||
|
||||
for result := range resCh {
|
||||
if result.err != nil {
|
||||
errs = append(errs, result.err)
|
||||
continue
|
||||
}
|
||||
resultAddrs := parseAddrsFromMsg(result.msg)
|
||||
addrs = append(addrs, resultAddrs...)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, errors.New("canceled")
|
||||
default:
|
||||
if len(addrs) == 0 {
|
||||
return addrs, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
sortAddrs(addrs)
|
||||
return addrs, nil
|
||||
}
|
28
dns/resolver/system.go
Normal file
28
dns/resolver/system.go
Normal file
@ -0,0 +1,28 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type SystemResolver struct {
|
||||
*net.Resolver
|
||||
}
|
||||
|
||||
func NewSystemResolver() *SystemResolver {
|
||||
return &SystemResolver{
|
||||
&net.Resolver{PreferGo: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SystemResolver) String() string {
|
||||
return "system resolver"
|
||||
}
|
||||
|
||||
func (r *SystemResolver) Resolve(ctx context.Context, host string, _ []uint16) ([]net.IPAddr, error) {
|
||||
addrs, err := r.LookupIPAddr(ctx, host)
|
||||
if err != nil {
|
||||
return []net.IPAddr{}, err
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
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" github.com/xvzc/SpoofDPI/cmd/spoof-dpi &&
|
||||
tar -zcvf spoof-dpi-${osarch%/*}-${osarch#*/}.tar.gz ./spoof-dpi &&
|
||||
rm -rf ./spoof-dpi
|
||||
done
|
||||
|
||||
for osarch in 'windows/amd64'; do
|
||||
GOOS=${osarch%/*} GOARCH=${osarch#*/} go build -o spoof-dpi-${osarch%/*}-${osarch#*/}.exe -ldflags="-w -s" github.com/xvzc/SpoofDPI/cmd/spoof-dpi
|
||||
done
|
@ -2,12 +2,14 @@ package packet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type TLSMessageType byte
|
||||
|
||||
const (
|
||||
TLSMaxPayloadLen uint16 = 16384 // 16 KB
|
||||
TLSHeaderLen = 5
|
||||
TLSInvalid TLSMessageType = 0x0
|
||||
TLSChangeCipherSpec TLSMessageType = 0x14
|
||||
@ -42,7 +44,10 @@ func ReadTLSMessage(r io.Reader) (*TLSMessage, error) {
|
||||
ProtoVersion: binary.BigEndian.Uint16(rawHeader[1:3]),
|
||||
PayloadLen: binary.BigEndian.Uint16(rawHeader[3:5]),
|
||||
}
|
||||
|
||||
if header.PayloadLen > TLSMaxPayloadLen {
|
||||
// Corrupted header? Check integer overflow
|
||||
return nil, fmt.Errorf("invalid TLS header. Type: %x, ProtoVersion: %x, PayloadLen: %x", header.Type, header.ProtoVersion, header.PayloadLen)
|
||||
}
|
||||
raw := make([]byte, header.PayloadLen+TLSHeaderLen)
|
||||
copy(raw[0:TLSHeaderLen], rawHeader[:])
|
||||
_, err = io.ReadFull(r, raw[TLSHeaderLen:])
|
||||
@ -62,5 +67,7 @@ func ReadTLSMessage(r io.Reader) (*TLSMessage, error) {
|
||||
func (m *TLSMessage) IsClientHello() bool {
|
||||
// According to RFC 8446 section 4.
|
||||
// first byte (Raw[5]) of handshake message should be 0x1 - means client_hello
|
||||
return m.Header.Type == TLSHandshake && m.Raw[5] == 0x01
|
||||
return len(m.Raw) > TLSHeaderLen &&
|
||||
m.Header.Type == TLSHandshake &&
|
||||
m.Raw[5] == 0x01
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func (pxy *Proxy) handleHttp(lConn *net.TCPConn, pkt *packet.HttpPacket, ip stri
|
||||
if pkt.Port() != "" {
|
||||
port, err = strconv.Atoi(pkt.Port())
|
||||
if err != nil {
|
||||
log.Debug("[HTTP] error while parsing port for ", pkt.Domain(), " aborting..")
|
||||
log.Debugf("[HTTP] error while parsing port for %s aborting..", pkt.Domain())
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,17 +29,17 @@ func (pxy *Proxy) handleHttp(lConn *net.TCPConn, pkt *packet.HttpPacket, ip stri
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("[HTTP] new connection to the server ", rConn.LocalAddr(), " -> ", pkt.Domain())
|
||||
log.Debugf("[HTTP] new connection to the server %s -> %s", rConn.LocalAddr(), pkt.Domain())
|
||||
|
||||
go Serve(rConn, lConn, "[HTTP]", pkt.Domain(), lConn.RemoteAddr().String(), pxy.timeout)
|
||||
|
||||
_, err = rConn.Write(pkt.Raw())
|
||||
if err != nil {
|
||||
log.Debug("[HTTP] error sending request to ", pkt.Domain(), err)
|
||||
log.Debugf("[HTTP] error sending request to %s: %s", pkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("[HTTP] sent a request to ", pkt.Domain())
|
||||
log.Debugf("[HTTP] sent a request to %s", pkt.Domain())
|
||||
|
||||
go Serve(lConn, rConn, "[HTTP]", lConn.RemoteAddr().String(), pkt.Domain(), pxy.timeout)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ func (pxy *Proxy) handleHttps(lConn *net.TCPConn, exploit bool, initPkt *packet.
|
||||
if initPkt.Port() != "" {
|
||||
port, err = strconv.Atoi(initPkt.Port())
|
||||
if err != nil {
|
||||
log.Debug("[HTTPS] error while parsing port for ", initPkt.Domain(), " aborting..")
|
||||
log.Debugf("[HTTPS] error parsing port for %s aborting..", initPkt.Domain())
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,40 +26,40 @@ func (pxy *Proxy) handleHttps(lConn *net.TCPConn, exploit bool, initPkt *packet.
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("[HTTPS] new connection to the server ", rConn.LocalAddr(), " -> ", initPkt.Domain())
|
||||
log.Debugf("[HTTPS] 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 {
|
||||
log.Debug("[HTTPS] error sending 200 connection established to the client: ", err)
|
||||
log.Debugf("[HTTPS] error sending 200 connection established to the client: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("[HTTPS] sent connection estabalished to ", lConn.RemoteAddr())
|
||||
log.Debugf("[HTTPS] sent connection estabalished to %s", lConn.RemoteAddr())
|
||||
|
||||
// Read client hello
|
||||
m, err := packet.ReadTLSMessage(lConn)
|
||||
if err != nil || !m.IsClientHello() {
|
||||
log.Debug("[HTTPS] error reading client hello from ", lConn.RemoteAddr().String(), " ", err)
|
||||
log.Debugf("[HTTPS] error reading client hello from %s: %s", lConn.RemoteAddr().String(), err)
|
||||
return
|
||||
}
|
||||
clientHello := m.Raw
|
||||
|
||||
log.Debug("[HTTPS] client sent hello ", len(clientHello), "bytes")
|
||||
log.Debugf("[HTTPS] client sent hello %d bytes", len(clientHello))
|
||||
|
||||
// Generate a go routine that reads from the server
|
||||
go Serve(rConn, lConn, "[HTTPS]", initPkt.Domain(), lConn.RemoteAddr().String(), pxy.timeout)
|
||||
|
||||
if exploit {
|
||||
log.Debug("[HTTPS] writing chunked client hello to ", initPkt.Domain())
|
||||
log.Debugf("[HTTPS] writing chunked client hello to %s", initPkt.Domain())
|
||||
chunks := splitInChunks(clientHello, pxy.windowSize)
|
||||
if _, err := writeChunks(rConn, chunks); err != nil {
|
||||
log.Debug("[HTTPS] error writing chunked client hello to ", initPkt.Domain(), err)
|
||||
log.Debugf("[HTTPS] error writing chunked client hello to %s: %s", initPkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Debug("[HTTPS] writing plain client hello to ", initPkt.Domain())
|
||||
log.Debugf("[HTTPS] writing plain client hello to %s", initPkt.Domain())
|
||||
if _, err := rConn.Write(clientHello); err != nil {
|
||||
log.Debug("[HTTPS] error writing plain client hello to ", initPkt.Domain(), err)
|
||||
log.Debugf("[HTTPS] error writing plain client hello to %s: %s", initPkt.Domain(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ func splitInChunks(bytes []byte, size int) [][]byte {
|
||||
var chunks [][]byte
|
||||
var raw []byte = bytes
|
||||
|
||||
log.Debug("[HTTPS] window-size: ", size)
|
||||
log.Debugf("[HTTPS] window-size: %d", size)
|
||||
|
||||
if size > 0 {
|
||||
for {
|
||||
|
@ -17,8 +17,9 @@ type Proxy struct {
|
||||
addr string
|
||||
port int
|
||||
timeout int
|
||||
resolver *dns.DnsResolver
|
||||
resolver *dns.Dns
|
||||
windowSize int
|
||||
enableDoh bool
|
||||
allowedPattern []*regexp.Regexp
|
||||
}
|
||||
|
||||
@ -28,13 +29,14 @@ func New(config *util.Config) *Proxy {
|
||||
port: *config.Port,
|
||||
timeout: *config.Timeout,
|
||||
windowSize: *config.WindowSize,
|
||||
enableDoh: *config.EnableDoh,
|
||||
allowedPattern: config.AllowedPatterns,
|
||||
resolver: dns.NewResolver(config),
|
||||
resolver: dns.NewDns(config),
|
||||
}
|
||||
}
|
||||
|
||||
func (pxy *Proxy) Start() {
|
||||
l, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.ParseIP(pxy.addr), Port: pxy.port})
|
||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(pxy.addr), Port: pxy.port})
|
||||
if err != nil {
|
||||
log.Fatal("[PROXY] error creating listener: ", err)
|
||||
os.Exit(1)
|
||||
@ -75,7 +77,7 @@ func (pxy *Proxy) Start() {
|
||||
matched := pxy.patternMatches([]byte(pkt.Domain()))
|
||||
useSystemDns := !matched
|
||||
|
||||
ip, err := pxy.resolver.Lookup(pkt.Domain(), useSystemDns)
|
||||
ip, err := pxy.resolver.ResolveHost(pkt.Domain(), pxy.enableDoh, useSystemDns)
|
||||
if err != nil {
|
||||
log.Debug("[PROXY] error while dns lookup: ", pkt.Domain(), " ", err)
|
||||
conn.Write([]byte(pkt.Version() + " 502 Bad Gateway\r\n\r\n"))
|
||||
@ -114,11 +116,6 @@ func (pxy *Proxy) patternMatches(bytes []byte) bool {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -133,7 +130,7 @@ func isLoopedRequest(ip net.IP) bool {
|
||||
|
||||
for _, addr := range addr {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
if ipnet.IP.To4() != nil && ipnet.IP.To4().Equal(ip) {
|
||||
if ipnet.IP.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,9 @@ func Serve(from *net.TCPConn, to *net.TCPConn, proto string, fd string, td strin
|
||||
from.Close()
|
||||
to.Close()
|
||||
|
||||
log.Debug("[HTTPS] closing proxy connection: ", fd, " -> ", td)
|
||||
log.Debugf("%s closing proxy connection: %s -> %s", proto, fd, td)
|
||||
}()
|
||||
|
||||
proto += " "
|
||||
buf := make([]byte, BufferSize)
|
||||
for {
|
||||
if timeout > 0 {
|
||||
@ -52,15 +51,15 @@ func Serve(from *net.TCPConn, to *net.TCPConn, proto string, fd string, td strin
|
||||
bytesRead, err := ReadBytes(from, buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
log.Debug(proto, "finished reading from", fd)
|
||||
log.Debugf("%s finished reading from %s", proto, fd)
|
||||
return
|
||||
}
|
||||
log.Debug(proto, "error reading from ", fd, " ", err)
|
||||
log.Debugf("%s error reading from %s: %s", proto, fd, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := to.Write(bytesRead); err != nil {
|
||||
log.Debug(proto, "error Writing to ", td)
|
||||
log.Debugf("%s error Writing to %s", proto, td)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
v0.10.8
|
||||
v0.10.10
|
||||
|
Loading…
Reference in New Issue
Block a user