You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用systemd-resolved的Linux系统中获取真实DNS服务器IP

在systemd-resolved系统中获取真实DNS服务器IP的方法

问题原因

当使用res_ninit()或res_init()时,函数会读取/etc/resolv.conf的配置。在启用systemd-resolved的系统中,该文件通常是指向/run/systemd/resolve/stub-resolv.conf的符号链接,其中的nameserver被设置为127.0.0.53(存根解析器地址),而非真实的上游DNS服务器,因此你得到的是回环地址。

解决方案

方法1:读取systemd-resolved的真实resolv.conf文件

systemd-resolved会将真实的上游DNS配置写入/run/systemd/resolve/resolv.conf文件(该文件仅包含真实的DNS服务器地址,无存根解析器条目)。你可以直接解析这个文件来获取真实DNS。

示例代码:

#include <stdio.h>
#include <string.h>

#define RESOLVED_REAL_CONF "/run/systemd/resolve/resolv.conf"

int main() {
    FILE *fp = fopen(RESOLVED_REAL_CONF, "r");
    if (!fp) {
        perror("Failed to open resolv.conf");
        return 1;
    }

    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        if (strncmp(line, "nameserver", 10) == 0) {
            char *ip = strtok(line + 11, " \t\n");
            if (ip) {
                printf("真实DNS服务器: %s\n", ip);
                // 可以收集所有nameserver条目,这里只打印第一个
            }
        }
    }

    fclose(fp);
    return 0;
}

注意:该路径在多数systemd发行版中是标准的,但部分系统可能略有不同,可通过systemctl status systemd-resolved确认路径。

方法2:通过DBus接口查询systemd-resolved

这是最可靠的方法,因为它直接与systemd-resolved进程通信,获取实时的DNS配置(包括动态更新的DNS,比如通过DHCP获取的)。

示例代码(使用libdbus):

#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    DBusError err;
    DBusConnection *conn;
    DBusMessage *msg, *reply;
    DBusMessageIter iter, sub_iter;
    const char *ip;

    // 初始化DBus错误
    dbus_error_init(&err);

    // 连接到系统总线
    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "DBus连接失败: %s\n", err.message);
        dbus_error_free(&err);
        return 1;
    }
    if (!conn) {
        fprintf(stderr, "无法获取DBus连接\n");
        return 1;
    }

    // 创建查询消息
    msg = dbus_message_new_method_call(
        "org.freedesktop.resolve1",
        "/org/freedesktop/resolve1",
        "org.freedesktop.resolve1.Manager",
        "GetLinkDNS"
    );
    if (!msg) {
        fprintf(stderr, "无法创建DBus消息\n");
        dbus_connection_unref(conn);
        return 1;
    }

    // 获取默认网络接口的索引(这里假设使用第一个接口,或替换为你需要的接口索引)
    // 可以通过`ip link`查看接口索引,通常默认路由的接口是主要的
    uint32_t ifindex = 2; // 示例:eth0或wlan0的索引,需根据实际情况调整
    dbus_message_append_args(msg, DBUS_TYPE_UINT32, &ifindex, DBUS_TYPE_INVALID);

    // 发送消息并等待回复
    reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
    dbus_message_unref(msg);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "DBus调用失败: %s\n", err.message);
        dbus_error_free(&err);
        dbus_connection_unref(conn);
        return 1;
    }
    if (!reply) {
        fprintf(stderr, "未收到DBus回复\n");
        dbus_connection_unref(conn);
        return 1;
    }

    // 解析回复中的DNS列表
    if (!dbus_message_iter_init(reply, &iter)) {
        fprintf(stderr, "无法解析DBus回复\n");
        dbus_message_unref(reply);
        dbus_connection_unref(conn);
        return 1;
    }

    if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
        dbus_message_iter_recurse(&iter, &sub_iter);
        while (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_ARRAY) {
            DBusMessageIter ip_iter;
            dbus_message_iter_recurse(&sub_iter, &ip_iter);
            if (dbus_message_iter_get_arg_type(&ip_iter) == DBUS_TYPE_STRING) {
                dbus_message_iter_get_basic(&ip_iter, &ip);
                printf("真实DNS服务器: %s\n", ip);
            }
            dbus_message_iter_next(&sub_iter);
        }
    }

    dbus_message_unref(reply);
    dbus_connection_unref(conn);
    return 0;
}

编译时需要链接libdbus:gcc -o get_dns get_dns.c -ldbus-1

方法3:使用libsystemd-resolve库

systemd提供了专门的库libsystemd-resolve,可以直接调用API获取DNS配置,这是最简洁的官方方法。

示例代码:

#include <systemd/resolve.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    _sd_resolve_context *ctx = NULL;
    int r;

    // 初始化解析上下文
    r = sd_resolve_context_new(&ctx);
    if (r < 0) {
        fprintf(stderr, "初始化上下文失败: %s\n", strerror(-r));
        return 1;
    }

    // 获取默认链接的DNS服务器
    sd_resolve_link *link = NULL;
    r = sd_resolve_context_get_link(ctx, 0, &link); // 0表示默认链接
    if (r < 0) {
        fprintf(stderr, "获取链接失败: %s\n", strerror(-r));
        sd_resolve_context_unref(ctx);
        return 1;
    }

    // 遍历DNS服务器列表
    const struct in_addr *dns;
    unsigned int n_dns;
    r = sd_resolve_link_get_dns(link, &dns, &n_dns);
    if (r < 0) {
        fprintf(stderr, "获取DNS失败: %s\n", strerror(-r));
        sd_resolve_link_unref(link);
        sd_resolve_context_unref(ctx);
        return 1;
    }

    for (unsigned int i = 0; i < n_dns; i++) {
        char ip_str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &dns[i], ip_str, sizeof(ip_str));
        printf("真实DNS服务器: %s\n", ip_str);
    }

    // 释放资源
    sd_resolve_link_unref(link);
    sd_resolve_context_unref(ctx);
    return 0;
}

编译时需要链接libsystemd:gcc -o get_dns get_dns.c -lsystemd

总结

  • 读取/run/systemd/resolve/resolv.conf是最简单的方法,但不适用于动态更新的DNS配置。
  • DBus接口和libsystemd-resolve方法能获取实时、动态的DNS配置,是推荐的方案,尤其是在需要处理DHCP或VPN动态分配DNS的场景。

内容的提问来源于stack exchange,提问作者Gogeta70

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:50:32