Opened 6 years ago
Closed 6 years ago
#1677 closed defect (wontfix)
nginx does not resolve hostnames if there is no route
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | nginx-core | Version: | 1.15.x |
Keywords: | dns | Cc: | |
uname -a: | Linux 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64 GNU/Linux | ||
nginx -V: | nginx version: nginx/1.12.2 |
Description
In my configuration is the following line:
proxy_pass http://localhost4:7500;
Problem is, that I am using it on a host without IPv4 connectivity.
This shouldn't b a problem since 127/8 can be accessed without a route over the lo interface.
But it is. nginx fails with the following message:
nginx: [emerg] host not found in upstream "localhost4" in
This should not be happening, since localhost4 can be resolved over /etc/hosts. So gethostbyname should work properly.
There is a working DNS resolution over IPv6.
A workaround would be something like that:
ip addr add 192.0.2.1/24 dev eth0 systemctl restart nginx ip addr del 192.0.2.1/24 dev eth0
Related:
https://trac.nginx.org/nginx/ticket/1040
https://trac.nginx.org/nginx/ticket/696
https://trac.nginx.org/nginx/ticket/1417
Change History (5)
comment:1 by , 6 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
comment:2 by , 6 years ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
it is likely to break configurations without IPv6 configured.
This is wrong. getaddrinfo dosen't return IPv6-addresses if they aren't reachable or even only reachable over tunneling. (At least on Linux and Windows.)
The comment to #ec8594b9bf11 is misleading:
On Linux, setting net.ipv6.conf.all.disable_ipv6 to 1 will now be respected.
This is right. But it was respected even before the patch.
He only difference is, when resolving a IPv6 only address on a IPv4 only system. Then NULL returns a IPv6-address while AI_ADDRCONFIG returns nothing. This is not the case on dualstack addresses. If there is at least one A-record no additional IPv6 addresses will be returned as long there is no route to them.
But in this case connect will fail anyway. IPv6 only address can never be accessed in a IPv4-only network. So there is no system that will work with AI_ADDRCONFIG but not without.
The only case when AI_ADDRCONFIG can be useful, is when you bind. (And even there I would highly prefer manage it in later.) For connect it's never a good idea.
It just breaks some systems while it will never do something useful.
comment:3 by , 6 years ago
Resolution: | → wontfix |
---|---|
Status: | reopened → closed |
While on some systems getaddrinfo() indeed does not return IPv6 addresses regardless of AI_ADDRCONFIG
(e.g., macOS seems to be doing this), at least Linux and FreeBSD without IPv6 on the host return all addresses when ai_flags
is 0, and only IPv4 addresses with ai_flags = AI_ADDRCONFIG
.
For example:
$ ./getaddrinfo 206.251.255.63 95.211.80.227 2001:1af8:4060:a004:21::e3 2606:7100:1:69::3f $ ./getaddrinfo addrconfig 206.251.255.63 95.211.80.227
Here is a simple test code you can play with to find it out yourself:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char buf[INET6_ADDRSTRLEN]; void *addr; struct addrinfo hints, *res; struct sockaddr_in *in; struct sockaddr_in6 *in6; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (argc > 1) { if (strcmp(argv[1], "addrconfig") != 0) { fprintf(stderr, "unknown argument\n"); exit(1); } hints.ai_flags = AI_ADDRCONFIG; } getaddrinfo("nginx.org", NULL, &hints, &res); for ( /* void */ ; res != NULL; res = res->ai_next) { if (res->ai_family == AF_INET) { in = (struct sockaddr_in *) res->ai_addr; addr = &in->sin_addr; } else if (res->ai_family == AF_INET6) { in6 = (struct sockaddr_in6 *) res->ai_addr; addr = &in6->sin6_addr; } else { fprintf(stderr, "unknown address family: %d\n", res->ai_family); continue; } fprintf(stdout, "%s\n", inet_ntop(res->ai_family, addr, buf, sizeof(buf))); } return 0; }
comment:4 by , 6 years ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
The for-loop is wrong if you want to use connect.
How do you connect to more than one address?
You can try everyone. Then everything works, since you would try every protocol.
Or you could use the first one. Which is on your system 206.251.255.63 (since it has no iPv6) if you use your program on a dualstack or IPv6-only system it would be 2001:1af8:4060:a004:21::e3
Which would work again.
Still: There is no system that works with AI_ADDRCONFIG but not without but a lot of systems that work without AI_ADDRCONFIG but not with AI_ADDRCONFIG.
comment:5 by , 6 years ago
Resolution: | → wontfix |
---|---|
Status: | reopened → closed |
When you write a name in the proxy_pass
directive, nginx uses all addresses, quoting docs:
If a domain name resolves to several addresses, all of them will be used in a round-robin fashion.
The approach of using the first result of getaddrinfo() without AI_ADDRCONFIG may work for some naive clients which use simple connect()
, as the first address is expected to be the best available one per RFC 3484. This approach won't work for nginx though, as it uses all addresses available.
It looks like due to no IPv4 connectivity
getaddrinfo()
on your OS filters out IPv4 addresses as nginx use AI_ADDRCONFIG flag (see ec8594b9bf11).I don't think we can do anything with this. Removing
AI_ADDRCONFIG
is not an option as it is likely to break configurations without IPv6 configured.If you want to use IPv4 loopback address for some reason on a system without IPv4 connectivity, consider using IPv4 address directly, e.g.:
Alternatively, consider tuning your system resolver to return such addresses even with IPv4 connectivity disabled. Not sure if it's possible though.