Re: [Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-10-15 Thread Dominik Derigs
Hey Simon,

when setting up a new docker server this morning I desperately
missed the specify-server-by-host feature in dnsmasq. I revived
my initial patch and added what was requested: I removed the
compile-time option and changed the code to always use all
suitable addresses (IPv6 is not suitable when source is IPv4 and
vice versa).

Along the way, I found a small bug: IPv6 source addresses are
incorrectly interpreted as interface names for IPv4 server
addresses (and vice versa). This is fixed in my patch:

BEFORE:
--server=1.2.3.4@fe80::
dnsmasq: using nameserver 1.2.3.4#53(via fe80::)

NOW:
--server=1.2.3.4@fe80::
dnsmasq: bad command line options: cannot use IPv4 server address
with IPv6 source address



Examples:

--server=localhost
dnsmasq: using nameserver 127.0.0.1#53


--server=a.test.dl6er.de@fe80::
dnsmasq: warning: no upstream servers configured


--server=both.test.dl6er.de@fe80::
dnsmasq: using nameserver dead:beef::#53


--server=both.test.dl6er.de@10.0.0.1
dnsmasq: using nameserver 1.2.3.4#53


--server=both.test.dl6er.de
dnsmasq: using nameserver dead:beef::#53
dnsmasq: using nameserver 1.2.3.4#53


--server=amazon.com
dnsmasq: using nameserver 54.239.28.85#53
dnsmasq: using nameserver 52.94.236.248#53
dnsmasq: using nameserver 205.251.242.103#53


--server=does-not-exist-4615465468435.com
dnsmasq: bad command line options: Name or service not known
  (dnsmasq refuses to start)


What do you think?

I'm undecided if dnsmasq should fail hard on the second example
(no IPv6 address for a hostname but IPv6 source address given)
but this may be a real edge-case where we can expect users to
understand what they're doing and read the logs. Catching this
would require extra logic (valid hostnames are returned but none
is used -> error out).

Best,
Dominik

On Thu, 2022-04-07 at 12:24 +0100, Simon Kelley wrote:
> This seems like a sensible idea, but it does need a clear
> warning in the 
> documentation that it will only work if the dnsmasq instance
> being 
> configured is not the one providing DNS to the local system.
> 
> Two comments about the patch.
> 
> 1) Geert's point is a good one: This patch uses only libc: it
> doesn't 
> add any build dependencies and it's small. There's no reason to
> make it 
> a compile-time option.
> 
> 2) Not handling multiple addresses from getaddrinfo() feels
> like a 
> mistake. What should happen in that case is obvious and the
> obvious 
> behaviour is useful. If it's not done now, we'll end up doing
> it later 
> when someone falls foul of this short-cut. the implementation
> is more 
> complex, but I think returning the struct addrinfo * linked
> list from 
> getaddrinfo instead of a single address should work: the caller
> of 
> parse_server becomes responsible for freeing the struct
> addrinfos
> 
> 3) One error that needs to be handled is if a source address is
> specified, and the address family of the source address doesn't
> match 
> the address family of an address returned  from getaddrinfo. My
> initial 
> thought was to make this a fatal error, but that has the
> problem that
> 
> server=dns.example.com@192.168.7.1
> 
> will work fine until an  record is added for
> dns.example.com, when 
> dnsmasq would no longer start. Better I thing to only use DNS
> records 
> that match the source address type if it's specified.
> 
> 
> Cheers,
> 
> Simon.
> 
> 
> 
> 
> 
> On 02/04/2022 20:40, Dominik Derigs wrote:
> > Dear Simon,
> > 
> > In docker swarm and compose configurations, other containers
> > are
> > only reachable via hostnames. It is not always possible to
> > assign
> > IP addresses beforehand. Hence, the upstream server IP is not
> > known at dnsmasq start when the upstream is part of the
> > deployed
> > configuration, e.g., a local cloudflared or unbound
> > container.
> > 
> > So far, getting dnsmasq to run in such a case requires hacks
> > that
> > somehow try to determine the IP address before starting
> > dnsmasq.
> > An example for such a hack (not invented by me):
> > https://github.com/tschaffter/docker-dnsmasq/blob/54b5d5d551746b6f1708fbf4a705e2de66c2eaee/docker-entrypoint.sh#L14-L23
> > 
> > This patch implements name resolution functionality for
> > server=... by querying the system resolver for a hostname. It
> > is
> > only used when a user supplied something that is not a valid
> > IP
> > address (dnsmasq currently fails hard in this case so this
> > isn't
> > a breaking change) and can be omitted by a compile time flag
> > (I
> > think it's worthwhile to have it).
> > 
> > I know my proposal does sound somewhat strange (resolving a
> > DNS
> > server name) but this is something that is somewhat
> > frequently
> > needed and currently only possible through external hacks.

From d1565206d541a0a6eeb4bebd69be5aaec9cca4fb Mon Sep 17 00:00:00 2001
From: Dominik Derigs 
Date: Sat, 15 Oct 2022 12:26:35 +0200
Subject: [PATCH] Extend server to accept hostnames for upstream resolver

Signed-off-by: DL6ER 
---
 man/dnsmasq.8 |   8 +-
 

Re: [Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-04-08 Thread Geert Stappers via Dnsmasq-discuss
On Thu, Apr 07, 2022 at 05:27:31PM +0200, Geert Stappers via Dnsmasq-discuss 
wrote:
> On Thu, Apr 07, 2022 at 12:24:15PM +0100, Simon Kelley wrote:
> > This seems like a sensible idea, but it does need a clear warning in the
> > documentation that it will only work if the dnsmasq instance being
> > configured is not the one providing DNS to the local system.
> 
> And the idea did trigger further idea.
> 
> Manual page has
>   -S, --local, 
> --server=[/[]/[domain/]][[#]][@][@[#]]
> 
> 
> ( I think the mailing archive has a request for 
>   -S, --local, 
> --server=[/[]/[domain/]][#]][@][@[#]
> so that  is mandatory. )

Found 
https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2020q4/014440.html
then deciced to gonna make a fresh patch.

 
> Making that
>   -S, --local, 
> --server=[/[]/[domain/]][#]][@][@[#]
> where  can be an IP-address or servername.
> When it is a servername is nameresolving started for servername.
> Succesfull name resolving allow dnsmasq to do its task, failed name
> resolving yields a fatal error.
> 
> The "name resolving"  is gethostbyname function or other function that
> works for the (container) environment that started this request / idea.
> I imagine that gethostbyname()  name resolving also reads /etc/hosts,
> so more "environment" can benefit from this new feature.


Groeten
Geert Stappers
-- 
Silence is hard to parse

___
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss


Re: [Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-04-07 Thread Geert Stappers via Dnsmasq-discuss
On Thu, Apr 07, 2022 at 12:24:15PM +0100, Simon Kelley wrote:
> This seems like a sensible idea, but it does need a clear warning in the
> documentation that it will only work if the dnsmasq instance being
> configured is not the one providing DNS to the local system.

And the idea did trigger further idea.

Manual page has
  -S, --local, 
--server=[/[]/[domain/]][[#]][@][@[#]]


( I think the mailing archive has a request for 
  -S, --local, 
--server=[/[]/[domain/]][#]][@][@[#]
so that  is mandatory. )

Making that
  -S, --local, 
--server=[/[]/[domain/]][#]][@][@[#]
where  can be an IP-address or servername.
When it is a servername is nameresolving started for servername.
Succesfull name resolving allow dnsmasq to do its task, failed name
resolving yields a fatal error.

The "name resolving"  is gethostbyname function or other function that
works for the (container) environment that started this request / idea.
I imagine that gethostbyname()  name resolving also reads /etc/hosts,
so more "environment" can benefit from this new feature.



Groeten
Geert Stappers
-- 
Silence is hard to parse

___
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss


Re: [Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-04-07 Thread Simon Kelley
This seems like a sensible idea, but it does need a clear warning in the 
documentation that it will only work if the dnsmasq instance being 
configured is not the one providing DNS to the local system.


Two comments about the patch.

1) Geert's point is a good one: This patch uses only libc: it doesn't 
add any build dependencies and it's small. There's no reason to make it 
a compile-time option.


2) Not handling multiple addresses from getaddrinfo() feels like a 
mistake. What should happen in that case is obvious and the obvious 
behaviour is useful. If it's not done now, we'll end up doing it later 
when someone falls foul of this short-cut. the implementation is more 
complex, but I think returning the struct addrinfo * linked list from 
getaddrinfo instead of a single address should work: the caller of 
parse_server becomes responsible for freeing the struct addrinfos


3) One error that needs to be handled is if a source address is 
specified, and the address family of the source address doesn't match 
the address family of an address returned  from getaddrinfo. My initial 
thought was to make this a fatal error, but that has the problem that


server=dns.example.com@192.168.7.1

will work fine until an  record is added for dns.example.com, when 
dnsmasq would no longer start. Better I thing to only use DNS records 
that match the source address type if it's specified.



Cheers,

Simon.





On 02/04/2022 20:40, Dominik Derigs wrote:

Dear Simon,

In docker swarm and compose configurations, other containers are
only reachable via hostnames. It is not always possible to assign
IP addresses beforehand. Hence, the upstream server IP is not
known at dnsmasq start when the upstream is part of the deployed
configuration, e.g., a local cloudflared or unbound container.

So far, getting dnsmasq to run in such a case requires hacks that
somehow try to determine the IP address before starting dnsmasq.
An example for such a hack (not invented by me):
https://github.com/tschaffter/docker-dnsmasq/blob/54b5d5d551746b6f1708fbf4a705e2de66c2eaee/docker-entrypoint.sh#L14-L23

This patch implements name resolution functionality for
server=... by querying the system resolver for a hostname. It is
only used when a user supplied something that is not a valid IP
address (dnsmasq currently fails hard in this case so this isn't
a breaking change) and can be omitted by a compile time flag (I
think it's worthwhile to have it).

I know my proposal does sound somewhat strange (resolving a DNS
server name) but this is something that is somewhat frequently
needed and currently only possible through external hacks.


___
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss


Re: [Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-04-04 Thread Geert Stappers via Dnsmasq-discuss
On Sat, Apr 02, 2022 at 09:40:15PM +0200, Dominik Derigs wrote:
> Dear Simon,

Dear Mailinglist audience,
(both the direct readers and the archive vistors)
 
> In docker swarm and compose configurations, other containers are
> only reachable via hostnames. It is not always possible to assign
> IP addresses beforehand. Hence, the upstream server IP is not
> known at dnsmasq start when the upstream is part of the deployed
> configuration, e.g., a local cloudflared or unbound container.
>   ...
> I know my proposal does sound somewhat strange (resolving a DNS
> server name)

I appriciate there is a patch.  ( Lets move beyond "strange" )


   ...
> --- a/src/option.c
> +++ b/src/option.c
> @@ -19,6 +19,10 @@
>  #include "dnsmasq.h"
>  #include 
>  
> +#ifdef HAVE_RESOLVESERVER
> +#include 
> +#endif
> +
>  static volatile int mem_recover = 0;
>  static jmp_buf mem_jmp;
>  static int one_file(char *file, int hard_opt);
> @@ -846,6 +850,11 @@ char *parse_server(char *arg, union mysockaddr *addr, 
> union mysockaddr *source_a
>char *interface_opt = NULL;
>int scope_index = 0;
>char *scope_id;
> +  int addr_type = 0;
> +#ifdef HAVE_RESOLVESERVER
> +  int ecode = 0;
> +  struct addrinfo *hostinfo, hints = { 0 };
> +#endif
>  

As long time direct reader of the mailinglist,
can I tell that the ML archive has entries
where dnsmasq projectleader Simon Kelley
says he is NOT happy with more '#ifdef's.


Groeten
Geert Stappers
-- 
Silence is hard to parse

___
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss


[Dnsmasq-discuss] Extend server to accept hostnames for upstream resolver

2022-04-02 Thread Dominik Derigs
Dear Simon,

In docker swarm and compose configurations, other containers are
only reachable via hostnames. It is not always possible to assign
IP addresses beforehand. Hence, the upstream server IP is not
known at dnsmasq start when the upstream is part of the deployed
configuration, e.g., a local cloudflared or unbound container.

So far, getting dnsmasq to run in such a case requires hacks that
somehow try to determine the IP address before starting dnsmasq.
An example for such a hack (not invented by me):
https://github.com/tschaffter/docker-dnsmasq/blob/54b5d5d551746b6f1708fbf4a705e2de66c2eaee/docker-entrypoint.sh#L14-L23

This patch implements name resolution functionality for
server=... by querying the system resolver for a hostname. It is
only used when a user supplied something that is not a valid IP
address (dnsmasq currently fails hard in this case so this isn't
a breaking change) and can be omitted by a compile time flag (I
think it's worthwhile to have it).

I know my proposal does sound somewhat strange (resolving a DNS
server name) but this is something that is somewhat frequently
needed and currently only possible through external hacks.
From 93f597e943283124af2e39620e748635cc6a04d6 Mon Sep 17 00:00:00 2001
From: Dominik Derigs 
Date: Thu, 3 Feb 2022 16:12:16 +0100
Subject: [PATCH] Extend server to accept hostnames for upstream resolver

Signed-off-by: DL6ER 
---
 man/dnsmasq.8 |  4 +++
 src/config.h  |  3 +++
 src/option.c  | 69 ++-
 3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 9af4ec8..87486a5 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -511,6 +511,10 @@ The query-port flag is ignored for any servers which have a
 source address specified but the port may be specified directly as
 part of the source address. Forcing queries to an interface is not
 implemented on all platforms supported by dnsmasq.
+
+If dnsmasq is compiled with HAVE_RESOLVESERVER, the upstream server may be specified with a hostname
+rather than an IP address. In this case, dnsmasq will try to use the system resolver to get the IP
+address of a server during startup. If name resolution fails, starting dnsmasq fails.
 .TP
 .B --rev-server=[/][,][#][@][@[#]]
 This is functionally the same as 
diff --git a/src/config.h b/src/config.h
index 227fb1f..20b9487 100644
--- a/src/config.h
+++ b/src/config.h
@@ -138,6 +138,9 @@ HAVE_LOOP
 HAVE_INOTIFY
use the Linux inotify facility to efficiently re-read configuration files.
 
+HAVE_RESOLVESERVER
+   lookup servers if specified via hostnames instead of IP addresses.
+
 NO_ID
Don't report *.bind CHAOS info to clients, forward such requests upstream instead.
 NO_TFTP
diff --git a/src/option.c b/src/option.c
index 5230eaf..bf1087a 100644
--- a/src/option.c
+++ b/src/option.c
@@ -19,6 +19,10 @@
 #include "dnsmasq.h"
 #include 
 
+#ifdef HAVE_RESOLVESERVER
+#include 
+#endif
+
 static volatile int mem_recover = 0;
 static jmp_buf mem_jmp;
 static int one_file(char *file, int hard_opt);
@@ -846,6 +850,11 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
   char *interface_opt = NULL;
   int scope_index = 0;
   char *scope_id;
+  int addr_type = 0;
+#ifdef HAVE_RESOLVESERVER
+  int ecode = 0;
+  struct addrinfo *hostinfo, hints = { 0 };
+#endif
 
   *interface = 0;
 
@@ -882,6 +891,64 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
   }
 
   if (inet_pton(AF_INET, arg, >in.sin_addr) > 0)
+  addr_type = AF_INET;
+  else if (inet_pton(AF_INET6, arg, >in6.sin6_addr) > 0)
+  addr_type = AF_INET6;
+#ifdef HAVE_RESOLVESERVER
+  /* if the argument is neither an IPv4 not an IPv6 address, it might be a
+ hostname and we should try to resolve it to a suitable address */
+  else
+{
+  memset(, 0, sizeof(hints));
+  /* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
+ the result only if the local system has at least one IPv4 address
+ configured, and IPv6 addresses are returned only if the local system
+ has at least one IPv6 address configured. The loopback address is not
+ considered for this case as valid as a configured address. This flag is
+ useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
+ does not return IPv6 socket addresses that would always fail in
+ subsequent connect() or bind() attempts. */
+  hints.ai_flags = AI_ADDRCONFIG;
+#if defined(HAVE_IDN) && defined(AI_IDN)
+  /* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
+ the node name given in node is converted to IDN format if necessary.
+ The source encoding is that of the current locale. */
+  hints.ai_flags |= AI_IDN;
+#endif
+  /* The value AF_UNSPEC indicates that getaddrinfo() should return socket
+ addresses for any address family (either IPv4 or IPv6, for