.NET ez 2011-11-29
域名系統(英文:Domain Name System,縮寫:DNS)是因特網的一項服務。它作為將域名和IP地址相互映射的一個分布式數據庫,能夠使人更方便的訪問互聯網。DNS 使用TCP和UDP端口53。當前,對於每一級域名長度的限制是63個字符,域名總長度則不能超過253個字符。
開始時,域名的字符僅限於ASCII字符的一個子集。2008年,ICANN通過一項決議,允許使用其它語言作為互聯網頂級域名的字符。使用基於Punycode碼的IDNA系統,可以將Unicode字符串映射為有效的DNS字符集。因此,諸如「x.臺灣」這樣的域名可以在地址欄直接輸入,而不需要安裝插件。但是,由於英語的廣泛使用,使用其他語言字符作為域名會產生多種問題,例如難以輸入,難以在國際推廣等。
利用WMI 控制 Microsoft DNS Server,以下為範例程式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
namespace DNSManager
{
#region Example usage code
class Program
{
static void Main(string[] args)
{
Console.Write("Connecting to the DNS Server...");
DNSServer d = new DNSServer("vex.nullify.net"); //my internal DNS Server, change to yours.
//You will need to be able to get to it using WMI.
Console.WriteLine("Connected to the DNS Server");
Console.Write("Creating a new zone as a test...");
try
{
d.CreateNewZone("testzone.uk.nullify.net.", DNSServer.NewZoneType.Primary);
Console.WriteLine("OK");
}
catch (Exception)
{
Console.WriteLine("Failed to create a new zone, it probably exists.");
}
Console.Write("Creating a DNS record as a test...");
try
{
d.CreateDNSRecord("testzone.uk.nullify.net.", "test1.testzone.uk.nullify.net. IN CNAME xerxes.nullify.net.");
Console.WriteLine("OK");
}
catch (Exception)
{
Console.WriteLine("Failed to create a new resource record, it probably exists");
}
Console.WriteLine("Getting a list of domains:");
foreach (DNSServer.DNSDomain domain in d.GetListOfDomains())
{
Console.WriteLine("\t"+domain.Name+" ("+domain.ZoneType+")");
//and a list of all the records in the domain:-
foreach (DNSServer.DNSRecord record in d.GetRecordsForDomain(domain.Name))
{
Console.WriteLine("\t\t"+record);
//any domains we are primary for we could go and edit the record now!
}
}
Console.WriteLine("Fetching existing named entry (can be really slow, read the warning):-");
DNSServer.DNSRecord[] records = d.GetExistingDNSRecords("test1.testzone.uk.nullify.net.");
foreach (DNSServer.DNSRecord record in records)
{
Console.WriteLine("\t\t" + record);
record.Target = "shodan.nullify.net.";
record.SaveChanges();
}
}
}
#endregion
#region Real code
/// <summary>
/// A Microsoft DNS Server class that abstracts out calls to WMI for MS DNS Server
/// </summary>
/// <remarks>
/// WMI Documentation:
/// http://msdn.microsoft.com/en-us/library/ms682123(VS.85).aspx
/// System.Management Documentation:
/// http://msdn.microsoft.com/en-us/library/system.management.managementobjectcollection%28VS.71%29.aspx
/// </remarks>
/// <c>(c) 2008 Simon Soanes, All Rights Reserved. No warranties express or implied.
/// DO NOT redistribute this source code publically without a link to the origin and this copyright
/// notice fully intact, also please send any modifications back to me at [email protected]
/// Including in your software is fine although attribution would be nice.</c>
public class DNSServer
{
#region Supporting classes
/// <summary>
/// Different types of DNS zone in MS DNS Server
/// </summary>
public enum ZoneType
{
DSIntegrated,
Primary,
Secondary
}
/// <summary>
/// Different types of DNS zone in MS DNS Server
/// </summary>
/// <remarks>For creation of new zones the list is different</remarks>
public enum NewZoneType
{
Primary,
Secondary,
/// <remarks>Server 2003+ only</remarks>
Stub,
/// <remarks>Server 2003+ only</remarks>
Forwarder
}
/// <summary>
/// A zone in MS DNS Server
/// </summary>
public class DNSDomain
{
/// <summary>
/// Create a DNS zone
/// </summary>
/// <param name="name">The name of the DNS zone</param>
/// <param name="wmiObject">The object that represents it in MS DNS server</param>
/// <param name="server">The DNS Server it is to be managed by</param>
public DNSDomain(string name, ManagementBaseObject wmiObject, DNSServer server)
{
_name = name;
_wmiObject = wmiObject;
_server = server;
}
private DNSServer _server = null;
private string _name = "";
/// <summary>
/// The name of the DNS zone
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
/// The zone type
/// </summary>
public ZoneType ZoneType
{
get
{
//_wmiObject["ZoneType"].ToString()
return (ZoneType)Convert.ToInt32(_wmiObject["ZoneType"]);
}
}
/// <summary>
/// Is this a reverse DNS zone?
/// </summary>
public bool ReverseZone
{
get
{
if (_wmiObject["Reverse"].ToString() == "1")
{
return true;
}
else
{
return false;
}
}
}
private ManagementBaseObject _wmiObject = null;
/// <summary>
/// Get a list of all objects at the base of this zone
/// </summary>
/// <returns>A list of <see cref="DNSRecord"/></returns>
public DNSRecord[] GetAllRecords()
{
return _server.GetRecordsForDomain(_name);
}
/// <summary>
/// Create a new DNS host record
/// </summary>
/// <param name="record">The record to create</param>
public void CreateDNSRecord(DNSRecord record)
{
_server.CreateDNSRecord(_name, record.ToString());
}
public override string ToString()
{
return _name;
}
}
/// <summary>
/// An entry in a zone
/// </summary>
public class DNSRecord
{
/// <summary>
/// Create an class wrapping a DNS record
/// Defaults to 1 hour TTL
/// </summary>
/// <param name="domain"></param>
/// <param name="recordType"></param>
/// <param name="target"></param>
public DNSRecord(string domain, DNSRecordType recordType, string target) :
this(domain, recordType, target, new TimeSpan(1, 0, 0))
{
}
/// <summary>
/// Create an class wrapping a DNS record
/// </summary>
/// <param name="domain"></param>
/// <param name="recordType"></param>
/// <param name="target"></param>
/// <param name="ttl"></param>
public DNSRecord(string domain, DNSRecordType recordType, string target, TimeSpan ttl)
{
_host = domain;
_ttl = ttl;
_target = target;
_recordType = recordType;
}
/// <summary>
/// Create an class wrapping a DNS record
/// </summary>
/// <param name="wmiObject"></param>
public DNSRecord(ManagementObject wmiObject)
{
_wmiObject = wmiObject;
_host = wmiObject["OwnerName"].ToString();
_target = wmiObject["RecordData"].ToString();
string[] recordParts = wmiObject["TextRepresentation"].ToString().Split(' ', '\t');
if (recordParts.Length > 2)
{
//the third offset is the location in the textual version of the data where the record type is.
//counting from zero that is location 2 in the array.
_recordType = new DNSRecordType(recordParts[2]);
}
_ttl = new TimeSpan(0, 0, Convert.ToInt32(wmiObject["TTL"]));
}
private ManagementObject _wmiObject = null;
private string _target = "";
/// <summary>
/// The value of the target is what is written to DNS as the value of a record
/// </summary>
/// <remarks>For MX records include the priority as a number with a space or tab between it and the actual target</remarks>
public string Target
{
get { return _target; }
set { _target = value; }
}
/// <summary>
/// Save the changes to the resource record
/// </summary>
public void SaveChanges()
{
//We can call modify and if we have the method available it will work as the sub-class may have it!!
//Some types DO NOT implement it or implement it differently
ManagementBaseObject parameters = _wmiObject.GetMethodParameters("Modify");
bool supported = false;
//This is a cludge based on the various types that are implemented by MS as they didn't stick to a simple value
//To add more, please refer to
if (RecordType.TextRepresentation == "A")
{
parameters["IPAddress"] = _target;
parameters["TTL"] = _ttl.TotalSeconds;
supported = true;
}
if (RecordType.TextRepresentation == "AAAA")
{
parameters["IPv6Address"] = _target;
parameters["TTL"] = _ttl.TotalSeconds;
supported = true;
}
if (RecordType.TextRepresentation == "CNAME")
{
parameters["PrimaryName"] = _target;
parameters["TTL"] = _ttl.TotalSeconds;
supported = true;
}
if (RecordType.TextRepresentation == "TXT")
{
parameters["DescriptiveText"] = _target;
parameters["TTL"] = _ttl.TotalSeconds;
supported = true;
}
if (RecordType.TextRepresentation == "MX")
{
string[] components = _target.Trim().Split(' ', '\t');
if (components.Length > 1)
{
parameters["Preference"] = Convert.ToUInt16(components[0]); //the preference is a UINT16 in MS DNS Server
parameters["MailExchange"] = components[1]; //the actual host name
//NOT SUPPORTED BY MX ACCORDING TO THE DOCUMENTATION!? parameters["TTL"] = _ttl;
supported = true;
}
}
Exception temporaryException = null;
try
{
//This supports improving this classes implementation of this method and adding
ManagementBaseObject lastDitchParameters = OnSaveChanges(parameters);
if (lastDitchParameters != null)
{
parameters = lastDitchParameters;
supported = true;
}
}
catch (Exception ex) //catch all as we do not know what someone will modify OnSaveChanges() to throw or cause
{
if (!supported) //if we support the data type already then we don't care about exceptions as at worst case
throw;
else
temporaryException = ex;
}
if (supported)
{
try
{
_wmiObject = (ManagementObject)_wmiObject.InvokeMethod("Modify", parameters, null);
}
catch (Exception ex)
{
if (temporaryException != null)
{
throw new ApplicationException("There were two exceptions, the primary failure"+
" was an exception that is encapsulated in this message however additionaly "+
"a virtual method that was optional to functionality also threw an exception "+
"but this was withheld till after the operation failed. Please examine the"+
" InnerException property for copy of the virtual methods exception. The "+
"virtual methods exception message was: " + temporaryException.Message+". "+
"The primary exceptions message (a "+ex.GetType().FullName.ToString()+") "+
"was: "+ex.Message, temporaryException);
}
else
{
throw;
}
}
if (temporaryException != null)
{
throw new ApplicationException("A virtual method that was optional to functionality "+
"threw an exception but this was withheld till after the operation completed "+
"successfully, please examine the InnerException property for a full copy of this "+
"exception. The message was: " + temporaryException.Message, temporaryException);
}
}
else
{
throw new NotSupportedException("The data type you attmpted to use ("+
RecordType.TextRepresentation+") was not supported, please implement support for"+
"it by overriding the method OnSaveChanges() and returning an array of filled WMI parameters "+
"or by updating this implementation.");
}
}
/// <summary>
/// Method to override to add additional methods to the DNS save changes support
/// </summary>
/// <param name="parametersIn">An array of parameters (not yet filled in if it's an
/// unknown type, potentially partially filled for known types)</param>
/// <returns>An array of filled in parameters, or null if the parameters are unknown</returns>
public virtual ManagementBaseObject OnSaveChanges(ManagementBaseObject parametersIn)
{
return null;
}
/// <summary>
/// Delete a record from DNS
/// </summary>
public void Delete()
{
_wmiObject.Delete();
//well that was easy...
}
private TimeSpan _ttl = new TimeSpan(0, 1, 0);
/// <summary>
/// The time that the resolvers should cache this record for
/// </summary>
public TimeSpan Ttl
{
get { return _ttl; }
set { _ttl = value; }
}
private DNSRecordType _recordType = null;
/// <summary>
/// The record type
/// </summary>
public DNSRecordType RecordType
{
get { return _recordType; }
}
private string _host = "";
/// <summary>
/// The location in the DNS system for this record
/// </summary>
public string DomainHost
{
get { return _host; }
set { _host = value; }
}
public override string ToString()
{
return _host + " " + _recordType.ToString() + " " + _target;
}
}
/// <summary>
/// The type of record in MS DNS Server
/// </summary>
public class DNSRecordType
{
/// <summary>
/// Create a new DNS record type
/// </summary>
/// <param name="textRepresentation">The type to create</param>
public DNSRecordType(string textRepresentation)
{
_textRepresentation = textRepresentation;
}
private string _textRepresentation = "";
/// <summary>
/// The text representation of the record type
/// </summary>
public string TextRepresentation
{
get
{
return _textRepresentation.ToUpper();
}
}
private string _recordMode = "IN";
/// <summary>
/// The mode of the record, usually IN but could oneday be something else like OUT
/// </summary>
public string RecordMode
{
get
{
return _recordMode;
}
set
{
_recordMode = value;
}
}
public override string ToString()
{
return _recordMode+" "+_textRepresentation;
}
#region Some Defaults!
/// <summary>
/// An alias
/// </summary>
public static DNSRecordType CNAME
{
get { return new DNSRecordType("CNAME"); }
}
/// <summary>
/// An IPv4 address
/// </summary>
public static DNSRecordType A
{
get { return new DNSRecordType("A"); }
}
/// <summary>
/// A reverse host address inside yoursubnet.in-addr.arpa
/// </summary>
public static DNSRecordType PTR
{
get { return new DNSRecordType("PTR"); }
}
/// <summary>
/// An MX record (mail exchange)
/// </summary>
public static DNSRecordType MX
{
get { return new DNSRecordType("MX"); }
}
/// <summary>
/// An IPv6 host address
/// </summary>
public static DNSRecordType AAAA
{
get { return new DNSRecordType("AAAA"); }
}
/// <summary>
/// A text record
/// </summary>
public static DNSRecordType TXT
{
get { return new DNSRecordType("TXT"); }
}
/// <summary>
/// A nameserver record (domain delegation)
/// </summary>
public static DNSRecordType NS
{
get { return new DNSRecordType("NS"); }
}
/// <summary>
/// An SOA record (start of authority)
/// </summary>
public static DNSRecordType SOA
{
get { return new DNSRecordType("SOA"); }
}
#endregion
}
#endregion
/// <summary>
/// Create a new DNS Server connection
/// </summary>
/// <param name="server">The hostname, IP or FQDN of a DNS server you have access to with the current credentials</param>
public DNSServer(string server)
{
ConnectionOptions co = new ConnectionOptions();
_scope = new ManagementScope(String.Format(@"\\{0}\Root\MicrosoftDNS", server), co);
_scope.Connect(); //no disconnect method appears to exist so we do not need to manage the
//persistence of this connection and tidy up
}
/// <summary>
/// Create a new DNS Server connection
/// </summary>
/// <param name="server">The hostname, IP or FQDN of a DNS server you have access to with the current credentials</param>
/// <param name="username">The username to connect with</param>
/// <param name="password">The users password</param>
public DNSServer(string server, string username, string password)
{
ConnectionOptions co = new ConnectionOptions();
co.Username = username;
co.Password = password;
co.Impersonation = ImpersonationLevel.Impersonate;
_scope = new ManagementScope(String.Format(@"\\{0}\Root\MicrosoftDNS", server), co);
_scope.Connect();
}
private string _server = "";
/// <summary>
/// The server this connection applies to
/// </summary>
public string Server
{
get { return _server; }
}
private ManagementScope _scope = null;
/// <summary>
/// Return a list of domains managed by this instance of MS DNS Server
/// </summary>
/// <returns></returns>
public DNSDomain[] GetListOfDomains()
{
ManagementClass mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_Zone"), null);
mc.Get();
ManagementObjectCollection collection = mc.GetInstances();
List<DNSDomain> domains = new List<DNSDomain>();
foreach (ManagementObject p in collection)
{
domains.Add(new DNSDomain(p["ContainerName"].ToString(), p, this));
}
return domains.ToArray();
}
/// <summary>
/// Return a list of records for a domain, note that it may include records
/// that are stubs to other domains inside the zone and does not automatically
/// recurse.
/// </summary>
/// <param name="domain">The domain to connect to</param>
/// <returns></returns>
public DNSRecord[] GetRecordsForDomain(string domain)
{
string query = String.Format("SELECT * FROM MicrosoftDNS_ResourceRecord WHERE DomainName='{0}'", domain);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery(query));
ManagementObjectCollection collection = searcher.Get();
List<DNSRecord> records = new List<DNSRecord>();
foreach (ManagementObject p in collection)
{
records.Add(new DNSRecord(p));
}
return records.ToArray();
}
/// <summary>
/// Create a new DNS host record
/// </summary>
/// <param name="zone"></param>
/// <param name="bindStyleHostEntry"></param>
/// <returns></returns>
public void CreateDNSRecord(string zone, string bindStyleHostEntry)
{
try
{
ManagementObject mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_ResourceRecord"), null);
mc.Get();
ManagementBaseObject parameters = mc.GetMethodParameters("CreateInstanceFromTextRepresentation");
parameters["ContainerName"] = zone;
parameters["DnsServerName"] = _server;
parameters["TextRepresentation"] = bindStyleHostEntry;
ManagementBaseObject createdEntry = mc.InvokeMethod("CreateInstanceFromTextRepresentation", parameters, null);
//return createdEntry; (no reason unless you changed your mind and wanted to delete it?!)
}
catch (ManagementException) //the details on this exception appear useless.
{
throw new ApplicationException("Unable to create the record " + bindStyleHostEntry + ", please check"+
" the format and that it does not already exist.");
}
}
/// <summary>
/// Create a new DNS host record
/// </summary>
/// <param name="zone"></param>
/// <param name="record"></param>
public void CreateDNSRecord(string zone, DNSRecord record)
{
CreateDNSRecord(zone, record.ToString());
}
/// <summary>
/// Fetch DNS records for a particular name
/// WARNING: This method has performance issues, iterate over the results of getting all the records for a domain instead.
/// </summary>
/// <remarks>Returns a collection as one hostname/entry can have multiple records but it can take longer
/// than getting all the records and scanning them!</remarks>
/// <param name="hostName">The name to look up</param>
/// <returns></returns>
public DNSRecord[] GetExistingDNSRecords(string hostName)
{
string query = String.Format("SELECT * FROM MicrosoftDNS_ResourceRecord WHERE OwnerName='{0}'", hostName);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery(query));
ManagementObjectCollection collection = searcher.Get();
List<DNSRecord> records = new List<DNSRecord>();
foreach (ManagementObject p in collection)
{
records.Add(new DNSRecord(p));
}
return records.ToArray();
}
/// <summary>
/// Create a new zone in MS DNS Server
/// </summary>
/// <param name="zoneName">The zone to create</param>
/// <param name="zoneType">The type of zone to create</param>
/// <returns>The domain</returns>
public DNSDomain CreateNewZone(string zoneName, NewZoneType zoneType)
{
try
{
ManagementObject mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_Zone"), null);
mc.Get();
ManagementBaseObject parameters = mc.GetMethodParameters("CreateZone");
/*
[in] string ZoneName,
[in] uint32 ZoneType,
[in] boolean DsIntegrated, (will always be false for us, if you need AD integration you will need to change this.
[in, optional] string DataFileName,
[in, optional] string IpAddr[],
[in, optional] string AdminEmailName,
*/
parameters["ZoneName"] = zoneName;
parameters["ZoneType"] = (UInt32)zoneType;
parameters["DsIntegrated"] = 0; //false
ManagementBaseObject createdEntry = mc.InvokeMethod("CreateZone", parameters, null);
DNSDomain d = new DNSDomain(zoneName, createdEntry, this);
return d;
}
catch (ManagementException) //returns generic error when it already exists, I'm guessing this is a generic answer!
{
throw new ApplicationException("Unable to create the zone "+zoneName+", please check "+
"the format of the name and that it does not already exist.");
}
}
public override string ToString()
{
return _server;
}
}
#endregion
}標籤: .NET
本文章網址:
https://www.ez2o.com/Blog/Post/csharp-WMI-Microsoft-DNS-Server
https://www.ez2o.com/Blog/Post/14
https://www.ez2o.com/Blog/Post/csharp-WMI-Microsoft-DNS-Server
https://www.ez2o.com/Blog/Post/14
