Added option res-nsaddr

This commit is contained in:
Gerhard Rieger 2023-10-01 22:16:20 +02:00
parent 282db9feda
commit 254958a34d
12 changed files with 234 additions and 12 deletions

View file

@ -140,6 +140,9 @@ Features:
number of times to retransmit.
Disable them and the old res-* opts with: ./configure --disable-resolve
Added option res-nsaddr that overrides /etc/resolv.conf nameserver
address based on an undocumented resolver feature.
Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than
0, its last sent data might have been lost depending on timing of read/

View file

@ -1 +1 @@
"1.7.4.5+"
"1.7.4.5+20230721"

View file

@ -680,6 +680,7 @@
#undef HAVE_RES_RETRANS
#undef HAVE_RES_RETRY
#undef HAVE_RES_NSADDR_LIST
#undef HAVE_SETGRENT
#undef HAVE_GETGRENT

View file

@ -2156,6 +2156,12 @@ AC_TRY_COMPILE([#include <resolv.h>],
[AC_MSG_RESULT(yes);
AC_DEFINE(HAVE_RES_RETRY, 1)],
[AC_MSG_RESULT(no)])
AC_MSG_CHECKING(for _res.nsaddr_list)
AC_TRY_COMPILE([#include <resolv.h>],
[_res.nsaddr_list[0].sin_family == 0],
[AC_MSG_RESULT(yes);
AC_DEFINE(HAVE_RES_NSADDR_LIST, 1)],
[AC_MSG_RESULT(no)])
dnl "tcpd" "tcpwrappers"

View file

@ -2397,23 +2397,39 @@ label(OPTOIN_IP_TRANSPARENT)
dit(bf(tt(ip-transparent)))
Sets the IP_TRANSPARENT socket option.
This option might require root privilege.
enddit()
startdit()enddit()nl()
label(OPTIONS_RESOLVER)em(bf(Resolver options))
These options temporarily change the behaviour of hostname resolution. The
options of form code(ai-*) affect behaviour of the code(getaddrinfo()) function that
includes code(/etc/hosts) and NIS based lookups.
The addresses of form code(res-*) only affect DNS lookups, and only when the
result is not cached in code(nscd). These options might not work on all
operating systems or libc implementations.
startdit()
label(OPTION_AI_ADDRCONFIG)
dit(bf(tt(ai-addrconfig[=0|1]))), dit(bf(tt(addrconfig[=0|1])))
dit(bf(tt(ai-addrconfig[=0|1]))) dit(bf(tt(addrconfig[=0|1])))
Sets or unsets the AI_ADDRCONFIG flag to prevent name resolution to address
families that are not available on the computer (e.g. IPv6). Default value
is 1 in case the resolver does not get an address family hint from Socat
address or defaults.
label(OPTION_AI_PASSIVE)
dit(bf(tt(ai-passive[=0|1]))), dit(bf(tt(passive[=0|1])))
Sets of unsets the AI_ADDRCONFIG flag for code(getaddrinfo()) calls.
dit(bf(tt(ai-passive[=0|1]))) dit(bf(tt(passive[=0|1])))
Sets of unsets the AI_PASSIVE flag for code(getaddrinfo()) calls.
Default is 1 for LISTEN, RECV, and RECVFROM type addresses, and with
link(bind)(OPTION_BIND) option.
label(OPTION_AI_V4MAPPED)
dit(bf(tt(ai-v4mapped[=0|1]))), dit(bf(tt(v4mapped[=0|1])))
dit(bf(tt(ai-v4mapped[=0|1]))) dit(bf(tt(v4mapped[=0|1])))
Sets or unsets the AI_V4MAPPED flag for code(getaddrinfo()). With socat()
addresses requiring IPv6 addresses, this resolves IPv4 addresses to the
approriate IPv6 address [::ffff:*:*]. For IPv6 Socat addresses, the default
is 1.
label(OPTION_RES_DEBUG)dit(bf(tt(res-debug)))
label(OPTION_RES_AAONLY)dit(bf(tt(res-aaonly)))
label(OPTION_RES_USEVC)dit(bf(tt(res-usevc)))
@ -2437,6 +2453,12 @@ label(OPTION_RES_RETRANS)dit(bf(tt(res-retrans=<int>)))
label(OPTION_RES_RETRY)dit(bf(tt(res-retry=<int>)))
Sets the number of retransmits of the DNS resolver (based on an undocumented
feature).
label(OPTION_RES_NSADDR)dit(bf(tt(res-nsaddr=<ipaddr>[:<port>])))
Tries to overwrite nameserver settings loaded from /etc/resolv.conf by
writing the given IPv4 address into the undocumented
code(_res:nsaddr_list[0]) field.
code(/etc/hosts) is still checked by resolver. Please note that glibc's
code(nscd) is always queried first when it is running!
enddit()
startdit()enddit()nl()
@ -2707,6 +2729,7 @@ label(OPTION_ACCEPT_TIMEOUT)dit(bf(tt(accept-timeout=<seconds>)))
End waiting for a connection after <seconds> [link(timeval)(TYPE_TIMEVAL)]
with error status.
enddit()
startdit()enddit()nl()
@ -2826,6 +2849,7 @@ label(GROUP_SHELL)
Options for link(address SHELL)(ADDRESS_SHELL)
startdit()
label(OPTION_SHELL)dit(bf(tt(shell=<filename>)))
Overwrites use the default shell with the named executable, e.g.
tt(/bin/dash). Also sets the tt(SHELL) environment variable.
@ -3217,6 +3241,7 @@ measures are required to get the VLAN information, see NOEXPAND(packet(7)) PACKE
and to optionally insert the tag into the packet again, use option
link(retrieve-vlan)(OPTION_RETRIEVE_VLAN) when you need this.
startdit()
label(OPTION_RETRIEVE_VLAN)dit(bf(tt(retrieve-vlan)))
On packets incoming on raw sockets, retrieve the VLAN information and insert
it into the packets for further processing (Linux)

75
test.sh
View file

@ -17287,6 +17287,80 @@ esac
N=$((N+1))
# Test the res-nsaddr (resolver, dns) option
NAME=RES_NSADDR
case "$TESTS" in
*%$N%*|*%functions%*|*%resolv%*|*%ip4%*|*%tcp4%*|*%socket%*|*%$NAME%*)
TEST="$NAME: test the res-nsaddr option"
# Start a supplementary Socat instance that will receive the DNS query.
# Run main Socat process, opening an IPv4 socket with option res-nsaddr
# directed to the aux process.
# When the supplementary Socat instance received the query the test succeeded.
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO IP4 UDP TCP); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO TCP4 UDP-RECVFROM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions res-nsaddr) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}IPv4 not available on host${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="$(echo test$N $(date) $RANDOM |tr ' :' '-')"
echo "$da" >"$td/test$N.da"
newport udp4 # or whatever proto, or drop this line
CMD0="$TRACE $SOCAT $opts -u UDP-RECVFROM:$PORT -"
CMD1="$TRACE $SOCAT $opts - TCP4:$da:0,res-nsaddr=$LOCALHOST:$PORT"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" >"${tf}0" &
pid0=$!
waitudp4port $PORT 1
$CMD1 >"${tf}1" 2>"${te}1"
rc1=$?
kill $pid0 2>/dev/null; wait
if grep "$da" "${tf}0" >/dev/null; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numOK=$((numOK+1))
elif pgrep -u root nscd >/dev/null 2>&1; then
$PRINTF "${YELLOW}FAILED (due to nscd?)${NORMAL}\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
namesCAnT="$namesCANT $NAME"
else
$PRINTF "$FAILED (query not received)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests
##################################################################################
@ -17502,6 +17576,7 @@ elif [ ??? ]; then
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
namesCANT="$namesCANT $NAME"
else
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi

View file

@ -116,6 +116,9 @@ const struct optdesc opt_res_retrans = { "res-retrans", "retrans", OPT_RES_RE
#if HAVE_RES_RETRY
const struct optdesc opt_res_retry = { "res-retry", NULL, OPT_RES_RETRY, GROUP_SOCK_IP, PH_OFFSET, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.retry), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY };
#endif
#if HAVE_RES_NSADDR_LIST
const struct optdesc opt_res_nsaddr = { "res-nsaddr", "dns", OPT_RES_NSADDR, GROUP_SOCK_IP, PH_OFFSET, TYPE_IP4SOCK, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.nsaddr), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY };
#endif
#endif /* HAVE_RESOLV_H */
#endif /* WITH_RESOLVE */
#endif /* WITH_IP4 || WITH_IP6 */
@ -139,7 +142,9 @@ int xioinit_ip(
return 0;
}
#if HAVE_RESOLV_H
int Res_init(void) {
int result;
Debug("res_init()");
@ -147,13 +152,9 @@ int Res_init(void) {
Debug1("res_init() -> %d", result);
return result;
}
#endif /* HAVE_RESOLV_H */
#if HAVE_RESOLV_H
unsigned long res_opts() {
return _res.options;
}
#endif /* HAVE_RESOLV_H */
/* the ultimate(?) socat resolver function
node: the address to be resolved; supported forms:
@ -1015,7 +1016,18 @@ int xio_res_init(
struct __res_state *save_res)
{
if (sfd->para.socket.ip.res.opts[0] ||
sfd->para.socket.ip.res.opts[1]) {
sfd->para.socket.ip.res.opts[1] ||
#if HAVE_RES_RETRANS
sfd->para.socket.ip.res.retrans >= 0 ||
#endif
#if HAVE_RES_RETRY
sfd->para.socket.ip.res.retry >= 0 ||
#endif
#if HAVE_RES_NSADDR_LIST
sfd->para.socket.ip.res.nsaddr.sin_family != PF_UNSPEC ||
#endif
0 /* for canonical reasons */
) {
if (!(_res.options & RES_INIT)) {
if (Res_init() < 0) {
Error("res_init() failed");
@ -1027,8 +1039,44 @@ int xio_res_init(
_res.options &= ~sfd->para.socket.ip.res.opts[1];
Debug2("changed _res.options from 0x%lx to 0x%lx",
save_res->options, _res.options);
#if HAVE_RES_RETRANS
if (sfd->para.socket.ip.res.retrans >= 0) {
_res.retrans = sfd->para.socket.ip.res.retrans;
Debug2("changed _res.retrans from 0x%x to 0x%x",
save_res->retrans, _res.retrans);
}
#endif
#if HAVE_RES_RETRY
if (sfd->para.socket.ip.res.retry >= 0) {
_res.retry = sfd->para.socket.ip.res.retry;
Debug2("changed _res.retry from 0x%x to 0x%x",
save_res->retry, _res.retry);
}
#endif
#if HAVE_RES_NSADDR_LIST
if (sfd->para.socket.ip.res.nsaddr.sin_family == PF_INET) {
_res.nscount = 1;
_res.nsaddr_list[0] = sfd->para.socket.ip.res.nsaddr;
if (_res.nsaddr_list[0].sin_port == htons(0))
_res.nsaddr_list[0].sin_port = htons(53);
Debug10("changed _res.nsaddr_list[0] from %u.%u.%u.%u:%u to %u.%u.%u.%u:%u",
((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[0],
((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[1],
((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[2],
((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[3],
ntohs(save_res->nsaddr_list[0].sin_port),
((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[0],
((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[1],
((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[2],
((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[3],
ntohs(_res.nsaddr_list[0].sin_port));
}
#endif /* HAVE_RES_NSADDR_LIST */
return 1;
}
return 0;
}

View file

@ -44,6 +44,7 @@ extern const struct optdesc opt_res_stayopen;
extern const struct optdesc opt_res_dnsrch;
extern const struct optdesc opt_res_retrans;
extern const struct optdesc opt_res_retry;
extern const struct optdesc opt_res_nsaddr;
extern int xioinit_ip(int *pf, char ipv);

5
xio.h
View file

@ -143,6 +143,10 @@ struct para_ip_res {
#if HAVE_RES_RETRY
int retry;
#endif
#if HAVE_RES_NSADDR_LIST
# undef nsaddr /* resolv.h might define this, breaks debugger */
struct sockaddr_in nsaddr;
#endif
} ;
#endif /* WITH_RESOLVE && HAVE_RESOLV_H */
@ -423,6 +427,7 @@ union integral {
#endif /* HAVE_STRUCT_TIMESPEC */
#if WITH_IP4
struct in_addr u_ip4addr;
struct sockaddr_in u_ip4sock;
#endif
} ;

View file

@ -22,7 +22,7 @@ static const char *optiontypenames[] = {
"OFF64_T", "INT:INT", "INT:INTP", "INT:BIN",
"INT:STRING", "INT:INT:INT", "INT:INT:BIN", "INT:INT:STRING",
"INT:INT:GENERIC",
"IP4NAME",
"IP4NAME", "IP4SOCK",
#if HAVE_STRUCT_LINGER
"STRUCT-LINGER",
#endif

View file

@ -430,6 +430,9 @@ const struct optname optionnames[] = {
#ifdef VDISCARD
IF_TERMIOS("discard", &opt_vdiscard)
#endif
#if HAVE_RES_NSADDR_LIST
IF_IP ("dns", &opt_res_nsaddr)
#endif
#if HAVE_RESOLV_H
IF_RESOLVE("dnsrch", &opt_res_dnsrch)
#endif /* HAVE_RESOLV_H */
@ -1020,6 +1023,9 @@ const struct optname optionnames[] = {
IF_IP ("multicast-ttl", &opt_ip_multicast_ttl)
IF_IP ("multicastloop", &opt_ip_multicast_loop)
IF_IP ("multicastttl", &opt_ip_multicast_ttl)
#if HAVE_RES_NSADDR_LIST
IF_IP ("nameserver", &opt_res_nsaddr)
#endif
#if defined(O_NDELAY) && (!defined(O_NONBLOCK) || O_NDELAY != O_NONBLOCK)
IF_ANY ("ndelay", &opt_o_ndelay)
#else
@ -1085,6 +1091,9 @@ const struct optname optionnames[] = {
IF_OPENSSL("nosni", &opt_openssl_no_sni)
#endif
IF_INTERFACE("notrailers", &opt_iff_notrailers)
#if HAVE_RES_NSADDR_LIST
IF_IP ("nsaddr", &opt_res_nsaddr)
#endif
#ifdef O_NSHARE
IF_OPEN ("nshare", &opt_o_nshare)
#endif
@ -1426,6 +1435,9 @@ const struct optname optionnames[] = {
# if HAVE_RES_RETRY
IF_RESOLVE("res-maxretry", &opt_res_retry)
# endif
# if HAVE_RES_NSADDR_LIST
IF_IP ("res-nsaddr", &opt_res_nsaddr)
# endif
# if WITH_RES_PRIMARY
IF_RESOLVE("res-primary", &opt_res_primary)
# endif
@ -2630,6 +2642,7 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts,
#if WITH_IP4
case TYPE_IP4NAME:
{
/*! On a good day merge this with code in retropt_bind() */
struct sockaddr_in sa; socklen_t salen = sizeof(sa);
const char *ends[] = { NULL };
const char *nests[] = { "[","]", NULL };
@ -2659,6 +2672,40 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts,
opt->value.u_ip4addr = sa.sin_addr;
}
break;
case TYPE_IP4SOCK:
{
/*! On a good day merge this with code for TYPE_IP4NAME */
struct sockaddr_in sa; socklen_t salen = sizeof(sa);
const char portsep[] = ":";
const char *ends[] = { portsep, NULL };
const char *nests[] = { "[","]", NULL };
char hostname[512], *hostp = hostname, *portp = NULL;
size_t hostlen = sizeof(hostname)-1;
tokp = token;
parsres =
nestlex((const char **)&tokp, &hostp, &hostlen,
ends, NULL, NULL, nests,
true, false, false);
if (parsres < 0) {
Error1("option too long: \"%s\"", *a);
return -1;
} else if (parsres > 0) {
Error1("syntax error in \"%s\"", *a);
return -1;
}
*hostp++ = '\0';
if (!strncmp(tokp, portsep, strlen(portsep))) {
portp = tokp + strlen(portsep);
}
if (xioresolve(hostname, portp, AF_INET, SOCK_DGRAM, IPPROTO_IP,
(union sockaddr_union *)&sa, &salen, 0)
!= STAT_OK) {
opt->desc = ODESC_ERROR; continue;
}
opt->value.u_ip4sock = sa;
}
break;
#endif /* defined(WITH_IP4) */
#if LATER
@ -4132,6 +4179,15 @@ static int applyopt_offset(struct single *sfd, struct opt *opt) {
case TYPE_CONST:
*(int *)ptr = opt->desc->minor;
break;
case TYPE_IP4NAME:
memset(ptr, 0, sizeof(struct sockaddr_in));
((struct sockaddr_in *)ptr)->sin_addr = opt->value.u_ip4addr;
((struct sockaddr_in *)ptr)->sin_family = PF_INET;
break;
case TYPE_IP4SOCK:
memset(ptr, 0, sizeof(struct sockaddr_in));
*(struct sockaddr_in *)ptr = opt->value.u_ip4sock;
break;
default:
Error2("applyopt_offset(opt:%s): type %s not implemented",
opt->desc->defname, xiohelp_opttypename(opt->desc->type));

View file

@ -68,6 +68,7 @@ enum e_types {
TYPE_INT_INT_GENERIC, /* 3 params: first and second are int, 3rd is specified by value (dalan syntax) */
TYPE_IP4NAME, /* IPv4 hostname or address */
TYPE_IP4SOCK, /* IPv4 hostname or address optionally with port */
#if HAVE_STRUCT_LINGER
TYPE_LINGER, /* struct linger */
#endif /* HAVE_STRUCT_LINGER */
@ -628,6 +629,7 @@ enum e_optcode {
OPT_RES_DEFNAMES, /* resolver(3) */
OPT_RES_DNSRCH, /* resolver(3) */
OPT_RES_IGNTC, /* resolver(3) */
OPT_RES_NSADDR, /* undocumented */
OPT_RES_PRIMARY, /* resolver(3) */
OPT_RES_RECURSE, /* resolver(3) */
OPT_RES_RETRANS, /* undocumented */