The Wayback Machine - https://web.archive.org/web/20210128103008/https://github.com/php/php-src/pull/6302
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #78479: Use gethostbyname2_r to specify AF_INET #6302

Draft
wants to merge 1 commit into
base: PHP-7.3
from

Conversation

@n-thumann
Copy link

@n-thumann n-thumann commented Oct 8, 2020

gethostbyname_r can return both AF_INET and AF_INET6 addresses, but php_gethostbyname will always memcopy the result into an in_addr (v4 only) struct. This PR therefore replaces gethostbyname_r with gethostbyname2_r and sets the address type to be returned to AF_INET.
Bug can be reproduced by enabling inet6 inresolv.conf, e.g. with Docker: docker run -it --rm --dns-opt inet6 php sh -c "echo '<?php echo gethostbyname(\"www.google.com\"); echo \"\\n\"; echo gethostbyname(\"downloads.wordpress.org\"); echo \"\\n\"; ?>' | php" will return

42.0.20.80
0.0.0.0

See Bug #78479 for more information.

@sundbry
Copy link

@sundbry sundbry commented Oct 8, 2020

Looks good to me, I wonder what happened in the test suite.

@n-thumann
Copy link
Author

@n-thumann n-thumann commented Oct 8, 2020

As @derickr latest commit (14d231b) on PHP-7.3 didn´t made it through the CI-Pipeline and I´m requesting to merge into that branch, mine failed as well :/

@derickr
Copy link
Contributor

@derickr derickr commented Oct 8, 2020

@n-thumann
Copy link
Author

@n-thumann n-thumann commented Oct 8, 2020

Can you link me to the exact spot of the test failure?

Sure, e.g. Travis of 14d231b ✌️

@derickr
Copy link
Contributor

@derickr derickr commented Oct 8, 2020

@@ -1287,7 +1287,7 @@ struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char *
}

while (( res =
gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))
gethostbyname2_r(host,AF_INET,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))

This comment has been minimized.

@nikic

nikic Oct 9, 2020
Member

Can this be fixed without using gethostbyname2_r? This file already tests many different gethostbyname prototypes for cross-platform support, and we shouldn't add more unless unavoidable.

The current change is incorrect as-is, because it assumes that gethostbyname2_r is available whenever a six-argument prototype of gethostbyname_r is available.

This comment has been minimized.

@sundbry

sundbry Oct 9, 2020

I don't think it can be properly fixed without it. As in the original bug report, it was possible for a domain with a valid A record yet no AAAA record return a result for AF_INET6. In this system call we need to explicitly request an AF_INET result only.

Any result from gethostbyname_r with an INET6 return struct is useless in this context, it doesn't indicate that there is no record for a normal ipv4 address.

This comment has been minimized.

@n-thumann

n-thumann Oct 9, 2020
Author

I think @nikic is right, also the issue would remain on other platforms as Linux is the only one to support gethostbyname2_r for specifying a type.
I think we can use getaddrinfo instead of gethostbyname_r or gethostbyname2_r (both marked as obsolescent). That works basically the same, but returns another struct addrinfo instead of struct hostent (shouldn´t be an issue, can be manually assigned) and as far as I could tell, the syntax/number of arguments is always the same (checked Linux, Solaris & HP-UX), so we should be able to remove the platform check, don´t we?

@n-thumann n-thumann force-pushed the n-thumann:bugfix/78479_gethostbyname branch from db020d8 to 6d18256 Oct 11, 2020
@n-thumann n-thumann marked this pull request as draft Oct 11, 2020
@n-thumann
Copy link
Author

@n-thumann n-thumann commented Oct 11, 2020

Changed gethostbyname2_r to getaddrinfo. As this is universal for all platforms, I also removed the HAVE_GETHOSTBYNAME_R checks. The memory deallocation is not quite done yet (there are still some efree missing everywhere, where php_network_gethostbyname is used), but I´d still be glad to hear your feedback ✌️

@n-thumann n-thumann force-pushed the n-thumann:bugfix/78479_gethostbyname branch 2 times, most recently from 73a2843 to c916fa9 Oct 11, 2020
gethostbyname_r can return both AF_INET and AF_INET6 addresses,
but php_gethostbyname will always memcopy the result
into an in_addr (v4 only) struct.
@n-thumann n-thumann force-pushed the n-thumann:bugfix/78479_gethostbyname branch from d02f9a4 to 7b7e81b Oct 11, 2020
nresults++;

hp = emalloc(sizeof(struct hostent));
hp->h_name = strdup(host);

This comment has been minimized.

@sundbry

sundbry Oct 11, 2020

Suggested change
hp->h_name = strdup(host);
hp->h_name = strndup(host, _POSIX_HOST_NAME_MAX);

I suggest we guard against a buffer overflow attack here.

Would also need to #include <limits.h> to get _POSIX_HOST_NAME_MAX (should be 256 bytes)

Copy link
Member

@nikic nikic left a comment

I agree that switching to getaddrinfo is generally the right approach here. If you want this to actually land in PHP-7.3 rather than master, we can't change the behavior of php_network_gethostbyname() in this way, as it may break 3rd-party consumers. I'm also not sure it really makes sense to polyfill gethostbyname in this manner, it might make more sense to just switch uses of this function to call getaddrinfo directly (there's already quite a few usages).

#ifdef HAVE_GETHOSTBYNAME_R
#ifdef HAVE_GETADDRINFO
#ifdef PHP_WIN32
#include "win32/ws2tcpip.h" #TODO: does this work?

This comment has been minimized.

@nikic

nikic Oct 12, 2020
Member

It should probably be just #include <Ws2tcpip.h>. (We already have a couple usages of this header.)

@nikic
Copy link
Member

@nikic nikic commented Oct 12, 2020

So, to put it into a more actionable form... For PHP 7.3, I would replace the gethostbyname/gethostbynamel function with implementations based on getaddrinfo and leave it at that. For master, we can replace misc other usages and drop php_network_gethostbyname entirely.

Only thing I'm unsure about is whether we need to guard by HAVE_GETADDRINFO. While it's used in some places, sockets.c notably uses getaddrinfo() unconditionally. That makes me think that we should just use it unconditionally, and let someone complain if it breaks their build :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
4 participants