Skip to content

Navigation Menu

Sign in
Sign up

Fix silent failure receiving files: read large frames in bounded chunks - #225

Open
aryzae wants to merge 2 commits into
grishka:master from
aryzae:fix/chunked-frame-reads
Open

Fix silent failure receiving files: read large frames in bounded chunks #225
aryzae wants to merge 2 commits into
grishka:master from
aryzae:fix/chunked-frame-reads

Conversation

@aryzae

@aryzae aryzae commented Sep 1, 2026
edited
Loading

Copy link
Copy Markdown

Problem

Receiving any file larger than ~512 KB fails silently. The consent prompt appears, the destination file is created in ~/Downloads, and then nothing is written. The sender's progress stays at 0% and it closes the connection after ~35 s. No error notification, no crash. This reproduces every time on my setup — macOS 26.6.2 (Apple Silicon), receiving from a Nothing Phone (1).

Cause

receiveFrameAsync(length:) asks NWConnection for the whole frame in one call:

connection.receive(minimumIncompleteLength: Int(length), maximumLength: Int(length))

File payloads arrive as ~512 KB frames. For a 524,456-byte frame this returns the right number of bytes, but they are not the bytes that were sent, so Securemessage_SecureMessage(serializedData:) throws malformedProtobuf and the receive loop stops. Handshake frames (100–300 bytes) are unaffected, which is why the connection gets all the way to the transfer before dying.

The damage is not deterministic. Across runs the same frame failed as malformedProtobuf, and as requiredFieldMissing("d2dMessage.message|sequenceNumber") after the body decrypted. The frame's own length arithmetic was always self-consistent (0a + varint(524418) + header 28 + body 524384 +たす 34 = 524456), but the trailing 34 bytes were never the 12 20 + 32-byte signature field, and their contents differed every run.

Ruled out along the way: SwiftProtobuf 1.21.0 (a synthetic buffer of the same shape parses fine), the BANDWIDTH_UPGRADE_RETRY frame, and the decrypt path.

Fix

Reassemble the frame from bounded reads. Nothing else changes; small frames still complete in a single read. Chunks accumulate in an NSMutableData, matching how payloadBuffers already collects payloads in this file. Bounded reads seem like the more robust shape here regardless of what makes the single large receive misbehave.

The 16 KB chunk size is a conservative pick, not a measured one. Larger chunks may work equally well and would mean fewer round trips through the receive callback — happy to change it.

Testing

Receiving from a Nothing Phone (1), where every one of these produced a 0-byte file before the change:

  • a single 4,016,108-byte JPEG — arrives complete and byte-exact, opens normally
  • three PNGs (1.8–2.1 MB) sent together — all three arrive complete

The reassembled frames pass the HMAC check, so verification is unaffected.

aryzae marked this pull request as ready for review September 1, 2026 09:06

grishka commented Sep 2, 2026

Copy link
Copy Markdown
Owner

From what I gather, this was never an issue to begin with.

Did you use AI to generate this?

aryzae commented Sep 3, 2026

Copy link
Copy Markdown
Author

You're right that this isn't a general problem. I've since tried the same transfers on a second Mac (my personal one, stock 2.2.0), and everything arrives fine there.

What I left out of the description: the Mac where this fails every time is a managed work machine with a third-party Endpoint Security extension that includes a network content filter (the connection logs show Output protocol connected (ne_filter)). On that machine the single large receive returns the right number of bytes with a damaged tail, and bounded reads make it work. I haven't been able to confirm the mechanism, since packet capture isn't an option on that machine.

So this is "make receiving robust when a content filter sits in the path" rather than a bug in NearDrop as such. I understand if that's not something you want to carry. If you'd rather, I can add this caveat to the description, or close the PR.

On your second question: yes, I used AI (Claude) to help with the investigation and with writing this up. I've read through the change myself and tested it on real hardware, both the failing Mac and the one where it works, as listed under Testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

AltStyle によって変換されたページ (->オリジナル) /