Skip to content

Add Windows proxy autodetection#222

Open
durck wants to merge 1 commit into
NHAS:mainfrom
durck:fix/wininet-auto-proxy-fallback
Open

Add Windows proxy autodetection#222
durck wants to merge 1 commit into
NHAS:mainfrom
durck:fix/wininet-auto-proxy-fallback

Conversation

@durck

@durck durck commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This adds Windows proxy autodetection for standalone clients and generated clients using --auto-proxy / link --auto-proxy.

The client reads the current user's WinINET manual proxy settings from:

HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings

Only the manual ProxyServer value is used. PAC (AutoConfigURL) and WPAD (AutoDetect) are intentionally not evaluated.

How proxy autodetection works

  • Supports a single WinINET proxy value, for example proxy.local:8080.
  • Supports per-scheme WinINET values, for example http=proxy.local:8080;https=secure-proxy.local:8443;socks=socks.local:1080.
  • Preserves explicit proxy URL schemes such as http://, https://, and socks5://.
  • Treats WinINET http and https entries as HTTP proxy endpoints when no explicit proxy URL scheme is present.
  • Treats WinINET socks entries as SOCKS5 endpoints when no explicit proxy URL scheme is present.
  • Ignores ftp and DIRECT entries because the client does not use FTP proxying and direct connection is already the first normal attempt.

Proxy candidate order is target-aware:

  • http / ws: prefer the WinINET HTTP proxy first.
  • https / tls / wss: prefer the WinINET Secure/HTTPS proxy first.
  • Raw SSH: prefer generic WinINET proxy entries first.

Fallback behavior

  • If WinINET proxying is enabled (ProxyEnable=1) and no explicit --proxy is set, the best matching detected proxy becomes the primary proxy.
  • If --proxy is set, the explicit proxy remains primary and detected WinINET proxies are fallback candidates.
  • If WinINET proxying is disabled (ProxyEnable=0) but ProxyServer still has values, those values are not used as primary. They are only tried after the direct or explicitly configured connection fails.
  • Detected fallback proxies are tried before standard *_PROXY environment variables.
  • Fallback proxies are deduplicated after normalization so the same proxy is not retried through equivalent forms such as proxy.local:8080 and http://proxy.local:8080.

Validation

  • go test ./internal/client
  • GOOS=windows GOARCH=amd64 go test ./internal/client
  • go vet ./internal/client
  • GOOS=linux GOARCH=amd64 go test -c ./internal/client
  • GOOS=darwin GOARCH=amd64 go test -c ./internal/client
  • go test -vet=off ./cmd/client ./internal/server/commands ./internal/server/webserver
  • git diff --check upstream/main..HEAD

The server package check uses -vet=off because upstream currently has unrelated fmt.Errorf non-constant format string vet findings. Those formatting-only changes are intentionally kept out of this feature PR.

@durck durck changed the title Improve Windows proxy autodetection Add Windows proxy autodetection Jul 9, 2026
Comment thread internal/server/commands/exec.go Outdated

if len(matchingClients) == 0 {
return fmt.Errorf("Unable to find match for '" + filter + "'\n")
return fmt.Errorf("Unable to find match for '%s'\n", filter)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random change? This should be %q anyway

Comment thread internal/server/commands/list.go Outdated
}

return fmt.Errorf("Unable to find match for '" + filter + "'")
return fmt.Errorf("Unable to find match for '%s'", filter)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another unrelated change

Comment thread internal/server/tcp/downloader.go Outdated
io.Copy(conn, file)
}

func readRawDownloadName(conn net.Conn) (string, error) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be code from another PR


if len(config.GOARCH) != 0 && !validArchs[config.GOARCH] {
return "", fmt.Errorf("GOARCH supplied is not valid: " + config.GOARCH)
return "", fmt.Errorf("GOARCH supplied is not valid: %s", config.GOARCH)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change

Comment thread internal/server/webserver/webserver.go Outdated
</body>
</html>`

func logDownloadTiming(path, remoteAddr, step string, started time.Time, acceptedAt time.Time, hasAccept bool) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change

Comment thread internal/server/webserver/webserver.go Outdated
acceptedAt, hasAccept := downloadConnAcceptedAt(req)

httpDownloadLog := logger.NewLog(fmt.Sprintf("%s:%q", req.RemoteAddr, req.Host))
httpDownloadLog := logger.NewLog(fmt.Sprintf("%s:%q", remoteAddr, req.Host))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Comment thread internal/server/sshd.go Outdated
Version: string(sshConn.ClientVersion()),
Timestamp: time.Now(),
Transport: connMetadata.Transport,
PublicKeyFingerprint: sshConn.Permissions.Extensions["pubkey-fp"],

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change

Comment thread pkg/mux/metadata.go Outdated
"time"
)

type timedConn struct {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why any of this?

Comment thread pkg/mux/multiplexer.go Outdated
if transportName == "" {
transportName = "http"
}
metadata := m.metadataFromRequest(req, realConn.RemoteAddr(), transportName)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Comment thread pkg/mux/multiplexer.go Outdated

if proto == protocols.HTTPDownload {
if acceptedAt, ok := AcceptedAt(newConnection); ok {
log.Printf("download mux routed remote=%s since_tcp_accept=%s", newConnection.RemoteAddr(), time.Since(acceptedAt))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why

Comment thread docker-entrypoint.sh Outdated
echo "Seeding authorized_keys...\n"
echo $SEED_AUTHORIZED_KEYS > /data/authorized_keys
echo "Seeding authorized_keys..."
printf '%s\n' "$SEED_AUTHORIZED_KEYS" > /data/authorized_keys

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

????

@NHAS

NHAS commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Hi,

Thanks for your PR. This is an enormous number of changes, and from what I can see this is more like 4 different PRs rolled into one.

I absolutely love your work with the upgrades to the windows proxy, and the other PR you've opened around making the download more robust. However this PR does far too much all in one go, I need you to chunk this up so I can understand each individual change.

Also please dont introduce random changes like moving from + <string> + to %s in the middle of a feature PR, it just adds more things and is harder to reason about.

@durck

durck commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Hi! You're right, this branch picked up unrelated work from my fork's main. I'll rebuild it from NHAS/main so this PR contains only the Windows proxy autodetection changes and the related tests/docs.

The raw download, mux metadata/logging, docker-entrypoint cleanup, and unrelated formatting changes should stay out of this PR and be split separately if needed.

@durck durck force-pushed the fix/wininet-auto-proxy-fallback branch from 8132a2b to a91aa78 Compare July 9, 2026 09:23
@durck

durck commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Updated the branch. This PR is now rebuilt from NHAS/main and scoped to Windows proxy autodetection only.

The previous unrelated raw download, mux/logging, docker-entrypoint, and formatting changes should now be out of this PR.

@durck durck marked this pull request as ready for review July 9, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants