Problem
When using a hostname in the parent directive (e.g. a DDNS entry), 3proxy resolves it once at config parse time via getip46() in conf.c:807 and stores the resulting sockaddr in chains->addr. This address is never re-resolved for the lifetime of the process.
If the DNS record changes (common with DDNS, dynamic IPs, mobile upstreams), 3proxy keeps connecting to the old, stale IP indefinitely. All client sessions fail with error 00013 (connection to remote host failed), even though the process appears healthy (active (running), listening on port, accepting connections).
How to reproduce
- Configure a parent with a hostname that points to a dynamic IP:
parent 1000 socks5 myhost.ddns.net 5219 user pass
auto -a -p8080
- Start 3proxy — it resolves
myhost.ddns.net to e.g. 1.2.3.4
- Change the DNS record for
myhost.ddns.net to point to 5.6.7.8
- All new client connections now fail with error 00013, targeting
1.2.3.4 (dead IP)
- Only a full process restart fixes it
Root cause
In conf.c, function h_parent():
// line 807: resolved once, stored forever
if(!getip46(46, argv[3], (struct sockaddr *)&chains->addr)) return (5);
// line 808: hostname saved as string but never used for re-resolution
chains->exthost = (unsigned char *)mystrdup((char *)argv[3]);
getip46() calls either the internal resolver (resolvfunc via nserver) or system getaddrinfo() — but only at parse time. Neither nserver/nscache nor any TTL mechanism triggers a re-resolution of the parent address later.
Impact
In environments with many proxy instances using DDNS parent hostnames (e.g. mobile/4G upstreams with rotating IPs), this causes silent outages. The proxy process stays alive and appears healthy to monitoring, but all traffic fails. The only workaround is restarting the process (which forces a config re-parse and fresh DNS lookup).
Suggested fix
Re-resolve chains->exthost periodically or on connection failure. Possible approaches:
- On connect failure: if connecting to
chains->addr fails with ECONNREFUSED/ETIMEDOUT, re-resolve chains->exthost via getip46() and update chains->addr before retrying
- TTL-based: honor the DNS TTL from the response and re-resolve when expired
- Periodic: add a config directive like
parentresolvinterval <seconds> to force re-resolution at a given interval
Option 1 (re-resolve on failure) would be the least invasive and cover the main use case.
Environment
- 3proxy version:
0.9.5
- OS: Debian Linux (kernel 6.12)
- Parent hostnames: DDNS (CNAME to dynamic A record, TTL 60s)
- Scale: 66 proxy instances, 22 unique parent hostnames
Problem
When using a hostname in the
parentdirective (e.g. a DDNS entry), 3proxy resolves it once at config parse time viagetip46()inconf.c:807and stores the resultingsockaddrinchains->addr. This address is never re-resolved for the lifetime of the process.If the DNS record changes (common with DDNS, dynamic IPs, mobile upstreams), 3proxy keeps connecting to the old, stale IP indefinitely. All client sessions fail with error 00013 (connection to remote host failed), even though the process appears healthy (
active (running), listening on port, accepting connections).How to reproduce
myhost.ddns.netto e.g.1.2.3.4myhost.ddns.netto point to5.6.7.81.2.3.4(dead IP)Root cause
In
conf.c, functionh_parent():getip46()calls either the internal resolver (resolvfuncvianserver) or systemgetaddrinfo()— but only at parse time. Neithernserver/nscachenor any TTL mechanism triggers a re-resolution of the parent address later.Impact
In environments with many proxy instances using DDNS parent hostnames (e.g. mobile/4G upstreams with rotating IPs), this causes silent outages. The proxy process stays alive and appears healthy to monitoring, but all traffic fails. The only workaround is restarting the process (which forces a config re-parse and fresh DNS lookup).
Suggested fix
Re-resolve
chains->exthostperiodically or on connection failure. Possible approaches:chains->addrfails withECONNREFUSED/ETIMEDOUT, re-resolvechains->exthostviagetip46()and updatechains->addrbefore retryingparentresolvinterval <seconds>to force re-resolution at a given intervalOption 1 (re-resolve on failure) would be the least invasive and cover the main use case.
Environment
0.9.5