From c13e350aa456b2322ddc4eeaaf78dad8ee5c6911 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Thu, 28 Nov 2013 20:38:38 +0100 Subject: [PATCH] DNS 2012 - first commit --- .../Microsoft.Management.Infrastructure.dll | Bin 0 -> 122880 bytes .../DnsCommands.cs | 287 ++++++++++++ .../MsDNS.cs | 424 ++++++++++++++++++ .../PowerShellHelper.cs | 84 ++++ .../Properties/AssemblyInfo.cs | 36 ++ .../RecordConverter.cs | 147 ++++++ .../RecordTypes.cs | 40 ++ ...ebsitePanel.Providers.DNS.MsDNS2012.csproj | 75 ++++ WebsitePanel/Sources/WebsitePanel.Server.sln | 16 + 9 files changed, 1109 insertions(+) create mode 100644 WebsitePanel/Lib/Microsoft.Management.Infrastructure.dll create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/DnsCommands.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/MsDNS.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/PowerShellHelper.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/Properties/AssemblyInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordConverter.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordTypes.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/WebsitePanel.Providers.DNS.MsDNS2012.csproj diff --git a/WebsitePanel/Lib/Microsoft.Management.Infrastructure.dll b/WebsitePanel/Lib/Microsoft.Management.Infrastructure.dll new file mode 100644 index 0000000000000000000000000000000000000000..9c72405baff28b030284404558bd466a731582f5 GIT binary patch literal 122880 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

