【问题标题】:C/C++ getnameinfo ai_family not supported only on macOS仅在 macOS 上不支持 C/C++ getnameinfo ai_family
【发布时间】:2022-01-13 22:33:51
【问题描述】:

如果 IPv6 或某些虚拟接口可用,则以下代码不再适用于 macOS。

我总是收到错误getnameinfo() failed: Unknown error (ai_family not supported)

知道这有什么问题吗?我只需要一个带有 ipv4 和互联网的正确网络接口。

问题首先出现在 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;
}

我知道已经有另一个similar question,但我不太明白答案

【问题讨论】:

  • 不会立即解决问题,但 Java_bla_JNISubNetMask_getSubNetMask 会泄漏内存。
  • ifa-&gt;ifa_netmask-&gt;sa_family == AF_INET?

标签: c++ c sockets networking


【解决方案1】:

出现问题是因为 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_INETsizeof 操作将适用于您的代码,但对于 IPV6 aka 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 执行。你能检查它是否正确吗?

【讨论】:

  • “问题很明显”是无用的,因为它是主观的。我有幸与大学里那些对广义相对论很清楚的人一起工作。
  • @Bathsheba 对不起,如果我冒犯了你。我正在编辑我的帖子。
  • 现在读起来更好。谢谢,请投赞成票。
  • @NaseefChowdhury 你是对的。 ifa-&gt;ifa_addr-&gt;sa_family != AF_INET 确实跳过了 IPv6 接口。问题是网络掩码。 IPv6 和虚拟接口只是掩盖了这一点。
【解决方案2】:

我总是得到错误 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-&gt;ifa_netmask-&gt;ai_family被设置为与AF_INET不同的值,要么

  • 一个不同的已知家族,其地址需要更大的地址结构,或
  • 一个无效/未知的家庭。

我可以想象其中任何一种可能出现的方式,但无论哪种方式,我都将其描述为一个错误,getifaddrs() 返回地址和网络掩码来自不同地址系列的任何条目。

合理的缓解措施取决于问题的具体性质。例如,

  • 如果整个条目完全无效,那么您应该检测到并跳过它。
  • 如果网络掩码数据采用 IPv4 地址的形式,但系统未能 [正确] 设置族,那么您可以尝试检测这种情况并更正它,然后再调用 getnameinfo()
  • 如果网络掩码采用 IPv6 地址的形式,那么您可以检测到它并将其作为 IPv6 地址读取,并找出从那里去哪里。结果可能是一个编码为 IPv6 地址的 IPv4 地址,在这种情况下,您可以从后者中提取前者。
  • 如果您可以不使用子网掩码,那么您可以在这种情况下将其虚拟化,或者甚至将其从方法结果中完全删除。

【讨论】:

  • 就是这样!谢啦。最后用if (s != 0) 替换if (s != 0 || s2 != 0),现在一切正常。谢谢大佬!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
相关资源
最近更新 更多