diff --git a/phantomtcp/dns.go b/phantomtcp/dns.go index 56d482f..956ca00 100755 --- a/phantomtcp/dns.go +++ b/phantomtcp/dns.go @@ -1089,6 +1089,7 @@ func GetDNSLie(index int) (string, *Outbound) { func (outbound *Outbound) NSLookup(name string) (uint32, []net.IP) { hint := outbound.Hint + dual := outbound.Protocol != NAT64 && outbound.hasIPv6() && hint&HINT_IPV6 == 0 && hint&HINT_IPV4 == 0 var qtype uint16 = 1 if hint&HINT_IPV6 != 0 { qtype = 28 @@ -1115,24 +1116,37 @@ func (outbound *Outbound) NSLookup(name string) (uint32, []net.IP) { offset++ } } - switch qtype { - case 1: + if dual { + if records.IPv6Hint != nil && len(records.IPv6Hint.Addresses) > 0 { + logPrintln(3, "cached:", name, qtype, records.IPv6Hint.Addresses) + return records.Index, records.IPv6Hint.Addresses + } if records.IPv4Hint != nil { logPrintln(3, "cached:", name, qtype, records.IPv4Hint.Addresses) return records.Index, records.IPv4Hint.Addresses } - case 28: - if records.IPv6Hint != nil { - logPrintln(3, "cached:", name, qtype, records.IPv6Hint.Addresses) - return records.Index, records.IPv6Hint.Addresses + } else { + switch qtype { + case 1: + if records.IPv4Hint != nil { + logPrintln(3, "cached:", name, qtype, records.IPv4Hint.Addresses) + return records.Index, records.IPv4Hint.Addresses + } + case 28: + if records.IPv6Hint != nil { + logPrintln(3, "cached:", name, qtype, records.IPv6Hint.Addresses) + return records.Index, records.IPv6Hint.Addresses + } + default: + return 0, nil } - default: - return 0, nil } var request []byte var response []byte var err error + var response6 []byte + var err6 error var options ServerOptions u, err := url.Parse(outbound.DNS) @@ -1151,30 +1165,48 @@ func (outbound *Outbound) NSLookup(name string) (uint32, []net.IP) { if u.Host != "" { switch u.Scheme { - case "udp": - request = PackRequest(_name, qtype, uint16(0), options.ECS, options.QType2) - response, err = UDPlookup(request, u.Host) - case "tcp": - request = PackRequest(_name, qtype, uint16(0), options.ECS, options.QType2) - response, err = TCPlookup(request, u.Host) - case "tls": - request = PackRequest(_name, qtype, uint16(0), options.ECS, options.QType2) - response, err = TLSlookup(request, u.Host) - case "https": - request = PackRequest(_name, qtype, uint16(0), options.ECS, options.QType2) - response, err = HTTPSlookup(request, u, options.Domain) - case "tfo": - request = PackRequest(_name, qtype, uint16(0), options.ECS, options.QType2) - response, err = TFOlookup(request, u.Host) + case "udp", "tcp", "tls", "https", "tfo": default: records.Index = AddDNSLie(name, outbound) records.ALPN = hint return records.Index, nil } + doLookup := func(qt uint16) ([]byte, error) { + request = PackRequest(_name, qt, uint16(0), options.ECS, options.QType2) + switch u.Scheme { + case "udp": + return UDPlookup(request, u.Host) + case "tcp": + return TCPlookup(request, u.Host) + case "tls": + return TLSlookup(request, u.Host) + case "https": + return HTTPSlookup(request, u, options.Domain) + case "tfo": + return TFOlookup(request, u.Host) + } + return nil, nil + } + if dual { + done := make(chan struct{}, 1) + go func() { response6, err6 = doLookup(28); done <- struct{}{} }() + response, err = doLookup(1) + <-done + } else { + response, err = doLookup(qtype) + } } if err != nil { logPrintln(1, err) - return 0, nil + if !dual { + return 0, nil + } + } + if err6 != nil { + logPrintln(1, err6) + if err != nil { + return 0, nil + } } if (hint&HINT_FAKEIP != 0) && records.Index == 0 { @@ -1183,9 +1215,40 @@ func (outbound *Outbound) NSLookup(name string) (uint32, []net.IP) { } records.GetAnswers(response, options) + records.GetAnswers(response6, options) DNSRecordMutex.Lock() defer DNSRecordMutex.Unlock() + if dual { + if records.IPv6Hint == nil && options.Fallback != nil { + if options.Fallback.To4() == nil { + records.IPv6Hint = &RecordAddresses{0, []net.IP{options.Fallback}} + } + } + if records.IPv6Hint == nil { + records.IPv6Hint = &RecordAddresses{0, []net.IP{}} + } + logPrintln(3, "nslookup", name, 28, records.IPv6Hint.Addresses) + if len(records.IPv6Hint.Addresses) > 0 { + addresses := make([]net.IP, len(records.IPv6Hint.Addresses)) + copy(addresses, records.IPv6Hint.Addresses) + return records.Index, addresses + } + if records.IPv4Hint == nil && options.Fallback != nil { + if options.Fallback.To4() != nil { + logPrintln(4, "request:", name, "fallback", options.Fallback) + records.IPv4Hint = &RecordAddresses{0, []net.IP{options.Fallback}} + } + } + if records.IPv4Hint == nil { + records.IPv4Hint = &RecordAddresses{0, []net.IP{}} + } + logPrintln(3, "nslookup", name, 1, records.IPv4Hint.Addresses) + addresses := make([]net.IP, len(records.IPv4Hint.Addresses)) + copy(addresses, records.IPv4Hint.Addresses) + return records.Index, addresses + } + switch qtype { case 1: if records.IPv4Hint == nil && options.Fallback != nil { diff --git a/phantomtcp/tcp.go b/phantomtcp/tcp.go index 0425ec7..c517624 100755 --- a/phantomtcp/tcp.go +++ b/phantomtcp/tcp.go @@ -1,13 +1,13 @@ package phantomtcp import ( + "crypto/rand" "io" + mathrand "math/rand" "net" "os" - "crypto/rand" "strconv" "syscall" - mathrand "math/rand" ) const domainBytes = "abcdefghijklmnopqrstuvwxyz0123456789-" @@ -88,6 +88,31 @@ func GetLocalTCPAddr(name string, ipv6 bool) (*net.TCPAddr, error) { return nil, nil } +func (outbound *Outbound) hasIPv6() bool { + if outbound.Device != "" { + addr, _ := GetLocalTCPAddr(outbound.Device, true) + return addr != nil + } + ifaces, err := net.Interfaces() + if err != nil { + return false + } + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { + continue + } + addrs, _ := iface.Addrs() + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok { + if ipnet.IP.To4() == nil && !ipnet.IP.IsLinkLocalUnicast() { + return true + } + } + } + } + return false +} + func (outbound *Outbound) GetRemoteAddresses(host string, port int) ([]*net.TCPAddr, error) { switch outbound.Protocol { case DIRECT: