the following code does not work on macOS anymore if IPv6 or some virtual interfaces are available.
i got always the error getnameinfo() failed: Unknown error (ai_family not supported)
any idea whats wrong with this? i only need a correct network interface with ipv4 and internet.
The problem first appeared with macOS Sierra.
#include "jni.h"
#include "bla_nativeclasses_JNISubNetMask.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
static jobjectArray make_row(JNIEnv *env, jsize count, const char* elements[])
{
jclass stringClass = (*env)->FindClass(env, "java/lang/String");
jobjectArray row = (*env)->NewObjectArray(env, count, stringClass, 0);
jsize i;
for (i = 0; i < count; ++i) {
(*env)->SetObjectArrayElement(env, row, i, (*env)->NewStringUTF(env, elements[i]));
}
return row;
}
JNIEXPORT jobjectArray JNICALL Java_bla_JNISubNetMask_getSubNetMask(JNIEnv *env, jobject jobj){
struct ifaddrs *ifaddr, *ifa;
int family, s ,s2;
int i = 0;
int count = 0;
char host[NI_MAXHOST];
char subnet[NI_MAXHOST];
char *tmp = NULL;
const char* net[1000];
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}
/* Walk through linked list, maintaining head pointer so we can free list later */
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
s2 = getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in), subnet, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s != 0 || s2 != 0) {
printf("getnameinfo() failed: %s (%s)\n", gai_strerror(s), gai_strerror(s2));
exit(EXIT_FAILURE);
}
tmp = (char *)malloc(100*sizeof(char));
strcpy (tmp,ifa->ifa_name);
net[i++] = tmp;
tmp = (char *)malloc(100*sizeof(char));
strcpy (tmp,host);
net[i++] = tmp;
tmp = (char *)malloc(100*sizeof(char));
strcpy (tmp,subnet);
net[i++] = tmp;
}
freeifaddrs(ifaddr);
count = i;
jobjectArray jnet = make_row(env, count, net);
return jnet;
}
I know that there was already another similar question, but I don't really understand the answer
The problem is occurring because IPV4 and IPV6 have different sizes. Consider the following two lines of your code
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
s2 = getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in), subnet, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
the sizeof operation will work fine for your code if the address familyis AF_INET, but for IPV6 aka AF_INET6 you have to use sizeof(sockaddr_in6);, they both have different size (4 and 6 bytes respectively).
what you can do is the following -
int len;
switch (fa->ifa_addr->sa_family) {
case AF_INET:
len = sizeof(sockaddr_in);
break;
case AF_INET6:
len= sizeof(sockaddr_in6);
break;
default:
std::cerr << "Unknown Address family\n";
break;
}
then use the length on you call to getnameinfo.
EDIT
#leo_poldX, It seems because of if (ifa->ifa_addr->sa_family != AF_INET)
continue;, the rest of code should not execute for IPV6. Can you check if it is correct?
i got always the error getnameinfo() failed: Unknown error (ai_family not supported)
Based on the origin of that message in your code, it seems clear that it arises from a case where
s2 = getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in), subnet, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
fails with error code EAI_FAMILY, even though the immediately preceding
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
succeeded. Based on the specific error code, the failure probably arises from ifa->ifa_netmask->ai_family being set to a value different from AF_INET, either
a different, known family whose addresses require a larger address structure, or
an invalid / unknown family.
I can imagine ways in which either one might arise, but either way, I would characterize it as a bug for getifaddrs() to return any entry in which the address and netmask were drawn from different address families.
Plausible mitigations depend on the specific nature of the problem. For example,
if the overall entry is simply invalid then you should detect that and skip it.
if the netmask data are in the form of an IPv4 address, but the system has failed to set the family [correctly] then you could try to detect that case and correct it before calling getnameinfo().
if the netmask is in the form of an IPv6 address, then you could detect that and read it as an IPv6 address, and figure out where to go from there. It might be that the result is an IPv4 address encoded as an IPv6 address, in which case you could extract the former from the latter.
if you can do without the subnet mask, then you could just dummy it up in this case, or perhaps even remove it from the method result altogether.
Related
I have to send to the server the IP address and MAC address of the network interface from which my client socket is connected and communicating with the server.
All machines are on intra-net.
I have extracted the IP of my socket and I am attempting to extract the H/W address.
My strategy :
Extract IP of the socket using getsockname() system call.
Use getifaddrs() system call to list all available network interfaces. Inside a for-loop I am using getnameinfo() system call to find IP for currently iterating interface name and then compare this IP with socket IP (extracted from step 1) to find interface name of the connected socket.
Use ioctl(fd, SIOCGIFHWADDR, &ifr) system call to get H/W address using interface name found out in stage 2.
I am facing problem getnameinfo() system call.
If I don't type cast the first parameter to (struct sockaddr_in*) I get the following error : ai_family not supported
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
If I type cast the first parameter to (struct sockaddr_in*) I get the following error : error: cannot convert ‘sockaddr_in*’ to ‘const sockaddr*’ for argument ‘1’ to ‘int getnameinfo(const sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, int)’
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
Kindly advice. I am even open to some alternative strategy to programmatically and dynamically get Socket IP and MAC address.
bool Ethernet::getIp(void)
{
struct sockaddr_in addr;
char bufferIp[INET_ADDRSTRLEN];
socklen_t addrLen = sizeof(addr);
if(getsockname(this->clientSocket, (struct sockaddr*) &addr, &addrLen) == -1)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "Unable to extract IP address of socket";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
if(inet_ntop(AF_INET, &addr.sin_addr, bufferIp, INET_ADDRSTRLEN) == NULL)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "Unable to convert extracted IP address from binary to char* in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
this->ip = string(bufferIp);
return RETURN_SUCCESS;
}
bool Ethernet::getMac(void)
{
int fd;
struct ifreq ifr;
// char *iface = "eth0";
unsigned char *mac;
fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name , this->interfaceName.c_str(), IFNAMSIZ-1);
ioctl(fd, SIOCGIFHWADDR, &ifr);
close(fd);
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
//display mac address
printf("Mac : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return RETURN_SUCCESS;
}
bool Ethernet::getInterfaceDetails(void)
{
struct ifaddrs *ifaddr, *ifa;
int s;
char host[NI_MAXHOST];
string tempAddr;
char buffer[INET_ADDRSTRLEN];
if (getifaddrs(&ifaddr) == -1)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "System call 'getifaddrs' failed in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
this->interfaceName = string(ifa->ifa_name);
if(this->interfaceName == string("lo"))
continue;
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if(s != 0)
{
string errStr = gai_strerror(s);
cout << "Error : " << errStr << endl;
FileOperations fo;
string str;
str = "Unable to convert extracted IP address address from binary to char* in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
tempAddr = string(host);
if(tempAddr == this->ip)
{
freeifaddrs(ifaddr);
return RETURN_SUCCESS;
}
}
return RETURN_FAILURE;
}
I see one strange thing regarding your call to getnameinfo: You are assuming that a non-null ifa_addr equals sockaddr_in type, but I could imagine you could get other types, e.g. sockaddr_in6. So you should check ifa_addr->sa_family field to make sure it's AF_INET. Perhaps you should handle IPv6 as well?
My theory here is that calling getnameinfo with a struct size that does not match what would be expected for the address family might be the reason for your error.
Also look at MAC address with getifaddrs for a related discussion.
The getifaddrs manual says
The ifa_addr field points to a structure containing the interface address. (The sa_family subfield should be consulted to determine the format of the address structure.) This field may contain a null pointer.
Thus
check that the field is not a null pointer (if it is, then it is not the IP you're looking for)
the length parameter must match the length of the addresses in sa_family / or filter just AF_INET.
But wouldn't it be easier to just create a datagram socket to the server then ask what's its address, or actually do the http connection and ask what mac address that socket is using?
If this is embedded Linux and the interface is always named the same, just read from /sys/class/net/eth0/address - much easier.
#Antti Haapala, #Mats;
Thanks for the help.
The problem was as you mentioned that other types/families of addresses where present in the interfaces and where causing problems to getnameinfo() system call.
Now I am filtering out other addresses and only allowing AF_INET.
I have added the following validation :
if(ifa->ifa_addr->sa_family != AF_INET)
continue;
I am getting an error when I try to connect to my ipv4 server. Currently the ios app users are required to enter their sever's an IP address, port, and account information.
The ios app then calls Connect on the SocketSender class (included in the header search path) which in turns calls the connect function of Socket.h and then checks the results.
Connect - SocketSender.cpp
bool SocketSender::Connect (const char *host, int port, CApiError &err)
{
errno = 0;
struct hostent *hostinfo;
hostinfo = gethostbyname (host);
if (!hostinfo) {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
#else
/* Linux stores the gethostbyname error in h_errno. */
m_nLastErrorNo = EINVAL; // h_errno value is incompatible with the "normal" error codes
err.SetError(FIX_SN(h_errno, hstrerror(h_errno)), CATEGORY_SYSTEM | ERR_TYPE_ERROR);
#endif
return false;
}
socket_fd = socket (AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
return false;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons (port);
address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
int result;
SetSocketOptions();
result = connect (socket_fd, (struct sockaddr *) &address, sizeof (address));
if (result == -1) {
if (IS_IN_PROGRESS()) {
fd_set f1,f2,f3;
struct timeval tv;
/* configure the sets */
FD_ZERO(&f1);
FD_ZERO(&f2);
FD_ZERO(&f3);
FD_SET(socket_fd, &f2);
FD_SET(socket_fd, &f3);
/* we will have a timeout period */
tv.tv_sec = 5;
tv.tv_usec = 0;
int selrez = select(socket_fd + 1,&f1,&f2,&f3,&tv);
if (selrez == -1) { // socket error
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (FD_ISSET(socket_fd, &f3)) { // failed to connect ..
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (!FD_ISSET(socket_fd, &f2)) { // cannot read, so some (unknown) error occured (probably time-out)
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#ifndef PLATFORM_WIN32 // FIXME: is the same needed for windows ?
// unix always marks socket as "success", however error code has to be double-checked
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
err.SetSystemError();
return false;
}
if(error != 0) {
m_nLastErrorNo = error;
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#endif
} else {
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
}
m_nIP = ntohl(address.sin_addr.s_addr);
m_bServerSocket = false;
return true;
}
That is the original version that worked without any problems. When i changed the above to use AF_INET6 and in_addr6->sin6_addr, i kept getting errors and the application failed to connect. I tried using getaddrinfo but this still did not connect.
struct addrinfo hints, *res, *res0;
int error;
const char *cause = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(host, "PORT", &hints, &res0);
if (error) {
errx(1, "%s", gai_strerror(error));
/*NOTREACHED*/
}
socket_fd = -1;
printf("IP addresses for %s:\n\n", host);
int result;
void *addr;
char *ipver;
for (res = res0; res!=NULL; res = res->ai_next) {
socket_fd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (socket_fd < 0) {
cause = "socket";
continue;
}
if ((result = connect(socket_fd, res->ai_addr, res->ai_addrlen)) < 0) {
cause = "connect";
close(socket_fd);
socket_fd = -1;
continue;
}
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (res->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
SetSocketOptions();
break; /* okay we got one */
}
I need to make it backwards compatible with ipv6 and ipv4. Any help would be much appreciated as i have been stuck testing this for the past week. Also if anyone knows how to debug the SocketSender.cpp on XCode that would be alot of help.
So after two weeks of testing out different approaches and familiarizing myself with networking (POSIX) I finally got this to work mainly due to #user102008 suggestion.
This is relevant to Client-Server applications.My application is a client application that connects to a IPv4 server/system at a remote location. We have yet to support IPv6 for our products which include clients(iOS,android,windows,unix) and servers (windows & unix), but will support upon future releases. The reason for this support was solely due to Apple changing their apple review process environment.
Approach, Tips and Issues
Apple has provided a way to test IPv6 compatibility with your app. This is sharing your connection from your Ethernet using NAT64/DNS64. This failed many times for me. After researching and resetting my SMC, I came across this article and realized i may have been messing with the configuration too much. So I reset my SMC, restarted and created the internet sharing host. Always remember to turn off WiFi before making any changes to internet sharing.
Users were required to connect to the server with a IPv4 IP address. The Application ran perfectly on an IPv4 networking but failed in an IPv6 network. This was due to the app not resolving the IP address literal. The networking library my application uses was a cpp library that was included as a preprocess macro. One of the biggest annoyance was trying to debug, because you cant debug compile time code. So what I did was move over my cpp files with their headers to the project (luckily it was only 3 files).
IMPORTANT TO AYONE PASSING PORT NUMBERS. This is tied to #2 and resolving the IPv4 literal. I used apples exact implementation on the networking overview (listing 10-1). Every time i tested, the connect function was returning -1, meaning it did not connect. Thanks to #user102008 providing me with this article, I realized apple implementation of getaddrinfo was broken when trying to pass a string literal for the port. Yes, they ask for a constant char, even when trying c_str() it would still return port number of 0. For this reason, an apple developer who Ive noticed answer and addresses countless networking problems provided a work around. This fixed my issue of the port continuously returning 0, code is posted below as well. What i did, was simply add this into my networking class (SocketSender.cpp) and instead of calling getaddrinfo in Connect, i called get getaddrinfo_compat. This allowed me to connect perfectly in IPv4 and IPv6 networks.
static int getaddrinfo_compat(
const char * hostname,
const char * servname,
const struct addrinfo * hints,
struct addrinfo ** res
) {
int err;
int numericPort;
// If we're given a service name and it's a numeric string, set `numericPort` to that,
// otherwise it ends up as 0.
numericPort = servname != NULL ? atoi(servname) : 0;
// Call `getaddrinfo` with our input parameters.
err = getaddrinfo(hostname, servname, hints, res);
// Post-process the results of `getaddrinfo` to work around <rdar://problem/26365575>.
if ( (err == 0) && (numericPort != 0) ) {
for (const struct addrinfo * addr = *res; addr != NULL; addr = addr->ai_next) {
in_port_t * portPtr;
switch (addr->ai_family) {
case AF_INET: {
portPtr = &((struct sockaddr_in *) addr->ai_addr)->sin_port;
} break;
case AF_INET6: {
portPtr = &((struct sockaddr_in6 *) addr->ai_addr)->sin6_port;
} break;
default: {
portPtr = NULL;
} break;
}
if ( (portPtr != NULL) && (*portPtr == 0) ) {
*portPtr = htons(numericPort);
}
}
}
return err;
}
I actually save IP (address.sin_addr.s_addr) in a long data type that is a private variable, m_nIP. problem was i didnt need the IPv6 as our entire product groups use IPv4. Solved this using the code is below.
const uint8_t *bytes = ((const struct sockaddr_in6 *)addrPtr)->sin6_addr.s6_addr;
bytes += 12;
struct in_addr addr = { *(const in_addr_t *)bytes };
m_nIP = ntohl(addr.s_addr);
RELEVANT GUIDES Beej's Guide to Network Programming, UserLevel IPv6 Intro, Porting Applications to IPv6
I am writing a Java interposer (using LD_PRELOAD method) that modifies the recipient information in network communication system calls (connect/sendto).
Whenever Java tries to connect to another socket, I modify the intended recipient IP and port. Java uses IPv4-mapped-IPv6 addresses. So, I need to extract the IPv4 part of it. I achieve this using the method prescribed by Nicolas Bachschmidt at link.
The problem I am facing is that for every IPv4-mapped-IPv6 address, the result string (IPv4 part) I obtain is always 0.0.0.1. Instead it should be 10.0.0.1 (for ::ffff:10.0.0.1). I have tried this with different IP addresses. The result is always the same.
Two things I would like to mention that I think may be related:
When I tested the same program a month ago on my local network (that has 192.168.1.XXX IP addresses), the program worked correctly. Point being (I don't think) there is any problem with code. To verify this, I asked a question on stackoverflow to convert IPv4-mapped-IPv6 addresses to IPv4, the link of which is mentioned earlier).
I am trying to test this program now on my university network (that has 10.XXX.XXX.XXX IP addresses) and VirtualBox (NAT mode that also gives 10.XXX.XXX.XXX addresses). However, I have tried to connect to 10.0.0.1 and 12.0.0.1 in these cases. Both give 0.0.0.1.
What am I doing wrong?
UPDATE: In Java, socket connection is done by the usual method:
Socket conn = new Socket("10.0.0.1", 50021);
The code to interpose this connect() system call is as follows:
int connect(int fd, const struct sockaddr *sk, socklen_t sl)
{
struct sockaddr_in *lsk_in = (struct sockaddr_in *) sk;
struct sockaddr_in6 *lsk_in6 = (struct sockaddr_in6 *) sk;
struct sockaddr_in addr4;
unsigned int len;
int nbytes, oport, tport, ret, i;
char ip_address[30];
char buffer[1024];
char tempBuffer[1024];
if((lsk_in->sin_family == AF_INET) || (lsk_in->sin_family == AF_INET6))
{
if(lsk_in->sin_family == AF_INET)
{
oport = ntohs(lsk_in->sin_port);
memcpy(&addr4.sin_addr.s_addr, &lsk_in->sin_addr.s_addr, sizeof(addr4.sin_addr.s_addr));
}
else if(lsk_in->sin_family == AF_INET6)
{
oport = ntohs(lsk_in6->sin6_port);
//This is where the problem is. I always get 0.0.0.1
memcpy(&addr4.sin_addr.s_addr, lsk_in6->sin6_addr.s6_addr+12, sizeof(addr4.sin_addr.s_addr));
}
memset(buffer, '\0', sizeof(buffer));
sprintf(buffer, "%s%c%s%c%i", NAT_VM_CONNECT_RULE, NAT_VM_DELIMITER, (char *)inet_ntoa(addr4.sin_addr), NAT_VM_DELIMITER, oport);
nbytes = send(sock, buffer, strlen(buffer), 0);
if(DEBUG_MODE)
fprintf(stdout, "[LD_INTERPOSER] Sent[%s]\n", buffer);
memset(buffer, '\0', sizeof(buffer));
nbytes = recv(sock, buffer, sizeof(buffer), 0);
fprintf(stderr, "[LD_INTERPOSER] Received CONNECT [%s]\n", buffer);
memset(ip_address, '\0', sizeof(ip_address));
int pos = strrchr(buffer, NAT_VM_DELIMITER) - buffer;
strncpy(ip_address, buffer, pos);
ip_address[pos] = '\0';
tport = atoi(buffer + pos + 1);
if(lsk_in->sin_family == AF_INET)
{
lsk_in->sin_addr.s_addr = inet_addr(ip_address + 7);
lsk_in->sin_port = htons(tport);
}
else if(lsk_in->sin_family == AF_INET6)
{
inet_pton(AF_INET6, ip_address, &(lsk_in6->sin6_addr));
lsk_in6->sin6_port = htons(tport);
}
fprintf(stderr, "[LD_INTERPOSER] IP[%s], Port[%d] for VM[%s]\n", ip_address, tport, vm_ip);
}
return real_connect(fd, sk, sl);
}
Thanks to #ugoren hex dump technique (in comments), I was able to figure out that the IPv6 structure itself contained a 0.0.0.1 address. I realized that the problem may be due to different JDKs. The Java project was built using OpenJDK 7 while the PC I was using had OpenJDK 6. When I updated the JDK to version 7, the error disappeared. However, it has landed me to another error which is documented at a new stackoverflow question which I am still unable to resolve.
I am currently working on an IPv6 class and use inet_pton to retrieve the actual binary representation of the IP from a string i.e.:
AdressV6::AdressV6(const String & _ip)
{
int result = inet_pton(AF_INET6, _ip.c_str(), &(m_nativeAdress));
if(result <= 0)
//throw...
//How can I retrieve the sope ID from that?
}
Is there a common way to do that? Do you just manually parse the string and look for the "%" that does not sound very bullet proof :(
Thank you!
I tried manual parsing for now which seems to work. Still, if there is a better way please let me know:
//retrieve scope ID
uint32 scopeId = 0;
size_t pos = _ip.find("%");
if(pos != String::npos)
{
String theId = _ip.substr(pos+1);
scopeId = atoi(theId.c_str());
}
m_scopeId = scopeId;
On BSD and BSD based systems (this includes MacOS X for example), the scope ID is embedded into the address itself for link local addresses as the second 16 bit word. Please refer to the FreeBSD Handbook and search for "8.1.1.3 Scope Index" (without the quotes).
So assuming that intf1 has scope ID 1 and intf2 has scope ID 2, inet_pton() will convert the strings as follows on these platforms:
"fe80::1234%intf1" -> fe80:1::1234
"fe80::1234%intf2" -> fe80:2::1234
"fe80::1234" -> fe80::1234
The last address is simply unscoped and thus cannot be really used for sending out data.
Please note that this is non-standard; inet_pton() does not work that way on Linux or Windows based systems. However, I think even on Linux and Windows based systems, inet_pton() allows a scope ID at the end, it will simply ignore it, though.
For non-link-local address, this trick doesn't work, of course, yet those addresses are usually not scoped. They can be scoped, but usually every interface has an own, unique interface IPv6 address, based on its interface identifier (even if you use DHCPv6, in which case it has a DHCP address assigned by the DHCP server, as well as the auto generated IPv6 interface address, unless this auto generation has been forbidden).
The struct sockaddr_in6 structure has a field for the scope ID but the RFC that defines this field (RFC 2553 - Section 3.3) does not really give much detail how this field is to be interpreted. It only says:
The mapping of sin6_scope_id to an interface or set of interfaces is
left to implementation and future specifications on the subject of
site identifiers.
So this field is entirely implementation specific.
If you want this field to be filled in correctly, and your code should be as cross-platform as possible, you should use getaddrinfo():
struct addrinfo hints;
struct addrinfo * result;
memset(&hints, 0, sizeof(hints));
// AI_NUMERICHOST prevents usage of DNS servers,
// it tells getaddrinfo that the input string is a numeric IP address.
hints.flags = AI_NUMERICHOST;
if (getaddrinfo("fe80::1234%intf1", NULL, &hints, &result) == 0) {
// result->ai_addr claims to be a pointer to struct sockaddr,
// in fact it will be a pointer to a struct sockaddr_in6 in our case.
struct sockaddr_in6 * so = (struct sockaddr_in6 *)result->ai_addr;
// It will be prefilled like this:
//
// so->sin6_family ==> AF_INET6;
// so->sin6_port ==> 0
// so->sin6_flowinfo ==> 0
// so->sin6_addr ==> fe80::1234
// so->sin6_scope_id ==> "intf1" as scope ID
// Do something with that sockaddr,
// e.g. set a port number and connect a socket to that address.
freeaddrinfo(result);
}
One extra tip: If you want to use the returned getaddrinfo() for a server socket (a socket that you want to bind locally and then call accept() on it), you should also set the passive flag:
hints.flags = AI_NUMERICHOST | AI_PASSIVE;
Not that it will play a role in most case but that is the correct way of using getaddrinfo().
inet_pton() does not support scope IDs. I don't know about other platforms, but on Windows you can use RtlIpv6StringToAddressEx() instead.
inet_pton() semi-supports scope identifiers, the scope is that it will not raise an error when parsing an address with one. The major limitation is that the parameter to the call is a struct in6_addr which does not contain a field for the scope identifier, the super structure struct sockaddr_in6 is required for that.
Easy way forward is to wrap getnameinfo() and getaddrinfo() with struct sockaddr parameters for convenience. For example,
socklen_t
sockaddr_len (
const struct sockaddr* sa
)
{
socklen_t sa_len;
switch (sa->sa_family) {
case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
default: sa_len = 0; break;
}
return sa_len;
}
int
sockaddr_ntop (
const struct sockaddr* restrict sa,
char* restrict host,
size_t hostlen
)
{
return getnameinfo (sa, sockaddr_len (sa),
host, hostlen,
NULL, 0,
NI_NUMERICHOST);
}
int
sockaddr_pton (
const char* restrict src,
struct sockaddr* restrict dst /* will error on wrong size */
)
{
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM, /* not really */
.ai_protocol = IPPROTO_TCP, /* not really */
.ai_flags = AI_NUMERICHOST
}, *result = NULL;
const int status = getaddrinfo (src, NULL, &hints, &result);
if (0 == status) {
memcpy (dst, result->ai_addr, result->ai_addrlen);
freeaddrinfo (result);
return 1;
}
return 0;
}
To answer the original premise but given a struct sockaddr, an additional API may be warranted, for example:
uint32_t
sockaddr_scope_id (
const struct sockaddr* sa
)
{
uint32_t scope_id;
if (AF_INET6 == sa->sa_family) {
struct sockaddr_in6 s6;
memcpy (&s6, sa, sizeof(s6));
scope_id = s6.sin6_scope_id;
} else
scope_id = 0;
return scope_id;
}
In my C++ linux application how can I get the IPs list reffering to an FQDN?
(static IPs + dynamic IPs)
10x
There is no principal difference between retrieving the mapping for a local and a fully qualified domain name. Therefore, you can call the getaddrinfo as you would with any other domain name. Note that there is no way to get the list of all IP addresses associated to a domain name because DNS servers are free to advertise only certain addresses or pick a few from a larger list. For example, google.com will usually map to servers on your continent.
Here's an example on how to use it:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
const char* domain = argc>1 ? argv[1] : "example.com";
struct addrinfo *result, *rp, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM; // TCP
int tmp = getaddrinfo(domain, NULL, &hints, &result);
if (tmp != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(tmp));
return 1;
}
for (rp = result;rp != NULL;rp = rp->ai_next) {
char buf[INET6_ADDRSTRLEN];
switch (rp->ai_family) {
case AF_INET:{
struct in_addr* a4 = & ((struct sockaddr_in*) rp->ai_addr)->sin_addr;
inet_ntop(rp->ai_family, a4, buf, sizeof(buf));
printf("IPv4: %s\n", buf);
break;}
case AF_INET6:{
struct in6_addr* a6 = & ((struct sockaddr_in6*) rp->ai_addr)->sin6_addr;
inet_ntop(rp->ai_family, a6, buf, sizeof(buf));
printf("IPv6: %s\n", buf);
break;
}}
}
freeaddrinfo(result);
return 0;
}
This will output:
IPv6: 2620:0:2d0:200::10
IPv4: 192.0.32.10
You need to use the getHostByName() function in the C++ sockets library. (here is an example)
It will give you back a hostent struct, from which you can get information like IP.