C语言中的IPv6解析

gijlo24d  于 2023-01-20  发布在  其他
关注(0)|答案(8)|浏览(284)

我想知道如何在C中解析IPv6地址并将其转换为128位值?
因此,像1:22:333:aaaa:b:c:d:e这样的十六进制地址需要转换为128位等效二进制。问题是IP地址可能是::2及其变体类型,因为它们是有效的IPv6地址。
输入来自键盘,因此是ASCII格式。

9rbhqvlz

9rbhqvlz1#

您可以使用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?
}
3pvhb19x

3pvhb19x2#

getaddrinfo()可以理解IPv6地址。在提示中将AF_INET6以及AI_NUMERICHOST(以防止DNS查找)传递给它。Linux具有此功能,Windows从Windows XP开始具有此功能。

gpnt7bae

gpnt7bae3#

您可以使用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
}
y3bcpkx1

y3bcpkx14#

要在C语言中解析IPv6,您需要自己构建一个实用函数,该函数对字符串进行标记化(冒号表示十六进制块,正斜杠表示子网位)。
1.将原始IPv6字符串标记为较小的子字符串。
1.将非空子字符串转换为十六进制块。(ASCII到十进制转换)
1.通过在前面填充零将十六进制块扩展为2个字节。(只有前导零被修剪)
1.完整的IPv6应该有8个十六进制块,计算丢失的十六进制块。(零分组只能发生一次)
1.重新插入丢失的十六进制块。(使用空子字符串的索引)

yuvru6vn

yuvru6vn5#

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

woobm2wo

woobm2wo7#

如果您可以使用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特定函数更容易移植。

lg40wkob

lg40wkob8#

我已经创建了自己的函数来将ipv6字符串转换为数组中的ipv6值,而不需要任何像"boost"这样的外部库的帮助。它适用于任何格式的ipv6地址。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define PRINT_IPV6_STR            "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]"
#define PRINT_IPV6_VAL(ipaddr)    ipaddr[0],ipaddr[1],ipaddr[2],ipaddr[3],ipaddr[4],ipaddr[5],ipaddr[6],ipaddr[7],ipaddr[8],ipaddr[9],ipaddr[10],ipaddr[11],ipaddr[12],ipaddr[13],ipaddr[14],ipaddr[15]

void getIpv6SubStr(const string inp, unsigned char* arr, unsigned char &dcolonIdx, unsigned char& arrIdxCnt)
{   
    unsigned char len=inp.size();

    unsigned char j=0;
    unsigned char k=0;
    string t_str;
    for(unsigned char i=0; i<=len-1; i++)
    {   
        if(((i!=0) && (inp[i] == ':') && (inp[i-1] == ':')) ||
           ((i==0) && (inp[i] == ':') && (inp[i+1] == ':')))
        {
            dcolonIdx = k;
            continue;
        }
        
        t_str[j++] = inp[i];
        t_str[j]='\0';
        
        if((inp[i] == ':') || (i==len-1))
        {
            unsigned short temp = stol (t_str,nullptr,16);
            arr[k++] = temp>>8;
            arr[k++] = temp;
            j=0;
            t_str.clear();
            continue;
        }
    }
    
    arrIdxCnt = k;
    
    printf("\n Input-ipv6-str[%s] Ipv6-substr" PRINT_IPV6_STR " dcolonIdx[%d] arrIdxCnt[%d]",
            inp.c_str(), PRINT_IPV6_VAL(arr), dcolonIdx,arrIdxCnt);
}

