C/C++ getnameinfo ai_family 仅在 macOS 上不受支持
C/C++ getnameinfo ai_family not supported only on macOS
如果 IPv6 或某些虚拟接口可用,以下代码将不再适用于 macOS。
我总是得到错误 getnameinfo() failed: Unknown error (ai_family not supported)
知道这有什么问题吗?我只需要一个正确的 ipv4 和 internet 网络接口。
问题首先出现在 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;
}
我知道已经有另一个,但我不太明白答案
出现此问题是因为 IPV4 和 IPV6 的大小不同。考虑以下两行代码
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);
如果地址族是 AF_INET,sizeof 操作将适用于您的代码,但对于 IPV6 又名 AF_INET6 你必须使用 sizeof(sockaddr_in6);,它们都有不同的大小(分别为 4 和 6 字节) .
你可以做的是以下-
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;
}
然后在调用 getnameinfo 时使用长度。
编辑
@leo_poldX, 好像是因为if (ifa->ifa_addr->sa_family != AF_INET)
继续;,其余代码不应针对 IPV6 执行。你能检查一下是否正确吗?
i got always the error getnameinfo() failed: Unknown error (ai_family not supported)
根据您代码中该消息的来源,很明显它是由以下情况引起的
s2 = getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in), subnet, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
失败,错误代码为 EAI_FAMILY
,即使前面的
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
成功。根据具体的错误代码,失败可能是由于 ifa->ifa_netmask->ai_family
被设置为不同于 AF_INET
的值,或者
- 一个不同的已知家族,其地址需要更大的地址结构,或者
- 一个无效/未知的家庭。
我可以想象其中任何一种可能出现的方式,但无论哪种方式,我都将其描述为 getifaddrs()
到 return 任何从中提取地址和网络掩码的条目的错误不同的地址系列。
合理的缓解措施取决于问题的具体性质。例如,
- 如果整个条目完全无效,那么您应该检测到并跳过它。
- 如果网络掩码数据采用 IPv4 地址形式,但系统未能[正确]设置系列,那么您可以尝试检测这种情况并在调用
getnameinfo()
之前更正它。
- 如果网络掩码是 IPv6 地址的形式,那么您可以检测到它并将其读取为 IPv6 地址,然后找出从那里去的地方。结果可能是编码为 IPv6 地址的 IPv4 地址,在这种情况下,您可以从后者中提取前者。
- 如果您可以不用子网掩码,那么在这种情况下您可以将其虚拟化,或者甚至可以将其从方法结果中完全删除。
如果 IPv6 或某些虚拟接口可用,以下代码将不再适用于 macOS。
我总是得到错误 getnameinfo() failed: Unknown error (ai_family not supported)
知道这有什么问题吗?我只需要一个正确的 ipv4 和 internet 网络接口。
问题首先出现在 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;
}
我知道已经有另一个
出现此问题是因为 IPV4 和 IPV6 的大小不同。考虑以下两行代码
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);
如果地址族是 AF_INET,sizeof 操作将适用于您的代码,但对于 IPV6 又名 AF_INET6 你必须使用 sizeof(sockaddr_in6);,它们都有不同的大小(分别为 4 和 6 字节) .
你可以做的是以下-
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;
}
然后在调用 getnameinfo 时使用长度。
编辑
@leo_poldX, 好像是因为if (ifa->ifa_addr->sa_family != AF_INET) 继续;,其余代码不应针对 IPV6 执行。你能检查一下是否正确吗?
i got always the error getnameinfo() failed: Unknown error (ai_family not supported)
根据您代码中该消息的来源,很明显它是由以下情况引起的
s2 = getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in), subnet, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
失败,错误代码为 EAI_FAMILY
,即使前面的
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
成功。根据具体的错误代码,失败可能是由于 ifa->ifa_netmask->ai_family
被设置为不同于 AF_INET
的值,或者
- 一个不同的已知家族,其地址需要更大的地址结构,或者
- 一个无效/未知的家庭。
我可以想象其中任何一种可能出现的方式,但无论哪种方式,我都将其描述为 getifaddrs()
到 return 任何从中提取地址和网络掩码的条目的错误不同的地址系列。
合理的缓解措施取决于问题的具体性质。例如,
- 如果整个条目完全无效,那么您应该检测到并跳过它。
- 如果网络掩码数据采用 IPv4 地址形式,但系统未能[正确]设置系列,那么您可以尝试检测这种情况并在调用
getnameinfo()
之前更正它。 - 如果网络掩码是 IPv6 地址的形式,那么您可以检测到它并将其读取为 IPv6 地址,然后找出从那里去的地方。结果可能是编码为 IPv6 地址的 IPv4 地址,在这种情况下,您可以从后者中提取前者。
- 如果您可以不用子网掩码,那么在这种情况下您可以将其虚拟化,或者甚至可以将其从方法结果中完全删除。