4166 lines
140 KiB
Plaintext
4166 lines
140 KiB
Plaintext
{
|
|
$Project$
|
|
$Workfile$
|
|
$Revision$
|
|
$DateUTC$
|
|
$Id$
|
|
|
|
This file is part of the Indy (Internet Direct) project, and is offered
|
|
under the dual-licensing agreement described on the Indy website.
|
|
(http://www.indyproject.org/)
|
|
|
|
Copyright:
|
|
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
|
|
|
|
|
$Log$
|
|
|
|
|
|
Rev 1.40 3/4/2005 12:35:32 PM JPMugaas
|
|
Removed some compiler warnings.
|
|
|
|
|
|
Rev 1.39 2/9/2005 4:35:06 AM JPMugaas
|
|
Should compile.
|
|
|
|
|
|
Rev 1.38 2/8/05 6:13:02 PM RLebeau
|
|
Updated to use new AppendString() function in IdGlobal unit
|
|
|
|
Updated TIdDNS_ProcessThread.CompleteQuery() to use CopyTId...() functions
|
|
instead of ToBytes() and AppendBytes().
|
|
|
|
|
|
Rev 1.37 2005/1/25 ¤U¤È 12:25:26 DChang
|
|
Modify UpdateTree method, make the NS record can be save in the lower level
|
|
node.
|
|
|
|
|
|
Rev 1.36 2005/1/5 ¤U¤È 04:21:06 DChang Version: 1.36
|
|
Fix parsing procedure while processing TXT record, in pass version, double
|
|
quota will not be processed, but now, any charector between 2 double quotas
|
|
will be treated as TXT message.
|
|
|
|
|
|
Rev 1.35 2004/12/15 ¤U¤È 12:05:26 DChang Version: 1.35
|
|
1. Move UpdateTree to public section.
|
|
2. add DoUDPRead of TIdDNSServer.
|
|
3. Fix TIdDNS_ProcessThread.CompleteQuery and
|
|
InternalQuery to fit Indy 10 Core.
|
|
|
|
|
|
Rev 1.34 12/2/2004 4:23:50 PM JPMugaas
|
|
Adjusted for changes in Core.
|
|
|
|
|
|
Rev 1.33 2004.10.27 9:17:46 AM czhower
|
|
For TIdStrings
|
|
|
|
|
|
Rev 1.32 10/26/2004 9:06:32 PM JPMugaas
|
|
Updated references.
|
|
|
|
|
|
Rev 1.31 2004.10.26 1:06:26 PM czhower
|
|
Further fixes for aliaser
|
|
|
|
|
|
Rev 1.30 2004.10.26 12:01:32 PM czhower
|
|
Resolved alias conflict.
|
|
|
|
|
|
Rev 1.29 9/15/2004 4:59:52 PM DSiders
|
|
Added localization comments.
|
|
|
|
|
|
Rev 1.28 22/07/2004 18:14:22 ANeillans
|
|
Fixed compile error.
|
|
|
|
|
|
Rev 1.27 7/21/04 2:38:04 PM RLebeau
|
|
Removed redundant string copying in TIdDNS_ProcessThread constructor and
|
|
procedure QueryDomain() method
|
|
|
|
Removed local variable from TIdDNS_ProcessThread.SendData(), not needed
|
|
|
|
|
|
Rev 1.26 2004/7/21 ¤U¤È 06:37:48 DChang
|
|
Fix compile error in TIdDNS_ProcessThread.SendData, and mark a case statment
|
|
to comments in TIdDNS_ProcessThread.SaveToCache.
|
|
|
|
|
|
Rev 1.25 2004/7/19 ¤U¤È 09:55:52 DChang
|
|
1. Move all textmoderecords to IdDNSCommon.pas
|
|
2. Making DNS Server load the domain definition file while DNS Server
|
|
component is active.
|
|
3. Add a new event : OnAfterCacheSaved
|
|
4. Add Full name condition to indicate if a domain is empty
|
|
(ConvertDNtoString)
|
|
5. Make Query request processed with independent thread.
|
|
6. Rewrite TIdDNSServer into multiple thread mode, all queries will search
|
|
and assemble the answer, and then share the TIdSocketHandle to send answer
|
|
back.
|
|
7. Add version information in TIdDNSServer, so class CHAOS can be taken, but
|
|
only for the label : "version.bind.".
|
|
8. Fix TIdRR_TXT.BinQueryRecord, to make sure it can be parsed in DNS client.
|
|
9. Modify the AXFR function, reduce the response data size and quantity.
|
|
10. Move all TIdTextModeResourceRecord and derived classes to IdDNSCommon.pas
|
|
|
|
|
|
Rev 1.24 7/8/04 11:43:54 PM RLebeau
|
|
Updated TIdDNS_TCPServer.DoConnect() to use new BytesToString() parameters
|
|
|
|
|
|
Rev 1.23 7/7/04 1:45:16 PM RLebeau
|
|
Compiler fixes
|
|
|
|
|
|
Rev 1.22 6/29/04 1:43:30 PM RLebeau
|
|
Bug fixes for various property setters
|
|
|
|
|
|
Rev 1.21 2004.05.20 1:39:32 PM czhower
|
|
Last of the IdStream updates
|
|
|
|
|
|
Rev 1.20 2004.03.01 9:37:06 PM czhower
|
|
Fixed name conflicts for .net
|
|
|
|
|
|
Rev 1.19 2004.02.07 5:03:32 PM czhower
|
|
.net fixes.
|
|
|
|
|
|
Rev 1.18 2/7/2004 5:39:44 AM JPMugaas
|
|
IdDNSServer should compile in both DotNET and WIn32.
|
|
|
|
|
|
Rev 1.17 2004.02.03 5:45:58 PM czhower
|
|
Name changes
|
|
|
|
|
|
Rev 1.16 1/22/2004 8:26:40 AM JPMugaas
|
|
Ansi* calls changed.
|
|
|
|
|
|
Rev 1.15 1/21/2004 2:12:48 PM JPMugaas
|
|
InitComponent
|
|
|
|
|
|
Rev 1.14 12/7/2003 8:07:26 PM VVassiliev
|
|
string -> TIdBytes
|
|
|
|
|
|
Rev 1.13 2003.10.24 10:38:24 AM czhower
|
|
UDP Server todos
|
|
|
|
|
|
Rev 1.12 10/19/2003 12:16:30 PM DSiders
|
|
Added localization comments.
|
|
|
|
|
|
Rev 1.11 2003.10.12 3:50:40 PM czhower
|
|
Compile todos
|
|
|
|
|
|
Rev 1.10 2003/5/14 ¤W¤È 01:17:36 DChang
|
|
Fix a flag named denoted in the function which check if a domain correct.
|
|
Update the logic of UpdateTree functions (make them unified).
|
|
Update the TextRecord function of all TIdRR_ classes, it checks if the RRName
|
|
the same as FullName, if RRName = FullName, it will not append the Fullname
|
|
to RRName.
|
|
|
|
|
|
Rev 1.9 2003/5/10 ¤W¤È 01:09:42 DChang
|
|
Patch the domainlist update when axfr action.
|
|
|
|
|
|
Rev 1.8 2003/5/9 ¤W¤È 10:03:36 DChang
|
|
Modify the sequence of records. To make sure when we resolve MX record, the
|
|
mail host A record can be additional record section.
|
|
|
|
|
|
Rev 1.7 2003/5/8 ¤U¤È 08:11:34 DChang
|
|
Add TIdDNSMap, TIdDomainNameServerMapping to monitor primary DNS, and
|
|
detecting if the primary DNS record changed, it will update automatically if
|
|
necessary.
|
|
|
|
|
|
Rev 1.6 2003/5/2 ¤U¤È 03:39:38 DChang
|
|
Fix all compile warnings and hints.
|
|
|
|
|
|
Rev 1.5 4/29/2003 08:26:30 PM DenniesChang
|
|
Fix TIdDNSServer Create, the older version miss to create the FBindings.
|
|
fix AXFR procedure, fully support BIND 8 AXFR procedures.
|
|
|
|
Rev 1.4 4/28/2003 02:30:58 PM JPMugaas
|
|
reverted back to the old one as the new one checked will not compile, has
|
|
problametic dependancies on Contrs and Dialogs (both not permitted).
|
|
|
|
Rev 1.3 04/28/2003 01:15:10 AM DenniesChang
|
|
|
|
|
|
Rev 1.2 4/28/2003 07:00:18 AM JPMugaas
|
|
Should now compile.
|
|
|
|
|
|
Rev 1.0 11/14/2002 02:18:42 PM JPMugaas
|
|
|
|
// Ver: 2003-04-28-0115
|
|
// Combine TCP, UDP Tunnel into single TIdDNSServer component.
|
|
// Update TIdDNSServer from TIdUDPServer to TComponent.
|
|
|
|
// Ver: 2003-04-26-1810
|
|
// Add AXFR command.
|
|
|
|
// Ver: 2002-10-30-1253
|
|
// Add TIdRR_AAAA class, RFC 1884 (Ipv6 AAAA)
|
|
// and add the coresponding fix in TIdDNSServer, but left
|
|
// external search option for future.
|
|
|
|
// Ver: 2002-07-10-1610
|
|
// Add a new event : OnAfterSendBack to handle all
|
|
// data logged after query result is sent back to
|
|
// the client.
|
|
|
|
// Ver: 2002-05-27-0910
|
|
// Add a check function in SOA loading function.
|
|
|
|
// Ver: 2002-04-25-1530
|
|
// IdDNSServer. Ver: 2002-03-12-0900
|
|
|
|
|
|
// To-do: RFC 2136 Zone transfer must be implemented.
|
|
|
|
|
|
// Add FindHandedNodeByName to pass the TIdDNTreeNode Object back.
|
|
// Append a blank char when ClearQuota, to avoid the possible of
|
|
// losting a field.
|
|
// Add IdDNTree.SaveToFile
|
|
// Fix SOA RRName assignment.
|
|
// Fix PTRName RRName assignment.
|
|
// Fix TIdDNTreeNode RemoveChild
|
|
|
|
// IdDNSServer. Ver: 2002-02-26-1420
|
|
// Convert the DN Tree Node type, earlier verison just
|
|
// store the A, PTR in the upper domain node, current
|
|
// version save SOA and its subdomain in upper node.
|
|
//
|
|
// Moreover, move Cached_Tree, Handed_Tree to public
|
|
// section, for using convinent.
|
|
//
|
|
// I forget return CName data, fixed.
|
|
// Seperate the seaching of Cache and handled tree into 2
|
|
// parts with a flag.
|
|
|
|
|
|
//IdDNSServer. Ver: 2002-02-24-1715
|
|
// Move TIdDNSServer protected property RootDNS_NET to public
|
|
|
|
|
|
//IdDNSServer. Ver: 2002-02-23-1800
|
|
|
|
Original Programmer: Dennies Chang <dennies@ms4.hinet.net>
|
|
No Copyright. Code is given to the Indy Pit Crew.
|
|
|
|
This DNS Server supports only IN record, but not Chaos system.
|
|
Most of resource records in DNS server was stored with text mode,
|
|
event the TREE structure, it's just for convininet.
|
|
|
|
Why I did it with this way is tring to increase the speed for
|
|
implementation, with Delphi/Kylix internal class and object,
|
|
we can promise the compatible in Windows and Linux.
|
|
|
|
Started: Jan. 20, 2002.
|
|
First Finished: Feb. 23, 2002.
|
|
|
|
RFC 1035 WKS record is not implemented.
|
|
|
|
ToDO: Load Master File automaticlly when DNS Server Active.
|
|
ToDO: patch WKS record data type.
|
|
ToDO: prepare a Tree Editor for DNS Server Construction. (optional)
|
|
}
|
|
unit IdDNSServer;
|
|
|
|
interface
|
|
|
|
{$i IdCompilerDefines.inc}
|
|
|
|
uses
|
|
Classes,
|
|
IdContainers,
|
|
IdAssignedNumbers,
|
|
IdSocketHandle,
|
|
IdIOHandlerSocket,
|
|
IdGlobal,
|
|
IdGlobalProtocols,
|
|
IdBaseComponent,
|
|
IdComponent,
|
|
IdContext,
|
|
IdUDPBase,
|
|
IdResourceStrings,
|
|
IdExceptionCore,
|
|
IdDNSResolver,
|
|
IdUDPServer,
|
|
IdCustomTCPServer,
|
|
IdStackConsts,
|
|
IdThread,
|
|
IdDNSCommon;
|
|
|
|
type
|
|
TIdDomainExpireCheckThread = class(TIdThread)
|
|
protected
|
|
FInterval: UInt32;
|
|
FSender: TObject;
|
|
FTimerEvent: TNotifyEvent;
|
|
FBusy : Boolean;
|
|
FDomain : string;
|
|
FHost : string;
|
|
//
|
|
procedure Run; override;
|
|
procedure TimerEvent;
|
|
end;
|
|
|
|
// forward declaration.
|
|
TIdDNSMap = class;
|
|
TIdDNS_UDPServer = class;
|
|
|
|
// This class is to record the mapping of Domain and its primary DNS IP
|
|
TIdDomainNameServerMapping = class(TObject)
|
|
private
|
|
FHost: string;
|
|
FDomainName: string;
|
|
FBusy : Boolean;
|
|
FInterval: UInt32;
|
|
FList: TIdDNSMap;
|
|
procedure SetHost(const Value: string);
|
|
procedure SetInterval(const Value: UInt32);
|
|
protected
|
|
CheckScheduler : TIdDomainExpireCheckThread;
|
|
property Interval : UInt32 read FInterval write SetInterval;
|
|
property List : TIdDNSMap read FList write FList;
|
|
public
|
|
constructor Create(AList : TIdDNSMap);
|
|
destructor Destroy; override;
|
|
//You can not make methods and properties published in this class.
|
|
//If you want to make properties publishes, this has to derrive from TPersistant
|
|
//and be used by TPersistant in a published property.
|
|
// published
|
|
procedure SyncAndUpdate(Sender : TObject);
|
|
property Host : string read FHost write SetHost;
|
|
property DomainName : string read FDomainName write FDomainName;
|
|
end;
|
|
|
|
TIdDNSMap = class(TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}<TIdDomainNameServerMapping>{$ENDIF})
|
|
private
|
|
FServer: TIdDNS_UDPServer;
|
|
{$IFNDEF HAS_GENERICS_TObjectList}
|
|
function GetItem(Index: Integer): TIdDomainNameServerMapping;
|
|
procedure SetItem(Index: Integer; const Value: TIdDomainNameServerMapping);
|
|
{$ENDIF}
|
|
procedure SetServer(const Value: TIdDNS_UDPServer);
|
|
public
|
|
constructor Create(Server: TIdDNS_UDPServer);
|
|
{$IFNDEF USE_OBJECT_ARC}
|
|
destructor Destroy; override;
|
|
{$ENDIF}
|
|
property Server : TIdDNS_UDPServer read FServer write SetServer;
|
|
{$IFNDEF HAS_GENERICS_TObjectList}
|
|
property Items[Index: Integer]: TIdDomainNameServerMapping read GetItem write SetItem; default;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
TIdMWayTreeNodeClass = class of TIdMWayTreeNode;
|
|
// TODO: derive from TObjectList instead and remove SubTree member?
|
|
TIdMWayTreeNode = class(TObject)
|
|
private
|
|
SubTree : TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}<TIdMWayTreeNode>{$ENDIF};
|
|
FFundmentalClass: TIdMWayTreeNodeClass;
|
|
function GetTreeNode(Index: Integer): TIdMWayTreeNode;
|
|
procedure SetFundmentalClass(const Value: TIdMWayTreeNodeClass);
|
|
procedure SetTreeNode(Index: Integer; const Value: TIdMWayTreeNode);
|
|
public
|
|
constructor Create(NodeClass : TIdMWayTreeNodeClass); virtual;
|
|
destructor Destroy; override;
|
|
property FundmentalClass : TIdMWayTreeNodeClass read FFundmentalClass write SetFundmentalClass;
|
|
property Children[Index : Integer] : TIdMWayTreeNode read GetTreeNode write SetTreeNode;
|
|
function AddChild : TIdMWayTreeNode;
|
|
function InsertChild(Index : Integer) : TIdMWayTreeNode;
|
|
procedure RemoveChild(Index : Integer);
|
|
end;
|
|
|
|
TIdDNTreeNode = class(TIdMWayTreeNode)
|
|
private
|
|
FCLabel : String;
|
|
FRRs: TIdTextModeRRs;
|
|
FChildIndex: TStrings;
|
|
FParentNode: TIdDNTreeNode;
|
|
FAutoSortChild: Boolean;
|
|
procedure SetCLabel(const Value: String);
|
|
procedure SetRRs(const Value: TIdTextModeRRs);
|
|
function GetNode(Index: integer): TIdDNTreeNode;
|
|
procedure SetNode(Index: integer; const Value: TIdDNTreeNode);
|
|
procedure SetChildIndex(const Value: TStrings);
|
|
function GetFullName: string;
|
|
function ConvertToDNString : string;
|
|
function DumpAllBinaryData(var RecordCount:integer) : TIdBytes;
|
|
public
|
|
property ParentNode : TIdDNTreeNode read FParentNode write FParentNode;
|
|
property CLabel : String read FCLabel write SetCLabel;
|
|
property RRs : TIdTextModeRRs read FRRs write SetRRs;
|
|
property Children[Index : Integer] : TIdDNTreeNode read GetNode write SetNode;
|
|
property ChildIndex : TStrings read FChildIndex write SetChildIndex;
|
|
property AutoSortChild : Boolean read FAutoSortChild write FAutoSortChild;
|
|
property FullName : string read GetFullName;
|
|
|
|
constructor Create(AParentNode : TIdDNTreeNode); reintroduce;
|
|
destructor Destroy; override;
|
|
function AddChild : TIdDNTreeNode;
|
|
function InsertChild(Index : Integer) : TIdDNTreeNode;
|
|
procedure RemoveChild(Index : Integer);
|
|
procedure SortChildren;
|
|
procedure Clear;
|
|
procedure SaveToFile(Filename : String);
|
|
function IndexByLabel(CLabel : String): Integer;
|
|
function IndexByNode(ANode : TIdDNTreeNode) : Integer;
|
|
end;
|
|
|
|
TIdDNS_TCPServer = class(TIdCustomTCPServer)
|
|
protected
|
|
FAccessList: TStrings;
|
|
FAccessControl: Boolean;
|
|
//
|
|
procedure DoConnect(AContext: TIdContext); override;
|
|
procedure InitComponent; override;
|
|
procedure SetAccessList(const Value: TStrings);
|
|
public
|
|
destructor Destroy; override;
|
|
published
|
|
property AccessList : TStrings read FAccessList write SetAccessList;
|
|
property AccessControl : boolean read FAccessControl write FAccessControl;
|
|
end;
|
|
|
|
TIdDNS_ProcessThread = class(TIdThread)
|
|
protected
|
|
FMyBinding: TIdSocketHandle;
|
|
FMainBinding: TIdSocketHandle;
|
|
FMyData: TStream;
|
|
FData : TIdBytes;
|
|
FServer: TIdDNS_UDPServer;
|
|
procedure SetMyBinding(const Value: TIdSocketHandle);
|
|
procedure SetMyData(const Value: TStream);
|
|
procedure SetServer(const Value: TIdDNS_UDPServer);
|
|
procedure ComposeErrorResult(var VFinal: TIdBytes; OriginalHeader: TDNSHeader;
|
|
OriginalQuestion : TIdBytes; ErrorStatus: Integer);
|
|
function CombineAnswer(Header : TDNSHeader; const EQuery, Answer : TIdBytes): TIdBytes;
|
|
procedure InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16;
|
|
var Answer: TIdBytes; IfMainQuestion: Boolean; IsSearchCache: Boolean = False;
|
|
IsAdditional: Boolean = False; IsWildCard : Boolean = False;
|
|
WildCardOrgName: string = '');
|
|
procedure ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader;
|
|
Question: TIdBytes; var Answer: TIdBytes);
|
|
function CompleteQuery(DNSHeader: TDNSHeader; Question: string;
|
|
OriginalQuestion: TIdBytes; var Answer : TIdBytes; QType, QClass : UInt16;
|
|
DNSResolver : TIdDNSResolver) : string;
|
|
procedure SaveToCache(ResourceRecord : TIdBytes; QueryName : string; OriginalQType : UInt16);
|
|
function SearchTree(Root : TIdDNTreeNode; QName : String; QType : UInt16): TIdDNTreeNode;
|
|
|
|
procedure Run; override;
|
|
procedure QueryDomain;
|
|
procedure SendData;
|
|
public
|
|
property MyBinding : TIdSocketHandle read FMyBinding write SetMyBinding;
|
|
property MyData: TStream read FMyData write SetMyData;
|
|
property Server : TIdDNS_UDPServer read FServer write SetServer;
|
|
|
|
constructor Create(ACreateSuspended: Boolean = True; Data : TIdBytes = nil;
|
|
MainBinding : TIdSocketHandle = nil; Binding : TIdSocketHandle = nil;
|
|
Server : TIdDNS_UDPServer = nil); reintroduce; overload;
|
|
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TIdDNSBeforeQueryEvent = procedure(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; var ADNSQuery: TIdBytes) of object;
|
|
TIdDNSAfterQueryEvent = procedure(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode: string; Query : TIdBytes) of object;
|
|
TIdDNSAfterCacheSaved = procedure(CacheRoot : TIdDNTreeNode) of object;
|
|
|
|
TIdDNS_UDPServer = class(TIdUDPServer)
|
|
private
|
|
FBusy: Boolean;
|
|
protected
|
|
FAutoUpdateZoneInfo: Boolean;
|
|
FZoneMasterFiles: TStrings;
|
|
FRootDNS_NET: TStrings;
|
|
FCacheUnknowZone: Boolean;
|
|
FCached_Tree: TIdDNTreeNode;
|
|
FHanded_Tree: TIdDNTreeNode;
|
|
FHanded_DomainList: TStrings;
|
|
FAutoLoadMasterFile: Boolean;
|
|
FOnAfterQuery: TIdDNSAfterQueryEvent;
|
|
FOnBeforeQuery: TIdDNSBeforeQueryEvent;
|
|
FCS: TIdCriticalSection;
|
|
FOnAfterSendBack: TIdDNSAfterQueryEvent;
|
|
FOnAfterCacheSaved: TIdDNSAfterCacheSaved;
|
|
FGlobalCS: TIdCriticalSection;
|
|
FDNSVersion: string;
|
|
FofferDNSVersion: Boolean;
|
|
|
|
procedure DoBeforeQuery(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader;
|
|
var ADNSQuery : TIdBytes); dynamic;
|
|
|
|
procedure DoAfterQuery(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader;
|
|
var QueryResult : TIdBytes; var ResultCode : String; Query : TIdBytes); dynamic;
|
|
|
|
procedure DoAfterSendBack(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader;
|
|
var QueryResult : TIdBytes; var ResultCode : String; Query : TIdBytes); dynamic;
|
|
|
|
procedure DoAfterCacheSaved(CacheRoot : TIdDNTreeNode); dynamic;
|
|
|
|
procedure SetZoneMasterFiles(const Value: TStrings);
|
|
procedure SetRootDNS_NET(const Value: TStrings);
|
|
procedure SetHanded_DomainList(const Value: TStrings);
|
|
procedure InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16;
|
|
var Answer: TIdBytes; IfMainQuestion: boolean; IsSearchCache: Boolean = False;
|
|
IsAdditional: Boolean = False; IsWildCard : Boolean = False;
|
|
WildCardOrgName: string = '');
|
|
procedure ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader;
|
|
Question: TIdBytes; var Answer: TIdBytes);
|
|
//modified in May 2004 by Dennies Chang.
|
|
//procedure SaveToCache(ResourceRecord : string);
|
|
procedure SaveToCache(ResourceRecord : TIdBytes; QueryName : string; OriginalQType : UInt16);
|
|
//procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TResultRecord); overload;
|
|
//MoveTo Public section for RaidenDNSD.
|
|
|
|
procedure InitComponent; override;
|
|
// Hide this property temporily, this property is prepared to maintain the
|
|
// TTL expired record auto updated;
|
|
property AutoUpdateZoneInfo : boolean read FAutoUpdateZoneInfo write FAutoUpdateZoneInfo;
|
|
property CS: TIdCriticalSection read FCS;
|
|
procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); override;
|
|
public
|
|
destructor Destroy; override;
|
|
function AXFR(Header : TDNSHeader; Question : string; var Answer : TIdBytes) : string;
|
|
function CompleteQuery(DNSHeader: TDNSHeader; Question: string;
|
|
OriginalQuestion: TIdBytes; var Answer : TIdBytes; QType, QClass : UInt16;
|
|
DNSResolver : TIdDNSResolver) : string; {$IFDEF HAS_DEPRECATED}deprecated;{$ENDIF}
|
|
function LoadZoneFromMasterFile(MasterFileName : String) : boolean;
|
|
function LoadZoneStrings(FileStrings: TStrings; Filename : String;
|
|
TreeRoot : TIdDNTreeNode): Boolean;
|
|
function SearchTree(Root : TIdDNTreeNode; QName : String; QType : UInt16): TIdDNTreeNode;
|
|
procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TIdTextModeResourceRecord); overload;
|
|
function FindNodeFullName(Root : TIdDNTreeNode; QName : String; QType : UInt16) : string;
|
|
function FindHandedNodeByName(QName : String; QType : UInt16) : TIdDNTreeNode;
|
|
procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TResultRecord); overload;
|
|
|
|
property RootDNS_NET : TStrings read FRootDNS_NET write SetRootDNS_NET;
|
|
property Cached_Tree : TIdDNTreeNode read FCached_Tree {write SetCached_Tree};
|
|
property Handed_Tree : TIdDNTreeNode read FHanded_Tree {write SetHanded_Tree};
|
|
property Busy : Boolean read FBusy;
|
|
property GlobalCS : TIdCriticalSection read FGlobalCS;
|
|
published
|
|
property DefaultPort default IdPORT_DOMAIN;
|
|
property AutoLoadMasterFile : Boolean read FAutoLoadMasterFile write FAutoLoadMasterFile Default False;
|
|
|
|
//property AutoUpdateZoneInfo : boolean read FAutoUpdateZoneInfo write SetAutoUpdateZoneInfo;
|
|
property ZoneMasterFiles : TStrings read FZoneMasterFiles write SetZoneMasterFiles;
|
|
property CacheUnknowZone : Boolean read FCacheUnknowZone write FCacheUnknowZone default False;
|
|
property Handed_DomainList : TStrings read FHanded_DomainList write SetHanded_DomainList;
|
|
property DNSVersion : string read FDNSVersion write FDNSVersion;
|
|
property offerDNSVersion : Boolean read FofferDNSVersion write FofferDNSVersion;
|
|
|
|
property OnBeforeQuery : TIdDNSBeforeQueryEvent read FOnBeforeQuery write FOnBeforeQuery;
|
|
property OnAfterQuery : TIdDNSAfterQueryEvent read FOnAfterQuery write FOnAfterQuery;
|
|
property OnAfterSendBack : TIdDNSAfterQueryEvent read FOnAfterSendBack write FOnAfterSendBack;
|
|
property OnAfterCacheSaved : TIdDNSAfterCacheSaved read FOnAfterCacheSaved write FOnAfterCacheSaved;
|
|
end;
|
|
|
|
TIdDNSServer = class(TIdComponent)
|
|
protected
|
|
FActive: Boolean;
|
|
FTCPACLActive: Boolean;
|
|
FServerType: TDNSServerTypes;
|
|
FTCPTunnel: TIdDNS_TCPServer;
|
|
FUDPTunnel: TIdDNS_UDPServer;
|
|
FAccessList: TStrings;
|
|
FBindings: TIdSocketHandles;
|
|
procedure SetAccessList(const Value: TStrings);
|
|
procedure SetActive(const Value: Boolean);
|
|
procedure SetTCPACLActive(const Value: Boolean);
|
|
procedure SetBindings(const Value: TIdSocketHandles);
|
|
procedure TimeToUpdateNodeData(Sender : TObject);
|
|
procedure InitComponent; override;
|
|
public
|
|
BackupDNSMap : TIdDNSMap;
|
|
|
|
destructor Destroy; override;
|
|
procedure CheckIfExpire(Sender: TObject);
|
|
published
|
|
property Active : Boolean read FActive write SetActive;
|
|
property AccessList : TStrings read FAccessList write SetAccessList;
|
|
property Bindings: TIdSocketHandles read FBindings write SetBindings;
|
|
|
|
property TCPACLActive : Boolean read FTCPACLActive write SetTCPACLActive;
|
|
property ServerType: TDNSServerTypes read FServerType write FServerType;
|
|
property TCPTunnel : TIdDNS_TCPServer read FTCPTunnel write FTCPTunnel;
|
|
property UDPTunnel : TIdDNS_UDPServer read FUDPTunnel write FUDPTunnel;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF VCL_XE3_OR_ABOVE}
|
|
{$IFNDEF NEXTGEN}
|
|
System.Contnrs,
|
|
{$ENDIF}
|
|
System.SyncObjs,
|
|
System.Types,
|
|
{$ENDIF}
|
|
IdException,
|
|
{$IFDEF DOTNET}
|
|
{$IFDEF USE_INLINE}
|
|
System.Threading,
|
|
System.IO,
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$IFDEF USE_VCL_POSIX}
|
|
Posix.SysSelect,
|
|
Posix.SysTime,
|
|
{$ENDIF}
|
|
IdIOHandler,
|
|
IdStack,
|
|
SysUtils;
|
|
|
|
{Common Utilities}
|
|
|
|
function CompareItems(Item1, Item2: {$IFDEF HAS_GENERICS_TObjectList}TIdMWayTreeNode{$ELSE}TObject{$ENDIF}): Integer;
|
|
var
|
|
LObj1, LObj2 : TIdDNTreeNode;
|
|
begin
|
|
LObj1 := Item1 as TIdDNTreeNode;
|
|
LObj2 := Item2 as TIdDNTreeNode;
|
|
Result := CompareStr(LObj1.CLabel, LObj2.CLabel);
|
|
end;
|
|
|
|
// TODO: move to IdGlobal.pas
|
|
function PosBytes(const SubBytes, SBytes: TIdBytes): Integer;
|
|
var
|
|
LSubLen, LBytesLen, I: Integer;
|
|
begin
|
|
LSubLen := Length(SubBytes);
|
|
LBytesLen := Length(SBytes);
|
|
if (LSubLen > 0) and (LBytesLen >= LSubLen) then
|
|
begin
|
|
for Result := 0 to LBytesLen-LSubLen do
|
|
begin
|
|
if SBytes[Result] = SubBytes[0] then
|
|
begin
|
|
for I := 1 to LSubLen-1 do
|
|
begin
|
|
if SBytes[Result+I] <> SubBytes[I] then begin
|
|
Break;
|
|
end;
|
|
end;
|
|
if I = LSubLen then begin
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
// TODO: move to IdGlobal.pas
|
|
function FetchBytes(var AInput: TIdBytes; const ADelim: TIdBytes;
|
|
const ADelete: Boolean = IdFetchDeleteDefault): TIdBytes;
|
|
var
|
|
LPos: integer;
|
|
begin
|
|
LPos := PosBytes(ADelim, AInput);
|
|
if LPos = -1 then begin
|
|
Result := AInput;
|
|
if ADelete then begin
|
|
SetLength(AInput, 0);
|
|
end;
|
|
end
|
|
else begin
|
|
Result := ToBytes(AInput, LPos);
|
|
if ADelete then begin
|
|
//slower Delete(AInput, 1, LPos + Length(ADelim) - 1);
|
|
RemoveBytes(AInput, LPos + Length(ADelim));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TIdMWayTreeNode }
|
|
|
|
function TIdMWayTreeNode.AddChild: TIdMWayTreeNode;
|
|
begin
|
|
Result := FundmentalClass.Create(FundmentalClass);
|
|
try
|
|
SubTree.Add(Result);
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
constructor TIdMWayTreeNode.Create(NodeClass : TIdMWayTreeNodeClass);
|
|
begin
|
|
inherited Create;
|
|
FundmentalClass := NodeClass;
|
|
SubTree := TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}<TIdMWayTreeNode>{$ENDIF}.Create;
|
|
end;
|
|
|
|
destructor TIdMWayTreeNode.Destroy;
|
|
begin
|
|
FreeAndNil(SubTree);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TIdMWayTreeNode.GetTreeNode(Index: Integer): TIdMWayTreeNode;
|
|
begin
|
|
Result := {$IFDEF HAS_GENERICS_TObjectList}SubTree.Items[Index]{$ELSE}TIdMWayTreeNode(SubTree.Items[Index]){$ENDIF};
|
|
end;
|
|
|
|
function TIdMWayTreeNode.InsertChild(Index: Integer): TIdMWayTreeNode;
|
|
begin
|
|
Result := FundmentalClass.Create(FundmentalClass);
|
|
try
|
|
SubTree.Insert(Index, Result);
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdMWayTreeNode.RemoveChild(Index: Integer);
|
|
begin
|
|
SubTree.Delete(Index);
|
|
end;
|
|
|
|
procedure TIdMWayTreeNode.SetFundmentalClass(const Value: TIdMWayTreeNodeClass);
|
|
begin
|
|
FFundmentalClass := Value;
|
|
end;
|
|
|
|
procedure TIdMWayTreeNode.SetTreeNode(Index: Integer; const Value: TIdMWayTreeNode);
|
|
begin
|
|
{$IFNDEF USE_OBJECT_ARC}
|
|
SubTree.Items[Index].Free;
|
|
{$ENDIF}
|
|
SubTree.Items[Index] := Value;
|
|
end;
|
|
|
|
{ TIdDNTreeNode }
|
|
|
|
function TIdDNTreeNode.AddChild: TIdDNTreeNode;
|
|
begin
|
|
Result := TIdDNTreeNode.Create(Self);
|
|
try
|
|
SubTree.Add(Result);
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.Clear;
|
|
var
|
|
I : Integer;
|
|
begin
|
|
for I := SubTree.Count - 1 downto 0 do begin
|
|
RemoveChild(I);
|
|
end;
|
|
end;
|
|
|
|
function TIdDNTreeNode.ConvertToDNString: string;
|
|
var
|
|
Count : Integer;
|
|
begin
|
|
Result := '$ORIGIN ' + FullName + EOL; {do not localize}
|
|
|
|
for Count := 0 to RRs.Count-1 do begin
|
|
Result := Result + RRs.Items[Count].TextRecord(FullName);
|
|
end;
|
|
|
|
for Count := 0 to FChildIndex.Count-1 do begin
|
|
Result := Result + Children[Count].ConvertToDNString;
|
|
end;
|
|
end;
|
|
|
|
constructor TIdDNTreeNode.Create(AParentNode : TIdDNTreeNode);
|
|
begin
|
|
inherited Create(TIdDNTreeNode);
|
|
FRRs := TIdTextModeRRs.Create;
|
|
FChildIndex := TStringList.Create;
|
|
FParentNode := AParentNode;
|
|
end;
|
|
|
|
destructor TIdDNTreeNode.Destroy;
|
|
begin
|
|
FreeAndNil(FRRs);
|
|
FreeAndNil(FChildIndex);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TIdDNTreeNode.DumpAllBinaryData(var RecordCount: Integer): TIdBytes;
|
|
var
|
|
Count, ChildCount : integer;
|
|
MyString, ChildString : TIdBytes;
|
|
begin
|
|
SetLength(ChildString, 0);
|
|
SetLength(MyString, 0);
|
|
Inc(RecordCount, RRs.Count + 1);
|
|
|
|
for Count := 0 to RRs.Count -1 do
|
|
begin
|
|
AppendBytes(MyString, RRs.Items[Count].BinQueryRecord(FullName));
|
|
end;
|
|
|
|
for Count := 0 to FChildIndex.Count -1 do
|
|
begin
|
|
// RLebeau: should ChildCount be set to 0 each time?
|
|
AppendBytes(ChildString, Children[Count].DumpAllBinaryData(ChildCount));
|
|
Inc(RecordCount, ChildCount);
|
|
end;
|
|
|
|
if RRs.Count > 0 then begin
|
|
if RRs.Items[0] is TIdRR_SOA then begin
|
|
AppendBytes(MyString, RRs.Items[0].BinQueryRecord(FullName));
|
|
Inc(RecordCount);
|
|
end;
|
|
end;
|
|
|
|
Result := MyString;
|
|
AppendBytes(Result, ChildString);
|
|
|
|
if RRs.Count > 0 then begin
|
|
AppendBytes(Result, RRs.Items[0].BinQueryRecord(FullName));
|
|
end;
|
|
end;
|
|
|
|
function TIdDNTreeNode.GetFullName: string;
|
|
begin
|
|
if ParentNode = nil then begin
|
|
if CLabel = '.' then begin
|
|
Result := '';
|
|
end else begin
|
|
Result := CLabel;
|
|
end;
|
|
end else begin
|
|
Result := CLabel + '.' + ParentNode.FullName;
|
|
end;
|
|
end;
|
|
|
|
function TIdDNTreeNode.GetNode(Index: Integer): TIdDNTreeNode;
|
|
begin
|
|
Result := TIdDNTreeNode(SubTree.Items[Index]);
|
|
end;
|
|
|
|
function TIdDNTreeNode.IndexByLabel(CLabel: String): Integer;
|
|
begin
|
|
Result := FChildIndex.IndexOf(CLabel);
|
|
end;
|
|
|
|
function TIdDNTreeNode.IndexByNode(ANode: TIdDNTreeNode): Integer;
|
|
begin
|
|
Result := SubTree.IndexOf(ANode);
|
|
end;
|
|
|
|
function TIdDNTreeNode.InsertChild(Index: Integer): TIdDNTreeNode;
|
|
begin
|
|
Result := TIdDNTreeNode.Create(Self);
|
|
try
|
|
SubTree.Insert(Index, Result);
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.RemoveChild(Index: Integer);
|
|
begin
|
|
SubTree.Remove(SubTree.Items[Index]);
|
|
FChildIndex.Delete(Index);
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SaveToFile(Filename: String);
|
|
var
|
|
DNSs : TStrings;
|
|
begin
|
|
DNSs := TStringList.Create;
|
|
try
|
|
DNSs.Add(ConvertToDNString);
|
|
ToDo('SaveToFile() method of TIdDNTreeNode class is not implemented yet'); {do not localized}
|
|
// DNSs.SaveToFile(Filename);
|
|
finally
|
|
FreeAndNil(DNSs);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SetChildIndex(const Value: TStrings);
|
|
begin
|
|
FChildIndex.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SetCLabel(const Value: String);
|
|
begin
|
|
FCLabel := Value;
|
|
if ParentNode <> nil then begin
|
|
ParentNode.ChildIndex.Insert(ParentNode.SubTree.IndexOf(Self), Value);
|
|
end;
|
|
if AutoSortChild then begin
|
|
SortChildren;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SetNode(Index: Integer; const Value: TIdDNTreeNode);
|
|
begin
|
|
SubTree.Items[Index] := Value;
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SetRRs(const Value: TIdTextModeRRs);
|
|
begin
|
|
FRRs.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNTreeNode.SortChildren;
|
|
begin
|
|
SubTree.BubbleSort(CompareItems);
|
|
TStringList(FChildIndex).Sort;
|
|
end;
|
|
|
|
{ TIdDNSServer }
|
|
|
|
{$I IdDeprecatedImplBugOff.inc}
|
|
function TIdDNS_UDPServer.CompleteQuery(DNSHeader : TDNSHeader; Question: string;
|
|
OriginalQuestion: TIdBytes; var Answer: TIdBytes; QType, QClass: UInt16;
|
|
DNSResolver : TIdDNSResolver): string;
|
|
{$I IdDeprecatedImplBugOn.inc}
|
|
var
|
|
IsMyDomains : Boolean;
|
|
LAnswer: TIdBytes;
|
|
WildQuestion, TempDomain : string;
|
|
begin
|
|
// QClass = 1 => IN, we support only "IN" class now.
|
|
// QClass = 2 => CS,
|
|
// QClass = 3 => CH,
|
|
// QClass = 4 => HS.
|
|
|
|
if QClass <> 1 then begin
|
|
Result := cRCodeQueryNotImplement;
|
|
Exit;
|
|
end;
|
|
|
|
TempDomain := LowerCase(Question);
|
|
IsMyDomains := (Handed_DomainList.IndexOf(TempDomain) > -1);
|
|
if not IsMyDomains then begin
|
|
Fetch(TempDomain, '.');
|
|
IsMyDomains := (Handed_DomainList.IndexOf(TempDomain) > -1);
|
|
end;
|
|
|
|
if IsMyDomains then begin
|
|
InternalSearch(DNSHeader, Question, QType, LAnswer, True, False, False);
|
|
Answer := LAnswer;
|
|
|
|
if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then
|
|
begin
|
|
InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, False, True);
|
|
AppendBytes(Answer, LAnswer);
|
|
end;
|
|
|
|
WildQuestion := Question;
|
|
Fetch(WildQuestion, '.');
|
|
WildQuestion := '*.' + WildQuestion;
|
|
InternalSearch(DNSHeader, WildQuestion, QType, LAnswer, True, False, False, True, Question);
|
|
AppendBytes(Answer, LAnswer);
|
|
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryOK;
|
|
end else begin
|
|
Result := cRCodeQueryNotFound;
|
|
end;
|
|
end else
|
|
begin
|
|
InternalSearch(DNSHeader, Question, QType, Answer, True, True, False);
|
|
|
|
if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then
|
|
begin
|
|
InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, True, False);
|
|
AppendBytes(Answer, LAnswer);
|
|
end;
|
|
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryCacheOK;
|
|
Exit;
|
|
end;
|
|
|
|
InternalSearch(DNSHeader, Question, TypeCode_Error, Answer, True, True, False);
|
|
if BytesToString(Answer) = 'Error' then begin {do not localize}
|
|
Result := cRCodeQueryCacheFindError;
|
|
Exit;
|
|
end;
|
|
|
|
ExternalSearch(DNSResolver, DNSHeader, OriginalQuestion, Answer);
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryReturned;
|
|
end else begin
|
|
Result := cRCodeQueryNotImplement;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.InitComponent;
|
|
begin
|
|
inherited InitComponent;
|
|
|
|
FRootDNS_NET := TStringList.Create;
|
|
FRootDNS_NET.Add('209.92.33.150'); // nic.net {do not localize}
|
|
FRootDNS_NET.Add('209.92.33.130'); // nic.net {do not localize}
|
|
FRootDNS_NET.Add('203.37.255.97'); // apnic.net {do not localize}
|
|
FRootDNS_NET.Add('202.12.29.131'); // apnic.net {do not localize}
|
|
FRootDNS_NET.Add('12.29.20.2'); // nanic.net {do not localize}
|
|
FRootDNS_NET.Add('204.145.119.2'); // nanic.net {do not localize}
|
|
FRootDNS_NET.Add('140.111.1.2'); // a.twnic.net.tw {do not localize}
|
|
|
|
FCached_Tree := TIdDNTreeNode.Create(nil);
|
|
FCached_Tree.AutoSortChild := True;
|
|
FCached_Tree.CLabel := '.';
|
|
|
|
FHanded_Tree := TIdDNTreeNode.Create(nil);
|
|
FHanded_Tree.AutoSortChild := True;
|
|
FHanded_Tree.CLabel := '.';
|
|
|
|
FHanded_DomainList := TStringList.Create;
|
|
FZoneMasterFiles := TStringList.Create;
|
|
|
|
DefaultPort := IdPORT_DOMAIN;
|
|
FCS := TIdCriticalSection.Create;
|
|
FGlobalCS := TIdCriticalSection.Create;
|
|
FBusy := False;
|
|
end;
|
|
|
|
destructor TIdDNS_UDPServer.Destroy;
|
|
begin
|
|
FreeAndNil(FCached_Tree);
|
|
FreeAndNil(FHanded_Tree);
|
|
FreeAndNil(FRootDNS_NET);
|
|
FreeAndNil(FHanded_DomainList);
|
|
FreeAndNil(FZoneMasterFiles);
|
|
FreeAndNil(FCS);
|
|
FreeAndNil(FGlobalCS);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.DoAfterQuery(ABinding: TIdSocketHandle;
|
|
ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode : String;
|
|
Query : TIdBytes);
|
|
begin
|
|
if Assigned(FOnAfterQuery) then begin
|
|
FOnAfterQuery(ABinding, ADNSHeader, QueryResult, ResultCode, Query);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.DoBeforeQuery(ABinding: TIdSocketHandle;
|
|
ADNSHeader: TDNSHeader; var ADNSQuery: TIdBytes);
|
|
begin
|
|
if Assigned(FOnBeforeQuery) then begin
|
|
FOnBeforeQuery(ABinding, ADNSHeader, ADNSQuery);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.ExternalSearch(ADNSResolver : TIdDNSResolver;
|
|
Header: TDNSHeader; Question: TIdBytes; var Answer: TIdBytes);
|
|
var
|
|
Server_Index : Integer;
|
|
MyDNSResolver : TIdDNSResolver;
|
|
begin
|
|
if RootDNS_NET.Count = 0 then begin
|
|
Exit;
|
|
end;
|
|
Server_Index := 0;
|
|
if ADNSResolver = nil then begin
|
|
MyDNSResolver := TIdDNSResolver.Create(Self);
|
|
MyDNSResolver.WaitingTime := 5000;
|
|
end else begin
|
|
MyDNSResolver := ADNSResolver;
|
|
end;
|
|
try
|
|
repeat
|
|
MyDNSResolver.Host := RootDNS_NET.Strings[Server_Index];
|
|
try
|
|
MyDNSResolver.InternalQuery := Question;
|
|
MyDNSResolver.Resolve('');
|
|
Answer := MyDNSResolver.PlainTextResult;
|
|
except
|
|
// Todo: Create DNS server interal resolver error.
|
|
on EIdDnsResolverError do begin
|
|
//Empty Event, for user to custom the event handle.
|
|
end;
|
|
on EIdSocketError do begin
|
|
end;
|
|
|
|
else
|
|
begin
|
|
end;
|
|
end;
|
|
|
|
Inc(Server_Index);
|
|
until (Server_Index >= RootDNS_NET.Count) or (Length(Answer) > 0);
|
|
finally
|
|
if ADNSResolver = nil then begin
|
|
FreeAndNil(MyDNSResolver);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.FindHandedNodeByName(QName: String; QType: UInt16): TIdDNTreeNode;
|
|
begin
|
|
Result := SearchTree(Handed_Tree, QName, QType);
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.FindNodeFullName(Root: TIdDNTreeNode; QName: String; QType : UInt16): string;
|
|
var
|
|
MyNode : TIdDNTreeNode;
|
|
begin
|
|
MyNode := SearchTree(Root, QName, QType);
|
|
if MyNode <> nil then begin
|
|
Result := MyNode.FullName;
|
|
end else begin
|
|
Result := '';
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.LoadZoneFromMasterFile(MasterFileName: String): Boolean;
|
|
var
|
|
FileStrings : TStrings;
|
|
begin
|
|
{MakeTagList;}
|
|
Result := FileExists(MasterFileName);
|
|
|
|
if Result then begin
|
|
FileStrings := TStringList.Create;
|
|
try
|
|
Todo('LoadZoneFromMasterFile() method of TIdDNS_UDPServer class is not implemented yet'); {do not localize}
|
|
// FileStrings.LoadFromFile(MasterFileName);
|
|
Result := LoadZoneStrings(FileStrings, MasterFileName, Handed_Tree);
|
|
finally
|
|
FreeAndNil(FileStrings);
|
|
end;
|
|
end;
|
|
{FreeTagList;}
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.LoadZoneStrings(FileStrings: TStrings; Filename : String;
|
|
TreeRoot : TIdDNTreeNode): Boolean;
|
|
var
|
|
TagList : TStrings;
|
|
|
|
function IsMSDNSFileName(theFileName : String; var DN: string) : Boolean;
|
|
var
|
|
namepart : TStrings;
|
|
Fullname : string;
|
|
Count : Integer;
|
|
begin
|
|
Fullname := theFilename;
|
|
repeat
|
|
if Pos('\', Fullname) > 0 then begin
|
|
Fetch(Fullname, '\');
|
|
end;
|
|
until Pos('\', Fullname) = 0;
|
|
|
|
namepart := TStringList.Create;
|
|
try
|
|
repeat
|
|
namepart.Add(Fetch(Fullname, '.'));
|
|
until Fullname = '';
|
|
|
|
Result := namepart.Strings[namepart.Count-1] = 'dns'; {do not localize}
|
|
if Result then begin
|
|
Count := 0;
|
|
DN := namepart.Strings[Count];
|
|
repeat
|
|
Inc(Count);
|
|
if Count <= namepart.Count -2 then begin
|
|
DN := DN + '.' + namepart.Strings[Count];
|
|
end;
|
|
until Count >= (namepart.Count-2);
|
|
end;
|
|
finally
|
|
FreeAndNil(namepart);
|
|
end;
|
|
end;
|
|
|
|
procedure MakeTagList;
|
|
begin
|
|
TagList := TStringList.Create;
|
|
try
|
|
TagList.Add(cAAAA);
|
|
TagList.Add(cA);
|
|
TagList.Add(cNS);
|
|
TagList.Add(cMD);
|
|
TagList.Add(cMF);
|
|
TagList.Add(cCName);
|
|
TagList.Add(cSOA);
|
|
TagList.Add(cMB);
|
|
TagList.Add(cMG);
|
|
TagList.Add(cMR);
|
|
TagList.Add(cNULL);
|
|
TagList.Add(cWKS);
|
|
TagList.Add(cPTR);
|
|
TagList.Add(cHINFO);
|
|
TagList.Add(cMINFO);
|
|
TagList.Add(cMX);
|
|
TagList.Add(cTXT);
|
|
|
|
// The Following Tags are used in master file, but not Resource Record.
|
|
TagList.Add(cOrigin);
|
|
TagList.Add(cInclude);
|
|
//TagList.Add(cAt);
|
|
except
|
|
FreeAndNil(TagList);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure FreeTagList;
|
|
begin
|
|
FreeAndNil(TagList);
|
|
end;
|
|
|
|
function ClearDoubleQutoa(Strs : TStrings): Boolean;
|
|
var
|
|
SSCount : Integer;
|
|
Mark, Found : Boolean;
|
|
begin
|
|
SSCount := 0;
|
|
Mark := False;
|
|
|
|
while SSCount <= (Strs.Count-1) do begin
|
|
Found := Pos('"', Strs.Strings[SSCount]) > 0;
|
|
while Found do begin
|
|
Mark := Mark xor Found;
|
|
Strs.Strings[SSCount] := ReplaceSpecString(Strs.Strings[SSCount], '"', '', False);
|
|
Found := Pos('"', Strs.Strings[SSCount]) > 0;
|
|
end;
|
|
|
|
if not Mark then begin
|
|
Inc(SSCount);
|
|
end else begin
|
|
Strs.Strings[SSCount] := Strs.Strings[SSCount] + ' ' + Strs.Strings[SSCount + 1];
|
|
Strs.Delete(SSCount + 1);
|
|
end;
|
|
end;
|
|
|
|
Result := not Mark;
|
|
end;
|
|
|
|
function IsValidMasterFile : Boolean;
|
|
var
|
|
EachLinePart : TStrings;
|
|
CurrentLineNum, TagField, Count : Integer;
|
|
LineData, DataBody, {Comment,} FPart, LTag : string;
|
|
Denoted, Stop, PassQuota : Boolean;
|
|
begin
|
|
EachLinePart := TStringList.Create;
|
|
try
|
|
CurrentLineNum := 0;
|
|
Stop := False;
|
|
// Check Denoted;
|
|
Denoted := false;
|
|
|
|
if FileStrings.Count > 0 then begin
|
|
repeat
|
|
LineData := Trim(FileStrings.Strings[CurrentLineNum]);
|
|
DataBody := Fetch(LineData, ';');
|
|
//Comment := LineData;
|
|
PassQuota := Pos('(', DataBody) = 0;
|
|
|
|
// Split each item into TStrings.
|
|
repeat
|
|
if not PassQuota then begin
|
|
Inc(CurrentLineNum);
|
|
LineData := Trim(FileStrings.Strings[CurrentLineNum]);
|
|
DataBody := DataBody + ' ' + Fetch(LineData, ';');
|
|
PassQuota := Pos(')', DataBody) > 0;
|
|
end;
|
|
until PassQuota or (CurrentLineNum > (FileStrings.Count-1));
|
|
|
|
Stop := not PassQuota;
|
|
|
|
if not Stop then begin
|
|
EachLinePart.Clear;
|
|
DataBody := ReplaceSpecString(DataBody, '(', '');
|
|
DataBody := ReplaceSpecString(DataBody, ')', '');
|
|
|
|
repeat
|
|
DataBody := Trim(DataBody);
|
|
FPart := Fetch(DataBody, #9);
|
|
|
|
repeat
|
|
FPart := Trim(FPart);
|
|
LTag := Fetch(FPart,' ');
|
|
|
|
if (LTag <> '') and (LTag <> '(') and (LTag <> ')') then begin
|
|
EachLinePart.Add(LTag);
|
|
end;
|
|
until FPart = '';
|
|
until DataBody = '';
|
|
|
|
if not Denoted then begin
|
|
if EachLinePart.Count > 1 then begin
|
|
Denoted := (EachLinePart.Strings[0] = cOrigin) or (EachLinePart.IndexOf(cSOA) <> -1);
|
|
end else begin
|
|
Denoted := False;
|
|
end;
|
|
end;
|
|
|
|
// Check Syntax;
|
|
if not ((EachLinePart.Count > 0) and (EachLinePart.Strings[0] = cOrigin)) then
|
|
begin
|
|
if not Denoted then begin
|
|
if EachLinePart.Count > 0 then begin
|
|
Stop := (EachLinePart.Count > 0) and (EachLinePart.IndexOf(cSOA) = -1);
|
|
end else begin
|
|
Stop := False;
|
|
end;
|
|
end else begin
|
|
//TagField := -1;
|
|
//FieldCount := 0;
|
|
|
|
// Search Tag Named 'IN';
|
|
TagField := EachLinePart.IndexOf('IN'); {do not localize}
|
|
|
|
if TagField = -1 then begin
|
|
Count := 0;
|
|
repeat
|
|
if EachLinePart.Count > 0 then begin
|
|
TagField := TagList.IndexOf(EachLinePart.Strings[Count]);
|
|
end;
|
|
Inc(Count);
|
|
until (Count >= EachLinePart.Count -1) or (TagField <> -1);
|
|
|
|
if TagField <> -1 then begin
|
|
TagField := Count;
|
|
end;
|
|
end else begin
|
|
if TagList.IndexOf(EachLinePart.Strings[TagField + 1]) = -1 then begin
|
|
TagField := -1;
|
|
end else begin
|
|
Inc(TagField);
|
|
end;
|
|
end;
|
|
|
|
if TagField > -1 then begin
|
|
case TagList.IndexOf(EachLinePart.Strings[TagField]) of
|
|
// Check ip
|
|
TypeCode_A : Stop := not IsValidIP(EachLinePart.Strings[TagField + 1]);
|
|
// Check ip v6
|
|
0 : Stop := not IsValidIPv6(EachLinePart.Strings[TagField + 1]);
|
|
|
|
// Check Domain Name
|
|
TypeCode_CName, TypeCode_NS, TypeCode_MR,
|
|
TypeCode_MD, TypeCode_MB, TypeCode_MG,
|
|
TypeCode_MF: Stop := not IsHostName(EachLinePart.Strings[TagField + 1]);
|
|
|
|
// Can be anything
|
|
TypeCode_TXT, TypeCode_NULL: Stop := False;
|
|
|
|
// Must be FQDN.
|
|
TypeCode_PTR: Stop := not IsFQDN(EachLinePart.Strings[TagField + 1]);
|
|
|
|
// HINFO should has 2 fields : CPU and OS. but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_HINFO:
|
|
begin
|
|
Stop := not (ClearDoubleQutoa(EachLinePart) and
|
|
((EachLinePart.Count - TagField - 1) = 2));
|
|
end;
|
|
|
|
// Check RMailBX and EMailBX but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_MINFO:
|
|
begin
|
|
Stop := ((EachLinePart.Count - TagField - 1) <> 2);
|
|
if not Stop then begin
|
|
Stop := not (IsHostName(EachLinePart.Strings[TagField + 1]) and
|
|
IsHostName(EachLinePart.Strings[TagField + 2]));
|
|
end;
|
|
end;
|
|
|
|
// Check Pref(Numeric) and Exchange. but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_MX:
|
|
begin
|
|
Stop := ((EachLinePart.Count - TagField - 1) <> 2);
|
|
if not Stop then begin
|
|
Stop := not (IsNumeric(EachLinePart.Strings[TagField + 1]) and
|
|
IsHostName(EachLinePart.Strings[TagField + 2]));
|
|
end;
|
|
end;
|
|
|
|
// TStrings is 0 base, so that we have to minus one
|
|
TypeCode_SOA:
|
|
begin
|
|
Stop := ((EachLinePart.Count - TagField - 1) <> 7);
|
|
if not Stop then begin
|
|
Stop := not (IsHostName(EachLinePart.Strings[TagField + 1]) and
|
|
IsHostName(EachLinePart.Strings[TagField + 2]) and
|
|
IsNumeric(EachLinePart.Strings[TagField + 3]) and
|
|
IsNumeric(EachLinePart.Strings[TagField + 4]) and
|
|
IsNumeric(EachLinePart.Strings[TagField + 5]) and
|
|
IsNumeric(EachLinePart.Strings[TagField + 6]) and
|
|
IsNumeric(EachLinePart.Strings[TagField + 7])
|
|
);
|
|
end;
|
|
end;
|
|
|
|
TypeCode_WKS: Stop := ((EachLinePart.Count - TagField) = 1);
|
|
end;
|
|
end else begin
|
|
if EachLinePart.Count > 0 then
|
|
Stop := True;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Inc(CurrentLineNum);
|
|
until (CurrentLineNum > (FileStrings.Count-1)) or Stop;
|
|
end;
|
|
Result := not Stop;
|
|
finally
|
|
FreeAndNil(EachLinePart);
|
|
end;
|
|
end;
|
|
|
|
function LoadMasterFile : Boolean;
|
|
var
|
|
Checks, EachLinePart, DenotedDomain : TStrings;
|
|
CurrentLineNum, TagField, Count, LastTTL : Integer;
|
|
LineData, DataBody, FPart, LTag, LText,
|
|
RName, LastDenotedDomain, LastTag, NewDomain, SingleHostName {CH: , PrevDNTag} : string;
|
|
Stop, PassQuota, Found {, canChangPrevDNTag } : Boolean;
|
|
LLRR_A : TIdRR_A;
|
|
LLRR_AAAA : TIdRR_AAAA;
|
|
LLRR_NS : TIdRR_NS;
|
|
LLRR_MB : TIdRR_MB;
|
|
LLRR_Name : TIdRR_CName;
|
|
LLRR_SOA : TIdRR_SOA;
|
|
LLRR_MG : TIdRR_MG;
|
|
LLRR_MR : TIdRR_MR;
|
|
LLRR_PTR : TIdRR_PTR;
|
|
LLRR_HINFO : TIdRR_HINFO;
|
|
LLRR_MINFO : TIdRR_MINFO;
|
|
LLRR_MX : TIdRR_MX;
|
|
LLRR_TXT : TIdRR_TXT;
|
|
begin
|
|
EachLinePart := TStringList.Create;
|
|
try
|
|
DenotedDomain := TStringList.Create;
|
|
try
|
|
CurrentLineNum := 0;
|
|
LastDenotedDomain := '';
|
|
LastTag := '';
|
|
NewDomain := '';
|
|
// PrevDNTag := '';
|
|
Stop := False;
|
|
//canChangPrevDNTag := True;
|
|
|
|
if IsMSDNSFileName(FileName, LastDenotedDomain) then begin
|
|
//canChangPrevDNTag := False;
|
|
Filename := Uppercase(Filename);
|
|
end else begin
|
|
LastDenotedDomain := '';
|
|
end;
|
|
|
|
if FileStrings.Count > 0 then begin
|
|
repeat
|
|
LineData := Trim(FileStrings.Strings[CurrentLineNum]);
|
|
DataBody := Fetch(LineData, ';');
|
|
// Comment := LineData;
|
|
PassQuota := Pos('(', DataBody) = 0;
|
|
|
|
// Split each item into TStrings.
|
|
repeat
|
|
if not PassQuota then begin
|
|
Inc(CurrentLineNum);
|
|
LineData := Trim(FileStrings.Strings[CurrentLineNum]);
|
|
DataBody := DataBody + ' ' + Fetch(LineData, ';');
|
|
PassQuota := Pos(')', DataBody) > 0;
|
|
end;
|
|
until PassQuota;
|
|
|
|
EachLinePart.Clear;
|
|
DataBody := ReplaceSpecString(DataBody, '(', '');
|
|
DataBody := ReplaceSpecString(DataBody, ')', '');
|
|
repeat
|
|
DataBody := Trim(DataBody);
|
|
FPart := Fetch(DataBody, #9);
|
|
|
|
repeat
|
|
FPart := Trim(FPart);
|
|
if Pos('"', FPart) = 1 then begin
|
|
Fetch(FPart, '"');
|
|
LText := Fetch(FPart, '"');
|
|
EachLinePart.Add(LText);
|
|
end;
|
|
|
|
LTag := Fetch(FPart, ' ');
|
|
if (TagList.IndexOf(LTag) = -1) and (LTag <> 'IN') then begin {do not localize}
|
|
LTag := LowerCase(LTag);
|
|
end;
|
|
|
|
if (LTag <> '') and (LTag <> '(') and (LTag <> ')') then begin
|
|
EachLinePart.Add(LTag);
|
|
end;
|
|
until FPart = '';
|
|
until DataBody = '';
|
|
|
|
if EachLinePart.Count > 0 then begin
|
|
if EachLinePart.Strings[0] = cOrigin then begin
|
|
// One Domain is found.
|
|
NewDomain := EachLinePart.Strings[1];
|
|
if TextEndsWith(NewDomain, '.') then begin
|
|
LastDenotedDomain := NewDomain;
|
|
NewDomain := '';
|
|
end else begin
|
|
LastDenotedDomain := NewDomain + '.' + LastDenotedDomain;
|
|
NewDomain := '';
|
|
end;
|
|
end else begin
|
|
// Search RR Type Tag;
|
|
Count := 0;
|
|
TagField := -1;
|
|
|
|
repeat
|
|
Found := TagList.IndexOf(EachLinePart.Strings[Count]) > -1;
|
|
if Found then begin
|
|
TagField := Count;
|
|
end;
|
|
Inc(Count);
|
|
until Found or (Count > (EachLinePart.Count-1));
|
|
|
|
// To initialize LastTTL;
|
|
LastTTL := 86400;
|
|
if TagField > -1 then begin
|
|
case TagField of
|
|
1 :
|
|
if EachLinePart.Strings[0] <> 'IN' then begin {do not localize}
|
|
// canChangPrevDNTag := True;
|
|
LastTag := EachLinePart.Strings[0];
|
|
if EachLinePart.Strings[TagField] <> 'SOA' then begin {do not localize}
|
|
// PrevDNTag := '';
|
|
end else begin
|
|
LastTTL := IndyStrToInt(EachLinePart.Strings[TagField + 6]);
|
|
end;
|
|
// end else begin
|
|
// canChangPrevDNTag := False;
|
|
end;
|
|
2 :
|
|
if EachLinePart.Strings[1] = 'IN' then begin {do not localize}
|
|
LastTag := EachLinePart.Strings[0];
|
|
// canChangPrevDNTag := True;
|
|
if EachLinePart.Strings[TagField] <> 'SOA' then begin {do not localize}
|
|
// PrevDNTag := '';
|
|
end else begin
|
|
LastTTL := IndyStrToInt(EachLinePart.Strings[TagField + 6]);
|
|
end;
|
|
end else begin
|
|
// canChangPrevDNTag := False;
|
|
end;
|
|
else
|
|
begin
|
|
// canChangPrevDNTag := False;
|
|
LastTTL := 86400;
|
|
end;
|
|
end;
|
|
|
|
//if (EachLinePart.Strings[0] = cAt) or (PrevDNTag = 'SOA') then
|
|
if EachLinePart.Strings[0] = cAt then begin
|
|
SingleHostName := LastDenotedDomain
|
|
end else begin
|
|
if LastTag = cAt then begin
|
|
LastTag := SingleHostName;
|
|
end;
|
|
if not TextEndsWith(LastTag, '.') then begin
|
|
SingleHostName := LastTag + '.' + LastDenotedDomain
|
|
end else begin
|
|
SingleHostName := LastTag;
|
|
end;
|
|
end;
|
|
|
|
case TagList.IndexOf(EachLinePart.Strings[TagField]) of
|
|
// Check ip
|
|
TypeCode_A :
|
|
begin
|
|
LLRR_A := TIdRR_A.Create;
|
|
LLRR_A.RRName := SingleHostName;
|
|
LLRR_A.Address := EachLinePart.Strings[TagField + 1];
|
|
LLRR_A.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_A);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'A';
|
|
// end;
|
|
end;
|
|
|
|
// Check IPv6 ip address 10/29,2002
|
|
0 :
|
|
begin
|
|
LLRR_AAAA := TIdRR_AAAA.Create;
|
|
LLRR_AAAA.RRName := SingleHostName;
|
|
LLRR_AAAA.Address := ConvertToValidv6IP(EachLinePart.Strings[TagField + 1]);
|
|
LLRR_AAAA.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_AAAA);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'AAAA'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// Check Domain Name
|
|
TypeCode_CName:
|
|
begin
|
|
LLRR_Name := TIdRR_CName.Create;
|
|
LLRR_Name.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_Name.CName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_Name.CName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_Name.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_Name);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'CNAME'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
TypeCode_NS :
|
|
begin
|
|
LLRR_NS := TIdRR_NS.Create;
|
|
LLRR_NS.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_NS.NSDName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_NS.NSDName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_NS.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_NS);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'NS'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
TypeCode_MR :
|
|
begin
|
|
LLRR_MR := TIdRR_MR.Create;
|
|
LLRR_MR.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_MR.NewName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_MR.NewName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_MR.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_MR);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'MR'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
TypeCode_MD, TypeCode_MB, TypeCode_MF :
|
|
begin
|
|
LLRR_MB := TIdRR_MB.Create;
|
|
LLRR_MB.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_MB.MADName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_MB.MADName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_MB.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_MB);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'MF'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
TypeCode_MG :
|
|
begin
|
|
LLRR_MG := TIdRR_MG.Create;
|
|
LLRR_MG.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_MG.MGMName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_MG.MGMName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_MG.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_MG);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'MG'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// Can be anything
|
|
TypeCode_TXT, TypeCode_NULL:
|
|
begin
|
|
LLRR_TXT := TIdRR_TXT.Create;
|
|
LLRR_TXT.RRName := SingleHostName;
|
|
LLRR_TXT.TXT := EachLinePart.Strings[TagField + 1];
|
|
LLRR_TXT.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_TXT);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'TXT'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// Must be FQDN.
|
|
TypeCode_PTR:
|
|
begin
|
|
LLRR_PTR := TIdRR_PTR.Create;
|
|
LLRR_PTR.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_PTR.PTRDName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_PTR.PTRDName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_PTR.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_PTR);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'PTR'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// HINFO should has 2 fields : CPU and OS. but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_HINFO:
|
|
begin
|
|
ClearDoubleQutoa(EachLinePart);
|
|
|
|
LLRR_HINFO := TIdRR_HINFO.Create;
|
|
LLRR_HINFO.RRName := SingleHostName;
|
|
LLRR_HINFO.CPU := EachLinePart.Strings[TagField + 1];
|
|
LLRR_HINFO.OS := EachLinePart.Strings[TagField + 2];
|
|
LLRR_HINFO.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_HINFO);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'HINFO'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// Check RMailBX and EMailBX but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_MINFO:
|
|
begin
|
|
LLRR_MINFO := TIdRR_MINFO.Create;
|
|
LLRR_MINFO.RRName := SingleHostName;
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_MINFO.Responsible_Mail := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_MINFO.Responsible_Mail := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin
|
|
LLRR_MINFO.ErrorHandle_Mail := EachLinePart.Strings[TagField + 2];
|
|
end else begin
|
|
LLRR_MINFO.ErrorHandle_Mail := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain;
|
|
end;
|
|
|
|
LLRR_MINFO.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_MINFO);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'MINFO'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// Check Pref(Numeric) and Exchange. but TStrings
|
|
// is 0 base, so that we have to minus one
|
|
TypeCode_MX:
|
|
begin
|
|
LLRR_MX := TIdRR_MX.Create;
|
|
LLRR_MX.RRName := SingleHostName;
|
|
LLRR_MX.Preference := EachLinePart.Strings[TagField + 1];
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin
|
|
LLRR_MX.Exchange := EachLinePart.Strings[TagField + 2];
|
|
end else begin
|
|
LLRR_MX.Exchange := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain;
|
|
end;
|
|
LLRR_MX.TTL := LastTTL;
|
|
|
|
UpdateTree(TreeRoot, LLRR_MX);
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'MX'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
// TStrings is 0 base, so that we have to minus one
|
|
TypeCode_SOA:
|
|
begin
|
|
LLRR_SOA := TIdRR_SOA.Create;
|
|
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin
|
|
LLRR_SOA.MName := EachLinePart.Strings[TagField + 1];
|
|
end else begin
|
|
LLRR_SOA.MName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain;
|
|
end;
|
|
|
|
//LLRR_SOA.RRName:= LLRR_SOA.MName;
|
|
if (SingleHostName = '') and (LastDenotedDomain = '') then begin
|
|
{$IFDEF STRING_IS_UNICODE}
|
|
LastDenotedDomain := String(LLRR_SOA.MName); // explicit convert to Unicode
|
|
{$ELSE}
|
|
LastDenotedDomain := LLRR_SOA.MName;
|
|
{$ENDIF}
|
|
Fetch(LastDenotedDomain, '.');
|
|
SingleHostName := LastDenotedDomain;
|
|
end;
|
|
LLRR_SOA.RRName := SingleHostName;
|
|
|
|
// Update the Handed List
|
|
{
|
|
if Handed_DomainList.IndexOf(LLRR_SOA.MName) = -1 then begin
|
|
Handed_DomainList.Add(LLRR_SOA.MName);
|
|
end;
|
|
}
|
|
if Handed_DomainList.IndexOf(LLRR_SOA.RRName) = -1 then begin
|
|
Handed_DomainList.Add(LLRR_SOA.RRName);
|
|
end;
|
|
|
|
{
|
|
if DenotedDomain.IndexOf(LLRR_SOA.MName) = -1 then begin
|
|
DenotedDomain.Add(LLRR_SOA.MName);
|
|
end;
|
|
LastDenotedDomain := LLRR_SOA.MName;
|
|
}
|
|
|
|
if DenotedDomain.IndexOf(LLRR_SOA.RRName) = -1 then begin
|
|
DenotedDomain.Add(LLRR_SOA.RRName);
|
|
end;
|
|
//LastDenotedDomain := LLRR_SOA.RRName;
|
|
|
|
if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin
|
|
LLRR_SOA.RName := EachLinePart.Strings[TagField + 2];
|
|
end else begin
|
|
LLRR_SOA.RName := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain;
|
|
end;
|
|
|
|
Checks := TStringList.Create;
|
|
try
|
|
{$IFDEF STRING_IS_UNICODE}
|
|
RName := String(LLRR_SOA.RName); // explicit convert to Unicode
|
|
{$ELSE}
|
|
RName := LLRR_SOA.RName;
|
|
{$ENDIF}
|
|
|
|
while RName <> '' do begin
|
|
Checks.Add(Fetch(RName, '.'));
|
|
end;
|
|
|
|
RName := '';
|
|
For Count := 0 to Checks.Count -1 do begin
|
|
if Checks.Strings[Count] <> '' then begin
|
|
RName := RName + Checks.Strings[Count] + '.';
|
|
end;
|
|
end;
|
|
|
|
LLRR_SOA.RName := RName;
|
|
finally
|
|
FreeAndNil(Checks);
|
|
end;
|
|
|
|
LLRR_SOA.Serial := EachLinePart.Strings[TagField + 3];
|
|
LLRR_SOA.Refresh := EachLinePart.Strings[TagField + 4];
|
|
LLRR_SOA.Retry := EachLinePart.Strings[TagField + 5];
|
|
LLRR_SOA.Expire := EachLinePart.Strings[TagField + 6];
|
|
LLRR_SOA.Minimum := EachLinePart.Strings[TagField + 7];
|
|
LastTTL := IndyStrToInt(LLRR_SOA.Expire);
|
|
LLRR_SOA.TTL := LastTTL;
|
|
UpdateTree(TreeRoot, LLRR_SOA);
|
|
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'SOA'; {do not localize}
|
|
// end;
|
|
end;
|
|
|
|
TypeCode_WKS:
|
|
begin
|
|
// if canChangPrevDNTag then begin
|
|
// PrevDNTag := 'WKS'; {do not localize}
|
|
// end;
|
|
end;
|
|
end;
|
|
end;
|
|
end; // if EachLinePart.Count == 0 => Only Comment
|
|
end;
|
|
Inc(CurrentLineNum);
|
|
until (CurrentLineNum > (FileStrings.Count -1));
|
|
end;
|
|
Result := not Stop;
|
|
finally
|
|
FreeAndNil(DenotedDomain);
|
|
end;
|
|
finally
|
|
FreeAndNil(EachLinePart);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
MakeTagList;
|
|
try
|
|
Result := IsValidMasterFile;
|
|
// IsValidMasterFile is used in local, so I design with not
|
|
// any parameter.
|
|
if Result then begin
|
|
Result := LoadMasterFile;
|
|
end;
|
|
finally
|
|
FreeTagList;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.SaveToCache(ResourceRecord: TIdBytes; QueryName : string; OriginalQType : UInt16);
|
|
var
|
|
TempResolver : TIdDNSResolver;
|
|
Count : Integer;
|
|
begin
|
|
TempResolver := TIdDNSResolver.Create(nil);
|
|
try
|
|
// RLebeau: FillResultWithOutCheckId() is deprecated, but not using FillResult()
|
|
// here yet because it validates the DNSHeader.RCode, and I do not know if that
|
|
// is needed here. I don't want to break this logic...
|
|
TempResolver.FillResultWithOutCheckId(ResourceRecord);
|
|
if TempResolver.DNSHeader.ANCount > 0 then begin
|
|
for Count := 0 to TempResolver.QueryResult.Count - 1 do begin
|
|
UpdateTree(Cached_Tree, TempResolver.QueryResult.Items[Count]);
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(TempResolver);
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.SearchTree(Root: TIdDNTreeNode; QName: String; QType : UInt16): TIdDNTreeNode;
|
|
var
|
|
RRIndex : integer;
|
|
NodeCursor : TIdDNTreeNode;
|
|
NameLabels : TStrings;
|
|
OneNode, FullName : string;
|
|
Found : Boolean;
|
|
begin
|
|
Result := nil;
|
|
NameLabels := TStringList.Create;
|
|
try
|
|
FullName := QName;
|
|
NodeCursor := Root;
|
|
Found := False;
|
|
|
|
repeat
|
|
OneNode := Fetch(FullName, '.');
|
|
if OneNode <> '' then begin
|
|
NameLabels.Add(OneNode);
|
|
end;
|
|
until FullName = '';
|
|
|
|
repeat
|
|
if QType <> TypeCode_SOA then begin
|
|
RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]);
|
|
if RRIndex <> -1 then begin
|
|
NameLabels.Delete(NameLabels.Count - 1);
|
|
NodeCursor := NodeCursor.Children[RRIndex];
|
|
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
end else begin
|
|
Found := NameLabels.Count = 0;
|
|
end;
|
|
end else begin
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
if not Found then begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end else begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end;
|
|
end else begin
|
|
RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]);
|
|
if RRIndex <> -1 then begin
|
|
NameLabels.Delete(NameLabels.Count - 1);
|
|
NodeCursor := NodeCursor.Children[RRIndex];
|
|
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
end else begin
|
|
Found := NameLabels.Count = 0;
|
|
end;
|
|
end else begin
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
if not Found then begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end else begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end;
|
|
end;
|
|
until (NameLabels.Count = 0) or Found;
|
|
|
|
if Found then begin
|
|
Result := NodeCursor;
|
|
end;
|
|
finally
|
|
FreeAndNil(NameLabels);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.SetHanded_DomainList(const Value: TStrings);
|
|
begin
|
|
FHanded_DomainList.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.SetRootDNS_NET(const Value: TStrings);
|
|
begin
|
|
FRootDNS_NET.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.SetZoneMasterFiles(const Value: TStrings);
|
|
begin
|
|
FZoneMasterFiles.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.UpdateTree(TreeRoot: TIdDNTreeNode; RR: TResultRecord);
|
|
var
|
|
NameNode : TStrings;
|
|
RRName, APart : String;
|
|
Count, NodeIndex : Integer;
|
|
NodeCursor : TIdDNTreeNode;
|
|
LRR_A : TIdRR_A;
|
|
LRR_AAAA : TIdRR_AAAA;
|
|
LRR_NS : TIdRR_NS;
|
|
LRR_MB : TIdRR_MB;
|
|
LRR_Name : TIdRR_CName;
|
|
LRR_SOA : TIdRR_SOA;
|
|
LRR_MG : TIdRR_MG;
|
|
LRR_MR : TIdRR_MR;
|
|
LRR_PTR : TIdRR_PTR;
|
|
LRR_HINFO : TIdRR_HINFO;
|
|
LRR_MINFO : TIdRR_MINFO;
|
|
LRR_MX : TIdRR_MX;
|
|
LRR_TXT : TIdRR_TXT;
|
|
begin
|
|
RRName := RR.Name;
|
|
|
|
NameNode := TStringList.Create;
|
|
try
|
|
repeat
|
|
APart := Fetch(RRName, '.');
|
|
if APart <> '' then begin
|
|
NameNode.Add(APart);
|
|
end;
|
|
until RRName = '';
|
|
|
|
NodeCursor := TreeRoot;
|
|
RRName := RR.Name;
|
|
if not TextEndsWith(RRName, '.') then begin
|
|
RRName := RRName + '.';
|
|
end;
|
|
if (RR.RecType <> qtSOA) and (Handed_DomainList.IndexOf(LowerCase(RRName)) = -1) and (RR.RecType <> qtNS) then begin
|
|
for Count := NameNode.Count-1 downto 1 do begin
|
|
NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]);
|
|
if NodeIndex = -1 then begin
|
|
NodeCursor := NodeCursor.AddChild;
|
|
NodeCursor.AutoSortChild := True;
|
|
NodeCursor.CLabel := NameNode.Strings[Count];
|
|
end else begin
|
|
NodeCursor := NodeCursor.Children[NodeIndex];
|
|
end;
|
|
end;
|
|
RRName := NameNode.Strings[0];
|
|
end else begin
|
|
for Count := NameNode.Count-1 downto 0 do begin
|
|
NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]);
|
|
RRName := NameNode.Strings[Count];
|
|
if NodeIndex = -1 then begin
|
|
NodeCursor := NodeCursor.AddChild;
|
|
//NodeCursor.CLabel := RRName;
|
|
NodeCursor.AutoSortChild := True;
|
|
NodeCursor.CLabel := RRName;
|
|
end else begin
|
|
NodeCursor := NodeCursor.Children[NodeIndex];
|
|
end;
|
|
end;
|
|
RRName := RR.Name;
|
|
end;
|
|
|
|
NodeCursor.RRs.ItemNames.Add(RRName);
|
|
|
|
case RR.RecType of
|
|
qtA :
|
|
begin
|
|
LRR_A := TIdRR_A.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_A);
|
|
except
|
|
LRR_A.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_A.RRName := RRName;
|
|
LRR_A.Address := TARecord(RR).IPAddress;
|
|
LRR_A.TTL := TARecord(RR).TTL;
|
|
|
|
if LRR_A.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_A.RRName := LRR_A.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtAAAA :
|
|
begin
|
|
LRR_AAAA := TIdRR_AAAA.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_AAAA);
|
|
except
|
|
LRR_AAAA.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_AAAA.RRName := RRName;
|
|
LRR_AAAA.Address := TAAAARecord(RR).Address;
|
|
LRR_AAAA.TTL := TAAAARecord(RR).TTL;
|
|
|
|
if LRR_AAAA.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_AAAA.RRName := LRR_AAAA.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtNS:
|
|
begin
|
|
LRR_NS := TIdRR_NS.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_NS);
|
|
except
|
|
LRR_NS.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_NS.RRName := RRName;
|
|
LRR_NS.NSDName := TNSRecord(RR).HostName;
|
|
LRR_NS.TTL := TNSRecord(RR).TTL;
|
|
|
|
if LRR_NS.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_NS.RRName := LRR_NS.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtMD, qtMF, qtMB:
|
|
begin
|
|
LRR_MB := TIdRR_MB.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_MB);
|
|
except
|
|
LRR_MB.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_MB.RRName := RRName;
|
|
LRR_MB.MADName := TNAMERecord(RR).HostName;
|
|
LRR_MB.TTL := TNAMERecord(RR).TTL;
|
|
|
|
if LRR_MB.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_MB.RRName := LRR_MB.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtName:
|
|
begin
|
|
LRR_Name := TIdRR_CName.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_Name);
|
|
except
|
|
LRR_Name.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_Name.RRName := RRName;
|
|
LRR_Name.CName := TNAMERecord(RR).HostName;
|
|
LRR_Name.TTL:= TNAMERecord(RR).TTL;
|
|
|
|
if LRR_Name.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_Name.RRName := LRR_Name.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtSOA:
|
|
begin
|
|
LRR_SOA := TIdRR_SOA.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_SOA);
|
|
except
|
|
LRR_SOA.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_SOA.RRName := RRName;
|
|
|
|
LRR_SOA.MName := TSOARecord(RR).Primary;
|
|
LRR_SOA.RName := TSOARecord(RR).ResponsiblePerson;
|
|
LRR_SOA.Serial := IntToStr(TSOARecord(RR).Serial);
|
|
LRR_SOA.Minimum := IntToStr(TSOARecord(RR).MinimumTTL);
|
|
LRR_SOA.Refresh := IntToStr(TSOARecord(RR).Refresh);
|
|
LRR_SOA.Retry := IntToStr(TSOARecord(RR).Retry);
|
|
LRR_SOA.Expire := IntToStr(TSOARecord(RR).Expire);
|
|
LRR_SOA.TTL:= TSOARecord(RR).TTL;
|
|
|
|
if LRR_SOA.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_SOA.RRName := LRR_SOA.RRName + '.'+ NodeCursor.FullName;
|
|
end
|
|
else if not TextEndsWith(LRR_SOA.RRName, '.') then begin
|
|
LRR_SOA.RRName := LRR_SOA.RRName + '.';
|
|
end;
|
|
end;
|
|
qtMG :
|
|
begin
|
|
LRR_MG := TIdRR_MG.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_MG);
|
|
except
|
|
LRR_MG.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_MG.RRName := RRName;
|
|
LRR_MG.MGMName := TNAMERecord(RR).HostName;
|
|
LRR_MG.TTL := TNAMERecord(RR).TTL;
|
|
|
|
if LRR_MG.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_MG.RRName := LRR_MG.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtMR :
|
|
begin
|
|
LRR_MR := TIdRR_MR.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_MR);
|
|
except
|
|
LRR_MR.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_MR.RRName := RRName;
|
|
LRR_MR.NewName := TNAMERecord(RR).HostName;
|
|
LRR_MR.TTL := TNAMERecord(RR).TTL;
|
|
|
|
if LRR_MR.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_MR.RRName := LRR_MR.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtWKS:
|
|
begin
|
|
end;
|
|
qtPTR:
|
|
begin
|
|
LRR_PTR := TIdRR_PTR.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_PTR);
|
|
except
|
|
LRR_PTR.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_PTR.RRName := RRName;
|
|
LRR_PTR.PTRDName := TPTRRecord(RR).HostName;
|
|
LRR_PTR.TTL := TPTRRecord(RR).TTL;
|
|
|
|
if LRR_PTR.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_PTR.RRName := LRR_PTR.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtHINFO:
|
|
begin
|
|
LRR_HINFO := TIdRR_HINFO.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_HINFO);
|
|
except
|
|
LRR_HINFO.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_HINFO.RRName := RRName;
|
|
LRR_HINFO.CPU := THINFORecord(RR).CPU;
|
|
LRR_HINFO.OS := THINFORecord(RR).OS;
|
|
LRR_HINFO.TTL := THINFORecord(RR).TTL;
|
|
|
|
if LRR_HINFO.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_HINFO.RRName := LRR_HINFO.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtMINFO:
|
|
begin
|
|
LRR_MINFO := TIdRR_MINFO.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_MINFO);
|
|
except
|
|
LRR_MINFO.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_MINFO.RRName := RRName;
|
|
LRR_MINFO.Responsible_Mail := TMINFORecord(RR).ResponsiblePersonMailbox;
|
|
LRR_MINFO.ErrorHandle_Mail := TMINFORecord(RR).ErrorMailbox;
|
|
LRR_MINFO.TTL := TMINFORecord(RR).TTL;
|
|
|
|
if LRR_MINFO.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_MINFO.RRName := LRR_MINFO.RRName + '.' + NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtMX:
|
|
begin
|
|
LRR_MX := TIdRR_MX.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_MX);
|
|
except
|
|
LRR_MX.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_MX.RRName := RRName;
|
|
LRR_MX.Exchange := TMXRecord(RR).ExchangeServer;
|
|
LRR_MX.Preference := IntToStr(TMXRecord(RR).Preference);
|
|
LRR_MX.TTL := TMXRecord(RR).TTL;
|
|
|
|
if LRR_MX.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_MX.RRName := LRR_MX.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
qtTXT, qtNULL:
|
|
begin
|
|
LRR_TXT := TIdRR_TXT.Create;
|
|
try
|
|
NodeCursor.RRs.Add(LRR_TXT);
|
|
except
|
|
LRR_TXT.Free;
|
|
raise;
|
|
end;
|
|
|
|
LRR_TXT.RRName := RRName;
|
|
LRR_TXT.TXT := TTextRecord(RR).Text.Text;
|
|
LRR_TXT.TTL := TTextRecord(RR).TTL;
|
|
|
|
if LRR_TXT.ifAddFullName(NodeCursor.FullName) then begin
|
|
LRR_TXT.RRName := LRR_TXT.RRName + '.'+ NodeCursor.FullName;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(NameNode);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.UpdateTree(TreeRoot: TIdDNTreeNode; RR: TIdTextModeResourceRecord);
|
|
var
|
|
NameNode : TStrings;
|
|
RRName, APart : String;
|
|
Count, NodeIndex, RRIndex : Integer;
|
|
NodeCursor : TIdDNTreeNode;
|
|
LRR_AAAA : TIdRR_AAAA;
|
|
LRR_A : TIdRR_A;
|
|
LRR_NS : TIdRR_NS;
|
|
LRR_MB : TIdRR_MB;
|
|
LRR_Name : TIdRR_CName;
|
|
LRR_SOA : TIdRR_SOA;
|
|
LRR_MG : TIdRR_MG;
|
|
LRR_MR : TIdRR_MR;
|
|
LRR_PTR : TIdRR_PTR;
|
|
LRR_HINFO : TIdRR_HINFO;
|
|
LRR_MINFO : TIdRR_MINFO;
|
|
LRR_MX : TIdRR_MX;
|
|
LRR_TXT : TIdRR_TXT;
|
|
LRR_Error : TIdRR_Error;
|
|
begin
|
|
RRName := RR.RRName;
|
|
|
|
NameNode := TStringList.Create;
|
|
try
|
|
repeat
|
|
APart := Fetch(RRName, '.');
|
|
if APart <> '' then begin
|
|
NameNode.Add(APart);
|
|
end;
|
|
until RRName = '';
|
|
|
|
NodeCursor := TreeRoot;
|
|
RRName := RR.RRName;
|
|
if not TextEndsWith(RRName, '.') then begin
|
|
RR.RRName := RR.RRName + '.';
|
|
end;
|
|
|
|
// VC: in2002-02-24-1715, it just denoted TIdRR_A and TIdRR_PTR,
|
|
// but that make search a domain name RR becoming complex,
|
|
// therefor I replace it with all RRs but not TIdRR_SOA
|
|
// SOA should own independent node.
|
|
if (not (RR is TIdRR_SOA)) and (Handed_DomainList.IndexOf(LowerCase(RR.RRName)) = -1) then begin
|
|
for Count := NameNode.Count - 1 downto 1 do begin
|
|
NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]);
|
|
if NodeIndex = -1 then begin
|
|
NodeCursor := NodeCursor.AddChild;
|
|
NodeCursor.AutoSortChild := True;
|
|
NodeCursor.CLabel := NameNode.Strings[Count];
|
|
end else begin
|
|
NodeCursor := NodeCursor.Children[NodeIndex];
|
|
end;
|
|
end;
|
|
RRName := NameNode.Strings[0];
|
|
end else begin
|
|
for Count := NameNode.Count -1 downto 0 do begin
|
|
NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]);
|
|
RRName := NameNode.Strings[Count];
|
|
if NodeIndex = -1 then begin
|
|
NodeCursor := NodeCursor.AddChild;
|
|
NodeCursor.AutoSortChild := True;
|
|
NodeCursor.CLabel := RRName;
|
|
end else begin
|
|
NodeCursor := NodeCursor.Children[NodeIndex];
|
|
end;
|
|
end;
|
|
RRName := RR.RRName;
|
|
end;
|
|
|
|
RRIndex := NodeCursor.RRs.ItemNames.IndexOf(RRName);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.ItemNames.Add(RRName);
|
|
end else begin
|
|
repeat
|
|
Inc(RRIndex);
|
|
if RRIndex > NodeCursor.RRs.ItemNames.Count -1 then begin
|
|
RRIndex := -1;
|
|
Break;
|
|
end;
|
|
if NodeCursor.RRs.ItemNames.Strings[RRIndex] <> RRName then begin
|
|
Break;
|
|
end;
|
|
until RRIndex > (NodeCursor.RRs.ItemNames.Count-1);
|
|
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.ItemNames.Add(RRName);
|
|
end else begin
|
|
NodeCursor.RRs.ItemNames.Insert(RRIndex, RRName);
|
|
end;
|
|
end;
|
|
|
|
case RR.TypeCode of
|
|
TypeCode_Error :
|
|
begin
|
|
LRR_Error := TIdRR_Error(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_Error);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_Error);
|
|
end;
|
|
end;
|
|
TypeCode_A :
|
|
begin
|
|
LRR_A := TIdRR_A(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_A);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_A);
|
|
end;
|
|
end;
|
|
TypeCode_AAAA :
|
|
begin
|
|
LRR_AAAA := TIdRR_AAAA(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_AAAA);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_AAAA);
|
|
end;
|
|
end;
|
|
TypeCode_NS:
|
|
begin
|
|
LRR_NS := TIdRR_NS(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_NS);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_NS);
|
|
end;
|
|
end;
|
|
TypeCode_MF:
|
|
begin
|
|
LRR_MB := TIdRR_MB(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_MB);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_MB);
|
|
end;
|
|
end;
|
|
TypeCode_CName:
|
|
begin
|
|
LRR_Name := TIdRR_CName(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_Name);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_Name);
|
|
end;
|
|
end;
|
|
TypeCode_SOA:
|
|
begin
|
|
LRR_SOA := TIdRR_SOA(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_SOA);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_SOA);
|
|
end;
|
|
end;
|
|
TypeCode_MG :
|
|
begin
|
|
LRR_MG := TIdRR_MG(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_MG);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_MG);
|
|
end;
|
|
end;
|
|
TypeCode_MR :
|
|
begin
|
|
LRR_MR := TIdRR_MR(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_MR);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_MR);
|
|
end;
|
|
end;
|
|
TypeCode_WKS:
|
|
begin
|
|
end;
|
|
TypeCode_PTR:
|
|
begin
|
|
LRR_PTR := TIdRR_PTR(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_PTR);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_PTR);
|
|
end;
|
|
end;
|
|
TypeCode_HINFO:
|
|
begin
|
|
LRR_HINFO := TIdRR_HINFO(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_HINFO);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_HINFO);
|
|
end;
|
|
end;
|
|
TypeCode_MINFO:
|
|
begin
|
|
LRR_MINFO := TIdRR_MINFO(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_MINFO);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_MINFO);
|
|
end;
|
|
end;
|
|
TypeCode_MX:
|
|
begin
|
|
LRR_MX := TIdRR_MX(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_MX);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_MX);
|
|
end;
|
|
end;
|
|
TypeCode_TXT, TypeCode_NULL:
|
|
begin
|
|
LRR_TXT := TIdRR_TXT(RR);
|
|
if RRIndex = -1 then begin
|
|
NodeCursor.RRs.Add(LRR_TXT);
|
|
end else begin
|
|
NodeCursor.RRs.Insert(RRIndex, LRR_TXT);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(NameNode);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.DoAfterSendBack(ABinding: TIdSocketHandle;
|
|
ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode: String;
|
|
Query : TIdBytes);
|
|
begin
|
|
if Assigned(FOnAfterSendBack) then begin
|
|
FOnAfterSendBack(ABinding, ADNSHeader, QueryResult, ResultCode, Query);
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_UDPServer.AXFR(Header : TDNSHeader; Question: string; var Answer: TIdBytes): string;
|
|
var
|
|
TargetNode : TIdDNTreeNode;
|
|
IsMyDomains : Boolean;
|
|
RRcount : Integer;
|
|
Temp: TIdBytes;
|
|
begin
|
|
Question := LowerCase(Question);
|
|
|
|
IsMyDomains := Handed_DomainList.IndexOf(Question) > -1;
|
|
if not IsMyDomains then begin
|
|
Fetch(Question, '.');
|
|
IsMyDomains := Handed_DomainList.IndexOf(Question) > -1;
|
|
end;
|
|
|
|
// Is my domain, go for searching the node.
|
|
TargetNode := nil;
|
|
SetLength(Answer, 0);
|
|
Header.ANCount := 0;
|
|
if IsMyDomains then begin
|
|
TargetNode := SearchTree(Handed_Tree, Question, TypeCode_SOA);
|
|
end;
|
|
if IsMyDomains and (TargetNode <> nil) then begin
|
|
// combine the AXFR Data(So many)
|
|
|
|
RRCount := 0;
|
|
Answer := TargetNode.DumpAllBinaryData(RRCount);
|
|
Header.ANCount := RRCount;
|
|
|
|
Header.QR := iQr_Answer;
|
|
Header.AA := iAA_Authoritative;
|
|
Header.RCode := iRCodeNoError;
|
|
Header.QDCount := 0;
|
|
Header.ARCount := 0;
|
|
Header.TC := 0;
|
|
Temp := Header.GenerateBinaryHeader;
|
|
AppendBytes(Temp, Answer);
|
|
Answer := Temp;
|
|
|
|
Result := cRCodeQueryOK;
|
|
end else begin
|
|
Header.QR := iQr_Answer;
|
|
Header.AA := iAA_Authoritative;
|
|
Header.RCode := iRCodeNameError;
|
|
Header.QDCount := 0;
|
|
Header.ARCount := 0;
|
|
Header.TC := 0;
|
|
|
|
Answer := Header.GenerateBinaryHeader;
|
|
Result := cRCodeQueryNotFound;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.InternalSearch(Header: TDNSHeader; QName: string;
|
|
QType : UInt16; var Answer: TIdBytes; IfMainQuestion : Boolean;
|
|
IsSearchCache : Boolean = False; IsAdditional : Boolean = False;
|
|
IsWildCard : Boolean = False; WildCardOrgName : string = '');
|
|
var
|
|
MoreAddrSearch : TStrings;
|
|
TargetNode : TIdDNTreeNode;
|
|
Server_Index, RRIndex, Count : Integer;
|
|
LocalAnswer, TempBytes, TempAnswer: TIdBytes;
|
|
temp_QName, temp: string;
|
|
AResult: TIdBytes;
|
|
Stop, Extra, IsMyDomains, ifAdditional : Boolean;
|
|
LDNSResolver : TIdDNSResolver;
|
|
|
|
procedure CheckMoreAddrSearch(const AStr: String);
|
|
begin
|
|
if (not IsValidIP(AStr)) and IsHostName(AStr) then begin
|
|
MoreAddrSearch.Add(AStr);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
SetLength(Answer, 0);
|
|
SetLength(Aresult, 0);
|
|
// Search the Handed Tree first.
|
|
MoreAddrSearch := TStringList.Create;
|
|
try
|
|
Extra := False;
|
|
//Pushed := False;
|
|
|
|
if not IsSearchCache then begin
|
|
TargetNode := SearchTree(Handed_Tree, QName, QType);
|
|
|
|
if TargetNode <> nil then begin //Assemble the Answer.
|
|
RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName));
|
|
if RRIndex = -1 then begin
|
|
{ below are added again by Dennies Chang in 2004/7/15
|
|
{ According RFC 1035, a full domain name must be tailed by a '.',
|
|
{ but in normal behavior, user will not input '.' in last
|
|
{ position of the full name. So we have to compare both of the
|
|
{ cases. }
|
|
if TextEndsWith(QName, '.') then begin
|
|
QName := Copy(QName, 1, Length(QName)-1);
|
|
end;
|
|
|
|
RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName));
|
|
{ above are added again by Dennies Chang in 2004/7/15}
|
|
|
|
if RRIndex = -1 then begin
|
|
QName := Fetch(QName, '.');
|
|
RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName));
|
|
end;
|
|
{ marked by Dennies Chang in 2004/7/15
|
|
QName:= Fetch(QName, '.');
|
|
RRIndex := TargetNode.RRs.ItemNames.IndexOf(IndyLowerCase(QName));
|
|
}
|
|
end;
|
|
|
|
repeat
|
|
temp_QName := QName;
|
|
SetLength(LocalAnswer, 0);
|
|
|
|
if RRIndex <> -1 then begin
|
|
case QType of
|
|
TypeCode_A:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_A then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_AAAA:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_AAAA then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_NS:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_NS).NSDName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MD:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MF:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_CName:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_CName then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_CName).CName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_SOA:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_SOA then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).MName);
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).RName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MB:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MG:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MG then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MG).MGMName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MR:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MR then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MR).NewName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_NULL:
|
|
begin
|
|
{
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_NULL then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
}
|
|
end;
|
|
TypeCode_WKS:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_WKS then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_PTR:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_PTR then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_HINFO:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_HINFO then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MINFO:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MINFO then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MX:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MX then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MX).Exchange);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_TXT:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_TXT then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_STAR:
|
|
begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
|
|
if IsWildCard and (Length(LocalAnswer) > 0) then begin
|
|
{
|
|
temp := DomainNameToDNSStr(QName+'.'+TargetNode.FullName);
|
|
Fetch(LocalAnswer, temp);
|
|
}
|
|
TempBytes := DomainNameToDNSStr(TargetNode.FullName);
|
|
FetchBytes(LocalAnswer, TempBytes);
|
|
TempBytes := DomainNameToDNSStr(WildCardOrgName);
|
|
AppendBytes(TempBytes, LocalAnswer);
|
|
LocalAnswer := TempBytes;
|
|
//LocalAnswer := DomainNameToDNSStr(WildCardOrgName) + LocalAnswer;
|
|
end;
|
|
|
|
if Length(LocalAnswer) > 0 then begin
|
|
AppendBytes(Answer, LocalAnswer);
|
|
if ((not Extra) and (not IsAdditional)) or (QType = TypeCode_AAAA) then begin
|
|
if (TargetNode.RRs.Items[RRIndex] is TIdRR_NS) then begin
|
|
if IfMainQuestion then begin
|
|
Header.ANCount := Header.ANCount + 1;
|
|
end else begin
|
|
Header.NSCount := Header.NSCount + 1;
|
|
end;
|
|
end
|
|
else if IfMainQuestion then begin
|
|
Header.ANCount := Header.ANCount + 1;
|
|
end else begin
|
|
Header.ARCount := Header.ARCount + 1;
|
|
end;
|
|
end
|
|
else if IsAdditional then begin
|
|
Header.ARCount := Header.ARCount + 1;
|
|
end
|
|
else begin
|
|
Header.ANCount := Header.ANCount + 1;
|
|
end;
|
|
|
|
Header.Qr := iQr_Answer;
|
|
Header.AA := iAA_Authoritative;
|
|
Header.RCode := iRCodeNoError;
|
|
end;
|
|
|
|
if RRIndex < (TargetNode.RRs.ItemNames.Count-1) then begin
|
|
Stop := False;
|
|
Inc(RRIndex);
|
|
end else begin
|
|
Stop := True;
|
|
end;
|
|
end else begin
|
|
Stop := True;
|
|
end;
|
|
|
|
if QName = temp_QName then begin
|
|
temp_QName := '';
|
|
end;
|
|
until (RRIndex = -1) or
|
|
(not ((not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], QName)) xor
|
|
(not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], Fetch(temp_QName, '.')))))
|
|
or Stop;
|
|
|
|
// Finish the Loop, but n record is found, we need to search if
|
|
// there is a widechar record in its subdomain.
|
|
// Main, Cache, Additional, Wildcard
|
|
if Length(Answer) > 0 then begin
|
|
InternalSearch(Header, '*.' + QName, QType, LocalAnswer, IfMAinQuestion, False, False, True, QName);
|
|
if LocalAnswer <> nil then begin
|
|
AppendBytes(Answer, LocalAnswer);
|
|
end;
|
|
end;
|
|
end else begin // Node can't be found.
|
|
MoreAddrSearch.Clear;
|
|
end;
|
|
|
|
if MoreAddrSearch.Count > 0 then begin
|
|
for Count := 0 to MoreAddrSearch.Count -1 do begin
|
|
Server_Index := 0;
|
|
if Handed_DomainList.Count > 0 then begin
|
|
repeat
|
|
IsMyDomains := IndyPos(
|
|
LowerCase(Handed_DomainList.Strings[Server_Index]),
|
|
LowerCase(MoreAddrSearch.Strings[Count])) > 0;
|
|
Inc(Server_Index);
|
|
until IsMyDomains or (Server_Index > (Handed_DomainList.Count-1));
|
|
end else begin
|
|
IsMyDomains := False;
|
|
end;
|
|
|
|
if IsMyDomains then begin
|
|
//ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA);
|
|
// modified by Dennies Chang in 2004/7/15.
|
|
ifAdditional := (QType <> TypeCode_CName);
|
|
|
|
//Search A record first.
|
|
// Main, Cache, Additional, Wildcard
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, False, ifAdditional, False);
|
|
{ modified by Dennies Chang in 2004/7/15.
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A,
|
|
LocalAnswer, True, ifAdditional, True);
|
|
}
|
|
|
|
if Length(LocalAnswer) = 0 then begin
|
|
temp := MoreAddrSearch.Strings[Count];
|
|
Fetch(temp, '.');
|
|
temp := '*.' + temp;
|
|
InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]);
|
|
{ marked by Dennies Chang in 2004/7/15.
|
|
InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, ifAdditional, True, True, MoreAddrSearch.Strings[Count]);
|
|
}
|
|
end;
|
|
|
|
TempAnswer := LocalAnswer;
|
|
|
|
// Search for AAAA also.
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True);
|
|
{ marked by Dennies Chang in 2004/7/15.
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, ifAdditional, True);
|
|
}
|
|
|
|
if Length(LocalAnswer) = 0 then begin
|
|
temp := MoreAddrSearch.Strings[Count];
|
|
Fetch(temp, '.');
|
|
temp := '*.' + temp;
|
|
InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]);
|
|
{ marked by Dennies Chang in 2004/7/15.
|
|
InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, ifAdditional, True, True, MoreAddrSearch.Strings[Count]);
|
|
}
|
|
end;
|
|
|
|
AppendBytes(TempAnswer, LocalAnswer);
|
|
LocalAnswer := TempAnswer;
|
|
end else begin
|
|
// Need add AAAA Search in future.
|
|
//QType := TypeCode_A;
|
|
LDNSResolver := TIdDNSResolver.Create(Self);
|
|
try
|
|
Server_Index := 0;
|
|
repeat
|
|
LDNSResolver.Host := RootDNS_NET.Strings[Server_Index];
|
|
LDNSResolver.QueryType := [qtA];
|
|
LDNSResolver.Resolve(MoreAddrSearch.Strings[Count]);
|
|
AResult := LDNSResolver.PlainTextResult;
|
|
Header.ARCount := Header.ARCount + LDNSResolver.QueryResult.Count;
|
|
until (Server_Index >= (RootDNS_NET.Count-1)) or (Length(AResult) > 0);
|
|
|
|
AppendBytes(LocalAnswer, AResult, 12);
|
|
finally
|
|
FreeAndNil(LDNSResolver);
|
|
end;
|
|
end;
|
|
|
|
if Length(LocalAnswer) > 0 then begin
|
|
AppendBytes(Answer, LocalAnswer);
|
|
end;
|
|
//Answer := LocalAnswer;
|
|
end;
|
|
end;
|
|
end else begin
|
|
//Search the Cache Tree;
|
|
{ marked by Dennies Chang in 2004/7/15.
|
|
{ it's mark for querying cache only.
|
|
{ if Length(Answer) = 0 then begin }
|
|
TargetNode := SearchTree(Cached_Tree, QName, QType);
|
|
if TargetNode <> nil then begin
|
|
//Assemble the Answer.
|
|
{ modified by Dennies Chang in 2004/7/15}
|
|
if (QType in [TypeCode_A, TypeCode_PTR, TypeCode_AAAA, TypeCode_Error, TypeCode_CName]) then begin
|
|
QName := Fetch(QName, '.');
|
|
end;
|
|
|
|
RRIndex := TargetNode.RRs.ItemNames.IndexOf(QName);
|
|
|
|
repeat
|
|
temp_QName := QName;
|
|
SetLength(LocalAnswer, 0);
|
|
|
|
if RRIndex <> -1 then begin
|
|
// TimeOut, update the record.
|
|
if CompareDate(Now, StrToDateTime(TargetNode.RRs.Items[RRIndex].TimeOut)) = 1 then begin
|
|
SetLength(LocalAnswer, 0);
|
|
end else begin
|
|
case QType of
|
|
TypeCode_Error:
|
|
begin
|
|
AppendString(Answer, 'Error'); {do not localize}
|
|
end;
|
|
TypeCode_A:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_A then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_AAAA:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_AAAA then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_NS:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_NS).NSDName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MD:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MF:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_CName:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_CName then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_CName).CName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_SOA:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_SOA then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).MName);
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).RName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MB:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MG:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MG then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MG).MGMName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MR:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MR then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MR).NewName);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_NULL:
|
|
begin
|
|
{
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_NULL then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
}
|
|
end;
|
|
TypeCode_WKS:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_WKS then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_PTR:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_PTR then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_HINFO:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_HINFO then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MINFO:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MINFO then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_MX:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_MX then begin
|
|
CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MX).Exchange);
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_TXT:
|
|
begin
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_TXT then begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
TypeCode_STAR:
|
|
begin
|
|
LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if BytesToString(LocalAnswer) = 'Error' then begin {do not localize}
|
|
Stop := True;
|
|
end else begin
|
|
if Length(LocalAnswer) > 0 then begin
|
|
AppendBytes(Answer, LocalAnswer);
|
|
if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin
|
|
if IfMainQuestion then begin
|
|
Header.ANCount := Header.ANCount + 1;
|
|
end else begin
|
|
Header.NSCount := Header.NSCount + 1;
|
|
end;
|
|
end
|
|
else if IfMainQuestion then begin
|
|
Header.ANCount := Header.ANCount + 1;
|
|
end
|
|
else begin
|
|
Header.ARCount := Header.ARCount + 1;
|
|
end;
|
|
|
|
Header.Qr := iQr_Answer;
|
|
Header.AA := iAA_NotAuthoritative;
|
|
Header.RCode := iRCodeNoError;
|
|
end;
|
|
|
|
if RRIndex < (TargetNode.RRs.ItemNames.Count-1) then begin
|
|
Stop := False;
|
|
Inc(RRIndex);
|
|
end else begin
|
|
Stop := True;
|
|
end;
|
|
end;
|
|
end else begin
|
|
Stop := True;
|
|
end;
|
|
until (RRIndex = -1) or
|
|
(not ((not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], QName)) xor
|
|
(not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], Fetch(temp_QName, '.')))))
|
|
or Stop;
|
|
|
|
end;
|
|
|
|
// Search MoreAddrSearch it's added in 2004/7/15, but the need is
|
|
// found in 2004 Feb.
|
|
if MoreAddrSearch.Count > 0 then begin
|
|
for Count := 0 to MoreAddrSearch.Count -1 do begin
|
|
Server_Index := 0;
|
|
if Handed_DomainList.Count > 0 then begin
|
|
repeat
|
|
IsMyDomains := IndyPos(
|
|
LowerCase(Handed_DomainList.Strings[Server_Index]),
|
|
LowerCase(MoreAddrSearch.Strings[Count])) > 0;
|
|
Inc(Server_Index);
|
|
until IsMyDomains or (Server_Index > (Handed_DomainList.Count-1));
|
|
end else begin
|
|
IsMyDomains := False;
|
|
end;
|
|
|
|
if IsMyDomains then begin
|
|
ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA);
|
|
|
|
//Search A record first.
|
|
// Main, Cache, Additional, Wildcard
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, False, ifAdditional, False);
|
|
|
|
if Length(LocalAnswer) = 0 then begin
|
|
temp := MoreAddrSearch.Strings[Count];
|
|
Fetch(temp, '.');
|
|
temp := '*.' + temp;
|
|
InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]);
|
|
end;
|
|
|
|
TempAnswer := LocalAnswer;
|
|
|
|
// Search for AAAA also.
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True);
|
|
|
|
if Length(LocalAnswer) = 0 then begin
|
|
temp := MoreAddrSearch.Strings[Count];
|
|
Fetch(temp, '.');
|
|
temp := '*.' + temp;
|
|
InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]);
|
|
end;
|
|
|
|
AppendBytes(TempAnswer, LocalAnswer);
|
|
LocalAnswer := TempAnswer;
|
|
end else begin
|
|
// §äCache
|
|
TempAnswer := LocalAnswer;
|
|
ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA);
|
|
|
|
//Search A record first.
|
|
// Main, Cache, Additional, Wildcard
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, True, ifAdditional, False);
|
|
|
|
if Length(LocalAnswer) = 0 then begin
|
|
temp := MoreAddrSearch.Strings[Count];
|
|
Fetch(temp, '.');
|
|
temp := '*.' + temp;
|
|
InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, True, ifAdditional, True, MoreAddrSearch.Strings[Count]);
|
|
end;
|
|
|
|
AppendBytes(TempAnswer, LocalAnswer);
|
|
LocalAnswer := TempAnswer;
|
|
|
|
// Search for AAAA also.
|
|
InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, True, ifAdditional, True);
|
|
|
|
if Length(LocalAnswer) > 0 then begin
|
|
AppendBytes(TempAnswer, LocalAnswer);
|
|
LocalAnswer := TempAnswer;
|
|
end;
|
|
|
|
Answer := LocalAnswer;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(MoreAddrSearch);
|
|
end;
|
|
end;
|
|
|
|
{ TIdDNSServer }
|
|
|
|
procedure TIdDNSServer.CheckIfExpire(Sender: TObject);
|
|
begin
|
|
end;
|
|
|
|
procedure TIdDNSServer.InitComponent;
|
|
begin
|
|
inherited InitComponent;
|
|
FAccessList := TStringList.Create;
|
|
FUDPTunnel := TIdDNS_UDPServer.Create(Self);
|
|
FTCPTunnel := TIdDNS_TCPServer.Create(Self);
|
|
|
|
FBindings := TIdSocketHandles.Create(Self);
|
|
FTCPTunnel.DefaultPort := IdPORT_DOMAIN;
|
|
FUDPTunnel.DefaultPort := IdPORT_DOMAIN;
|
|
ServerType := stPrimary;
|
|
BackupDNSMap := TIdDNSMap.Create(FUDPTunnel);
|
|
end;
|
|
|
|
destructor TIdDNSServer.Destroy;
|
|
begin
|
|
FreeAndNil(FAccessList);
|
|
FreeAndNil(FUDPTunnel);
|
|
FreeAndNil(FTCPTunnel);
|
|
FreeAndNil(FBindings);
|
|
FreeAndNil(BackupDNSMap);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdDNSServer.SetAccessList(const Value: TStrings);
|
|
begin
|
|
FAccessList.Assign(Value);
|
|
FTCPTunnel.AccessList.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNSServer.SetActive(const Value: Boolean);
|
|
var
|
|
Count : Integer;
|
|
DNSMap : TIdDomainNameServerMapping;
|
|
begin
|
|
FActive := Value;
|
|
FUDPTunnel.Active := Value;
|
|
if ServerType = stSecondary then begin
|
|
TCPTunnel.Active := False;
|
|
// TODO: should this loop only be run if Value=True?
|
|
for Count := 0 to BackupDNSMap.Count-1 do begin
|
|
DNSMap := BackupDNSMap.Items[Count];
|
|
DNSMap.CheckScheduler.Start;
|
|
end;
|
|
end else begin
|
|
TCPTunnel.Active := Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNSServer.SetBindings(const Value: TIdSocketHandles);
|
|
begin
|
|
FBindings.Assign(Value);
|
|
FUDPTunnel.Bindings.Assign(Value);
|
|
FTCPTunnel.Bindings.Assign(Value);
|
|
end;
|
|
|
|
procedure TIdDNSServer.SetTCPACLActive(const Value: Boolean);
|
|
begin
|
|
FTCPACLActive := Value;
|
|
TCPTunnel.AccessControl := Value;
|
|
|
|
if Value then begin
|
|
FTCPTunnel.FAccessList.Assign(FAccessList);
|
|
end else begin
|
|
FTCPTunnel.FAccessList.Clear;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNSServer.TimeToUpdateNodeData(Sender: TObject);
|
|
var
|
|
Resolver : TIdDNSResolver;
|
|
Count : Integer;
|
|
begin
|
|
Resolver := TIdDNSResolver.Create(Self);
|
|
try
|
|
Resolver.Host := UDPTunnel.RootDNS_NET.Strings[0];
|
|
Resolver.QueryType := [qtAXFR];
|
|
|
|
Resolver.Resolve((Sender as TIdDNTreeNode).FullName);
|
|
|
|
for Count := 0 to Resolver.QueryResult.Count-1 do begin
|
|
UDPTunnel.UpdateTree(UDPTunnel.Handed_Tree, Resolver.QueryResult.Items[Count]);
|
|
end;
|
|
finally
|
|
FreeAndNil(Resolver);
|
|
end;
|
|
end;
|
|
|
|
{ TIdDNS_TCPServer }
|
|
|
|
procedure TIdDNS_TCPServer.InitComponent;
|
|
begin
|
|
inherited InitComponent;
|
|
FAccessList := TStringList.Create;
|
|
end;
|
|
|
|
destructor TIdDNS_TCPServer.Destroy;
|
|
begin
|
|
FreeAndNil(FAccessList);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdDNS_TCPServer.DoConnect(AContext: TIdContext);
|
|
var
|
|
Answer, Data, Question: TIdBytes;
|
|
QName, QLabel, QResult, PeerIP : string;
|
|
LData, QPos, LLength : Integer;
|
|
TestHeader : TDNSHeader;
|
|
|
|
procedure GenerateAXFRData;
|
|
begin
|
|
TestHeader := TDNSHeader.Create;
|
|
try
|
|
TestHeader.ParseQuery(Data);
|
|
if TestHeader.QDCount > 0 then begin
|
|
// parse the question.
|
|
QPos := 13;
|
|
QLabel := '';
|
|
QName := '';
|
|
|
|
repeat
|
|
LLength := Byte(Data[QPos]);
|
|
Inc(QPos);
|
|
QLabel := BytesToString(Data, QPos, LLength);
|
|
Inc(QPos, LLength);
|
|
QName := QName + QLabel + '.';
|
|
until (QPos >= LData) or (Data[QPos] = 0);
|
|
|
|
Question := Copy(Data, 13, Length(Data)-12);
|
|
QResult := TIdDNSServer(Owner).UDPTunnel.AXFR(TestHeader, QName, Answer);
|
|
end;
|
|
finally
|
|
FreeAndNil(TestHeader);
|
|
end;
|
|
end;
|
|
|
|
procedure GenerateAXFRRefuseData;
|
|
begin
|
|
TestHeader := TDNSHeader.Create;
|
|
try
|
|
TestHeader.ParseQuery(Data);
|
|
TestHeader.Qr := iQr_Answer;
|
|
TestHeader.RCode := iRCodeRefused;
|
|
Answer := TestHeader.GenerateBinaryHeader;
|
|
finally
|
|
FreeAndNil(TestHeader);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
inherited DoConnect(AContext);
|
|
|
|
LData := AContext.Connection.IOHandler.ReadInt16;
|
|
SetLength(Data, 0);
|
|
|
|
// RLebeau - why not use ReadBuffer() here?
|
|
// Dennies - Sure, in older version, my concern is for real time generate system
|
|
// might not generate the data with correct data size we expect.
|
|
AContext.Connection.IOHandler.ReadBytes(Data, LData);
|
|
{for Count := 1 to LData do begin
|
|
AppendByte(Data, AThread.Connection.IOHandler.ReadByte);
|
|
end;
|
|
}
|
|
|
|
// PeerIP is ip address.
|
|
PeerIP := AContext.Binding.PeerIP;
|
|
if AccessControl and (AccessList.IndexOf(PeerIP) = -1) then begin
|
|
GenerateAXFRRefuseData;
|
|
end else begin
|
|
GenerateAXFRData;
|
|
end;
|
|
|
|
if Length(Answer) > 32767 then begin
|
|
SetLength(Answer, 32767);
|
|
end;
|
|
|
|
AContext.Connection.IOHandler.Write(Int16(Length(Answer)));
|
|
AContext.Connection.IOHandler.Write(Answer);
|
|
end;
|
|
|
|
procedure TIdDNS_TCPServer.SetAccessList(const Value: TStrings);
|
|
begin
|
|
FAccessList.Assign(Value);
|
|
end;
|
|
|
|
{ TIdDomainExpireCheckThread }
|
|
|
|
procedure TIdDomainExpireCheckThread.Run;
|
|
var
|
|
LInterval, LStep: Integer;
|
|
begin
|
|
LInterval := FInterval;
|
|
while LInterval > 0 do begin
|
|
LStep := IndyMin(LInterval, 500);
|
|
IndySleep(LStep);
|
|
Dec(LInterval, LStep);
|
|
if Terminated then begin
|
|
Exit;
|
|
end;
|
|
if Assigned(FTimerEvent) then begin
|
|
Synchronize(TimerEvent);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDomainExpireCheckThread.TimerEvent;
|
|
begin
|
|
if Assigned(FTimerEvent) then begin
|
|
FTimerEvent(FSender);
|
|
end;
|
|
end;
|
|
|
|
{ TIdDomainNameServerMapping }
|
|
|
|
constructor TIdDomainNameServerMapping.Create(AList : TIdDNSMap);
|
|
begin
|
|
inherited Create;
|
|
|
|
CheckScheduler := TIdDomainExpireCheckThread.Create;
|
|
CheckScheduler.FInterval := 100000;
|
|
CheckScheduler.FSender := Self;
|
|
CheckScheduler.FDomain := DomainName;
|
|
CheckScheduler.FHost := Host;
|
|
CheckScheduler.FTimerEvent := SyncAndUpdate;
|
|
|
|
FList := List;
|
|
FBusy := False;
|
|
end;
|
|
|
|
destructor TIdDomainNameServerMapping.Destroy;
|
|
begin
|
|
//Self.CheckScheduler.TerminateAndWaitFor;
|
|
CheckScheduler.Terminate;
|
|
FreeAndNil(CheckScheduler);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdDomainNameServerMapping.SetHost(const Value: string);
|
|
begin
|
|
if (not IsValidIP(Value)) and (not IsValidIPv6(Value)) then begin
|
|
raise EIdDNSServerSettingException.Create(RSDNSServerSettingError_MappingHostError);
|
|
end;
|
|
FHost := Value;
|
|
end;
|
|
|
|
procedure TIdDomainNameServerMapping.SetInterval(const Value: UInt32);
|
|
begin
|
|
FInterval := Value;
|
|
CheckScheduler.FInterval := Value;
|
|
end;
|
|
|
|
procedure TIdDomainNameServerMapping.SyncAndUpdate(Sender: TObject);
|
|
//Todo - Dennies Chang should append axfr and update Tree.
|
|
var
|
|
Resolver : TIdDNSResolver;
|
|
RR : TResultRecord;
|
|
TNode : TIdDNTreeNode;
|
|
Server : TIdDNS_UDPServer;
|
|
NeedUpdated, NotThis : Boolean;
|
|
Count, TIndex : Integer;
|
|
RRName : string;
|
|
begin
|
|
if FBusy then begin
|
|
Exit;
|
|
end;
|
|
|
|
FBusy := True;
|
|
try
|
|
Resolver := TIdDNSResolver.Create(nil);
|
|
try
|
|
Resolver.Host := Host;
|
|
Resolver.QueryType := [qtAXFR];
|
|
|
|
Resolver.Resolve(DomainName);
|
|
|
|
if Resolver.QueryResult.Count = 0 then begin
|
|
raise EIdDNSServerSyncException.Create(RSDNSServerAXFRError_QuerySequenceError);
|
|
end;
|
|
|
|
RR := Resolver.QueryResult.Items[0];
|
|
if RR.RecType <> qtSOA then begin
|
|
raise EIdDNSServerSyncException.Create(RSDNSServerAXFRError_QuerySequenceError);
|
|
end;
|
|
|
|
Server := List.Server;
|
|
Interval := TSOARecord(RR).Expire * 1000;
|
|
|
|
{
|
|
//Update MyDomain
|
|
if Copy(RR.Name, Length(RR.Name),1) <> '.' then begin
|
|
RRName := RR.Name + '.';
|
|
end;
|
|
}
|
|
|
|
if Server.Handed_DomainList.IndexOf(RR.Name) = -1 then begin
|
|
Server.Handed_DomainList.Add(RR.Name);
|
|
end;
|
|
|
|
TNode := Server.SearchTree(Server.Handed_Tree, RR.Name, TypeCode_SOA);
|
|
|
|
if TNode = nil then begin
|
|
NeedUpdated := True;
|
|
end else begin
|
|
RRName := RRName;
|
|
RRName := Fetch(RRName, '.');
|
|
TIndex := TNode.RRs.ItemNames.IndexOf(RR.Name);
|
|
NotThis := True;
|
|
|
|
while (TIndex > -1) and (TIndex <= (TNode.RRs.Count-1)) and
|
|
(TNode.RRs.Items[TIndex].RRName = RR.Name) and NotThis do
|
|
begin
|
|
NotThis := not (TNode.RRs.Items[TIndex] is TIdRR_SOA);
|
|
Inc(TIndex);
|
|
end;
|
|
|
|
if not NotThis then begin
|
|
Dec(TIndex);
|
|
NeedUpdated := (TNode.RRs.Items[TIndex] as TIdRR_SOA).Serial = IntToStr(TSOARecord(RR).Serial);
|
|
end else begin
|
|
NeedUpdated := True;
|
|
end;
|
|
end;
|
|
|
|
if NeedUpdated then begin
|
|
if TNode <> nil then begin
|
|
Server.Handed_Tree.RemoveChild(Server.Handed_Tree.IndexByNode(TNode));
|
|
end;
|
|
|
|
for Count := 0 to Resolver.QueryResult.Count-1 do begin
|
|
RR := Resolver.QueryResult.Items[Count];
|
|
Server.UpdateTree(Server.Handed_Tree, RR);
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(Resolver);
|
|
end;
|
|
finally
|
|
FBusy := False;
|
|
end;
|
|
end;
|
|
|
|
{ TIdDNSMap }
|
|
|
|
constructor TIdDNSMap.Create(Server: TIdDNS_UDPServer);
|
|
begin
|
|
inherited Create;
|
|
FServer := Server;
|
|
end;
|
|
|
|
{$IFNDEF USE_OBJECT_ARC}
|
|
destructor TIdDNSMap.Destroy;
|
|
var
|
|
I : Integer;
|
|
DNSMP : TIdDomainNameServerMapping;
|
|
begin
|
|
if Count > 0 then begin
|
|
for I := Count-1 downto 0 do begin
|
|
DNSMP := Items[I];
|
|
FreeAndNil(DNSMP);
|
|
Delete(I);
|
|
end;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFNDEF HAS_GENERICS_TObjectList}
|
|
function TIdDNSMap.GetItem(Index: Integer): TIdDomainNameServerMapping;
|
|
begin
|
|
Result := TIdDomainNameServerMapping(inherited GetItem(Index));
|
|
end;
|
|
|
|
procedure TIdDNSMap.SetItem(Index: Integer; const Value: TIdDomainNameServerMapping);
|
|
begin
|
|
inherited SetItem(Index, Value);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TIdDNSMap.SetServer(const Value: TIdDNS_UDPServer);
|
|
begin
|
|
FServer := Value;
|
|
end;
|
|
|
|
{ TIdDNS_ProcessThread }
|
|
|
|
constructor TIdDNS_ProcessThread.Create(ACreateSuspended: Boolean;
|
|
Data: TIdBytes; MainBinding, Binding: TIdSocketHandle;
|
|
Server: TIdDNS_UDPServer);
|
|
begin
|
|
inherited Create(ACreateSuspended);
|
|
|
|
FMyData := nil;
|
|
FData := Data;
|
|
|
|
FMyBinding := Binding;
|
|
FMainBinding := MainBinding;
|
|
|
|
FServer := Server;
|
|
FreeOnTerminate := True;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.ComposeErrorResult(var VFinal: TIdBytes;
|
|
OriginalHeader: TDNSHeader; OriginalQuestion : TIdBytes;
|
|
ErrorStatus: Integer);
|
|
begin
|
|
case ErrorStatus of
|
|
iRCodeQueryNotImplement :
|
|
begin
|
|
OriginalHeader.Qr := iQr_Answer;
|
|
OriginalHeader.RCode := iRCodeNotImplemented;
|
|
|
|
VFinal := OriginalHeader.GenerateBinaryHeader;
|
|
AppendBytes(VFinal, OriginalQuestion, 12);
|
|
end;
|
|
iRCodeQueryNotFound :
|
|
begin
|
|
OriginalHeader.Qr := iQr_Answer;
|
|
OriginalHeader.RCode := iRCodeNameError;
|
|
OriginalHeader.ANCount := 0;
|
|
|
|
VFinal := OriginalHeader.GenerateBinaryHeader;
|
|
//VFinal := VFinal;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
destructor TIdDNS_ProcessThread.Destroy;
|
|
begin
|
|
FServer := nil;
|
|
FMainBinding := nil;
|
|
FMyBinding.CloseSocket;
|
|
FreeAndNil(FMyBinding);
|
|
FreeAndNil(FMyData);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.QueryDomain;
|
|
var
|
|
QName, QLabel, RString : string;
|
|
Temp, ExternalQuery, Answer, FinalResult : TIdBytes;
|
|
DNSHeader_Processing : TDNSHeader;
|
|
QType, QClass : UInt16;
|
|
QPos, QLength, LLength : Integer;
|
|
ABinding: TIdSocketHandle;
|
|
begin
|
|
ExternalQuery := FData;
|
|
ABinding := MyBinding;
|
|
Temp := Copy(FData, 0, Length(FData));
|
|
SetLength(FinalResult, 0);
|
|
QType := TypeCode_A;
|
|
|
|
if Length(FData) >= 12 then begin
|
|
DNSHeader_Processing := TDNSHeader.Create;
|
|
try
|
|
// RLebeau: this does not make sense to me. ParseQuery() always returns
|
|
// 0 when the data length is >= 12 unless an exception is raised, which
|
|
// should only happen if the GStack object is invalid...
|
|
//
|
|
if DNSHeader_Processing.ParseQuery(ExternalQuery) <> 0 then begin
|
|
FServer.DoAfterQuery(ABinding, DNSHeader_Processing, Temp, RString, ExternalQuery);
|
|
AppendBytes(FinalResult, Temp);
|
|
end else begin
|
|
if DNSHeader_Processing.QDCount > 0 then begin
|
|
|
|
QPos := 12; //13; Modified in Dec. 13, 2004 by Dennies
|
|
QLength := Length(ExternalQuery);
|
|
if QLength > 12 then begin
|
|
QName := '';
|
|
repeat
|
|
SetLength(Answer, 0);
|
|
LLength := ExternalQuery[QPos];
|
|
Inc(QPos);
|
|
QLabel := BytesToString(ExternalQuery, QPos, LLength);
|
|
Inc(QPos, LLength);
|
|
QName := QName + QLabel + '.';
|
|
until (QPos >= QLength) or (ExternalQuery[QPos] = 0);
|
|
Inc(QPos);
|
|
|
|
QType := GStack.NetworkToHost(TwoByteToUInt16(ExternalQuery[QPos], ExternalQuery[QPos + 1]));
|
|
Inc(QPos, 2);
|
|
QClass := GStack.NetworkToHost(TwoByteToUInt16(ExternalQuery[QPos], ExternalQuery[QPos + 1]));
|
|
FServer.DoBeforeQuery(ABinding, DNSHeader_Processing, Temp);
|
|
|
|
RString := CompleteQuery(DNSHeader_Processing, QName, ExternalQuery, Answer, QType, QClass, nil);
|
|
|
|
if RString = cRCodeQueryNotImplement then begin
|
|
ComposeErrorResult(FinalResult, DNSHeader_Processing, ExternalQuery, iRCodeQueryNotImplement);
|
|
end
|
|
else if (RString = cRCodeQueryReturned) then begin
|
|
FinalResult := Answer;
|
|
end
|
|
else if (RString = cRCodeQueryNotFound) or (RString = cRCodeQueryCacheFindError) then begin
|
|
ComposeErrorResult(FinalResult, DNSHeader_Processing, ExternalQuery, iRCodeQueryNotFound);
|
|
end
|
|
else begin
|
|
FinalResult := CombineAnswer(DNSHeader_Processing, ExternalQuery, Answer);
|
|
end;
|
|
|
|
FServer.DoAfterQuery(ABinding, DNSHeader_Processing, FinalResult, RString, Temp);
|
|
//AppendString(FinalResult, Temp);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
try
|
|
FData := FinalResult;
|
|
|
|
FServer.DoAfterSendBack(ABinding, DNSHeader_Processing, FinalResult, RString, ExternalQuery);
|
|
|
|
if (FServer.CacheUnknowZone) and
|
|
(RString <> cRCodeQueryCacheFindError) and
|
|
(RString <> cRCodeQueryCacheOK) and
|
|
(RString <> cRCodeQueryOK) and
|
|
(RString <> cRCodeQueryNotImplement) then
|
|
begin
|
|
FServer.SaveToCache(FinalResult, QName, QType);
|
|
FServer.DoAfterCacheSaved(Self.FServer.FCached_Tree);
|
|
end;
|
|
finally
|
|
FreeAndNil(DNSHeader_Processing);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.Run;
|
|
begin
|
|
try
|
|
QueryDomain;
|
|
SendData;
|
|
finally
|
|
Stop;
|
|
Terminate;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.SetMyBinding(const Value: TIdSocketHandle);
|
|
begin
|
|
FMyBinding := Value;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.SetMyData(const Value: TStream);
|
|
begin
|
|
FMyData := Value;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.SetServer(const Value: TIdDNS_UDPServer);
|
|
begin
|
|
FServer := Value;
|
|
end;
|
|
|
|
function TIdDNS_ProcessThread.CombineAnswer(Header: TDNSHeader; const EQuery, Answer: TIdBytes): TIdBytes;
|
|
begin
|
|
Result := Header.GenerateBinaryHeader;
|
|
AppendBytes(Result, EQuery, 12);
|
|
AppendBytes(Result, Answer);
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader;
|
|
Question: TIdBytes; var Answer: TIdBytes);
|
|
var
|
|
Server_Index : Integer;
|
|
MyDNSResolver : TIdDNSResolver;
|
|
begin
|
|
Server_Index := 0;
|
|
if ADNSResolver = nil then begin
|
|
MyDNSResolver := TIdDNSResolver.Create;
|
|
MyDNSResolver.WaitingTime := 2000;
|
|
end else
|
|
begin
|
|
MyDNSResolver := ADNSResolver;
|
|
end;
|
|
|
|
try
|
|
repeat
|
|
MyDNSResolver.Host := FServer.RootDNS_NET.Strings[Server_Index];
|
|
try
|
|
MyDNSResolver.InternalQuery := Question;
|
|
MyDNSResolver.Resolve('');
|
|
Answer := MyDNSResolver.PlainTextResult;
|
|
except
|
|
// Todo: Create DNS server interal resolver error.
|
|
on EIdDnsResolverError do
|
|
begin
|
|
//Empty Event, for user to custom the event handle.
|
|
end;
|
|
on EIdSocketError do
|
|
begin
|
|
end;
|
|
|
|
else
|
|
begin
|
|
end;
|
|
end;
|
|
|
|
Inc(Server_Index);
|
|
until (Server_Index >= FServer.RootDNS_NET.Count) or (Length(Answer) > 0);
|
|
finally
|
|
if ADNSResolver = nil then begin
|
|
FreeAndNil(MyDNSResolver);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16;
|
|
var Answer: TIdBytes; IfMainQuestion: boolean; IsSearchCache: Boolean = False;
|
|
IsAdditional: boolean = false; IsWildCard : boolean = false;
|
|
WildCardOrgName: string = '');
|
|
begin
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.SaveToCache(ResourceRecord: TIdBytes; QueryName: string; OriginalQType: UInt16);
|
|
var
|
|
TempResolver : TIdDNSResolver;
|
|
Count : Integer;
|
|
TNode : TIdDNTreeNode;
|
|
RR_Err : TIdRR_Error;
|
|
begin
|
|
TempResolver := TIdDNSResolver.Create(nil);
|
|
try
|
|
// RLebeau: FillResultWithOutCheckId() is deprecated, but not using FillResult()
|
|
// here yet because it validates the DNSHeader.RCode, and I do not know if that
|
|
// is needed here. I don't want to break this logic...
|
|
TempResolver.FillResultWithOutCheckId(ResourceRecord);
|
|
|
|
if TempResolver.DNSHeader.ANCount > 0 then begin
|
|
for Count := 0 to TempResolver.QueryResult.Count-1 do begin
|
|
FServer.UpdateTree(FServer.Cached_Tree, TempResolver.QueryResult.Items[Count]);
|
|
end; // for loop
|
|
end else begin
|
|
TNode := Self.SearchTree(FServer.Cached_Tree, QueryName, TypeCode_Error);
|
|
if TNode = nil then begin
|
|
RR_Err := TIdRR_Error.Create;
|
|
RR_Err.RRName := QueryName;
|
|
RR_Err.TTL := 600;
|
|
FServer.UpdateTree(FServer.Cached_Tree, RR_Err);
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(TempResolver);
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_ProcessThread.SearchTree(Root: TIdDNTreeNode; QName: String; QType: UInt16): TIdDNTreeNode;
|
|
var
|
|
RRIndex : integer;
|
|
NodeCursor : TIdDNTreeNode;
|
|
NameLabels : TStrings;
|
|
OneNode, FullName : string;
|
|
Found : Boolean;
|
|
begin
|
|
Result := nil;
|
|
NameLabels := TStringList.Create;
|
|
try
|
|
FullName := QName;
|
|
NodeCursor := Root;
|
|
Found := False;
|
|
|
|
repeat
|
|
OneNode := Fetch(FullName, '.');
|
|
if OneNode <> '' then begin
|
|
NameLabels.Add(OneNode);
|
|
end;
|
|
until FullName = '';
|
|
|
|
repeat
|
|
IndySleep(0);
|
|
if QType <> TypeCode_SOA then begin
|
|
RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]);
|
|
if RRIndex <> -1 then begin
|
|
NameLabels.Delete(NameLabels.Count - 1);
|
|
NodeCursor := NodeCursor.Children[RRIndex];
|
|
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
end else begin
|
|
Found := NameLabels.Count = 0;
|
|
end;
|
|
end
|
|
else if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
if not Found then begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end
|
|
else begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end else begin
|
|
RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]);
|
|
if RRIndex <> -1 then begin
|
|
NameLabels.Delete(NameLabels.Count - 1);
|
|
NodeCursor := NodeCursor.Children[RRIndex];
|
|
|
|
if NameLabels.Count = 1 then begin
|
|
Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1;
|
|
end else begin
|
|
Found := NameLabels.Count = 0;
|
|
end;
|
|
end
|
|
else if NameLabels.Count = 1 then begin
|
|
Found := (NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1);
|
|
if not Found then begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end
|
|
else begin
|
|
NameLabels.Clear;
|
|
end;
|
|
end;
|
|
until (NameLabels.Count = 0) or Found;
|
|
|
|
if Found then begin
|
|
Result := NodeCursor;
|
|
end;
|
|
finally
|
|
FreeAndNil(NameLabels);
|
|
end;
|
|
end;
|
|
|
|
function TIdDNS_ProcessThread.CompleteQuery(DNSHeader: TDNSHeader;
|
|
Question: string; OriginalQuestion: TIdBytes; var Answer : TIdBytes;
|
|
QType, QClass : UInt16; DNSResolver : TIdDNSResolver) : string;
|
|
var
|
|
IsMyDomains : boolean;
|
|
LAnswer, TempAnswer, RRData: TIdBytes;
|
|
WildQuestion, TempDomain : string;
|
|
LIdx: Integer;
|
|
begin
|
|
// QClass = 1 => IN, we support only "IN" class now.
|
|
// QClass = 2 => CS,
|
|
// QClass = 3 => CH, we suppor "CHAOS" class now, but only "version.bind." info.
|
|
// from 2004/6/28
|
|
// QClass = 4 => HS.
|
|
RRData := nil;
|
|
TempAnswer := nil;
|
|
TempDomain := LowerCase(Question);
|
|
|
|
case QClass of
|
|
Class_IN :
|
|
begin
|
|
IsMyDomains := FServer.Handed_DomainList.IndexOf(TempDomain) > -1;
|
|
if not IsMyDomains then begin
|
|
Fetch(TempDomain, '.');
|
|
IsMyDomains := FServer.Handed_DomainList.IndexOf(TempDomain) > -1;
|
|
end;
|
|
|
|
if IsMyDomains then begin
|
|
FServer.InternalSearch(DNSHeader, Question, QType, LAnswer, True, False, False);
|
|
Answer := LAnswer;
|
|
|
|
if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then begin
|
|
FServer.InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, False, True);
|
|
if Length(LAnswer) > 0 then begin
|
|
AppendBytes(Answer, LAnswer);
|
|
end;
|
|
end;
|
|
|
|
WildQuestion := Question;
|
|
Fetch(WildQuestion, '.');
|
|
WildQuestion := '*.' + WildQuestion;
|
|
FServer.InternalSearch(DNSHeader, WildQuestion, QType, LAnswer, True, False, False, True, Question);
|
|
{
|
|
FServer.InternalSearch(DNSHeader, Question, QType, LAnswer, True, True, False);
|
|
}
|
|
if Length(LAnswer) > 0 then begin
|
|
AppendBytes(Answer, LAnswer);
|
|
end;
|
|
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryOK;
|
|
end else begin
|
|
Result := cRCodeQueryNotFound;
|
|
end;
|
|
end else begin
|
|
FServer.InternalSearch(DNSHeader, Question, QType, Answer, True, True, False);
|
|
|
|
if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then begin
|
|
FServer.InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, True, False);
|
|
if Length(LAnswer) > 0 then begin
|
|
AppendBytes(Answer, LAnswer);
|
|
end;
|
|
end;
|
|
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryCacheOK;
|
|
end else begin
|
|
//QType := TypeCode_Error;
|
|
|
|
FServer.InternalSearch(DNSHeader, Question, QType, Answer, True, True, False);
|
|
if BytesToString(Answer) = 'Error' then begin {do not localize}
|
|
Result := cRCodeQueryCacheFindError;
|
|
end else begin
|
|
FServer.ExternalSearch(DNSResolver, DNSHeader, OriginalQuestion, Answer);
|
|
|
|
if Length(Answer) > 0 then begin
|
|
Result := cRCodeQueryReturned;
|
|
end else begin
|
|
Result := cRCodeQueryNotImplement;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Class_CHAOS :
|
|
begin
|
|
if TempDomain = 'version.bind.' then begin {do not localize}
|
|
if FServer.offerDNSVersion then begin
|
|
TempAnswer := DomainNameToDNSStr('version.bind.'); {do not localize}
|
|
RRData := NormalStrToDNSStr(FServer.DNSVersion);
|
|
|
|
SetLength(LAnswer, Length(TempAnswer) + (SizeOf(UInt16)*3) + SizeOf(UInt32) + Length(RRData));
|
|
CopyTIdBytes(TempAnswer, 0, LAnswer, 0, Length(TempAnswer));
|
|
LIdx := Length(TempAnswer);
|
|
CopyTIdUInt16(GStack.HostToNetwork(UInt16(TypeCode_TXT)), LAnswer, LIdx);
|
|
Inc(LIdx, SizeOf(UInt16));
|
|
CopyTIdUInt16(GStack.HostToNetwork(UInt16(Class_CHAOS)), LAnswer, LIdx);
|
|
Inc(LIdx, SizeOf(UInt16));
|
|
CopyTIdUInt32(GStack.HostToNetwork(UInt32(86400)), LAnswer, LIdx); {do not localize}
|
|
Inc(LIdx, SizeOf(UInt32));
|
|
CopyTIdUInt16(GStack.HostToNetwork(UInt16(Length(RRData))), LAnswer, LIdx);
|
|
Inc(LIdx, SizeOf(UInt16));
|
|
CopyTIdBytes(RRData, 0, LAnswer, LIdx, Length(RRData));
|
|
|
|
Answer := LAnswer;
|
|
DNSHeader.ANCount := 1;
|
|
DNSHeader.AA := 1;
|
|
Result := cRCodeQueryOK;
|
|
end else begin
|
|
Result := cRCodeQueryNotImplement;
|
|
end;
|
|
end else begin
|
|
Result := cRCodeQueryNotImplement;
|
|
end;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
Result := cRCodeQueryNotImplement;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_ProcessThread.SendData;
|
|
begin
|
|
FServer.GlobalCS.Enter;
|
|
try
|
|
FMainBinding.SendTo(FMyBinding.PeerIP, FMyBinding.PeerPort, FData, FMyBinding.IPVersion);
|
|
finally
|
|
FServer.GlobalCS.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.DoAfterCacheSaved(CacheRoot: TIdDNTreeNode);
|
|
begin
|
|
if Assigned(FOnAfterCacheSaved) then begin
|
|
FOnAfterCacheSaved(CacheRoot);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdDNS_UDPServer.DoUDPRead(AThread: TIdUDPListenerThread;
|
|
const AData: TIdBytes; ABinding: TIdSocketHandle);
|
|
var
|
|
PThread : TIdDNS_ProcessThread;
|
|
BBinding : TIdSocketHandle;
|
|
Binded : Boolean;
|
|
begin
|
|
inherited DoUDPRead(AThread, AData, ABinding);
|
|
|
|
Binded := False;
|
|
|
|
BBinding := TIdSocketHandle.Create(nil);
|
|
try
|
|
BBinding.SetPeer(ABinding.PeerIP, ABinding.PeerPort, ABinding.IPVersion);
|
|
BBinding.IP := ABinding.IP;
|
|
|
|
repeat
|
|
try
|
|
BBinding.Port := 53;
|
|
BBinding.AllocateSocket(Id_SOCK_DGRAM);
|
|
Binded := True;
|
|
except
|
|
end;
|
|
until Binded;
|
|
|
|
PThread := TIdDNS_ProcessThread.Create(True, AData, ABinding, BBinding, Self);
|
|
except
|
|
FreeAndNil(BBinding);
|
|
raise;
|
|
end;
|
|
|
|
PThread.Start;
|
|
end;
|
|
|
|
end.
|