Long story short: I have a need to test a Windows machine's ability to locate a Windows domain controller, and my homebrew algorithm came up short. I spelunked around CPAN for awhile and concluded that there were no modules already built to do this. So I decided to turn to the experts at Microsoft to see how they did it. They use an API provided by the NetLogon service with a C prototype that looks like this:
DWORD DsGetDcName( __in LPCTSTR ComputerName, __in LPCTSTR DomainName, __in GUID *DomainGuid, __in LPCTSTR SiteName, __in ULONG Flags, __out PDOMAIN_CONTROLLER_INFO *DomainControllerInfo );
As you can see, the goods are returned via the PDOMAIN_CONTROLLER_INFO parameter, a pointer to a DomainControllerInfo struct.
typedef struct _DOMAIN_CONTROLLER_INFO { LPTSTR DomainControllerName; LPTSTR DomainControllerAddress; ULONG DomainControllerAddressType; GUID DomainGuid; LPTSTR DomainName; LPTSTR DnsForestName; ULONG Flags; LPTSTR DcSiteName; LPTSTR ClientSiteName; } DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO;
There is also a GUID struct specified in the prototype. According to the MSDN docs, it can be null, but things turned nasty when I didn't define it. Here's a look at its definition:
typedef struct _GUID { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; }GUID;
Hmm, I thought, this is my opportunity to get more familiar with Win32::API. Goody. I'm no C programmer, but I'll try to fake it long enough to get *one simple function call working*. Riiiight....
So after a few hours of reading docs and coding, I came up with something.
use Win32::API; use strict; use Error qw(:try); use Data::Dumper; # Define result constants use constant ERROR_INVALID_DOMAINNAME => 1212; # The form +at of the specified DomainName is invalid. use constant ERROR_INVALID_FLAGS => 1004; # The Flag +s parameter contains conflicting or superfluous flags. use constant ERROR_NOT_ENOUGH_MEMORY => 8; # A memory + allocation failure occurred. use constant ERROR_NO_SUCH_DOMAIN => 1355; # No domai +n controller is available for the specified domain or the domain does + not exist. use constant ERROR_SUCCESS => 0; # No worri +es! # Define structs used as parameters typedef Win32::API::Struct GUID => qw { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; }; typedef Win32::API::Struct DOMAIN_CONTROLLER_INFO => qw { LPTSTR DomainControllerName; LPTSTR DomainControllerAddress; ULONG DomainControllerAddressType; GUID DomainGuid; LPTSTR DomainName; LPTSTR DnsForestName; ULONG Flags; LPTSTR DcSiteName; LPTSTR ClientSiteName; }; # Create instances of these structs my $domainguid = Win32::API::Struct->new('GUID'); my $dci = Win32::API::Struct->new('DOMAIN_CONTROLLER_INFO'); #Import our function my $DsGetDcName = Win32::API->new('Netapi32', 'DsGetDcName', 'PPSPNS', + 'N') or die Win32::FormatMessage(Win32::GetLastError); if (not defined $DsGetDcName) {die "Couldn't get DsGetDcName! $!\n";} try { # Call the imported function... my $dc = $DsGetDcName->Call('mymachine', 'mydomain', $domainguid, + 'myADsite', 0x00020000, $dci); print "Return value: [$dc]\n"; if ($dc == ERROR_INVALID_DOMAINNAME) {print "ERROR_INVALID_DOMAIN +NAME\n"} if ($dc == ERROR_INVALID_FLAGS) {print "ERROR_INVALID_FLAGS\n"} if ($dc == ERROR_NOT_ENOUGH_MEMORY) {print "ERROR_NOT_ENOUGH_MEMO +RY\n"} if ($dc == ERROR_NO_SUCH_DOMAIN) {print "ERROR_NO_SUCH_DOMAIN\n"} if ($dc == ERROR_SUCCESS) {print "ERROR_SUCCESS\n"} } catch Error with { my $err = shift; print "Error: $err\n"; }; print Dumper($dci);
Essentially, DsGetDcName returns a value of 0 when it works, and a non-zero value (potential values are defined in the constants) when something breaks. I *am* getting a 0 value (and I get the constant values when I break things deliberately), so I have every reason to believe that the call itself is actually working.
The problem is I cannot decipher the output no matter how I try. Data::Dumper outputs something that looks like this:
'DcSiteName' => '', 'DnsForestName' => '', 'Flags' => 0, 'DomainControllerName' => 'áµ ', 'DomainControllerAddress' => '', 'buffer' => 'pµ ╘g ( 	 +560;g (╘g ( ╘g (╘g (', '__typedef__' => 'DOMAIN_CONTROLLER_INFO', 'ClientSiteName' => '', 'DomainGuid' => bless( { '__typedef__' => 'GUID', 'Data1' => 0, 'Data3' => 0, 'typedef' => [ [ 'Data1', 'L', 'DWORD' ], [ 'Data2', 'S', 'WORD' ], [ 'Data3', 'S', 'WORD' ], [ 'Data4', 'C*8', 'BYTE' ] ], 'Data4' => '', 'Data2' => 0 }, 'Win32::API::Struct' ), 'buffer_recipients' => [ $VAR1, $VAR1, $VAR1, $VAR1->{'DomainGuid'}, $VAR1->{'DomainGuid'}, $VAR1->{'DomainGuid'}, $VAR1->{'DomainGuid'}, $VAR1, $VAR1, $VAR1, $VAR1, $VAR1 ], 'typedef' => [ [ 'DomainControllerName', 'p', 'LPTSTR' ], [ 'DomainControllerAddress', 'p', 'LPTSTR' ], [ 'DomainControllerAddressType', 'L', 'ULONG' ], [ 'DomainGuid', '>', 'GUID' ], [ 'DomainName', 'p', 'LPTSTR' ], [ 'DnsForestName', 'p', 'LPTSTR' ], [ 'Flags', 'L', 'ULONG' ], [ 'DcSiteName', 'p', 'LPTSTR' ], [ 'ClientSiteName', 'p', 'LPTSTR' ] ], 'DomainName' => '', 'DomainControllerAddressType' => 0 }, 'Win32::API::Struct' );
See the funky chars in the DomainControllerName element? That's what I'm talkin' about.
A careful re-re-re-read of the Win32::API documentation revealed:
ABSTRACT ^ With this module you can import and call arbitrary functions from Win3 +2's Dynamic Link Libraries (DLL), without having to write an XS exten +sion. Note, however, that this module can't do everything. In fact, p +arameters input and output is limited to simpler cases.
At this point, what I need is closure! :-) Have I run headfirst into the brick wall of "this is one of those not-simple cases for which the module will not work?!"
I eagerly await your insight. Thank you!
--Geoff
In reply to Win32::API: deciphering a returned pointer to a struct by ggariepy
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |