2016-06-30 3 views
0

Я использую библиотеку, которая выполняет некоторые работы nework, и когда клиент подключается, эта библиотека предоставляет «struct sockaddr *», который содержит клиентский сокет. Я просто хотел, чтобы извлечь IP и порт, и я делаю это в настоящее время так:C/C++ getnameinfo ai_family не поддерживается

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

const std::string Client::prepareIPandPort(struct sockaddr *hostaddr) { 
    assert(hostaddr != nullptr); 

    std::string ipport; 
    char clienthost[NI_MAXHOST]; 
    char clientport[NI_MAXSERV]; 
    int result = getnameinfo(hostaddr, sizeof(*hostaddr), 
          clienthost, sizeof(clienthost), 
          clientport, sizeof(clientport), 
          NI_NUMERICHOST | NI_NUMERICSERV); 

    if (result != 0) { 
     std::cerr << "Error: " << gai_strerror(result) << std::endl; 
     ipport = "unknown"; 
    } else { 
     switch (hostaddr->sa_family) { 
      case AF_INET: 
       ipport = std::string {clienthost} + ":" 
         + std::string {clientport}; 
       break; 
      case AF_INET6: 
       ipport = "[" + std::string {clienthost} + "]:" 
         + std::string {clientport}; 
       break; 
      default: 
       ipport = "unknown"; 
     } 
    } 

    return ipport; 
} 

При использовании IPv4 на моем Mac, это работает. Если я использую это приложение на моем сервере Gentoo Linux с полной поддержкой IPv6, я только получаю:

Ошибки: ai_family не поддерживается

Соединительный клиент имеет AAAA и ip6 запись на месте.

Я добавил несколько цитат и напечатал hostaddr-> sa_family, который возвращает 10, это AF_INET6.

Почему это не работает? :-)

ответ

1

Вы не можете использовать sizeof(*hostaddr), потому что hostaddr является общим указателем sockaddr*. В разных семействах адресов используются разные типы sockaddr_..., которые не имеют одинакового размера как sockaddr. getnameinfo() необходимо знать true размер sockaddr_... структура, что hostaddr фактически указывает на, исходя из своего адреса семьи.

Per в Linux getnameinfo() documentation:

EAI_FAMILY
The address family was not recognized, or the address length was invalid for the specified family

sockaddr_in (IPv4) имеет тот же размер, как sockaddr, поэтому getnameinfo() "работает" для IPv4. Но (IPv6) больше sockaddr, поэтому getnameinfo() не удается.

Лучшим решением является пропуск абоненте в правильном размере:

const std::string Client::prepareIPandPort(struct sockaddr *hostaddr, int hostaddrlen) { 
    ... 
    int result = getnameinfo(hostaddr, hostaddrlen, ...); 
    ... 
} 

sockaddr_in ipv4host; 
... 
client.prepareIPandPort((sockaddr*)&ipv4host, sizeof(ipv4host)); 

sockaddr_in6 ipv6host; 
... 
client.prepareIPandPort((sockaddr*)&ipv6host, sizeof(ipv6host)); 

В противном случае, вы должны вычислить его:

const std::string Client::prepareIPandPort(struct sockaddr *hostaddr) { 
    ... 
    int hostaddrlen; 
    switch (hostaddr->sa_family) { 
     case AF_INET: 
      hostaddrlen = sizeof(sockaddr_in); 
      break; 
     case AF_INET6: 
      hostaddrlen = sizeof(sockaddr_in6); 
      break; 
     default: 
      std::cerr << "Error: " << gai_strerror(EAI_FAMILY) << std::endl; 
      return "unknown"; 
    } 
    int result = getnameinfo(hostaddr, hostaddrlen, ...); 
    ... 
} 
+0

Есть ли способ узнать, если sockaddr несет IPv4 или IPv6? –

+1

извините. конечно. я просто проверяю sa_family –

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