- pkg/nathole: add RLock when reading clientCfgs map in PreCheck path
to prevent concurrent map read/write crash
- server/proxy: fix error variable shadowing in GetWorkConnFromPool
that could return a closed connection with nil error
- pkg/util/net: check ListenUDP error before spawning goroutines
and assign readConn to struct field so Close() works correctly
- Replace duplicate parseBasicAuth with existing httppkg.ParseBasicAuth
- Extract buildDomains helper in BaseProxy for HTTP/HTTPS/TCPMux proxies
- Extract toProxyStats helper to deduplicate ProxyStats construction
- Extract startVisitorListener helper in BaseProxy for STCP/SUDP proxies
- Extract acceptLoop helper in BaseVisitor for STCP/XTCP visitors
* client/proxy: extract wrapWorkConn to deduplicate UDP/SUDP connection wrapping
Move the repeated rate-limiting, encryption, and compression wrapping
logic from UDPProxy and SUDPProxy into a shared BaseProxy.wrapWorkConn
method, reducing ~18 lines of duplication in each proxy type.
* client/proxy: unify work conn wrapping with pooled compression for all proxy types
Refactor wrapWorkConn to accept encKey parameter and return
(io.ReadWriteCloser, recycleFn, error), enabling HandleTCPWorkConnection
to reuse the same limiter/encryption/compression pipeline.
Switch all proxy types from WithCompression to WithCompressionFromPool.
TCP non-plugin path calls recycleFn via defer after Join; plugin and
UDP/SUDP paths skip recycle (objects are GC'd safely, per golib contract).
Consolidate the separate custom-domain loop and subdomain block into a
single unified loop, matching the pattern already applied to HTTPProxy
in PR #5207. No behavioral change.
The Run() method had two nearly identical loop blocks for registering
custom domains and subdomain, with the same group/non-group registration
logic copy-pasted (~30 lines of duplication).
Consolidate by collecting all domains into a single slice first, then
iterating once with the shared registration logic. Also fixes a minor
inconsistency where the custom domain block used routeConfig.Domain in
CanonicalAddr but the subdomain block used tmpRouteConfig.Domain.
In AddPrefix, the loop `for _, p := range l.prefixes` creates a copy
of each element. Assignments to p.Value and p.Priority only modify
the local copy, not the original slice element, causing updates to
existing prefixes to be silently lost.
This affects client/service.go where AddPrefix is called with
Name:"runID" on reconnection — the old runID value would persist
in log output instead of being updated to the new one.
Fix by using index-based access `l.prefixes[i]` to modify the
original slice element, and add break since prefix names are unique.
- pkg/transport/tls.go: check AppendCertsFromPEM return value and
return clear error when CA file contains no valid PEM certificates
- pkg/plugin/client/http2http.go: set ReadHeaderTimeout to 60s to
match other plugins and prevent slow header attacks
- pkg/plugin/client/http2https.go: same ReadHeaderTimeout fix
- pkg/util/net/websocket.go: store ln parameter in struct to prevent
nil pointer panic when Addr() is called
- pkg/auth/oidc.go: replace unsynchronized []string with map + RWMutex
for subjectsFromLogin to fix data race across concurrent connections
- pkg/nathole/nathole.go: add pool.PutBuf(buf) on ReadFromUDP error
and DecodeMessageInto error paths in waitDetectMessage
- pkg/proto/udp/udp.go: add defer pool.PutBuf(buf) in writerFn to
ensure buffer is returned when the goroutine exits
Fix connection leaks in multiple error paths across client and server:
- server/proxy/http: close tmpConn when WithEncryption fails
- client/proxy: close localConn when ProxyProtocol WriteTo fails
- client/visitor/sudp: close visitorConn on all error paths in getNewVisitorConn
- client/visitor/xtcp: close tunnelConn when WithEncryption fails
- client/visitor/xtcp: close lConn when NewKCPConnFromUDP fails
- pkg/plugin/client/unix_domain_socket: close localConn and connInfo.Conn when WriteTo fails, close connInfo.Conn when DialUnix fails
- pkg/plugin/client/tls2raw: close tlsConn when Handshake or Dial fails
Fix two bugs in TCPGroup.Listen():
- Release acquired port when net.Listen fails to prevent port leak
- Use realPort instead of port for net.Listen to ensure consistency
between port manager records and actual listening port
When frpc verify encounters config errors, the error messages now include
line/column information to help users locate problems:
- TOML syntax errors: report line and column from the TOML parser
(e.g., "toml: line 4, column 11: expected character ]")
- Type mismatch errors: report the field name and approximate line number
(e.g., "line 3: field \"proxies\": cannot unmarshal string into ...")
- File format detection: use file extension to determine format, preventing
silent fallthrough from TOML to YAML parser which produced confusing errors
Previously, a TOML file with syntax errors would silently fall through to the
YAML parser, which would misinterpret the content and produce unhelpful errors
like "json: cannot unmarshal string into Go value of type v1.ClientConfig".
https://claude.ai/code/session_017HWLfcXS3U2hLoy4dsg8Nv
Co-authored-by: Claude <noreply@anthropic.com>
* config: introduce ExecSource value source
* auth: introduce OidcTokenSourceAuthProvider
* auth: use OidcTokenSourceAuthProvider if tokenSource config is present on the client
* cmd: allow exec token source only if CLI flag was passed