* Inside a jail, no lo0 loopback interface. * Calling this function returns the ip "0.0.0.0" (also known as "IN_ADDR_ANY"). * But we asked for the hostname "localhost" - which in our /etc/hosts file is set to "127.0.0.1". The proof: Example 'C' test program "test.c" is attached. getaddrinfo ~/ root~# gcc test.c -o test && ./test 0.0.0.0 This affects the gSOAP library, it's function soap_bind(), which is called by VirtualBox's SOAP webservice ("vboxwebsrv"). These applications seem to do everything correctly. But the result is a bug. Please see test script "test.c" included. The real-life bug occurs in this code where: vboxweb.cpp:858: SOAP_SOCKET m, s; // master and slave sockets m = soap_bind(&soap, g_pcszBindToHost ? g_pcszBindToHost : "localhost", // safe default host g_uBindToPort, // port g_uBacklog); // backlog = max queue size for requests if (m < 0) WebLogSoapError(&soap); and: stdsoap2.cpp:4143: err = getaddrinfo(host, soap_int2s(soap, port), &hints, &addrinfo); Is passing in "localhost", is told "0.0.0.0". Then later on in soap_bind(), the returned address "0.0.0.0" is passed into bind(). And bind() then binds to ALL interfaces (not just only the localhost interface, or should error out in the jail where lo0 has no IP address). I looked hard in these applications but the bug isn't in there - it seems to be in the result returned be "getaddrinfo()" C library call. I confirmed this was the case with "test.c" example program (below). Which was run in the same jail. The test jail was created like this: $ qjail create -4 192.168.1.203 getaddrinfo $ qjail config -k getaddrinfo $ qjail start getaddrinfo Fix: I suspect this function (getaddrinfo) shouldn't be returning IN_ADDR_ANY. It should either fail, or return "127.0.0.1" as per "/etc/hosts". How-To-Repeat: getaddrinfo ~/ root~# ifconfig re0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE> ether 00:1e:ec:d7:3a:1f inet 192.168.1.203 netmask 0xffffffff broadcast 192.168.1.203 media: Ethernet autoselect (1000baseT <full-duplex>) status: active ipfw0: flags=8801<UP,SIMPLEX,MULTICAST> metric 0 mtu 65536 lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6> getaddrinfo ~/ root~# ping -c 1 "localhost" PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.168 ms --- localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.168/0.168/0.168/0.000 ms getaddrinfo ~/ root~# cat /etc/hosts | grep localhost ::1 localhost localhost.my.domain 127.0.0.1 localhost localhost.my.domain getaddrinfo ~/ root~# cat test.c #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> /* man getaddrinfo */ int main() { struct addrinfo *addrinfo = NULL; struct addrinfo hints; struct addrinfo res; int err; memset((void*)&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo("localhost", "18083", &hints, &addrinfo); if (addrinfo) { res = *addrinfo; printf("%s\n", inet_ntoa( ((struct sockaddr_in*)addrinfo->ai_addr)->sin_addr )); freeaddrinfo(addrinfo); return(0); } if (err || !addrinfo) { printf("getaddrinfo failed with code %i.\n",err); return(1); } printf("end_main()\n"); return (0); } getaddrinfo ~/ root~# gcc test.c -o test && ./test 0.0.0.0 getaddrinfo ~/ root~# exit logout
Responsible Changed From-To: freebsd-bugs->freebsd-doc reclassify.
for me, the 0.0.0.0 results go away when I remove the ipv6 ::1 entry from /etc/hosts in my jail However, I get the same results both inside and outside of a jail. -- Allan Jude
Ah, I have researched a little more. It is my impression that the expected result aught to be "::1" if the protocol family was PF_UNSPEC or PF_INET6. Now that Allan has mentioned it. Commenting out the ::1 in /etc/hosts does change the result. However that isn't something the gSOAP library which is making that API call has any control over. Also: This bug occurs when the protocol family being requested is set to "PF_UNSPEC" (unspecified). OR "PF_INET6". With the default "/etc/hosts" file (where that ::1 localhost entry exists). Wheras setting: hints.ai_family = PF_INET; returns 127.0.0.1 regardless of the ::1 in /etc/hosts file. Setting: hints.ai_family = PF_INET6; also gives such incorrect "0.0.0.0", except if commenting out ::1 in /etc/hosts as Alan says. Then it gives: getaddrinfo ~/ root~# gcc test.c -o test && ./test getaddrinfo failed with code 8. Which would be correct to error out since no corresponding ip6 address found. The gSOAP library seems to choose PF_UNSPEC because they want to support both ipv4 and ipv6 protocols simultaneously (as many others also do). I'm not sure if they could fix downstream the issue by avoiding PF_UNSPEC because the bug will still occur on PF_INET6. Anyway. Heres is the gSOAP code where getaddrinfo() is being called in "soap_bind()": http://sourceforge.net/p/gsoap2/code/HEAD/tree/gsoap/stdsoap2.cpp#l4789 http://sourceforge.net/p/gsoap2/code/HEAD/tree/gsoap/stdsoap2.cpp#l4791 Then later on in soap_bind() the incorrect address (0.0.0.0), is used are the parameter being passed into bind() http://sourceforge.net/p/gsoap2/code/HEAD/tree/gsoap/stdsoap2.cpp#l4876
getaddrinfo() returns a list of results. So I have updated my test program to walk the entire list. It now prints two results for PF_UNSPEC getaddrinfo ~/ root~# gcc test.c -o test && ./test 0.0.0.0 127.0.0.1 getaddrinfo ~/ root~# Where the first result in the list is supposed to be the ipv6's "::1" (but isn't). Anyway heres the new version of the test.c getaddrinfo ~/ root~# cat test.c #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> /* man getaddrinfo */ int main() { struct addrinfo *addrinfo = NULL; struct addrinfo *rp = NULL; struct addrinfo hints; struct addrinfo res; int err; memset((void*)&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo("localhost", "18083", &hints, &addrinfo); if (addrinfo) { for (rp = addrinfo; rp != NULL; rp = rp->ai_next) { printf("%s\n", inet_ntoa( ((struct sockaddr_in*)rp->ai_addr)->sin_addr )); } freeaddrinfo(addrinfo); return(0); } if (err || !addrinfo) { printf("getaddrinfo failed with code %i.\n",err); return(1); } printf("end_main()\n"); return (0); } getaddrinfo ~/ root~# gcc test.c -o test && ./test 0.0.0.0 127.0.0.1 getaddrinfo ~/ root~#
So do you still think there is an issue here? or can I close this PR? -- Allan Jude
(In reply to Allan Jude from comment #5) > So do you still think there is an issue here? or can I close this PR? > Allan Jude Yeah I believe this one should stay open because it was discovered as a bug when such a query was being executed by a real piece of software (virtualbox). Rationale: The virtualbox source code running this query was multi platform. If their developers can only test their work on one platform (for example linux), and result the other platform works correctly... they kindda need to be able to assume it work's gonna the same way on FreeBSD too. Problem Recap. In short. The problem is that: running a "localhost" DNS query should never be returning "0.0.0.0" anywhere in it's results list. As "0.0.0.0" != "localhost". This bug occurs if the protocol family being requested is set to "PF_UNSPEC" (unspecified). OR "PF_INET6". With the default "/etc/hosts" file (with a line that says "::1 localhost"). The bug is more likely to occur on new "IPV4+IPV6" code. The bug may not be evident on older IP4-only code, if the DNS query's "protocol family" struct field was set to "PF_INET4".
Should this PR really be in 'Documentation' ? Or 'Base System' ?
I do not see why it is believed to be correct behavior to blindly cast addrinfo->ai_addr to type 'struct sockaddr_in *' without first checking that addrinfo->ai_family is PF_INET (or that the sockaddr's sa_family is AFS_INET); such a cast is expected to yield "nonsense" results when ai_family is PF_INET6.
(In reply to Benjamin Kaduk from comment #8) > I do not see why it is believed to be correct behavior to blindly cast > addrinfo->ai_addr to type 'struct sockaddr_in *' without first checking that > addrinfo->ai_family is PF_INET (or that the sockaddr's sa_family is > AFS_INET); such a cast is expected to yield "nonsense" results when > ai_family is PF_INET6. I made the change you suggested. https://gist.github.com/dreamcat4/86706bba25c468fc0ecc For PF_INET6 hints, the returned structure says it is of type ai_family '0x1C' (which is '28' in decimal base r10). If we look in the header file '/usr/include/sys/socket.h', we see that returned type is: AF_ISDN :/ Maybe that aught to be a returned value of type '30' (0x1E) for PF_INET6. Since that was the type we actually requested? #define pseudo_AF_PIP 25 /* Help Identify PIP packets */ #ifdef __APPLE__ /*define pseudo_AF_BLUE 26 Identify packets for Blue Box - Not used */ #define AF_NDRV 27 /* Network Driver 'raw' access */ #endif #define AF_ISDN 28 /* Integrated Services Digital Network*/ #define AF_E164 AF_ISDN /* CCITT E.164 recommendation */ #define pseudo_AF_KEY 29 /* Internal key-management function */ #endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */ #define AF_INET6 30 /* IPv6 */ #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define AF_NATM 31 /* native ATM access */ #ifdef __APPLE__ #define AF_SYSTEM 32 /* Kernel event messages */
(In reply to dreamcat4 from comment #9) > For PF_INET6 hints, the returned structure says it is of type ai_family > '0x1C' (which is '28' in decimal base r10). > > If we look in the header file '/usr/include/sys/socket.h', we see that > returned type is: AF_ISDN > > :/ > > Maybe that aught to be a returned value of type '30' (0x1E) for PF_INET6. > Since that was the type we actually requested? > > > #define pseudo_AF_PIP 25 /* Help Identify PIP packets */ > #ifdef __APPLE__ > /*define pseudo_AF_BLUE 26 Identify packets for Blue Box - Not used */ > #define AF_NDRV 27 /* Network Driver 'raw' access */ > #endif > #define AF_ISDN 28 /* Integrated Services Digital Network*/ > #define AF_E164 AF_ISDN /* CCITT E.164 recommendation */ > #define pseudo_AF_KEY 29 /* Internal key-management function */ > #endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */ > #define AF_INET6 30 /* IPv6 */ > #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) > #define AF_NATM 31 /* native ATM access */ > #ifdef __APPLE__ > #define AF_SYSTEM 32 /* Kernel event messages */ I think you are looking at the /usr/include/sys/socket.h from an OS X machine, not a FreeBSD machine. On my FreeBSD machine, AF_INET6 is decimal 28, matching the 0x1c you see.
(In reply to Benjamin Kaduk from comment #10) > I think you are looking at the /usr/include/sys/socket.h from an OS X > machine, not a FreeBSD machine. On my FreeBSD machine, AF_INET6 is decimal > 28, matching the 0x1c you see. Yes. Oh sorry! Been looking in the wrong window. Silly me. And my test program should be: if ( (addrinfo) && (addrinfo->ai_family == hints.ai_family) ) ^^^^^^^^^^^^^^^ in order to filter on the same requested / returned type. Gist link is updated to reflect that, https://gist.github.com/dreamcat4/86706bba25c468fc0ecc BTW This bug I have observed on FreeBSD 9.2-RELEASE. It is also suspected to occur on 9.3, 10.0, and high. I just don't have any newer FreeBSD servers to test it on. Can check on them by running this program 'test.c'.
(In reply to dreamcat4 from comment #11) > > Gist link is updated to reflect that, > https://gist.github.com/dreamcat4/86706bba25c468fc0ecc > No, this is still incorrect. The point I was making, is that the ai_addr field must be cast to the type 'struct sockaddr_in6 *' (note the '6') when it is in the INET6 family. Your code is still using 'struct sockaddr_in *', which is incorrect.
(In reply to Benjamin Kaduk from comment #12) > (In reply to dreamcat4 from comment #11) > No, this is still incorrect. The point I was making, is that the ai_addr > field must be cast to the type 'struct sockaddr_in6 *' (note the '6') when > it is in the INET6 family. Your code is still using 'struct sockaddr_in *', > which is incorrect. Thanks Ben! I am pleased to say that with that change, the new version of 'test.c' and test result you can see here: https://gist.github.com/dreamcat4/86706bba25c468fc0ecc So this proves that there is no bug in getaddrinfo. My bug report is invalid. This also proves there is definitely some bug in GSOAP library (which the previous version of this 'test.c' program was exactly reproducing). Many thanks. Bug is on GSOAP. As previously stated in my very 1st comment: stdsoap2.cpp:4143: err = getaddrinfo(host, soap_int2s(soap, port), &hints, &addrinfo); Is passing in "localhost", is told "0.0.0.0". Then later on in soap_bind(), the returned address "0.0.0.0" is passed into bind(). And bind() then binds to ALL interfaces. We can see as of today, comparing before and after, the '0.0.0.0' is occur because incorrect cast of structure members. When the INET6 structure actually contain '::1' ipv6 address for localhost. Appreciate this. It clarifies the issue totally.
Just for completeness (and not to forget it), here is link to IPV4 tutorial page. Which was the most helpful to improve / fix today this parts of broken networking code (where error occurred). http://long.ccaba.upc.edu/long/045Guidelines/eva/ipv6.html Many thanks.