2012-06-27 2 views
4

У меня есть структура struct sockaddr, содержащая IPv4-отображаемый IPv6-адрес, такой как ::ffff:10.0.0.1. Я хочу получить только версию IPv4 в строке (в данном случае 10.0.0.1) на языке программирования C. Как мне добиться этого?Как преобразовать IPv4-отображаемый-IPv6-адрес в IPv4 (строковый формат)?

ответ

3

Поскольку ваша структура содержит адрес IPV6, я предполагаю, что у вас есть указатель struct sockaddr * (назовем его addrPtr), указав на структуру struct sockaddr_in6.

Вы можете легко получить адресные байты.

const uint8_t *bytes = ((const struct sockaddr_in6 *)addrPtr)->sin6_addr.s6_addr; 

Затем добавьте 12 к указателю, потому что 12 первые байты не интересны (10 0x00, затем 2 0xff). Только 4 последних матер.

bytes += 12; 

Теперь мы можем использовать эти четыре байта, чтобы делать все, что захотим. Например, мы можем сохранить их в адрес IPv4 struct in_addr.

struct in_addr addr = { *(const in_addr_t *)bytes }; 

Тогда мы можем получить строку, используя inet_ntop (объявленный в <arpa/inet.h>).

char buffer[16]; // 16 characters at max: "xxx.xxx.xxx.xxx" + NULL terminator 
const char *string = inet_ntop(AF_INET, &addr, buffer, sizeof(buffer)); 
+1

Или используйте 'inet_ntoa()' вместо 'Sprintf()' –

+2

Хороший момент. Я отредактировал свой ответ, чтобы использовать 'inet_ntop', который рекомендуется использовать для' inet_ntoa', потому что вместо статического он использует локальный буфер (и, следовательно, потокобезопасен). –

+0

AFAIK, статический буфер, используемый 'inet_ntoa()', объявляется в хранилище TLS, поэтому он является потокобезопасным. Но если ваша платформа имеет 'inet_ntop()', тем лучше. –

0

После того, как вы распознали IP-адрес, отображаемый IPv4, часть IPv4 является просто наименее значимыми четырьмя байтами адреса. Я считаю, что это может быть сделано следующим образом:

struct sockaddr *address; // this is the address 
struct sockaddr_in6 *addrv6 = (struct sockaddr_in6 *)address; 
unsigned long address; 
memcpy(&address, addrv6->sin6_addr.s6_addr + 11, 4); 

В документации говорится, что адрес отображается в сетевом порядке (самый старший байт первый). Если это отличается от вашей машинной архитектуры, вам нужно вызвать htonl(), чтобы изменить порядок байтов.

+1

Вы не имеете в виду 'ntohl()'? – Jite

+0

'+ 12', а не' + 11', я думаю. –

1

Если вы хотите быть совместимым с другими типами адресов, используйте getnameinfo.

char hostbuf[NI_MAXHOST]; 
char *host; 

if (getnameinfo(addr, addrlen, hostbuf, sizeof(hostbuf), NULL, 0, NI_NUMERICHOST)) 
    ;//error 

if (strncmp(hostbuf, "::ffff:", sizeof("::ffff:") - 1) == 0) 
    host = hostbuf + sizeof("::ffff:") - 1; 
else 
    host = hostbuf; 
+0

Вам нужно использовать 'strlen (" :: ffff: ")' вместо 'sizeof (" :: ffff: ")'. –

+0

@RemyLebeau нет, это работает. Он включает в себя байт NUL, хотя, следовательно, '- 1'. –

+0

Вы предполагаете, что 'sizeof (" :: ffff: ")' интерпретируется так же, как 'sizeof (char [8])' для всех компиляторов, что не является гарантией. Его можно интерпретировать как 'sizeof (char *)' вместо этого, который вернет 4 в 32-битных системах и 8 в 64-битных системах. Правильное значение, необходимое для этого кода, равно 7, что означает, что 'strlen (" :: ffff: ")' возвращает без необходимости '-1'. –

 Смежные вопросы

  • Нет связанных вопросов^_^