Reputation: 581
I wanted to know how I can parse an IPv6 address in C and convert it to a 128 bit value?
So a hex address like 1:22:333:aaaa:b:c:d:e
needs to be converted to its 128 bit equivalent binary. The problem is the IP address could be of the type ::2
and its variant since they are valid IPv6 address.
The input is from the keyboard and hence is in ASCII format.
Upvotes: 16
Views: 42953
Reputation: 3
I have created my own function to convert ipv6 string into ipv6 value in array without the help of any external library like "boost". It works for any format of ipv6 address.
#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;
}
output:
- Input-ipv6-str[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001] dcolonIdx[255] arrIdxCnt[16] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
- Input-ipv6-str[a1] Ipv6-substr[00a1:0000:0000:0000:0000:0000:0000:0000] dcolonIdx[255] arrIdxCnt[2] Input-ipv6-str[a1] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0000]
- Input-ipv6-str[a1::] Ipv6-substr[00a1:0000:0000:0000:0000:0000:0000:0000] dcolonIdx[2] arrIdxCnt[2] Input-ipv6-str[a1::] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0000]
- Input-ipv6-str[a1::1] Ipv6-substr[00a1:0001:0000:0000:0000:0000:0000:0000] dcolonIdx[2] arrIdxCnt[4] Input-ipv6-str[a1::1] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0001]
- Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:00e5:0000:0000:0000] dcolonIdx[8] arrIdxCnt[10] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:0000:0000:00e5]
- Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5:6f:1] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:00e5:006f:0001:0000] dcolonIdx[8] arrIdxCnt[14] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5:6f:1] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
- Input-ipv6-str[::e5:0:678] Ipv6-substr[00e5:0000:0678:0000:0000:0000:0000:0000] dcolonIdx[0] arrIdxCnt[6] Input-ipv6-str[::e5:0:678] Converted-Ipv6-Addr-Val[0000:0000:0000:0000:0000:00e5:0000:0678]
Upvotes: 0
Reputation: 1288
You can use getaddrinfo()
POSIX function. It is more flexible than inet_pton()
, for example it automatically detects IPv4 and IPv6 address formats, it can resolve even hostnames (using DNS resolving) and port/service names (using /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);
The IPv6 address is stored in the struct sockaddr_storage result
variable.
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
}
Upvotes: 5
Reputation: 1441
if you can use boost, something like this should work:
#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;
}
It is a bit more portable than for example POSIX specific functions.
Upvotes: 0
Reputation: 3485
Rosetta has samples in several languages: https://rosettacode.org/wiki/Parse_an_IP_Address
Upvotes: 2
Reputation: 58800
In Windows, you can use WSAStringToAddress
, which is available since Windows 2000.
Upvotes: 1
Reputation: 3218
To parse IPv6 in C, you need to build yourself a utility function, which tokenized string (colon for hex blocks, and forward-slash for subnet bits).
Upvotes: 2
Reputation: 95365
You can use POSIX inet_pton
to convert a string to a 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?
}
Upvotes: 18
Reputation: 44334
getaddrinfo()
can understand IPv6 addresses. Pass AF_INET6 to it in the hints, as well as AI_NUMERICHOST (to prevent a DNS lookup). Linux has it, Windows has it as of Windows XP.
Upvotes: 10