Skip to content

parent hostname is resolved once at config parse and never refreshed (stale DNS) #1208

@imclint21

Description

@imclint21

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

  1. Configure a parent with a hostname that points to a dynamic IP:
    parent 1000 socks5 myhost.ddns.net 5219 user pass
    auto -a -p8080
    
  2. Start 3proxy — it resolves myhost.ddns.net to e.g. 1.2.3.4
  3. Change the DNS record for myhost.ddns.net to point to 5.6.7.8
  4. All new client connections now fail with error 00013, targeting 1.2.3.4 (dead IP)
  5. 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:

  1. On connect failure: if connecting to chains->addr fails with ECONNREFUSED/ETIMEDOUT, re-resolve chains->exthost via getip46() and update chains->addr before retrying
  2. TTL-based: honor the DNS TTL from the response and re-resolve when expired
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions