首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 透過 DNS API 查詢 MX 記錄完整實作

     編輯:關於C語言

前言

.NET 基礎類別庫(System.Net.Dns)僅支援 A/AAAA 記錄查詢,無法直接取得郵件交換器(MX)記錄。本文示範如何透過 P/Invoke 呼叫 Windows dnsapi.dll 中的 DnsQuery_W,完整解析 MX 記錄並取得郵件伺服器主機名稱與優先順序。

核心 P/Invoke 定義


using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;

public static class DnsMxResolver
{
    // 查詢選項
    private enum DnsQueryOptions : uint
    {
        Standard = 0,
        BypassCache = 0x8,
        NoRecursion = 0x4,
        // ... 其他選項略
    }

    // 記錄類型
    private enum DnsRecordType : ushort
    {
        MX = 15,
        // A = 1, AAAA = 28, etc.
    }

    // MX 記錄結構體(對應 DNS_RECORD 聯集中的 MX 部分)
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct DnsMxRecord
    {
        public IntPtr pNext;           // 下一筆記錄指標
        public string pName;           // 記錄名稱
        public ushort wType;           // 記錄類型
        public ushort wDataLength;     // 資料長度
        public uint flags;
        public uint dwTtl;
        public uint dwReserved;
        public IntPtr pNameExchange;   // 郵件伺服器主機名稱指標
        public ushort wPreference;     // 優先順序(數值越小優先級越高)
        public ushort Pad;
    }

    // 匯入 DnsQuery_W
    [DllImport("dnsapi.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
    private static extern int DnsQuery(
        [MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName,
        DnsRecordType wType,
        DnsQueryOptions options,
        IntPtr aipServers,        // IntPtr.Zero 使用系統預設 DNS
        ref IntPtr ppQueryResults,
        IntPtr pReserved);

    // 釋放記錄鏈結記憶體
    [DllImport("dnsapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern void DnsRecordListFree(IntPtr pRecordList, int freeType);

    /// 
    /// 查詢指定網域的 MX 記錄
    /// 
    /// 依優先順序排序的 (Preference, HostName) 元組列表
    public static List<(ushort Preference, string Host)> QueryMx(string domain)
    {
        if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            throw new PlatformNotSupportedException("僅支援 Windows NT 核心系列作業系統。");

        IntPtr ptr = IntPtr.Zero;
        int result = DnsQuery(ref domain, DnsRecordType.MX, DnsQueryOptions.BypassCache, IntPtr.Zero, ref ptr, IntPtr.Zero);
        if (result != 0)
            throw new Win32Exception(result, $"DnsQuery 失敗,錯誤碼:{result}");

        var list = new List<(ushort, string)>();
        try
        {
            IntPtr current = ptr;
            while (current != IntPtr.Zero)
            {
                var rec = Marshal.PtrToStructure(current);
                if (rec.wType == (ushort)DnsRecordType.MX)
                {
                    string host = Marshal.PtrToStringUni(rec.pNameExchange);
                    list.Add((rec.wPreference, host.TrimEnd('.'))); // 移除結尾點
                }
                current = rec.pNext;
            }
        }
        finally
        {
            DnsRecordListFree(ptr, 0); // 0 = DnsFreeRecordList
        }

        // 依優先順序排序
        list.Sort((a, b) => a.Item1.CompareTo(b.Item1));
        return list;
    }
}

使用範例


class Program
{
    static void Main()
    {
        try
        {
            var mxRecords = DnsMxResolver.QueryMx("microsoft.com");
            Console.WriteLine($"{"Priority",-10} {"Mail Server"}");
            Console.WriteLine(new string('-', 50));
            foreach (var (pref, host) in mxRecords)
            {
                Console.WriteLine($"{pref,-10} {host}");
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"查詢失敗:{ex.Message}");
        }
    }
}

輸出範例


Priority     Mail Server
--------------------------------------------------
10           microsoft-com.mail.protection.outlook.com
20           microsoft-com.mail.protection.outlook.com

進階議題與替代方案

需求方案
跨平台使用 DnsClient.NET (NuGet) 純 Managed 實作,支援 Linux/macOS
查詢其他記錄類型擴充 DnsRecordType 並定義對應結構體(A、AAAA、TXT、CNAME、SRV 等)
指定自訂 DNS 伺服器將 aipServers 參數傳入 IP4_ARRAY 結構體指標
非同步查詢包裝為 Task.Run(() => QueryMx(domain)) 或使用 DnsClient.NET 內建非同步 API

常見錯誤排查

  • 錯誤碼 9003 (DNS_ERROR_RCODE_NAME_ERROR):網域不存在。
  • 錯誤碼 9501 (DNS_INFO_NO_RECORDS):網域存在但無 MX 記錄(可能僅有 A 記錄)。
  • 記憶體洩漏:務必在 finally 區塊呼叫 DnsRecordListFree。
  • Unicode/ANSI 不匹配:CharSet.Unicode + DnsQuery_W + Marshal.PtrToStringUni 三者必須一致。

總結

雖然 .NET Core/.NET 5+ 仍未內建 MX 查詢,但透過 dnsapi.dll 即可在 Windows 環境獲得原生效能與完整功能。跨平台專案建議改用 DnsClient.NET 等成熟套件,避免維護 P/Invoke 細節。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved