How can I retrieve IP and port from a sockaddr_storage
instance with getnameinfo()
(ref)?
When I used inet_ntop
, it worked like normal, but when I replaced it with the getnameinfo
function, Windows returned an error:
Code 10047: An address incompatible with the requested protocol was used.
#ifdef USE_IPV6
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
#else
int fd = socket(AF_INET, SOCK_DGRAM, 0);
#endif
sockaddr_storage address;
int length = sizeof address;
char buffer[1];
recvfrom(fd, buffer, sizeof buffer, 0, (struct sockaddr *) &address, &length);
// Error
char ip[NI_MAXHOST];
char port[NI_MAXSERV];
int rc = getnameinfo((struct sockaddr *) &address, length, ip, sizeof ip, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV);
if (rc) WSAGetLastError(); // Error Code = 10047
// Works
#ifdef USE_IPV6
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) &address;
inet_ntop(AF_INET6, &sa->sin6_addr, ip, INET6_ADDRSTRLEN);
uint16_t port_ = ntohs(sa->sin6_port);
#else
struct sockaddr_in *sa = (struct sockaddr_in *) &address;
inet_ntop(AF_INET, &sa->sin_addr, ip, INET_ADDRSTRLEN);
uint16_t port_ = ntohs(sa->sin_port);
#endif
I took a look at your issue and was able to reproduce the error you were having.
Reproducible example:
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
int main(int, char **) {
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
#ifdef USE_IPV6
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
#else
SOCKET fd = socket(AF_INET, SOCK_DGRAM, 0);
#endif
sockaddr_storage address;
int length = sizeof(sockaddr_storage);
char buffer[1];
recvfrom(fd, buffer, sizeof buffer, 0, (struct sockaddr*)&address, &length);
// Error
char ip[NI_MAXHOST];
char port[NI_MAXSERV];
int rc = getnameinfo((struct sockaddr*)&address,
length,
ip,
sizeof(ip),
port,
sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc)
WSAGetLastError(); // Error Code = 10047
std::cout << "IP: " << ip << std::endl;
std::cout << "Port: " << port << std::endl;
// Works
#ifdef USE_IPV6
struct sockaddr_in6* sa = (struct sockaddr_in6*)&address;
inet_ntop(AF_INET6, &sa->sin6_addr, ip, INET6_ADDRSTRLEN);
uint16_t port_ = ntohs(sa->sin6_port);
#else
struct sockaddr_in* sa = (struct sockaddr_in*)&address;
inet_ntop(AF_INET, &sa->sin_addr, ip, INET_ADDRSTRLEN);
uint16_t port_ = ntohs(sa->sin_port);
#endif
}
The issue lies in the fact that you aren't setting the ss_family
field of the sockaddr_storage
structure.
To fix this, you need to specify the ss_family
value in the sockaddr_storage
as follows:
sockaddr_storage address;
address.ss_family = AF_INET;
Once you do this, you should not see error 10047 anymore and you should be able to print the contents of the ip and port buffers and see the respective information.
References: