【问题标题】:IPv6 parsing in CC中的IPv6解析
【发布时间】:2011-02-27 02:33:14
【问题描述】:

我想知道如何在 C 中解析 IPv6 地址并将其转换为 128 位值?

因此,像1:22:333:aaaa:b:c:d:e 这样的十六进制地址需要转换为其等效的 128 位二进制。问题是 IP 地址可能是 ::2 类型及其变体,因为它们是有效的 IPv6 地址。

输入来自键盘,因此是 ASCII 格式。

【问题讨论】:

  • 这似乎是一个 144 位的 IP 地址。
  • 哎呀...感谢您指出这一点..实际上我的意思是 1:22:333:aaaa:b:c:d:e

标签: c parsing ipv6


【解决方案1】:

您可以使用getaddrinfo() POSIX 函数。它比inet_pton()更灵活,例如它自动检测IPv4和IPv6地址格式,它甚至可以解析主机名(使用DNS解析)和端口/服务名称(使用/etc/services)。

#include <sys/types.h>
#include <netdb.h>
#include <netdb.h>

....

const char *ip6str = "::2";

struct sockaddr_storage result;
socklen_t result_len;

struct addrinfo *res = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT | AI_NUMERICHOST | AI_NUMERICSERV;

rc = getaddrinfo(ip6str, NULL, &hints, &res);
if (rc != 0)
{
    fprintf(stderr, "Failure to parse host '%s': %s (%d)", ip6str, gai_strerror(rc), rc);
    return -1;
}

if (res == NULL)
{
    // Failure to resolve 'ip6str'
    fprintf(stderr, "No host found for '%s'", ip6str);
    return -1;
}

// We use the first returned entry
result_len = res->ai_addrlen;
memcpy(&result, res->ai_addr, res->ai_addrlen);

freeaddrinfo(res);

IPv6 地址存储在struct sockaddr_storage result 变量中。

if (result.ss_family == AF_INET6) // Ensure that we deal with IPv6
{
    struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *) &result;
    struct in6_addr * in6 = &sa6->sin6_addr;
    in6->s6_addr[0]; // This is a first byte of the IPv6
    in6->s6_addr[15]; // This is a last byte of the IPv6
}

【讨论】:

    【解决方案2】:

    如果你可以使用 boost,这样的东西应该可以工作:

    #include<boost/asio.hpp>
    
    using boost::asio::ip;
    
    bool parseIpv6String(std::string ipv6_string, char* dest){
        try{
            address_v6 addr = address_v6::from_string(ipv6_string);
            memcpy(dest,addr.to_bytes().data(), 16);
        }catch(...){
            return false;
        }
        return true;
    }
    

    它比例如 POSIX 特定功能更便携。

    【讨论】:

    • memcpy()从什么时候抛出异常?!
    • 你是对的,from_string() 确实如此。感谢您的通知。
    【解决方案3】:

    Rosetta 有多种语言的示例:https://rosettacode.org/wiki/Parse_an_IP_Address

    【讨论】:

      【解决方案4】:

      在 Windows 中,您可以使用 WSAStringToAddress,它从 Windows 2000 开始可用。

      【讨论】:

        【解决方案5】:

        要在 C 中解析 IPv6,您需要自己构建一个实用函数,该函数将字符串标记化(冒号表示十六进制块,正斜杠表示子网位)。

        1. 将原始 IPv6 字符串标记为更小的子字符串。
        2. 将非空子字符串转换为十六进制块。 (ASCII 到十进制转换)
        3. 通过在前面填充零将十六进制块扩展为 2 个字节。 (只有前导零被修剪)
        4. 完整的 IPv6 应该有 8 个十六进制块,计算缺少的十六进制块。 (零分组只能发生一次)
        5. 重新插入丢失的十六进制块。 (使用空子字符串的索引)

        【讨论】:

        • 如果有人想学习 IPv6 解析,很好的描述。如果他只是想实际解析地址,其他答案要简单得多。
        【解决方案6】:

        您可以使用 POSIX inet_pton 将字符串转换为 struct in6_addr

        #include <arpa/inet.h>
        
          ...
        
        const char *ip6str = "::2";
        struct in6_addr result;
        
        if (inet_pton(AF_INET6, ip6str, &result) == 1) // success!
        {
            //successfully parsed string into "result"
        }
        else
        {
            //failed, perhaps not a valid representation of IPv6?
        }
        

        【讨论】:

        • 你可以传递任何足够大的东西来保存结果,例如,8 个 short 的数组也可以;只要缓冲区的长度至少为 128 位。
        • 我个人比getaddrinfo() 更喜欢这个,我在下面建议。可悲的是,Windows 似乎只有从 Vista 开始的inet_pton()。 (而且我几乎敢打赌,它不会根据 RFC 2373 进行解析,而且只有您的典型 IPv6 地址……有人知道吗?)
        • @Thanatos: This link 指的是RFC 2553,它将函数inet_pton 声明为将 IPv4 和 IPv6 文本表示形式转换为二进制形式的函数,并表示它必须接受 IPv6 地址RFC 2373 的第 2.2 节中描述的表示。
        • 感谢 Thanatos 和 dreamlax 的解答。我在我的 Linux 环境中使用了它们,它运行良好。但问题是我想要合并的实际代码是 Linux 的精简版本,没有所有库。不幸的是,inet.h 头文件不存在在环境中,因此不支持inet_pton ...看起来我最终可能会像YeenFei指出的那样编写自己的函数..需要一些帮助来解决这个问题...谢谢!问候-TG
        • 有时值得尝试一下:自己声明结构和函数原型,然后调用函数,希望它在库中(glibc 的 inet_pton 部分,而不是其他随机库),尽管你没有标题。
        【解决方案7】:

        getaddrinfo() 可以理解 IPv6 地址。在提示中将 AF_INET6 以及 AI_NUMERICHOST 传递给它(以防止 DNS 查找)。 Linux 有,Windows 和 Windows XP 一样。

        【讨论】:

        • 如果函数成功,别忘了使用freeaddrinfo
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-15
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多