Greetings, brothers,

Update: took someone's suggestion about readmore tags....

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.

That struct looks like this:


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.

An abbreviated version of that looks like this:

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 ( &#9 +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

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.