扩展IPv6地址以便能够在标准输出中打印。

10

我正在使用getifaddrs()和inet_ntop()函数获取系统上的IP地址。当系统设置为IPv6时,返回的地址是缩写版本(使用::代替零)。有没有办法将该地址扩展为完整的地址?

这是我正在使用的代码:

struct ifaddrs *myaddrs, *ifa;
void *in_addr;
char buf[64];

if(getifaddrs(&myaddrs) != 0)
{
    perror("getifaddrs");
    exit(1);
}

for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
{
    if (ifa->ifa_addr == NULL)
        continue;
    if (!(ifa->ifa_flags & IFF_UP))
        continue;

    switch (ifa->ifa_addr->sa_family)
    {
        case AF_INET:
        {
            struct sockaddr_in *s4 = (struct sockaddr_in *)ifa->ifa_addr;
            in_addr = &s4->sin_addr;
            break;
        }

        case AF_INET6:
        {
            struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ifa->ifa_addr;
            in_addr = &s6->sin6_addr;
            break;
        }

        default:
            continue;
    }

    if (!inet_ntop(ifa->ifa_addr->sa_family, in_addr, buf, sizeof(buf)))
    {
        printf("%s: inet_ntop failed!\n", ifa->ifa_name);
    }
    else
    {
        printf("IP address: %s\n", buf);
    }
}

freeifaddrs(myaddrs);

感激不尽您的代码。

编辑:
由于这显然非常难理解,我会给你一个例子:

如果我得到 abcd:12::3 ,我需要将其扩展为 abcd:0012:0000:0000:0000:0000:0000:0003
原因? 因为它是要求的一部分。就这么简单。


1
缩短的IPv6地址是有效的 - 那么为什么要将其扩展呢? - Femaref
1
@Jessica:你是将它存储为128位整数,对吗?因为将其存储为文本表示略显无意义——有些IPv6地址可以用许多方式表示,这正是因为::缩写语法。 - Piskvor left the building
3
好的,我会为您进行翻译。您希望将它存储在字符串中,可以使用char*、char[]或其他方式。您只需要存储地址就可以了,很容易理解吧?我问了一个非常具体的问题,我不关心SQL数据库。 - Jessica
3
您的要求是错误的。请查阅RFC 5952《IPv6地址文本表示的建议》。 - bortzmeyer
1
@bortzmeyer:不行。不符合要求的建议是错误的。如果排序顺序(数据库!)很重要,RFC 5952就是一团糟。 - Tino
显示剩余9条评论
3个回答

12
void ipv6_to_str_unexpanded(char *str, const struct in6_addr *addr) {
   sprintf(str, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
                 (int)addr->s6_addr[0], (int)addr->s6_addr[1],
                 (int)addr->s6_addr[2], (int)addr->s6_addr[3],
                 (int)addr->s6_addr[4], (int)addr->s6_addr[5],
                 (int)addr->s6_addr[6], (int)addr->s6_addr[7],
                 (int)addr->s6_addr[8], (int)addr->s6_addr[9],
                 (int)addr->s6_addr[10], (int)addr->s6_addr[11],
                 (int)addr->s6_addr[12], (int)addr->s6_addr[13],
                 (int)addr->s6_addr[14], (int)addr->s6_addr[15]);
}

2
太棒了。第一个%02x%02应该是%02x%02x。顺便说一下。 - Jessica

0
            #include<stdio.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>

            struct in6_addrr
            {
                unsigned char addr[16];
            };

            void ipv6_expander(const struct in6_addr * addr)
            {
                char str[40];
                sprintf(str,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
                (int)addr->s6_addr[0], (int)addr->s6_addr[1],
                (int)addr->s6_addr[2], (int)addr->s6_addr[3],
                (int)addr->s6_addr[4], (int)addr->s6_addr[5],
                (int)addr->s6_addr[6], (int)addr->s6_addr[7],
                (int)addr->s6_addr[8], (int)addr->s6_addr[9],
                (int)addr->s6_addr[10], (int)addr->s6_addr[11],
                (int)addr->s6_addr[12], (int)addr->s6_addr[13],
                (int)addr->s6_addr[14], (int)addr->s6_addr[15]);
                 printf("\nExpanded ipv6 Addr %s\n",str);
            }

            int main(int argc,char *argv[])
            {
                struct in6_addrr ipv6;
                printf("\nGiven IPv6 Addr %s\n",argv[1]);
                if(inet_pton(AF_INET6,argv[1],&ipv6.addr))
                {
                    ipv6_expander(&ipv6.addr);
                }
                else
                {
                    printf("\n error\n");
                }
                return;
            }

为什么我们需要在这里使用结构体?我们不能只传递无符号字符数组吗? - omjego

0
你可以使用inet_ntop
将IPv4和IPv6地址从二进制转换为文本形式
来自https://man7.org/linux/man-pages/man3/inet_pton.3.html的示例:
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    int main(int argc, char *argv[])
    {
        unsigned char buf[sizeof(struct in6_addr)];
        int domain, s;
        char str[INET6_ADDRSTRLEN];

        if (argc != 3) {
            fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        domain = (strcmp(argv[1], "i4") == 0) ? AF_INET :
                 (strcmp(argv[1], "i6") == 0) ? AF_INET6 : atoi(argv[1]);

        s = inet_pton(domain, argv[2], buf);
        if (s <= 0) {
            if (s == 0)
                fprintf(stderr, "Not in presentation format");
            else
                perror("inet_pton");
            exit(EXIT_FAILURE);
        }

        if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) {
            perror("inet_ntop");
            exit(EXIT_FAILURE);
        }

        printf("%s\n", str);

        exit(EXIT_SUCCESS);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接