void convertIpv6StringToAddr(const string inp, unsigned char* arr)
{
    unsigned char substr_arr[16] = {};
    unsigned char dcolonIdx = 0xFF;
    unsigned char arrIdxCnt = 0;
    
    /* Get the rcvd ipv6 address */
    getIpv6SubStr(inp, substr_arr, dcolonIdx, arrIdxCnt);
    
    /* If doubleColon is not present in rcvd IPv6 address, then the parsed ipv6 address can be directly used */
    if(dcolonIdx == 0xFF)
    {
        memcpy(arr, substr_arr, 16);
        printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
                inp.c_str(), PRINT_IPV6_VAL(arr));
        return;
    }
    
    /** 
     * If doubleColon is present in rcvd IPv6 address, then we should repace it with required number of 0's 
     * For eg: 
     *  - if rcvd ipv6 address is "a1:a2:a3::a6:a7:a8" then the output ipv6 should be 
     *         "00a1:00a2:00a3:0000:0000:00a6:00a7:00a8"
     *  - if rcvd ipv6 address is "::124" then the output ipv6 should be 
     *         "0000:0000:0000:0000:0000:0000:0000:0124"
     */
    
    unsigned char idxZeros = 0;
    
    /**
     * if rcvd IPv6 address starts with doubleColon, 
     * find the number of indices which need to filled with Zeros. 
     * then from idx=0 fill zeros until the required idx, and then fill the rcvd ipv6 address
     * eg: "::124" => "0000:0000:0000:0000:0000:0000:0000:0124"
     */
    if(dcolonIdx == 0)
    {
        idxZeros = 16-arrIdxCnt;
        
        memcpy(&arr[idxZeros], substr_arr, arrIdxCnt);
        printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
                inp.c_str(), PRINT_IPV6_VAL(arr));
        return;
    }
    
    /**
     * if rcvd IPv6 address contians doubleColon inbetween,
     * find the number of indices which need to filled with Zeros and the position of doubleColon,
     * then from the position of doubleColon fill zeros until the required idx, and fill the rcvd 
     * ipv6 address in its required indices
     * eg: "a1:a2:a3::a6:a7:a8" => "00a1:00a2:00a3:0000:0000:00a6:00a7:00a8"
     */
    idxZeros = 16-arrIdxCnt;    
    memcpy(arr, substr_arr, dcolonIdx);
    memcpy(&arr[dcolonIdx+idxZeros], &substr_arr[dcolonIdx], arrIdxCnt-dcolonIdx);
    
    printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
            inp.c_str(), PRINT_IPV6_VAL(arr));
}

void test_ut(int i, string str)
{
    printf("\n%d.",i);
    unsigned char arr[16] = {};
    convertIpv6StringToAddr(str, arr);
    i++;
}

int main()
{
    test_ut(1, "a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001");
    test_ut(2, "a1");
    test_ut(3, "a1::");
    test_ut(4, "a1::1");
    test_ut(5, "a1a2:b3b4:c5c6:d7d8::e5");
    test_ut(6, "a1a2:b3b4:c5c6:d7d8::e5:6f:1");
    test_ut(7, "::e5:0:678");

    return 0;
}

输出:
1.输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001] IPv6-子字符串[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001] dcolonIdx [255] arrIdxCnt [16]输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001]转换后的-IPv6-地址-值[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
1.输入-IPv6-字符串[a1] IPv6-子字符串[00a1:0000:0000:0000:0000:0000:0000] dcolonIdx [255] arrIdxCnt [2]输入-IPv6-字符串[a1]转换后的-IPv6-地址-值[00a1:0000:0000:0000:0000:0000:0000:0000]
1.输入-IPv6-字符串[a1::] IPv6-子字符串[00a1:0000:0000:0000:0000:0000:0000] dcolonIdx [2] arrIdxCnt [2]输入-IPv6-字符串[a1::]转换后的-IPv6-地址-值[00a1:0000:0000:0000:0000:0000:0000:0000]
1.输入-IPv6-字符串[a1::1] IPv6-子字符串[00a1:0000:0000:0000:0000:0000:0000]数据集标识x [2] arrIdxCnt [4]输入-IPv6-字符串[a1::1]转换后的-IPv6-地址-值[00a1:0000:0000:0000:0000:0000:0000:0001]
1.输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8::e5] IPv6-子字符串[a1a2:b3b4:c5c6:d7d8:00e5:0000:0000:0000] dcolonIdx [8] arrIdxCnt [10]输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8::e5]转换后的-IPv6-地址-值[a1a2:b3b4:c5c6:d7d8:0000:0000:000e5]
1.输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8::e5:6f:1] IPv6-子字符串[a1a2:b3b4:c5c6:d7d8:00e5:006f:0001:0000] dcolonIdx [8] arrIdxCnt [14]输入-IPv6-字符串[a1a2:b3b4:c5c6:d7d8::e5:6f:1]转换后的-IPv6-地址-值[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
1.输入-IPv6-字符串[::e5:0:678] IPv6-子字符串[00e5:0000:0000:0000:0000:0000:0000] dcolonIdx [0] arrIdxCnt [6]输入-IPv6-字符串[::e5:0:678]转换后的-IPv6-地址-值[0000:0000:0000:0000:0000:000e5:0000:0678]

相关问题