-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: ensure connection cleanup in JARM fingerprint loop #2545
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -49,21 +49,26 @@ func fingerprint(dialer *fastdialer.Dialer, t target, duration int) string { | |||||||||||||||||||
| if conn == nil { | ||||||||||||||||||||
| return "" | ||||||||||||||||||||
| } | ||||||||||||||||||||
| _ = conn.SetWriteDeadline(time.Now().Add(timeout)) | ||||||||||||||||||||
| _, err = conn.Write(jarm.BuildProbe(probe)) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| _ = conn.Close() | ||||||||||||||||||||
| return "" | ||||||||||||||||||||
| } | ||||||||||||||||||||
| _ = conn.SetReadDeadline(time.Now().Add(timeout)) | ||||||||||||||||||||
| buff := make([]byte, 1484) | ||||||||||||||||||||
| _, _ = conn.Read(buff) | ||||||||||||||||||||
| _ = conn.Close() | ||||||||||||||||||||
| ans, err := jarm.ParseServerHello(buff, probe) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| return "" | ||||||||||||||||||||
| } | ||||||||||||||||||||
| results = append(results, ans) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func() { | ||||||||||||||||||||
| defer conn.Close() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| _ = conn.SetWriteDeadline(time.Now().Add(timeout)) | ||||||||||||||||||||
| _, err = conn.Write(jarm.BuildProbe(probe)) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
| _ = conn.SetReadDeadline(time.Now().Add(timeout)) | ||||||||||||||||||||
| buff := make([]byte, 1484) | ||||||||||||||||||||
| if _, err = conn.Read(buff); err != nil { | ||||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
| ans, err := jarm.ParseServerHello(buff, probe) | ||||||||||||||||||||
|
Comment on lines
+63
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n -C3 'ParseServerHello|\.Read\(' common/hashes/jarm --glob '*.go'Repository: projectdiscovery/httpx Length of output: 734 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== file outline =="
ast-grep outline common/hashes/jarm/jarmhash.go || true
echo
echo "== jarmhash.go relevant sections =="
sed -n '1,140p' common/hashes/jarm/jarmhash.go
echo
echo "== parse server hello implementation/usages =="
rg -n -C4 'func ParseServerHello|ParseServerHello\(' common --glob '*.go'
echo
echo "== any connection wrappers/readers in common/hashes/jarm =="
rg -n -C3 'Read\(|conn.*Read|Read\(' common/hashes/jarm --glob '*.go'Repository: projectdiscovery/httpx Length of output: 3543 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Acquire/OneTimePool definition and implementations =="
rg -n -C5 'func newOneTimePool|type .*OneTime|func \(.*\) Acquire|type .*Connection|type .*Conn' common --glob '*.go'
echo
echo "== imports and pool files =="
fd -e go . common/hashes/jarm -x sh -c 'echo "--- $1"; sed -n "1,160p" "$1"' sh {}Repository: projectdiscovery/httpx Length of output: 6673 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== fastdialer Dial implementations and net.Conn wrappers =="
rg -n -C5 'func \(.*\) Dial|type .*Dial|Dial\(ctx|net.Conn|Read\(.*error' -g '*.go' .Repository: projectdiscovery/httpx Length of output: 6826 🌐 Web query:
💡 Result: In the context of the jarm-go library (widely used for JARM fingerprinting), the ParseServerHello function is responsible for parsing a TLS ServerHello message from raw byte data [1][2][3]. The error regarding "buffer length expected" or similar validation failures typically arises when the provided byte buffer is too short to contain a valid TLS ServerHello structure [4]. JARM probing relies on receiving a response from a server, which is then passed to the parsing logic [1][3]. Key technical context: 1. Expected Data: The function expects a byte array containing a valid TLS handshake response. If the connection received fewer bytes than required by the TLS protocol specification (or the specific implementation's parser), an error will be returned [4]. 2. JARM Context: When using jarm-go, the library typically receives up to 1484 bytes from the server [1][5]. If the network read is interrupted or the server sends an incomplete response, the buffer passed to ParseServerHello will not meet the minimum length requirements for header fields, resulting in a parsing error [1][4]. 3. Troubleshooting: If you are encountering this error, ensure that the data being passed into ParseServerHello is actually a complete TLS ServerHello handshake message. In many implementations, the data is expected to start with the TLS record header (e.g., 5 bytes for the record layer) followed by the handshake message [6][4]. If you are writing custom code or debugging, verify that your read operation has fully buffered the expected amount of data before attempting to parse it [4]. Citations:
Preserve bytes returned with a read error.
Suggested adjustment- if _, err = conn.Read(buff); err != nil {
+ n, readErr := conn.Read(buff)
+ if n == 0 && readErr != nil {
return
}
- ans, err := jarm.ParseServerHello(buff, probe)
+ ans, err := jarm.ParseServerHello(buff[:n], probe)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
| results = append(results, ans) | ||||||||||||||||||||
| }() | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return jarm.RawHashToFuzzyHash(strings.Join(results, ",")) | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Handle the deferred
Closeerror.Line [54] triggers
errcheck. Sincefingerprintcannot propagate cleanup errors, explicitly discard the result or add a narrowly scoped lint exemption.Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 golangci-lint (2.12.2)
[error] 54-54: Error return value of
conn.Closeis not checked(errcheck)
🤖 Prompt for AI Agents
Source: Linters/SAST tools