Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: recovering preHandleMetadata failure from sniffing #769

Merged
merged 1 commit into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions component/sniffer/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ type SnifferDispatcher struct {
parsePureIp bool
}

func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
// TCPSniff returns true if the connection is sniffed to have a domain
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool {
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
inWhitelist := false
overrideDest := false
Expand All @@ -50,47 +51,50 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
}

if !inWhitelist {
return
return false
}

sd.rwMux.RLock()
dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort)
if count, ok := sd.skipList.Get(dst); ok && count > 5 {
log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst)
defer sd.rwMux.RUnlock()
return
return false
}
sd.rwMux.RUnlock()

if host, err := sd.sniffDomain(conn, metadata); err != nil {
sd.cacheSniffFailed(metadata)
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return
return false
} else {
if sd.skipSNI.Has(host) {
log.Debugln("[Sniffer] Skip sni[%s]", host)
return
return false
}

sd.rwMux.RLock()
sd.skipList.Delete(dst)
sd.rwMux.RUnlock()

sd.replaceDomain(metadata, host, overrideDest)
return true
}
}
return false
}

func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
// show log early, since the following code may mutate `metadata.Host`
log.Debugln("[Sniffer] Sniff TCP [%s]-->[%s] success, replace domain [%s]-->[%s]",
metadata.SourceDetail(),
metadata.RemoteAddress(),
metadata.Host, host)
metadata.SniffHost = host
if overrideDest {
metadata.Host = host
}
metadata.DNSMode = C.DNSNormal
log.Debugln("[Sniffer] Sniff TCP [%s]-->[%s] success, replace domain [%s]-->[%s]",
metadata.SourceDetail(),
metadata.RemoteAddress(),
metadata.Host, host)
}

func (sd *SnifferDispatcher) Enable() bool {
Expand Down
17 changes: 15 additions & 2 deletions tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,15 +419,28 @@ func handleTCPConn(connCtx C.ConnContext) {
return
}

preHandleFailed := false
if err := preHandleMetadata(metadata); err != nil {
log.Debugln("[Metadata PreHandle] error: %s", err)
return
preHandleFailed = true
}

conn := connCtx.Conn()
conn.ResetPeeked() // reset before sniffer
if sniffer.Dispatcher.Enable() && sniffingEnable {
sniffer.Dispatcher.TCPSniff(conn, metadata)
// Try to sniff a domain when `preHandleMetadata` failed, this is usually
// caused by a "Fake DNS record missing" error when enhanced-mode is fake-ip.
if sniffer.Dispatcher.TCPSniff(conn, metadata) {
// we now have a domain name
preHandleFailed = false
}
}

// If both trials have failed, we can do nothing but give up
if preHandleFailed {
log.Debugln("[Metadata PreHandle] failed to sniff a domain for connection %s --> %s, give up",
metadata.SourceDetail(), metadata.RemoteAddress())
return
}

peekMutex := sync.Mutex{}
Expand Down