# How the Communication Layer in comm.go Works in croc

> Explore the communication layer in croc's comm.go. Learn how it uses magic-byte framing, length-prefixed messages, and timeout handling for robust data transfer.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: internals
- Published: 2026-07-26

---

**The [`comm.go`](https://github.com/schollz/croc/blob/main/comm.go) file in croc implements a low-level binary communication protocol that wraps `net.Conn` with magic-byte framing, length-prefixed messages, timeout handling, and optional SOCKS5/HTTP proxy support.**

The `schollz/croc` secure file transfer tool relies on a robust communication layer to handle network I/O across relays and peer connections. At the heart of this layer lies [`src/comm/comm.go`](https://github.com/schollz/croc/blob/main/src/comm/comm.go), which abstracts raw TCP connections into a structured binary protocol with built-in error detection and proxy support.

## Connection Establishment and Proxy Support

The entry point for creating connections is the **`NewConnection(address)`** function. This helper evaluates global configuration variables—specifically `Socks5Proxy` and `HttpProxy`—to determine whether to route traffic through an intermediary.

When proxy variables are set and the target address is not a local IP, the function constructs a `proxy.Dialer` using either `golang.org/x/net/proxy` for SOCKS5 or `github.com/magisterquis/connectproxy` for HTTP CONNECT tunnels. If no proxy is configured or the target is local, the code falls back to `net.DialTimeout` with a default 30-second deadline.

Upon successful dial, the raw `net.Conn` is passed to **`comm.New(c net.Conn)`**, which returns a `*Comm` instance. This constructor immediately applies generous **3-hour read and write deadlines** using `SetReadDeadline` and `SetWriteDeadline` to keep long-running file transfers alive.

## The Comm Struct: Wrapping net.Conn

The `Comm` type provides a thin, stateful wrapper around the standard library connection:

```go
type Comm struct {
    connection net.Conn
}

```

The **`New(c net.Conn)`** method attaches the connection and configures the initial timeouts. For advanced use cases requiring direct access to the underlying socket, the **`Connection()`** method returns the raw `net.Conn`.

## Binary Framing Protocol: Sending Data

Data transmission follows a strict binary framing protocol implemented in the **`Write`** method (and its convenience wrapper **`Send`**). The process encodes every message with three components:

1. **Magic bytes** – The constant `MAGIC_BYTES = []byte("croc")` (hex `0x63 0x72 0x6f 0x63`) identifies valid croc traffic
2. **Length prefix** – The payload length as a little-endian `uint32` (4 bytes)
3. **Payload** – The raw message bytes

The implementation writes these components as a single atomic operation to minimize TCP fragmentation:

```go
func (c *Comm) Write(b []byte) (n int, err error)

```

Errors during transmission are wrapped with context to aid debugging of network failures.

## Receiving Data: Framing Verification

The **`Read`** method (exposed via the **`Receive`** convenience function) performs the inverse operation with strict validation:

1. **Magic verification** – Reads the first 4 bytes and validates against `MAGIC_BYTES`; mismatches abort immediately with an error
2. **Length decoding** – Reads the next 4 bytes as a little-endian `uint32` and validates against **`maxReadMessageSize`** (64 MiB) to prevent memory exhaustion attacks
3. **Payload extraction** – Adjusts the read deadline to **10 minutes** (`messageBodyReadTimeout`) to accommodate large control messages, then reads exactly the declared number of bytes

The signature returns the payload along with metadata:

```go
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error)

```

## Timeout Strategy and Connection Lifecycle

The communication layer implements a **dual-timeout strategy** to balance responsiveness with reliability:

- **Initial deadline** – A 3-hour read deadline applied at instantiation keeps connections alive during idle periods or large file transfers
- **Per-message deadline** – After parsing the length header, the deadline tightens to 10 minutes for the actual payload read, protecting against stuck or malformed streams while allowing legitimate large messages (such as resume-range lists) to traverse relays

Write operations inherit the same 3-hour deadline established during `Comm` initialization.

## Error Handling and Safety Limits

The layer includes defensive checks for protocol violations. Messages exceeding **`maxReadMessageSize`** (64 MiB) trigger an explicit `"message too large"` error, as verified by `TestReceiveRejectsOversizedMessage`. Conversely, `TestReceiveAllowsLargeMessage` confirms that legitimate messages near the limit are accepted when the extended 10-minute timeout is applied.

Proxy parsing errors are logged and returned early, preventing invalid proxy URLs from causing ambiguous connection failures.

## Practical Usage Example

The following pattern demonstrates establishing a connection and exchanging framed messages, mirroring the approach used in [`src/comm/comm_test.go`](https://github.com/schollz/croc/blob/main/src/comm/comm_test.go):

```go
package main

import (
    "fmt"
    "log"

    "github.com/schollz/croc/v10/src/comm"
)

func main() {
    // Establish a connection (use a reachable croc relay address)
    c, err := comm.NewConnection("relay.example.com:4000")
    if err != nil {
        log.Fatalf("failed to connect: %v", err)
    }
    defer c.Close()

    // Send a simple control message
    if err := c.Send([]byte("hello, computer")); err != nil {
        log.Fatalf("send error: %v", err)
    }

    // Receive the peer's response
    payload, err := c.Receive()
    if err != nil {
        log.Fatalf("receive error: %v", err)
    }
    fmt.Printf("peer says: %s\n", string(payload))
}

```

## Summary

- **[`src/comm/comm.go`](https://github.com/schollz/croc/blob/main/src/comm/comm.go)** provides the foundational transport abstraction for all croc network operations
- **Magic-byte framing** (`croc` header) and little-endian length prefixes ensure protocol integrity
- **Dual-timeout architecture** (3-hour connection, 10-minute message body) prevents hangs while supporting large transfers
- **64 MiB message limit** protects against denial-of-service via memory exhaustion
- **Proxy support** for both SOCKS5 and HTTP CONNECT is handled transparently during connection establishment

## Frequently Asked Questions

### What is the purpose of the MAGIC_BYTES constant in comm.go?

The `MAGIC_BYTES` constant—defined as `[]byte("croc")`—serves as a protocol identifier prepended to every message. When receiving data, [`comm.go`](https://github.com/schollz/croc/blob/main/comm.go) validates that the first four bytes match this sequence, immediately aborting connections that send malformed or non-croc traffic.

### How does croc handle proxy connections in the communication layer?

When global variables `Socks5Proxy` or `HttpProxy` are configured, `NewConnection` instantiates a `proxy.Dialer` from either `golang.org/x/net/proxy` or `github.com/magisterquis/connectproxy`. This dialer wraps the TCP connection establishment, tunneling all subsequent traffic through the specified proxy unless the target is a local IP address.

### What is the maximum message size supported by croc's comm.go?

The communication layer enforces a hard limit of **64 MiB** (`maxReadMessageSize`). Any message declaring a larger payload in its length header is rejected with an error before the body is read, preventing memory exhaustion attacks on relay servers or peers.

### How does the timeout mechanism prevent connection hangs during file transfers?

The `Comm` struct applies a liberal **3-hour deadline** to the underlying connection to accommodate large file transfers. However, once a message header is parsed, the deadline temporarily reduces to **10 minutes** for reading the specific payload. This two-tier approach keeps long-running transfers alive while quickly detecting stalled or zombie connections.