4

I am facing issues in getting ipv6 using getaddrinfo():

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

int main()
{
        int soc = 00;
        struct addrinfo hints,*results,*res;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;

        int m = getaddrinfo("myhost","2018",&hints,&results);
        if (m<0)
        {
                printf("getaddrinfo error :  %s\n", gai_strerror(m));
                return 1;
        }
        res=results;
        do
        {
                if(res->ai_family == AF_INET6)
                {
                        struct sockaddr_in6* a= (struct sockaddr_in6*)res->ai_addr;
                        char straddr[INET6_ADDRSTRLEN];
                        inet_ntop(AF_INET6, &a->sin6_addr, straddr, sizeof(straddr));
                        printf("IP v6 - %s\n",straddr);
                }
                if(res->ai_family == AF_INET)
                {
                        struct sockaddr_in* b= (struct sockaddr_in*)res->ai_addr;
                        const char* dotted_decimal1 = inet_ntoa(b->sin_addr);
                        printf("IP v4 - %s\n",dotted_decimal1);
                }
        }
        while((res=res->ai_next) !=NULL);

        return 0;
}

Output:

IP v4 - xx.xx.xx.xx

Since I have set hints.ai_family = AF_UNSPEC, it should return me both the details (ipv4 and ipv6). But it is returning details of ipv4 only.

Note: on command ifconfig I can see both ipv4 and ipv6 address.

> $ ifconfig
> eth3      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
> inet addr:x.x.x.x  Bcast:x.x.x.x  Mask:255.255.255.128
> inet6 addr: xx::xx:xx:xx:xx/64 Scope:Link
  1. Can onyone explain me why it is not picking ipv6?

  2. Do I need to make any changes on my host (Linux)?

I have removed the actual ip details and replaced with xxxx.

Community
  • 1
  • 1
kaps
  • 186
  • 4
  • 18
  • 3
    Just because you have IPv6 on your system does not mean that the name "myhost" is associated with an IPv6 address. This kind of association need to be done in the DNS (network wide) or in `/etc/hosts` (only visible to the local system). – Steffen Ullrich Dec 07 '18 at 14:15
  • myhost is just for example.... I have different machine name there – kaps Dec 07 '18 at 14:25
  • 2
    It was clear to me that "myhost" is just an example. But what I said regarding the DNS/hosts setup for "myhost" is still true even if the actual name is different. – Steffen Ullrich Dec 07 '18 at 14:32
  • 1
    @kaps Are you using an actual hostname for which you know for certain has that hostname to an ipv6 address mapping , that can be resolved ? – nos Dec 07 '18 at 23:22
  • @kaps what is your goal? Do you want to resolve host name? or do you want to retrive your local IPv6 address? – miradham Dec 18 '18 at 04:40
  • @miradham - Yes I am trying to get the ipv6 from a given hostname – kaps Dec 18 '18 at 07:21
  • @kaps are you sure that the hostname has IPv6 addres? – miradham Dec 18 '18 at 07:33
  • @miradham - **> $ ifconfig > eth3 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx > inet addr:x.x.x.x Bcast:x.x.x.x Mask:255.255.255.128 > inet6 addr: xx::xx:xx:xx:xx/64 Scope:Link** – kaps Dec 18 '18 at 09:18
  • @kaps is this IP configuration of `myhost`? If so, then you have only link local IPv6 address. It cannot be used to access outside of LAN – miradham Dec 19 '18 at 01:24
  • @SteffenUllrich - Thank you.. There was a small mistake in the /etc/hosts.. Now I am able to fetch ipv6 but when I try to connect the server, it says "Invalid argument".. This is due to the scope id not set in the result... can you please share the syntax for the ipv6 entry in /etc/hosts file – kaps Jan 14 '19 at 13:43
  • @kaps: there is nothing special with the IPv6 syntax in `/etc/hosts`. For the general syntax see https://en.wikipedia.org/wiki/IPv6_address#Representation – Steffen Ullrich Jan 14 '19 at 17:32
  • I have below entry in /etc/hosts "fe80::e41b:2c23:a286:77a myhost". Is this correct ? – kaps Jan 15 '19 at 11:44

2 Answers2

4

To retrieve local addresses (since you are using AI_PASSIVE), you can (and should) set the node parameter to NULL instead of specifying an actual name, per the documentation:

If the AI_PASSIVE flag is specified in hints.ai_flags, and node is NULL, then the returned socket addresses will be suitable for bind(2)ing a socket that will accept(2) connections.

Also, inet_ntop() supports AF_INET, so you can get rid of your if statements and use inet_ntop() unconditionally for both address families.

Try this:

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

void* getSinAddr(int af, sockaddr *addr)
{
    switch (af)
    {
        case AF_INET:
            return &(reinterpret_cast<sockaddr_in*>(addr)->sin_addr);

        case AF_INET6:
            return &(reinterpret_cast<sockaddr_in6*>(addr)->sin6_addr);
    }
    return NULL;
}

int main()
{
    addrinfo hints = {}, *results, *res;

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    int m = getaddrinfo(NULL, "2018", &hints, &results);
    if (m < 0)
    {
        std::cout << "getaddrinfo error : " << gai_strerror(m) << std::endl;
        return 1;
    }

    for(res = results; res != NULL; res = res->ai_next)
    {
        char straddr[INET6_ADDRSTRLEN] = {};
        if (inet_ntop(res->ai_family, getSinAddr(res->ai_family, res->ai_addr), straddr, sizeof(straddr)))
            std::cout << "IP - " << straddr << "\n";
        else
            std::cout << "inet_ntop error : " << strerror(errno) << std::endl;
    }

    freeaddrinfo(results);

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you @Remy Lebeay, I ran the code you provided, it gave **ipv4 as "0.0.0.0"** and **ipv6 as "::"**. I removed **hints.ai_flags = AI_PASSIVE;** and gave the actual host name and it gave me just the ipv4 again. – kaps Dec 11 '18 at 06:17
2

Since I have set hints.ai_family = AF_UNSPEC it should return me both the details (ipv4 and ipv6)

No, it should not. getaddrinfo API does not guarantee to return both IPv4 and IPv6 addresses. It returns IP address type that is associated with given host name.

In order to make sure that your code is working fine you can simply check google.com host over 80 or 443 ports i.e.

 int m = getaddrinfo("google.com","80",&hints,&results);

This check returns both IPv4 and IPv6 addresses in my Linux PC(fyi, I also don't have global IPv6 address).

If you need to retrieve your local IPv6 address, I would recommend to use getifaddrs or read /proc/net/if_inet6 file in Linux, and GetAdaptersAddresses in Windows.
Also please pay attention that your IPv6 address scope is link local, which is auto generated by OS.

miradham
  • 2,285
  • 16
  • 26