1@HNQlp6V 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 v%oVzFGThis class wraps MS DNS server PowerShell commands used by the WebsitePanel. + internal static class DnsCommands + { + ///

Add parameter to PS command + /// command + /// Parameter name + /// Parameter value + /// Same command + private static Command addParam( this Command cmd, string name, object value ) + { + cmd.Parameters.Add( name, value ); + return cmd; + } + + /// Add parameter without value to the PS command + /// command + /// Parameter name + /// Same command + private static Command addParam( this Command cmd, string name ) + { + cmd.Parameters.Add( name ); + return cmd; + } + + /// Create "Where-Object -Property ... -eq -Value ..." command + /// + /// + /// + private static Command where( string property, object value ) + { + return new Command( "Where-Object" ) + .addParam( "Property", property ) + .addParam( "eq" ) + .addParam( "Value", value ); + } + + /// Test-DnsServer -IPAddress 127.0.0.1 + /// PowerShell host to use + /// true if localhost is an MS DNS server + public static bool Test_DnsServer( this PowerShellHelper ps ) + { + if( null == ps ) + throw new ArgumentNullException( "ps" ); + + var cmd = new Command( "Test-DnsServer" ) + .addParam( "IPAddress", IPAddress.Loopback ); + + PSObject res = ps.RunPipeline( cmd ).FirstOrDefault(); + + if( null == res || null == res.Properties ) + return false; + PSPropertyInfo p = res.Properties[ "Result" ]; + if( null == p || null == p.Value ) + return false; + return p.Value.ToString() == "Success"; + } + + #region Zones + + /// Get-DnsServerZone | Select-Object -Property ZoneName + /// Only primary DNS zones are returned + /// Array of zone names + public static string[] Get_DnsServerZone_Names( this PowerShellHelper ps ) + { + var allZones = ps.RunPipeline( new Command( "Get-DnsServerZone" ), + where( "IsAutoCreated", false ) ); + + string[] res = allZones + .Select( pso => new + { + name = (string)pso.Properties[ "ZoneName" ].Value, + type = (string)pso.Properties[ "ZoneType" ].Value + } ) + .Where( obj => obj.type == "Primary" ) + .Select( obj => obj.name ) + .ToArray(); + + Log.WriteInfo( "Get_DnsServerZone_Names: {{{0}}}", String.Join( ", ", res ) ); + return res; + } + + /// Returns true if the specified zone exists. + /// The PS pipeline being run: Get-DnsServerZone | Where-Object -Property ZoneName -eq -Value "name" + /// + /// + /// + public static bool ZoneExists( this PowerShellHelper ps, string name ) + { + Log.WriteStart( "ZoneExists {0}", name ); + bool res = ps.RunPipeline( new Command( "Get-DnsServerZone" ), + where( "ZoneName", name ) ) + .Any(); + Log.WriteEnd( "ZoneExists: {0}", res ); + return res; + } + + /* public enum eReplicationScope: byte + { + Custom, Domain, Forest, Legacy + } */ + + /// + /// + /// + /// Specifies a partition on which to store an Active Directory-integrated zone. + /// + public static void Add_DnsServerPrimaryZone( this PowerShellHelper ps, string zoneName, string[] secondaryServers ) + { + Log.WriteStart( "Add_DnsServerPrimaryZone {0} {{{1}}}", zoneName, String.Join( ", ", secondaryServers ) ); + + // Add-DnsServerPrimaryZone -Name zzz.com -ZoneFile zzz.com.dns + var cmd = new Command( "Add-DnsServerPrimaryZone" ); + cmd.addParam( "Name", zoneName ); + cmd.addParam( "ZoneFile", zoneName + ".dns" ); + ps.RunPipeline( cmd ); + + // Set-DnsServerPrimaryZone -Name zzz.com -SecureSecondaries ... -Notify ... Servers .. + cmd = new Command( "Set-DnsServerPrimaryZone" ); + cmd.addParam( "Name", zoneName ); + + if( secondaryServers == null || secondaryServers.Length == 0 ) + { + // transfers are not allowed + // inParams2[ "SecureSecondaries" ] = 3; + // inParams2[ "Notify" ] = 0; + cmd.addParam( "SecureSecondaries", "NoTransfer" ); + cmd.addParam( "Notify", "NoNotify" ); + } + else if( secondaryServers.Length == 1 && secondaryServers[ 0 ] == "*" ) + { + // allowed transfer from all servers + // inParams2[ "SecureSecondaries" ] = 0; + // inParams2[ "Notify" ] = 1; + cmd.addParam( "SecureSecondaries", "TransferAnyServer" ); + cmd.addParam( "Notify", "Notify" ); + } + else + { + // allowed transfer from specified servers + // inParams2[ "SecureSecondaries" ] = 2; + // inParams2[ "SecondaryServers" ] = secondaryServers; + // inParams2[ "NotifyServers" ] = secondaryServers; + // inParams2[ "Notify" ] = 2; + cmd.addParam( "SecureSecondaries", "TransferToSecureServers" ); + cmd.addParam( "Notify", "NotifyServers" ); + cmd.addParam( "SecondaryServers", secondaryServers ); + cmd.addParam( "NotifyServers", secondaryServers ); + } + ps.RunPipeline( cmd ); + Log.WriteEnd( "Add_DnsServerPrimaryZone" ); + } + + public static void Add_DnsServerSecondaryZone( this PowerShellHelper ps, string zoneName, string[] masterServers ) + { + // Add-DnsServerSecondaryZone -Name zzz.com -ZoneFile zzz.com.dns + var cmd = new Command( "Add-DnsServerSecondaryZone" ); + cmd.addParam( "Name", zoneName ); + cmd.addParam( "ZoneFile", zoneName + ".dns" ); + ps.RunPipeline( cmd ); + + // Set-DnsServerSecondaryZone -Name zzz.com -MasterServers ... + cmd = new Command( "Set-DnsServerSecondaryZone" ); + cmd.addParam( "Name", zoneName ); + cmd.addParam( "MasterServers", masterServers ); + ps.RunPipeline( cmd ); + } + + public static void Remove_DnsServerZone( this PowerShellHelper ps, string zoneName ) + { + var cmd = new Command( "Remove-DnsServerZone" ); + cmd.addParam( "Name", zoneName ); + cmd.addParam( "Force" ); + ps.RunPipeline( cmd ); + } + #endregion + + /// Get all records, except the SOA + /// + /// Name of the zone + /// Array of records + public static DnsRecord[] GetZoneRecords( this PowerShellHelper ps, string zoneName ) + { + // Get-DnsServerResourceRecord -ZoneName xxxx.com + var allRecords = ps.RunPipeline( new Command( "Get-DnsServerResourceRecord" ).addParam( "ZoneName", zoneName ) ); + + return allRecords.Select( o => o.asDnsRecord( zoneName ) ) + .Where( r => null != r ) + .Where( r => r.RecordType != DnsRecordType.SOA ) + // .Where( r => !( r.RecordName == "@" && DnsRecordType.NS == r.RecordType ) ) + .ToArray(); + } + + #region Records add / remove + + public static void Add_DnsServerResourceRecordA( this PowerShellHelper ps, string zoneName, string Name, string address ) + { + var cmd = new Command( "Add-DnsServerResourceRecordA" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "IPv4Address", address ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordAAAA( this PowerShellHelper ps, string zoneName, string Name, string address ) + { + var cmd = new Command( "Add-DnsServerResourceRecordAAAA" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "IPv6Address", address ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordCName( this PowerShellHelper ps, string zoneName, string Name, string alias ) + { + var cmd = new Command( "Add-DnsServerResourceRecordCName" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "HostNameAlias", alias ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordMX( this PowerShellHelper ps, string zoneName, string Name, string mx, UInt16 pref ) + { + var cmd = new Command( "Add-DnsServerResourceRecordMX" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "MailExchange", mx ); + cmd.addParam( "Preference", pref ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordNS( this PowerShellHelper ps, string zoneName, string Name, string NameServer ) + { + var cmd = new Command( "Add-DnsServerResourceRecord" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "NS" ); + cmd.addParam( "NameServer", NameServer ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordTXT( this PowerShellHelper ps, string zoneName, string Name, string txt ) + { + var cmd = new Command( "Add-DnsServerResourceRecord" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "Txt" ); + cmd.addParam( "DescriptiveText", txt ); + ps.RunPipeline( cmd ); + } + + public static void Add_DnsServerResourceRecordSRV( this PowerShellHelper ps, string zoneName, string Name, string DomainName, UInt16 Port, UInt16 Priority, UInt16 Weight ) + { + var cmd = new Command( "Add-DnsServerResourceRecord" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "Srv" ); + cmd.addParam( "DomainName", DomainName ); + cmd.addParam( "Port", Port ); + cmd.addParam( "Priority", Priority ); + cmd.addParam( "Weight", Weight ); + ps.RunPipeline( cmd ); + } + + public static void Remove_DnsServerResourceRecord( this PowerShellHelper ps, string zoneName, string Name, string type ) + { + // Remove-DnsServerResourceRecord -ZoneName xxxx.com -Name "@" -RRType Soa -Force + var cmd = new Command( "Remove-DnsServerResourceRecord" ); + cmd.addParam( "ZoneName", zoneName ); + cmd.addParam( "Name", Name ); + cmd.addParam( "RRType", type ); + cmd.addParam( "Force" ); + ps.RunPipeline( cmd ); + } + + #endregion + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/MsDNS.cs b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/MsDNS.cs new file mode 100644 index 00000000..9dfa9f69 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/MsDNS.cs @@ -0,0 +1,424 @@ +// Copyright (c) 2012, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A lot of modifications made since 2012.. + +using System; +using System.Management; +using System.Collections.Generic; +using System.Text; +using Microsoft.Win32; + +using WebsitePanel.Server.Utils; +using WebsitePanel.Providers.Utils; + +namespace WebsitePanel.Providers.DNS +{ + public class MsDNS: HostingServiceProviderBase, IDnsServer + { + + protected int ExpireLimit + { + get { return ProviderSettings.GetInt( "ExpireLimit" ); } + } + + protected int MinimumTTL + { + get { return ProviderSettings.GetInt( "MinimumTTL" ); } + } + + protected int RefreshInterval + { + get { return ProviderSettings.GetInt( "RefreshInterval" ); } + } + + protected int RetryDelay + { + get { return ProviderSettings.GetInt( "RetryDelay" ); } + } + + protected bool AdMode + { + get { return ProviderSettings.GetBool( "AdMode" ); } + } + + private PowerShellHelper ps = null; + private WmiHelper wmi = null; //< We still need WMI because PowerShell doesn't support SOA updates. + private bool bulkRecords; + + public MsDNS() + { + // Create PowerShell helper + ps = new PowerShellHelper(); + if( !this.IsInstalled() ) + return; + + // Create WMI helper + wmi = new WmiHelper( "root\\MicrosoftDNS" ); + } + + #region Zones + + public virtual string[] GetZones() + { + return ps.Get_DnsServerZone_Names(); + } + + public virtual bool ZoneExists( string zoneName ) + { + return ps.ZoneExists( zoneName ); + } + + public virtual DnsRecord[] GetZoneRecords( string zoneName ) + { + return ps.GetZoneRecords( zoneName ); + } + + public virtual void AddPrimaryZone( string zoneName, string[] secondaryServers ) + { + ps.Add_DnsServerPrimaryZone( zoneName, secondaryServers ); + + // delete orphan NS records + DeleteOrphanNsRecords( zoneName ); + } + + public virtual void AddSecondaryZone( string zoneName, string[] masterServers ) + { + ps.Add_DnsServerSecondaryZone( zoneName, masterServers ); + + // delete orphan NS records + DeleteOrphanNsRecords( zoneName ); + } + + public virtual void DeleteZone( string zoneName ) + { + try + { + ps.Remove_DnsServerZone( zoneName ); + } + catch( Exception ex ) + { + Log.WriteError( ex ); + } + } + + public virtual void AddZoneRecord( string zoneName, DnsRecord record ) + { + try + { + string name = record.RecordName; + if( String.IsNullOrEmpty( name ) ) + name = "."; + + if( record.RecordType == DnsRecordType.A ) + ps.Add_DnsServerResourceRecordA( zoneName, name, record.RecordData ); + else if( record.RecordType == DnsRecordType.AAAA ) + ps.Add_DnsServerResourceRecordAAAA( zoneName, name, record.RecordData ); + else if( record.RecordType == DnsRecordType.CNAME ) + ps.Add_DnsServerResourceRecordCName( zoneName, name, record.RecordData ); + else if( record.RecordType == DnsRecordType.MX ) + ps.Add_DnsServerResourceRecordMX( zoneName, name, record.RecordData, (ushort)record.MxPriority ); + else if( record.RecordType == DnsRecordType.NS ) + ps.Add_DnsServerResourceRecordNS( zoneName, name, record.RecordData ); + else if( record.RecordType == DnsRecordType.TXT ) + ps.Add_DnsServerResourceRecordTXT( zoneName, name, record.RecordData ); + else if( record.RecordType == DnsRecordType.SRV ) + ps.Add_DnsServerResourceRecordSRV( zoneName, name, record.RecordData, (ushort)record.SrvPort, (ushort)record.SrvPriority, (ushort)record.SrvWeight ); + else + throw new Exception( "Unknown record type" ); + } + catch( Exception ex ) + { + // log exception + Log.WriteError( ex ); + } + } + + public virtual void AddZoneRecords( string zoneName, DnsRecord[] records ) + { + bulkRecords = true; + try + { + foreach( DnsRecord record in records ) + AddZoneRecord( zoneName, record ); + } + finally + { + bulkRecords = false; + } + + UpdateSoaRecord( zoneName ); + } + + public virtual void DeleteZoneRecord( string zoneName, DnsRecord record ) + { + try + { + string rrType; + if( !RecordTypes.rrTypeFromRecord.TryGetValue( record.RecordType, out rrType ) ) + throw new Exception( "Unknown record type" ); + ps.Remove_DnsServerResourceRecord( zoneName, record.RecordName, rrType ); + } + catch( Exception ex ) + { + // log exception + Log.WriteError( ex ); + } + } + + public virtual void DeleteZoneRecords( string zoneName, DnsRecord[] records ) + { + foreach( DnsRecord record in records ) + DeleteZoneRecord( zoneName, record ); + } + + public void AddZoneRecord( string zoneName, string recordText ) + { + try + { + Log.WriteStart( string.Format( "Adding MS DNS Server zone '{0}' record '{1}'", zoneName, recordText ) ); + AddDnsRecord( zoneName, recordText ); + Log.WriteEnd( "Added MS DNS Server zone record" ); + } + catch( Exception ex ) + { + Log.WriteError( ex ); + throw; + } + } + #endregion + + #region SOA Record + public virtual void UpdateSoaRecord( string zoneName, string host, string primaryNsServer, string primaryPerson ) + { + host = CorrectHostName( zoneName, host ); + + // delete record if exists + DeleteSoaRecord( zoneName ); + + // format record data + string recordText = GetSoaRecordText( host, primaryNsServer, primaryPerson ); + + // add record + AddDnsRecord( zoneName, recordText ); + + // update SOA record + UpdateSoaRecord( zoneName ); + } + + private void DeleteSoaRecord( string zoneName ) + { + // TODO: find a PowerShell replacement + + string query = String.Format( "SELECT * FROM MicrosoftDNS_SOAType " + + "WHERE OwnerName = '{0}'", + zoneName ); + using( ManagementObjectCollection objRRs = wmi.ExecuteQuery( query ) ) + { + foreach( ManagementObject objRR in objRRs ) using( objRR ) + objRR.Delete(); + } + + // This doesn't work: no errors in PS, but the record stays in the DNS + /* try + { + ps.Remove_DnsServerResourceRecord( zoneName, "@", "Soa" ); + } + catch( System.Exception ex ) + { + Log.WriteWarning( "{0}", ex.Message ); + } */ + } + + private string GetSoaRecordText( string host, string primaryNsServer, string primaryPerson ) + { + return String.Format( "{0} IN SOA {1} {2} 1 900 600 86400 3600", host, primaryNsServer, primaryPerson ); + } + + private static string RemoveTrailingDot( string str ) + { + return ( str.EndsWith( "." ) ) ? str.Substring( 0, str.Length - 1 ) : str; + } + + private void UpdateSoaRecord( string zoneName ) + { + if( bulkRecords ) + return; + + // TODO: find a PowerShell replacement + + // get existing SOA record in order to read serial number + try + { + + ManagementObject objSoa = wmi.GetWmiObject( "MicrosoftDNS_SOAType", "ContainerName = '{0}'", RemoveTrailingDot( zoneName ) ); + + if( objSoa != null ) + { + if( objSoa.Properties[ "OwnerName" ].Value.Equals( zoneName ) ) + { + string primaryServer = (string)objSoa.Properties[ "PrimaryServer" ].Value; + string responsibleParty = (string)objSoa.Properties[ "ResponsibleParty" ].Value; + UInt32 serialNumber = (UInt32)objSoa.Properties[ "SerialNumber" ].Value; + + // update record's serial number + string sn = serialNumber.ToString(); + string todayDate = DateTime.Now.ToString( "yyyyMMdd" ); + if( sn.Length < 10 || !sn.StartsWith( todayDate ) ) + { + // build a new serial number + sn = todayDate + "01"; + serialNumber = UInt32.Parse( sn ); + } + else + { + // just increment serial number + serialNumber += 1; + } + + // update SOA record + using( ManagementBaseObject methodParams = objSoa.GetMethodParameters( "Modify" ) ) + { + methodParams[ "ResponsibleParty" ] = responsibleParty; + methodParams[ "PrimaryServer" ] = primaryServer; + methodParams[ "SerialNumber" ] = serialNumber; + + methodParams[ "ExpireLimit" ] = ExpireLimit; + methodParams[ "MinimumTTL" ] = MinimumTTL; + methodParams[ "TTL" ] = MinimumTTL; + methodParams[ "RefreshInterval" ] = RefreshInterval; + methodParams[ "RetryDelay" ] = RetryDelay; + + ManagementBaseObject outParams = objSoa.InvokeMethod( "Modify", methodParams, null ); + } + // + objSoa.Dispose(); + } + + } + } + catch( Exception ex ) + { + Log.WriteError( ex ); + } + } + + #endregion + + private void DeleteOrphanNsRecords( string zoneName ) + { + // TODO: find a PowerShell replacement + string machineName = System.Net.Dns.GetHostEntry( "LocalHost" ).HostName.ToLower(); + string computerName = Environment.MachineName.ToLower(); + + using( ManagementObjectCollection objRRs = wmi.ExecuteQuery( String.Format( "SELECT * FROM MicrosoftDNS_NSType WHERE DomainName = '{0}'", zoneName ) ) ) + { + foreach( ManagementObject objRR in objRRs ) + { + using( objRR ) + { + string ns = ( (string)objRR.Properties[ "NSHost" ].Value ).ToLower(); + if( ns.StartsWith( machineName ) || ns.StartsWith( computerName ) ) + objRR.Delete(); + + } + } + } + } + + #region private helper methods + + private string GetDnsServerName() + { + // TODO: find a PowerShell replacement + using( ManagementObject objServer = wmi.GetObject( "MicrosoftDNS_Server.Name=\".\"" ) ) + { + return (string)objServer.Properties[ "Name" ].Value; + } + } + + private string AddDnsRecord( string zoneName, string recordText ) + { + // get the name of the server + string serverName = GetDnsServerName(); + + // TODO: find a PowerShell replacement + // add record + using( ManagementClass clsRR = wmi.GetClass( "MicrosoftDNS_ResourceRecord" ) ) + { + object[] prms = new object[] { serverName, zoneName, recordText, null }; + clsRR.InvokeMethod( "CreateInstanceFromTextRepresentation", prms ); + return (string)prms[ 3 ]; + } + } + + private string CorrectHostName( string zoneName, string host ) + { + // if host is empty or null + if( host == null || host == "" ) + return zoneName; + + // if there are not dot at all + else if( host.IndexOf( "." ) == -1 ) + return host + "." + zoneName; + + // if only one dot at the end + else if( host[ host.Length - 1 ] == '.' && host.IndexOf( "." ) == ( host.Length - 1 ) ) + return host + zoneName; + + // other cases + else + return host; + } + #endregion + + public override void DeleteServiceItems( ServiceProviderItem[] items ) + { + foreach( ServiceProviderItem item in items ) + { + if( item is DnsZone ) + { + try + { + // delete DNS zone + DeleteZone( item.Name ); + } + catch( Exception ex ) + { + Log.WriteError( String.Format( "Error deleting '{0}' MS DNS zone", item.Name ), ex ); + } + } + } + } + + public override bool IsInstalled() + { + return ps.Test_DnsServer(); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/PowerShellHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/PowerShellHelper.cs new file mode 100644 index 00000000..980e5ac7 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/PowerShellHelper.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using WebsitePanel.Server.Utils; + +namespace WebsitePanel.Providers.DNS +{ + /// This class is a generic helper hosting the PowerShell runtime. + /// It's probably a good idea to move to some utility module. + public class PowerShellHelper: IDisposable + { + private static InitialSessionState s_session = null; + + static PowerShellHelper() + { + s_session = InitialSessionState.CreateDefault(); + // s_session.ImportPSModule( new string[] { "FileServerResourceManager" } ); + } + + public PowerShellHelper() + { + Log.WriteStart( "PowerShellHelper::ctor" ); + + Runspace rs = RunspaceFactory.CreateRunspace( s_session ); + rs.Open(); + // rs.SessionStateProxy.SetVariable( "ConfirmPreference", "none" ); + + this.runSpace = rs; + Log.WriteEnd( "PowerShellHelper::ctor" ); + } + + public void Dispose() + { + try + { + if( this.runSpace == null ) + return; + if( this.runSpace.RunspaceStateInfo.State == RunspaceState.Opened ) + this.runSpace.Close(); + this.runSpace = null; + } + catch( Exception ex ) + { + Log.WriteError( "Runspace error", ex ); + } + } + + public Runspace runSpace { get; private set; } + + public Collection RunPipeline( params Command[] pipelineCommands ) + { + Log.WriteStart( "ExecuteShellCommand" ); + List errorList = new List(); + + Collection results = null; + using( Pipeline pipeLine = runSpace.CreatePipeline() ) + { + // Add the command + foreach( var cmd in pipelineCommands ) + pipeLine.Commands.Add( cmd ); + + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Only non-terminating errors are delivered here. + // Terminating errors raise exceptions instead. + if( null != pipeLine.Error && pipeLine.Error.Count > 0 ) + { + foreach( object item in pipeLine.Error.ReadToEnd() ) + { + errorList.Add( item ); + string errorMessage = string.Format( "Invoke error: {0}", item ); + Log.WriteWarning( errorMessage ); + } + } + } + // errors = errorList.ToArray(); + Log.WriteEnd( "ExecuteShellCommand" ); + return results; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/Properties/AssemblyInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b37fc853 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebsitePanel.Providers.DNS.MsDNS2012")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WebsitePanel.Providers.DNS.MsDNS2012")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f2a902f2-5654-4e67-8c5e-4d2fdf6de873")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordConverter.cs b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordConverter.cs new file mode 100644 index 00000000..3cf72bfb --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordConverter.cs @@ -0,0 +1,147 @@ +using System; +using System.Linq; +using System.Management.Automation; +using Microsoft.Management.Infrastructure; +using Microsoft.Management.Infrastructure.Generic; +using WebsitePanel.Server.Utils; + +namespace WebsitePanel.Providers.DNS +{ + /// Copy fields from CimInstance#DnsServerResourceRecord into DnsRecord + /// It's also possible to access native CIM object, and use Mgmtclassgen.exe for that. + internal static class RecordConverter + { + private static string RemoveTrailingDot( string str ) + { + if( !str.EndsWith( "." ) ) + return str; + return str.Substring( 0, str.Length - 1 ); + } + + private static string CorrectHost( string zoneName, string host ) + { + if( host.ToLower() == zoneName.ToLower() ) + return ""; + if( host.ToLower().EndsWith( "." + zoneName.ToLower() ) ) + return host.Substring( 0, ( host.Length - zoneName.Length - 1 ) ); + return host; + } + + public static DnsRecord asDnsRecord( this PSObject obj, string zoneName ) + { + // Here's what comes from Server 2012 in the TypeNames: + // "Microsoft.Management.Infrastructure.CimInstance#root/Microsoft/Windows/DNS/DnsServerResourceRecord" + // "Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/DNS/DnsDomain" + // "Microsoft.Management.Infrastructure.CimInstance#DnsServerResourceRecord" + // "Microsoft.Management.Infrastructure.CimInstance#DnsDomain" + // "Microsoft.Management.Infrastructure.CimInstance" + // "System.Object" string + + if( !obj.TypeNames.Contains( "Microsoft.Management.Infrastructure.CimInstance#DnsServerResourceRecord" ) ) + { + Log.WriteWarning( "asDnsRecord: wrong object type {0}", obj.TypeNames.FirstOrDefault() ); + return null; + } + + string strRT = (string)obj.Properties[ "RecordType" ].Value; + DnsRecordType tp; + if( !RecordTypes.recordFromString.TryGetValue( strRT, out tp ) ) + return null; + + /*// Debug code below: + obj.dumpProperties(); + CimInstance rd = (CimInstance)obj.Properties[ "RecordData" ].Value; + rd.dumpProperties(); //*/ + + CimKeyedCollection data = ( (CimInstance)obj.Properties[ "RecordData" ].Value ).CimInstanceProperties; + string host = CorrectHost( zoneName, (string)obj.Properties[ "HostName" ].Value ); + + switch( tp ) + { + // The compiler should create a Dictionary<> from dis switch + case DnsRecordType.A: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = data[ "IPv4Address" ].Value as string, + }; + } + case DnsRecordType.AAAA: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = data[ "IPv6Address" ].Value as string, + }; + } + case DnsRecordType.CNAME: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = RemoveTrailingDot( data[ "HostNameAlias" ].Value as string ), + }; + } + case DnsRecordType.MX: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = RemoveTrailingDot( data[ "MailExchange" ].Value as string ), + MxPriority = (UInt16)data[ "Preference" ].Value, + }; + } + case DnsRecordType.NS: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = RemoveTrailingDot( data[ "NameServer" ].Value as string ), + }; + } + case DnsRecordType.TXT: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = data[ "DescriptiveText" ].Value as string, + }; + } + case DnsRecordType.SOA: + { + string PrimaryServer = data[ "PrimaryServer" ].Value as string; + string ResponsiblePerson = data[ "ResponsiblePerson" ].Value as string; + UInt32? sn = (UInt32?)data[ "SerialNumber" ].Value; + return new DnsSOARecord() + { + RecordType = tp, + RecordName = host, + PrimaryNsServer = PrimaryServer, + PrimaryPerson = ResponsiblePerson, + SerialNumber = ( sn.HasValue ) ? sn.Value.ToString() : null, + }; + } + case DnsRecordType.SRV: + { + return new DnsRecord() + { + RecordType = tp, + RecordName = host, + RecordData = RemoveTrailingDot( data[ "DomainName" ].Value as string ), + SrvPriority = (UInt16)data[ "Priority" ].Value, + SrvWeight = (UInt16)data[ "Weight" ].Value, + SrvPort = (UInt16)data[ "Port" ].Value, + }; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordTypes.cs b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordTypes.cs new file mode 100644 index 00000000..cb3f9864 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/RecordTypes.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace WebsitePanel.Providers.DNS +{ + /// This static class holds 2 lookup tables, from/to DnsRecordType enum + internal static class RecordTypes + { + static readonly Dictionary s_lookup; + static readonly Dictionary s_lookupInv; + + static RecordTypes() + { + s_lookup = new Dictionary() + { + { "A", DnsRecordType.A }, + { "AAAA", DnsRecordType.AAAA }, + { "NS", DnsRecordType.NS }, + { "MX", DnsRecordType.MX }, + { "CNAME", DnsRecordType.CNAME }, + { "SOA", DnsRecordType.SOA }, + { "TXT", DnsRecordType.TXT }, + { "SRV", DnsRecordType.SRV }, + }; + + TextInfo ti = new CultureInfo( "en-US", false ).TextInfo; + + s_lookupInv = s_lookup + .ToDictionary( kvp => kvp.Value, kvp => ti.ToTitleCase( kvp.Key ) ); + } + + /// The dictionary that maps string record types to DnsRecordType enum + public static Dictionary recordFromString { get { return s_lookup; } } + + /// the dictionary that maps DnsRecordType enum to strings, suitable for PowerShell + public static Dictionary rrTypeFromRecord { get { return s_lookupInv; } } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/WebsitePanel.Providers.DNS.MsDNS2012.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/WebsitePanel.Providers.DNS.MsDNS2012.csproj new file mode 100644 index 00000000..0dcf61bc --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.DNS.MsDNS2012/WebsitePanel.Providers.DNS.MsDNS2012.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4} + Library + Properties + WebsitePanel.Providers.DNS.MsDNS2012 + WebsitePanel.Providers.DNS.MsDNS2012 + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\Lib\Microsoft.Management.Infrastructure.dll + True + + + + + + False + ..\..\Lib\System.Management.Automation.dll + True + + + + + + + + + + + + + + {684c932a-6c75-46ac-a327-f3689d89eb42} + WebsitePanel.Providers.Base + + + {e91e52f3-9555-4d00-b577-2b1dbdd87ca7} + WebsitePanel.Server.Utils + + + + + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Server.sln b/WebsitePanel/Sources/WebsitePanel.Server.sln index aaca761a..97e2925c 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.sln +++ b/WebsitePanel/Sources/WebsitePanel.Server.sln @@ -142,6 +142,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Host EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Web.WebDav", "WebsitePanel.Providers.Web.WebDav\WebsitePanel.Providers.Web.WebDav.csproj", "{CE2DF3D7-D6FF-48FA-B2EA-7B836FCBF698}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.DNS.MsDNS2012", "WebsitePanel.Providers.DNS.MsDNS2012\WebsitePanel.Providers.DNS.MsDNS2012.csproj", "{FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}" + ProjectSection(ProjectDependencies) = postProject + {684C932A-6C75-46AC-A327-F3689D89EB42} = {684C932A-6C75-46AC-A327-F3689D89EB42} + {E91E52F3-9555-4D00-B577-2B1DBDD87CA7} = {E91E52F3-9555-4D00-B577-2B1DBDD87CA7} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -732,6 +738,16 @@ Global {CE2DF3D7-D6FF-48FA-B2EA-7B836FCBF698}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {CE2DF3D7-D6FF-48FA-B2EA-7B836FCBF698}.Release|Mixed Platforms.Build.0 = Release|Any CPU {CE2DF3D7-D6FF-48FA-B2EA-7B836FCBF698}.Release|x86.ActiveCfg = Release|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Release|Any CPU.Build.0 = Release|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FA0FB0BA-5A39-4F4E-8EC2-B806B58B74D4}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE