getaddrinfo and IPv6
I'm trying to understand what the getaddrinfo function returns :
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
int main (int argc, char *argv[])
{
struct addrinfo *res = 0 ;
getaddrinfo("localhost", NULL ,NULL,&res);
printf("ai_flags -> %i\n", res->ai_flags) ;
printf("ai_family -> %i\n", res->ai_family) ;
printf("ai_socktype -> %i\n", res->ai_socktype) ;
printf("ai_protocol -> %i\n", res->ai_protocol) ;
printf("ai_addrlen -> %i\n", res->ai_addrlen) ;
struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
printf("ai_addr hostname -> %s\n", inet开发者_如何学Python_ntoa(saddr->sin_addr));
freeaddrinfo(res);
return 0 ;
}
results :
ai_flags -> 40
ai_family -> 2
ai_socktype -> 1
ai_protocol -> 6
ai_addrlen -> 16
ai_addr hostname -> 127.0.0.1
In /etc/hosts, I 've got :
127.0.0.1 localhost
::1 localhost
Getaddrinfo returns only 127.0.0.1 and not ::1 ? I don't understand why ?
The second question is where can I find the meaning of those ints (40,2,1,6 etc) ? I've read the man but there is nothing about that.
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
Thanks a lot !!
@jwodder and @onteria_ covered the IPv6 portion well, so I'll just tackle the numbers portion:
ai_flags -> 40
Probably this is going to be the sum of the following two in /usr/include/netdb.h
:
# define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */
# define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose
This is the protocol family, inet, inet6, apx, unix, etc.:
ai_family -> 2
bits/socket.h:78:#define PF_INET 2 /* IP protocol family. */
bits/socket.h:119:#define AF_INET PF_INET
This is the socket type, stream, dgram, packet, rdm, seqpacket:
ai_socktype -> 1
bits/socket.h:42: SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
The higher-level protocol, TCP, UDP, TCP6, UDP6, UDPlite, ospf, icmp, etc:
ai_protocol -> 6
Funny enough, in /etc/protocols
:
tcp 6 TCP # transmission control protocol
The size of the struct sockaddr
. (Differs based on the address family! Ugh.)
ai_addrlen -> 16
This is because you're getting back a struct sockaddr_in
, see linux/in.h
:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
And the last one, from /etc/hosts
:)
ai_addr hostname -> 127.0.0.1
res
also contains a field struct addrinfo *ai_next;
, which is a pointer to additional entries found by getaddrinfo
, or NULL if there were no other entries. If you examine res->ai_next
, you should find the IPv6 entry.
As for the integer fields in a struct addrinfo
, they correspond to predefined constants with implementation-defined values, and the integer values themselves are not of general interest. If you want to know what a given field means, compare it against the constants that can be assigned to that field (SOCK_STREAM
, SOCK_DGRAM
, etc. for ai_socktype
; IPPROTO_TCP
, IPPROTO_UDP
, etc. for ai_protocol
; and so forth) or, for ai_flags
, test each bit corresponding to a predefined constant (e.g., if (res->ai_flags & AI_NUMERICHOST) {printf("ai_flags has AI_NUMERICHOST\n"); }
).
extern struct sockaddr_in6 create_socket6(int port, const char * address) {
struct addrinfo hints, *res, *resalloc;
struct sockaddr_in6 input_socket6;
int errcode;
/* 0 out our structs to be on the safe side */
memset (&hints, 0, sizeof (hints));
memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
/* We only care about IPV6 results */
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
errcode = getaddrinfo (address, NULL, &hints, &res);
if (errcode != 0)
{
perror ("[ERROR] getaddrinfo ");
return input_socket6;
}
resalloc = res;
while (res)
{
/* Check to make sure we have a valid AF_INET6 address */
if(res->ai_family == AF_INET6) {
/* Use memcpy since we're going to free the res variable later */
memcpy (&input_socket6, res->ai_addr, res->ai_addrlen);
/* Here we convert the port to network byte order */
input_socket6.sin6_port = htons (port);
input_socket6.sin6_family = AF_INET6;
break;
}
res = res->ai_next;
}
freeaddrinfo(resalloc);
return input_socket6;
}
Here is some code that explains it. Basically unless you give getaddrinfo some hints to tell it to only work with IPV6, it will also give IPV4 results. That is why you have to loop through the results as shown.
Other answers have been given to most parts, but to answer this final part:
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
The function you want there is getnameinfo()
; given a socket address it returns a string name.
精彩评论