From 994526dbc4a6b836e5cdc517f524c10cef56e1b4 Mon Sep 17 00:00:00 2001
From: Vladimir Dubrovin <3proxy@3proxy.ru>
Date: Tue, 20 Oct 2026 19:02:37 +0300
Subject: [PATCH] resolver: sysresolve, names resolved by the system routines
The command keeps the C library's resolution which a configuration
without nserver had before the system's name servers became the
default: no hosts file imported. nserver lines, if any, are still
there for dnspr to forward to. Of sysresolve and fakeresolve the last
given applies.
Tests: sysdns, with sysresolve dnspr has no hosts file names and a
name is resolved through the system routines.
Co-Authored-By: Claude Opus 5.5
sysresolve
+
+Names are resolved by the system routines for name
+resolution (the C library), as without nserver before
+the system's name servers became the default; the hosts file
+is not imported. nserver lines, if any, are still used
+by dnspr to forward queries. Of sysresolve and
+fakeresolve the last one given applies.
nserver
<ipaddr>[:port][/tcp]
Nameserver to use for name resolutions; up to 3, asked in
@@ -507,7 +516,8 @@ has to be enclosed in square brackets:
nserver 1.1.1.1
nserver [2001:4860:4860::8844]
nserver [2001:4860:4860::8844]:5353/tcp
-If none is specified (and there is no fakeresolve),
+If none is specified (and there is no fakeresolve or
+sysresolve),
the system's name servers are used: up to 3 from
/etc/resolv.conf, on Windows from the registry (those
set for the system, those set for an interface, then those
diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5
index b03c629..7c7bd94 100644
--- a/man/3proxy.cfg.5
+++ b/man/3proxy.cfg.5
@@ -555,7 +555,7 @@ An IPv6 address has to be enclosed in square brackets:
.br
\fBnserver [2001:4860:4860::8844]:5353/tcp\fR
.br
- If none is specified (and there is no \fBfakeresolve\fR), the system's name
+ If none is specified (and there is no \fBfakeresolve\fR or \fBsysresolve\fR), the system's name
servers are used: up to 3 from \fI/etc/resolv.conf\fR, on Windows from the
registry (those set for the system, those set for an interface, then those
DHCP gave). Names in the hosts file (\fI/etc/hosts\fR, on Windows
@@ -600,6 +600,15 @@ are never consulted. A static record takes precedence over the hosts file. If 0.
is used as a hostaddr host will never resolve, it can be used to
blacklist something.
+.br
+.B sysresolve
+.br
+ Names are resolved by the system routines for name resolution (the C
+library), as without \fBnserver\fR before the system's name servers became
+the default; the hosts file is not imported. \fBnserver\fR lines, if any, are
+still used by \fBdnspr\fR to forward queries. Of \fBsysresolve\fR and
+\fBfakeresolve\fR the last one given applies.
+
.br
.B fakeresolve
.br
diff --git a/src/conf.c b/src/conf.c
index 7e08461..a235fdd 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -741,6 +741,8 @@ static int h_flushusers(int argc, unsigned char **argv){
force (the process has one list) once it is committed */
static struct nserver cfgns[MAXNSERVERS];
static int cfgnsn = 0;
+/* sysresolve: names resolved by the system routines */
+static int cfgsysresolve = 0;
static int h_nserver(int argc, unsigned char **argv){
char *str;
@@ -764,13 +766,13 @@ static int h_nserver(int argc, unsigned char **argv){
/* The configuration's name servers, or the system's in their place */
void confdnscommit(struct extparam *c){
- sysdnsapply(c, cfgns, cfgnsn);
+ sysdnsapply(c, cfgns, cfgnsn, cfgsysresolve);
}
/* Whether names would be resolved slowly: by the C library, or by name
servers, the system's taken by default, without a cache */
static int noresolver(void){
- if(!conf->resolvfunc && !sysdnscount()) return 1;
+ if(cfgsysresolve || (!conf->resolvfunc && !sysdnscount())) return 1;
return (!conf->resolvfunc || conf->resolvfunc == myresolver) && !dns_table.poolsize;
}
@@ -790,6 +792,12 @@ static int h_authnserver(int argc, unsigned char **argv){
static int h_fakeresolve(int argc, unsigned char **argv){
conf->resolvfunc = fakeresolver;
+ cfgsysresolve = 0;
+ return 0;
+}
+
+static int h_sysresolve(int argc, unsigned char **argv){
+ cfgsysresolve = 1;
return 0;
}
@@ -2146,6 +2154,7 @@ struct commands commandhandlers[]={
{NULL, "flush", h_flush, 1, 1},
{NULL, "nserver", h_nserver, 2, 2},
{NULL, "fakeresolve", h_fakeresolve, 1, 1},
+ {NULL, "sysresolve", h_sysresolve, 1, 1},
{NULL, "nscache", h_nscache, 2, 2},
{NULL, "nscache6", h_nscache6, 2, 2},
{NULL, "nsrecord", h_nsrecord, 3, 3},
@@ -2534,6 +2543,7 @@ void confbegin(void){
char * args[] = {"auth", "iponly", NULL};
cfgnsn = 0;
+ cfgsysresolve = 0;
h_auth(2, (unsigned char **)args);
}
diff --git a/src/proxy.h b/src/proxy.h
index 972ce1f..cf90e37 100644
--- a/src/proxy.h
+++ b/src/proxy.h
@@ -435,7 +435,7 @@ int nscacheresolv(int af, unsigned char *name, unsigned char *value, uint32_t *t
void setnservers(const struct nserver *ns, int n);
void sysdnsinit(void);
int sysdnscount(void);
-void sysdnsapply(struct extparam *c, const struct nserver *ns, int n);
+void sysdnsapply(struct extparam *c, const struct nserver *ns, int n, int sysresolve);
void sysdnscheck(void);
void confdnscommit(struct extparam *c);
uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_t *retttl, struct clientparam* param, int makeauth);
diff --git a/src/resolve.c b/src/resolve.c
index 0be7c1d..7989ca3 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -585,9 +585,16 @@ int sysdnscount(void){
/* A configuration committed: the name servers of its nserver lines, else,
with nothing else to resolve names, the system's and its hosts file, read
- again. Until then those in force stay. */
-void sysdnsapply(struct extparam *c, const struct nserver *ns, int n){
+ again. Until then those in force stay. With sysresolve the system
+ routines resolve names, nserver lines left to dnspr. */
+void sysdnsapply(struct extparam *c, const struct nserver *ns, int n, int sysresolve){
sysdns.active = 0;
+ if(sysresolve){
+ c->resolvfunc = NULL;
+ setnservers(ns, n);
+ hostsremove();
+ return;
+ }
if(n){
setnservers(ns, n);
hostsremove();
diff --git a/tests/cases/sysdns.py b/tests/cases/sysdns.py
index e4b949b..38e156f 100644
--- a/tests/cases/sysdns.py
+++ b/tests/cases/sysdns.py
@@ -1,6 +1,6 @@
"""The system's name servers and hosts file as the default resolver: with
no nserver in a configuration, names in the hosts file are answered from
-it, through the name cache; with nserver lines, the file is not used."""
+it; with nserver lines, or sysresolve (the system routines), it is not."""
import os
import re
@@ -81,3 +81,26 @@ def run(t):
wait(t, other, "localhost")
t.ne(["127.0.0.1"], t.dns_query(other, "localhost") or [],
"with nserver, the hosts file is not used")
+
+ # sysresolve: the system routines resolve names, and nothing is imported
+ # for dnspr to answer from
+ sysport, proxy, srv = t.free_port(), t.free_port(), t.free_port()
+ t.start("sysdns_sysresolve", f"""
+ log
+ flush
+ sysresolve
+ auth iponly
+ allow *
+ dnspr -p{sysport}
+ proxy -p{proxy}
+ flush
+ auth iponly
+ allow *
+ http echo * /echo**
+ httpsrv -p{srv}
+ """, ports=[proxy, srv])
+ wait(t, sysport, "localhost")
+ t.ne(["127.0.0.1"], t.dns_query(sysport, "localhost") or [],
+ "with sysresolve, dnspr has no hosts file names")
+ t.eq(200, t.http(f"http://localhost:{srv}/echo", proxy=f"127.0.0.1:{proxy}").status,
+ "and names resolve through the system routines")