# Understanding croc's Internal DNS Resolver: How the `--internal-dns` Flag Works

> Learn how croc's internal DNS resolver works with the --internal-dns flag. Bypass system DNS and query public servers directly for faster connections.

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

---

**The `--internal-dns` flag forces croc to bypass the operating system's DNS resolver and use a built-in stub resolver that queries public DNS servers directly on port 53.**

The `schollz/croc` secure file transfer tool provides an alternative DNS resolution mechanism for environments where system DNS is unreliable or restricted. When enabled via the `--internal-dns` flag, croc ignores the host's resolver configuration and uses its own implementation defined in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) to ensure reliable connectivity to relay servers.

## How the Internal DNS Resolver Activates

According to the croc source code, the system checks for the `--internal-dns` flag in two ways before establishing connections.

### Command-Line Flag Detection

In [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) (lines 58-66), croc parses command-line arguments at startup. If `--internal-dns` is present, the tool sets the package-level variable `INTERNAL_DNS` to `true` for the duration of the session.

### Persistent Configuration via `--remember`

If the flag is not supplied on the command line, croc checks for a file named `internal-dns` in the user's config directory (lines 49-55). When you run croc with the `--remember` flag alongside `--internal-dns`, it creates this marker file. On subsequent runs, the presence of this file automatically enables `INTERNAL_DNS` without requiring the command-line argument.

## The Dual Resolution Logic in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go)

The core resolution logic resides in the `lookup` function (lines 4-33), which conditionally routes DNS queries based on the `INTERNAL_DNS` state.

**When `INTERNAL_DNS` is false:** The function calls `localLookupIP`, which utilizes the standard `net.Resolver` with a strict 500ms timeout implemented via `context.WithTimeout`.

**When `INTERNAL_DNS` is true:** The function triggers the built-in stub resolver by calling `remoteLookupIP`.

### Local Resolution (`localLookupIP`)

The local path relies entirely on the operating system's DNS configuration. It creates a context with a 500ms cancellation deadline and passes the query to the default Go resolver, which respects [`/etc/resolv.conf`](https://github.com/schollz/croc/blob/main//etc/resolv.conf), systemd-resolved, or Windows DNS settings depending on the platform.

### Built-in Stub Resolution (`remoteLookupIP`)

When internal DNS is enabled, croc spawns a goroutine for every server listed in the `publicDNS` slice (defined in lines 27-47). Each goroutine instantiates a `net.Resolver` configured with `PreferGo: true` and a custom `Dial` function that forces TCP/UDP connections directly to the specific DNS server on port 53, bypassing any system resolvers.

Results stream into a buffered channel, and the `lookup` function returns the first non-empty response it receives. If all goroutines fail to retrieve a valid record, the function returns an error.

## Practical Usage Examples

Enable the built-in resolver for a single transfer:

```bash
croc send --internal-dns file.txt

```

Permanently enable the stub resolver by creating the config marker file:

```bash
croc send --remember --internal-dns file.txt

```

Verify which resolver is active using debug output:

```bash
croc --debug send --internal-dns file.txt

```

The first command activates the resolver only for that invocation. The second creates the `internal-dns` file in your config directory, making `INTERNAL_DNS` true on every subsequent execution unless explicitly overridden.

## Key Implementation Files

- **[`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go)**: Contains the `INTERNAL_DNS` flag logic, the `publicDNS` server list (lines 27-47), the config file handling (lines 49-55), CLI argument parsing (lines 58-66), and the `lookup`, `localLookupIP`, and `remoteLookupIP` implementations (lines 4-33).
- **[`src/cli/cli.go`](https://github.com/schollz/croc/blob/main/src/cli/cli.go)**: Declares the global `--internal-dns` and `--remember` flags and passes CLI arguments to the underlying resolution logic.

## Summary

- The `--internal-dns` flag forces croc to use a built-in stub resolver instead of the OS DNS.
- When enabled, croc queries servers from the `publicDNS` list directly on port 53 using goroutines and returns the first successful result.
- The `internal-dns` config file allows persistent activation without passing the flag on every run.
- This mechanism bypasses system-wide DNS caching and restrictive resolver configurations, ensuring reliable relay host resolution.

## Frequently Asked Questions

### What is croc's internal DNS resolver?

Croc's internal DNS resolver is a built-in stub implementation located in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) that queries public DNS servers directly rather than using the operating system's resolver libraries. It activates when the `--internal-dns` flag is set or when an `internal-dns` marker file exists in the config directory.

### How do I permanently enable internal DNS in croc?

Run any croc command with both `--remember` and `--internal-dns` flags, such as `croc send --remember --internal-dns file.txt`. This creates an `internal-dns` file in your config directory that automatically enables the built-in resolver on all future runs unless you delete the file or override with explicit flags.

### What is the difference between local and internal DNS resolution in croc?

**Local DNS** uses the operating system's resolver via `localLookupIP`, respecting [`/etc/resolv.conf`](https://github.com/schollz/croc/blob/main//etc/resolv.conf) and local DNS caches with a 500ms timeout. **Internal DNS** uses `remoteLookupIP` to spawn parallel goroutines that query hardcoded public DNS servers directly on port 53, bypassing the host's resolver entirely.

### Which public DNS servers does croc use when internal DNS is enabled?

The specific servers are defined in the `publicDNS` slice in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) (lines 27-47). The resolver initiates parallel queries to every server in this list simultaneously, returning the first successful response to minimize latency.