У меня есть структура struct sockaddr
, содержащая IPv4-отображаемый IPv6-адрес, такой как ::ffff:10.0.0.1
. Я хочу получить только версию IPv4 в строке (в данном случае 10.0.0.1
) на языке программирования C. Как мне добиться этого?Как преобразовать IPv4-отображаемый-IPv6-адрес в IPv4 (строковый формат)?
ответ
Поскольку ваша структура содержит адрес 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));
После того, как вы распознали 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(), чтобы изменить порядок байтов.
Вы не имеете в виду 'ntohl()'? – Jite
'+ 12', а не' + 11', я думаю. –
Если вы хотите быть совместимым с другими типами адресов, используйте 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;
Вам нужно использовать 'strlen (" :: ffff: ")' вместо 'sizeof (" :: ffff: ")'. –
@RemyLebeau нет, это работает. Он включает в себя байт NUL, хотя, следовательно, '- 1'. –
Вы предполагаете, что 'sizeof (" :: ffff: ")' интерпретируется так же, как 'sizeof (char [8])' для всех компиляторов, что не является гарантией. Его можно интерпретировать как 'sizeof (char *)' вместо этого, который вернет 4 в 32-битных системах и 8 в 64-битных системах. Правильное значение, необходимое для этого кода, равно 7, что означает, что 'strlen (" :: ffff: ")' возвращает без необходимости '-1'. –
Или используйте 'inet_ntoa()' вместо 'Sprintf()' –
Хороший момент. Я отредактировал свой ответ, чтобы использовать 'inet_ntop', который рекомендуется использовать для' inet_ntoa', потому что вместо статического он использует локальный буфер (и, следовательно, потокобезопасен). –
AFAIK, статический буфер, используемый 'inet_ntoa()', объявляется в хранилище TLS, поэтому он является потокобезопасным. Но если ваша платформа имеет 'inet_ntop()', тем лучше. –