| 1 | #!/usr/bin/perl -w | 
|---|
| 2 |  | 
|---|
| 3 | # Copyright (C) Guenther Deschner <gd@samba.org> 2006 | 
|---|
| 4 |  | 
|---|
| 5 | use strict; | 
|---|
| 6 | use IO::Socket; | 
|---|
| 7 | use Convert::ASN1 qw(:debug); | 
|---|
| 8 | use Getopt::Long; | 
|---|
| 9 |  | 
|---|
| 10 | # TODO: timeout handling, user CLDAP query | 
|---|
| 11 |  | 
|---|
| 12 | ################################## | 
|---|
| 13 |  | 
|---|
| 14 | my $server = ""; | 
|---|
| 15 | my $domain = ""; | 
|---|
| 16 | my $host   = ""; | 
|---|
| 17 |  | 
|---|
| 18 | ################################## | 
|---|
| 19 |  | 
|---|
| 20 | my ( | 
|---|
| 21 | $opt_debug, | 
|---|
| 22 | $opt_domain, | 
|---|
| 23 | $opt_help, | 
|---|
| 24 | $opt_host, | 
|---|
| 25 | $opt_server, | 
|---|
| 26 | ); | 
|---|
| 27 |  | 
|---|
| 28 | my %cldap_flags = ( | 
|---|
| 29 | ADS_PDC                 => 0x00000001, # DC is PDC | 
|---|
| 30 | ADS_GC                  => 0x00000004, # DC is a GC of forest | 
|---|
| 31 | ADS_LDAP                => 0x00000008, # DC is an LDAP server | 
|---|
| 32 | ADS_DS                  => 0x00000010, # DC supports DS | 
|---|
| 33 | ADS_KDC                 => 0x00000020, # DC is running KDC | 
|---|
| 34 | ADS_TIMESERV            => 0x00000040, # DC is running time services | 
|---|
| 35 | ADS_CLOSEST             => 0x00000080, # DC is closest to client | 
|---|
| 36 | ADS_WRITABLE            => 0x00000100, # DC has writable DS | 
|---|
| 37 | ADS_GOOD_TIMESERV       => 0x00000200, # DC has hardware clock (and running time) | 
|---|
| 38 | ADS_NDNC                => 0x00000400, # DomainName is non-domain NC serviced by LDAP server | 
|---|
| 39 | ); | 
|---|
| 40 |  | 
|---|
| 41 | my %cldap_samlogon_types = ( | 
|---|
| 42 | SAMLOGON_AD_UNK_R       => 23, | 
|---|
| 43 | SAMLOGON_AD_R           => 25, | 
|---|
| 44 | ); | 
|---|
| 45 |  | 
|---|
| 46 | my $MAX_DNS_LABEL = 255 + 1; | 
|---|
| 47 |  | 
|---|
| 48 | my %cldap_netlogon_reply = ( | 
|---|
| 49 | type                    => 0, | 
|---|
| 50 | flags                   => 0x0, | 
|---|
| 51 | guid                    => 0, | 
|---|
| 52 | forest                  => undef, | 
|---|
| 53 | domain                  => undef, | 
|---|
| 54 | hostname                => undef, | 
|---|
| 55 | netbios_domain          => undef, | 
|---|
| 56 | netbios_hostname        => undef, | 
|---|
| 57 | unk                     => undef, | 
|---|
| 58 | user_name               => undef, | 
|---|
| 59 | server_site_name        => undef, | 
|---|
| 60 | client_site_name        => undef, | 
|---|
| 61 | version                 => 0, | 
|---|
| 62 | lmnt_token              => 0x0, | 
|---|
| 63 | lm20_token              => 0x0, | 
|---|
| 64 | ); | 
|---|
| 65 |  | 
|---|
| 66 | sub usage { | 
|---|
| 67 | print "usage: $0 [--domain|-d domain] [--help] [--host|-h host] [--server|-s server]\n\n"; | 
|---|
| 68 | } | 
|---|
| 69 |  | 
|---|
| 70 | sub connect_cldap ($) { | 
|---|
| 71 |  | 
|---|
| 72 | my $server = shift || return undef; | 
|---|
| 73 |  | 
|---|
| 74 | return IO::Socket::INET->new( | 
|---|
| 75 | PeerAddr        => $server, | 
|---|
| 76 | PeerPort        => 389, | 
|---|
| 77 | Proto           => 'udp', | 
|---|
| 78 | Type            => SOCK_DGRAM, | 
|---|
| 79 | Timeout         => 10, | 
|---|
| 80 | ); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | sub send_cldap_netlogon ($$$$) { | 
|---|
| 84 |  | 
|---|
| 85 | my ($sock, $domain, $host, $ntver) = @_; | 
|---|
| 86 |  | 
|---|
| 87 | my $asn_cldap_req = Convert::ASN1->new; | 
|---|
| 88 |  | 
|---|
| 89 | $asn_cldap_req->prepare(q< | 
|---|
| 90 |  | 
|---|
| 91 | SEQUENCE { | 
|---|
| 92 | msgid INTEGER, | 
|---|
| 93 | [APPLICATION 3] SEQUENCE { | 
|---|
| 94 | basedn OCTET STRING, | 
|---|
| 95 | scope ENUMERATED, | 
|---|
| 96 | dereference ENUMERATED, | 
|---|
| 97 | sizelimit INTEGER, | 
|---|
| 98 | timelimit INTEGER, | 
|---|
| 99 | attronly BOOLEAN, | 
|---|
| 100 | [CONTEXT 0] SEQUENCE { | 
|---|
| 101 | [CONTEXT 3] SEQUENCE { | 
|---|
| 102 | dnsdom_attr OCTET STRING, | 
|---|
| 103 | dnsdom_val  OCTET STRING | 
|---|
| 104 | } | 
|---|
| 105 | [CONTEXT 3] SEQUENCE { | 
|---|
| 106 | host_attr OCTET STRING, | 
|---|
| 107 | host_val  OCTET STRING | 
|---|
| 108 | } | 
|---|
| 109 | [CONTEXT 3] SEQUENCE { | 
|---|
| 110 | ntver_attr OCTET STRING, | 
|---|
| 111 | ntver_val  OCTET STRING | 
|---|
| 112 | } | 
|---|
| 113 | } | 
|---|
| 114 | SEQUENCE { | 
|---|
| 115 | netlogon OCTET STRING | 
|---|
| 116 | } | 
|---|
| 117 | } | 
|---|
| 118 | } | 
|---|
| 119 | >); | 
|---|
| 120 |  | 
|---|
| 121 | my $pdu_req = $asn_cldap_req->encode( | 
|---|
| 122 | msgid => 0, | 
|---|
| 123 | basedn => "", | 
|---|
| 124 | scope => 0, | 
|---|
| 125 | dereference => 0, | 
|---|
| 126 | sizelimit => 0, | 
|---|
| 127 | timelimit => 0, | 
|---|
| 128 | attronly => 0, | 
|---|
| 129 | dnsdom_attr => $domain ? 'DnsDomain' : "", | 
|---|
| 130 | dnsdom_val => $domain ? $domain : "", | 
|---|
| 131 | host_attr => 'Host', | 
|---|
| 132 | host_val => $host, | 
|---|
| 133 | ntver_attr => 'NtVer', | 
|---|
| 134 | ntver_val => $ntver, | 
|---|
| 135 | netlogon => 'NetLogon', | 
|---|
| 136 | ) || die "failed to encode pdu: $@"; | 
|---|
| 137 |  | 
|---|
| 138 | if ($opt_debug) { | 
|---|
| 139 | print"------------\n"; | 
|---|
| 140 | asn_dump($pdu_req); | 
|---|
| 141 | print"------------\n"; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | return $sock->send($pdu_req) || die "no send: $@"; | 
|---|
| 145 | } | 
|---|
| 146 |  | 
|---|
| 147 | # from source/libads/cldap.c : | 
|---|
| 148 | # | 
|---|
| 149 | #/* | 
|---|
| 150 | #  These seem to be strings as described in RFC1035 4.1.4 and can be: | 
|---|
| 151 | # | 
|---|
| 152 | #   - a sequence of labels ending in a zero octet | 
|---|
| 153 | #   - a pointer | 
|---|
| 154 | #   - a sequence of labels ending with a pointer | 
|---|
| 155 | # | 
|---|
| 156 | #  A label is a byte where the first two bits must be zero and the remaining | 
|---|
| 157 | #  bits represent the length of the label followed by the label itself. | 
|---|
| 158 | #  Therefore, the length of a label is at max 64 bytes.  Under RFC1035, a | 
|---|
| 159 | #  sequence of labels cannot exceed 255 bytes. | 
|---|
| 160 | # | 
|---|
| 161 | #  A pointer consists of a 14 bit offset from the beginning of the data. | 
|---|
| 162 | # | 
|---|
| 163 | #  struct ptr { | 
|---|
| 164 | #    unsigned ident:2; // must be 11 | 
|---|
| 165 | #    unsigned offset:14; // from the beginning of data | 
|---|
| 166 | #  }; | 
|---|
| 167 | # | 
|---|
| 168 | #  This is used as a method to compress the packet by eliminated duplicate | 
|---|
| 169 | #  domain components.  Since a UDP packet should probably be < 512 bytes and a | 
|---|
| 170 | #  DNS name can be up to 255 bytes, this actually makes a lot of sense. | 
|---|
| 171 | #*/ | 
|---|
| 172 |  | 
|---|
| 173 | sub pull_netlogon_string (\$$$) { | 
|---|
| 174 |  | 
|---|
| 175 | my ($ret, $ptr, $str) = @_; | 
|---|
| 176 |  | 
|---|
| 177 | my $pos = $ptr; | 
|---|
| 178 |  | 
|---|
| 179 | my $followed_ptr = 0; | 
|---|
| 180 | my $ret_len = 0; | 
|---|
| 181 |  | 
|---|
| 182 | my $retp = pack("x$MAX_DNS_LABEL"); | 
|---|
| 183 |  | 
|---|
| 184 | do { | 
|---|
| 185 |  | 
|---|
| 186 | $ptr = unpack("c", substr($str, $pos, 1)); | 
|---|
| 187 | $pos++; | 
|---|
| 188 |  | 
|---|
| 189 | if (($ptr & 0xc0) == 0xc0) { | 
|---|
| 190 |  | 
|---|
| 191 | my $len; | 
|---|
| 192 |  | 
|---|
| 193 | if (!$followed_ptr) { | 
|---|
| 194 | $ret_len += 2; | 
|---|
| 195 | $followed_ptr = 1; | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | my $tmp0 = $ptr; #unpack("c", substr($str, $pos-1, 1)); | 
|---|
| 199 | my $tmp1 = unpack("c", substr($str, $pos, 1)); | 
|---|
| 200 |  | 
|---|
| 201 | if ($opt_debug) { | 
|---|
| 202 | printf("tmp0: 0x%x\n", $tmp0); | 
|---|
| 203 | printf("tmp1: 0x%x\n", $tmp1); | 
|---|
| 204 | } | 
|---|
| 205 |  | 
|---|
| 206 | $len = (($tmp0 & 0x3f) << 8) | $tmp1; | 
|---|
| 207 | $ptr = unpack("c", substr($str, $len, 1)); | 
|---|
| 208 | $pos = $len; | 
|---|
| 209 |  | 
|---|
| 210 | } elsif ($ptr) { | 
|---|
| 211 |  | 
|---|
| 212 | my $len = scalar $ptr; | 
|---|
| 213 |  | 
|---|
| 214 | if ($len + 1 > $MAX_DNS_LABEL) { | 
|---|
| 215 | warn("invalid string size: %d", $len + 1); | 
|---|
| 216 | return 0; | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | $ptr = unpack("a*", substr($str, $pos, $len)); | 
|---|
| 220 |  | 
|---|
| 221 | $retp = sprintf("%s%s\.", $retp, $ptr); | 
|---|
| 222 |  | 
|---|
| 223 | $pos += $len; | 
|---|
| 224 | if (!$followed_ptr) { | 
|---|
| 225 | $ret_len += $len + 1; | 
|---|
| 226 | } | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | } while ($ptr); | 
|---|
| 230 |  | 
|---|
| 231 | $retp =~ s/\.$//; #ugly hack... | 
|---|
| 232 |  | 
|---|
| 233 | $$ret = $retp; | 
|---|
| 234 |  | 
|---|
| 235 | return $followed_ptr ? $ret_len : $ret_len + 1; | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | sub dump_cldap_flags ($) { | 
|---|
| 239 |  | 
|---|
| 240 | my $flags = shift || return; | 
|---|
| 241 | printf("Flags:\n". | 
|---|
| 242 | "\tIs a PDC:                                   %s\n". | 
|---|
| 243 | "\tIs a GC of the forest:                      %s\n". | 
|---|
| 244 | "\tIs an LDAP server:                          %s\n". | 
|---|
| 245 | "\tSupports DS:                                %s\n". | 
|---|
| 246 | "\tIs running a KDC:                           %s\n". | 
|---|
| 247 | "\tIs running time services:                   %s\n". | 
|---|
| 248 | "\tIs the closest DC:                          %s\n". | 
|---|
| 249 | "\tIs writable:                                %s\n". | 
|---|
| 250 | "\tHas a hardware clock:                       %s\n". | 
|---|
| 251 | "\tIs a non-domain NC serviced by LDAP server: %s\n", | 
|---|
| 252 | ($flags & $cldap_flags{ADS_PDC}) ? "yes" : "no", | 
|---|
| 253 | ($flags & $cldap_flags{ADS_GC}) ? "yes" : "no", | 
|---|
| 254 | ($flags & $cldap_flags{ADS_LDAP}) ? "yes" : "no", | 
|---|
| 255 | ($flags & $cldap_flags{ADS_DS}) ? "yes" : "no", | 
|---|
| 256 | ($flags & $cldap_flags{ADS_KDC}) ? "yes" : "no", | 
|---|
| 257 | ($flags & $cldap_flags{ADS_TIMESERV}) ? "yes" : "no", | 
|---|
| 258 | ($flags & $cldap_flags{ADS_CLOSEST}) ? "yes" : "no", | 
|---|
| 259 | ($flags & $cldap_flags{ADS_WRITABLE}) ? "yes" : "no", | 
|---|
| 260 | ($flags & $cldap_flags{ADS_GOOD_TIMESERV}) ? "yes" : "no", | 
|---|
| 261 | ($flags & $cldap_flags{ADS_NDNC}) ? "yes" : "no"); | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | sub guid_to_string ($) { | 
|---|
| 265 |  | 
|---|
| 266 | my $guid = shift || return undef; | 
|---|
| 267 | if ((my $len = length $guid) != 16) { | 
|---|
| 268 | printf("invalid length: %d\n", $len); | 
|---|
| 269 | return undef; | 
|---|
| 270 | } | 
|---|
| 271 | my $string = sprintf "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", | 
|---|
| 272 | unpack("I", $guid), | 
|---|
| 273 | unpack("S", substr($guid, 4, 2)), | 
|---|
| 274 | unpack("S", substr($guid, 6, 2)), | 
|---|
| 275 | unpack("C", substr($guid, 8, 1)), | 
|---|
| 276 | unpack("C", substr($guid, 9, 1)), | 
|---|
| 277 | unpack("C", substr($guid, 10, 1)), | 
|---|
| 278 | unpack("C", substr($guid, 11, 1)), | 
|---|
| 279 | unpack("C", substr($guid, 12, 1)), | 
|---|
| 280 | unpack("C", substr($guid, 13, 1)), | 
|---|
| 281 | unpack("C", substr($guid, 14, 1)), | 
|---|
| 282 | unpack("C", substr($guid, 15, 1)); | 
|---|
| 283 | return lc($string); | 
|---|
| 284 | } | 
|---|
| 285 |  | 
|---|
| 286 | sub recv_cldap_netlogon ($\$) { | 
|---|
| 287 |  | 
|---|
| 288 | my ($sock, $return_string) = @_; | 
|---|
| 289 | my ($ret, $pdu_out); | 
|---|
| 290 |  | 
|---|
| 291 | $ret = $sock->recv($pdu_out, 8192) || die "failed to read from socket: $@"; | 
|---|
| 292 | #$ret = sysread($sock, $pdu_out, 8192); | 
|---|
| 293 |  | 
|---|
| 294 | if ($opt_debug) { | 
|---|
| 295 | print"------------\n"; | 
|---|
| 296 | asn_dump($pdu_out); | 
|---|
| 297 | print"------------\n"; | 
|---|
| 298 | } | 
|---|
| 299 |  | 
|---|
| 300 | my $asn_cldap_rep = Convert::ASN1->new; | 
|---|
| 301 | my $asn_cldap_rep_fail = Convert::ASN1->new; | 
|---|
| 302 |  | 
|---|
| 303 | $asn_cldap_rep->prepare(q< | 
|---|
| 304 | SEQUENCE { | 
|---|
| 305 | msgid INTEGER, | 
|---|
| 306 | [APPLICATION 4] SEQUENCE { | 
|---|
| 307 | dn OCTET STRING, | 
|---|
| 308 | SEQUENCE { | 
|---|
| 309 | SEQUENCE { | 
|---|
| 310 | attr OCTET STRING, | 
|---|
| 311 | SET { | 
|---|
| 312 | val OCTET STRING | 
|---|
| 313 | } | 
|---|
| 314 | } | 
|---|
| 315 | } | 
|---|
| 316 | } | 
|---|
| 317 | } | 
|---|
| 318 | SEQUENCE { | 
|---|
| 319 | msgid2 INTEGER, | 
|---|
| 320 | [APPLICATION 5] SEQUENCE { | 
|---|
| 321 | error_code ENUMERATED, | 
|---|
| 322 | matched_dn OCTET STRING, | 
|---|
| 323 | error_message OCTET STRING | 
|---|
| 324 | } | 
|---|
| 325 | } | 
|---|
| 326 | >); | 
|---|
| 327 |  | 
|---|
| 328 | $asn_cldap_rep_fail->prepare(q< | 
|---|
| 329 | SEQUENCE { | 
|---|
| 330 | msgid2 INTEGER, | 
|---|
| 331 | [APPLICATION 5] SEQUENCE { | 
|---|
| 332 | error_code ENUMERATED, | 
|---|
| 333 | matched_dn OCTET STRING, | 
|---|
| 334 | error_message OCTET STRING | 
|---|
| 335 | } | 
|---|
| 336 | } | 
|---|
| 337 | >); | 
|---|
| 338 |  | 
|---|
| 339 | my $asn1_rep =  $asn_cldap_rep->decode($pdu_out) || | 
|---|
| 340 | $asn_cldap_rep_fail->decode($pdu_out) || | 
|---|
| 341 | die "failed to decode pdu: $@"; | 
|---|
| 342 |  | 
|---|
| 343 | if ($asn1_rep->{'error_code'} == 0) { | 
|---|
| 344 | $$return_string = $asn1_rep->{'val'}; | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 | return $ret; | 
|---|
| 348 | } | 
|---|
| 349 |  | 
|---|
| 350 | sub parse_cldap_reply ($) { | 
|---|
| 351 |  | 
|---|
| 352 | my $str = shift || return undef; | 
|---|
| 353 | my %hash; | 
|---|
| 354 | my $p = 0; | 
|---|
| 355 |  | 
|---|
| 356 | $hash{type}     = unpack("L", substr($str, $p, 4)); $p += 4; | 
|---|
| 357 | $hash{flags}    = unpack("L", substr($str, $p, 4)); $p += 4; | 
|---|
| 358 | $hash{guid}     = unpack("a16", substr($str, $p, 16)); $p += 16; | 
|---|
| 359 |  | 
|---|
| 360 | $p += pull_netlogon_string($hash{forest}, $p, $str); | 
|---|
| 361 | $p += pull_netlogon_string($hash{domain}, $p, $str); | 
|---|
| 362 | $p += pull_netlogon_string($hash{hostname}, $p, $str); | 
|---|
| 363 | $p += pull_netlogon_string($hash{netbios_domain}, $p, $str); | 
|---|
| 364 | $p += pull_netlogon_string($hash{netbios_hostname}, $p, $str); | 
|---|
| 365 | $p += pull_netlogon_string($hash{unk}, $p, $str); | 
|---|
| 366 |  | 
|---|
| 367 | if ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_R}) { | 
|---|
| 368 | $p += pull_netlogon_string($hash{user_name}, $p, $str); | 
|---|
| 369 | } else { | 
|---|
| 370 | $hash{user_name} = ""; | 
|---|
| 371 | } | 
|---|
| 372 |  | 
|---|
| 373 | $p += pull_netlogon_string($hash{server_site_name}, $p, $str); | 
|---|
| 374 | $p += pull_netlogon_string($hash{client_site_name}, $p, $str); | 
|---|
| 375 |  | 
|---|
| 376 | $hash{version}          = unpack("L", substr($str, $p, 4)); $p += 4; | 
|---|
| 377 | $hash{lmnt_token}       = unpack("S", substr($str, $p, 2)); $p += 2; | 
|---|
| 378 | $hash{lm20_token}       = unpack("S", substr($str, $p, 2)); $p += 2; | 
|---|
| 379 |  | 
|---|
| 380 | return %hash; | 
|---|
| 381 | } | 
|---|
| 382 |  | 
|---|
| 383 | sub display_cldap_reply { | 
|---|
| 384 |  | 
|---|
| 385 | my $server = shift; | 
|---|
| 386 | my (%hash) = @_; | 
|---|
| 387 |  | 
|---|
| 388 | my ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($server); | 
|---|
| 389 |  | 
|---|
| 390 | printf("Information for Domain Controller: %s\n\n", $name); | 
|---|
| 391 |  | 
|---|
| 392 | printf("Response Type: "); | 
|---|
| 393 | if ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_R}) { | 
|---|
| 394 | printf("SAMLOGON_USER\n"); | 
|---|
| 395 | } elsif ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_UNK_R}) { | 
|---|
| 396 | printf("SAMLOGON\n"); | 
|---|
| 397 | } else { | 
|---|
| 398 | printf("unknown type 0x%x, please report\n", $hash{type}); | 
|---|
| 399 | } | 
|---|
| 400 |  | 
|---|
| 401 | # guid | 
|---|
| 402 | printf("GUID: %s\n", guid_to_string($hash{guid})); | 
|---|
| 403 |  | 
|---|
| 404 | # flags | 
|---|
| 405 | dump_cldap_flags($hash{flags}); | 
|---|
| 406 |  | 
|---|
| 407 | # strings | 
|---|
| 408 | printf("Forest:\t\t\t%s\n", $hash{forest}); | 
|---|
| 409 | printf("Domain:\t\t\t%s\n", $hash{domain}); | 
|---|
| 410 | printf("Domain Controller:\t%s\n", $hash{hostname}); | 
|---|
| 411 |  | 
|---|
| 412 | printf("Pre-Win2k Domain:\t%s\n", $hash{netbios_domain}); | 
|---|
| 413 | printf("Pre-Win2k Hostname:\t%s\n", $hash{netbios_hostname}); | 
|---|
| 414 |  | 
|---|
| 415 | if ($hash{unk}) { | 
|---|
| 416 | printf("Unk:\t\t\t%s\n", $hash{unk}); | 
|---|
| 417 | } | 
|---|
| 418 | if ($hash{user_name}) { | 
|---|
| 419 | printf("User name:\t%s\n", $hash{user_name}); | 
|---|
| 420 | } | 
|---|
| 421 |  | 
|---|
| 422 | printf("Server Site Name:\t%s\n", $hash{server_site_name}); | 
|---|
| 423 | printf("Client Site Name:\t%s\n", $hash{client_site_name}); | 
|---|
| 424 |  | 
|---|
| 425 | # some more int | 
|---|
| 426 | printf("NT Version:\t\t%d\n", $hash{version}); | 
|---|
| 427 | printf("LMNT Token:\t\t%.2x\n", $hash{lmnt_token}); | 
|---|
| 428 | printf("LM20 Token:\t\t%.2x\n", $hash{lm20_token}); | 
|---|
| 429 | } | 
|---|
| 430 |  | 
|---|
| 431 | sub main() { | 
|---|
| 432 |  | 
|---|
| 433 | my ($ret, $sock, $reply); | 
|---|
| 434 |  | 
|---|
| 435 | GetOptions( | 
|---|
| 436 | 'debug'         => \$opt_debug, | 
|---|
| 437 | 'domain|d=s'    => \$opt_domain, | 
|---|
| 438 | 'help'          => \$opt_help, | 
|---|
| 439 | 'host|h=s'      => \$opt_host, | 
|---|
| 440 | 'server|s=s'    => \$opt_server, | 
|---|
| 441 | ); | 
|---|
| 442 |  | 
|---|
| 443 | $server = $server || $opt_server; | 
|---|
| 444 | $domain = $domain || $opt_domain || undef; | 
|---|
| 445 | $host = $host || $opt_host; | 
|---|
| 446 | if (!$host) { | 
|---|
| 447 | $host = `/bin/hostname`; | 
|---|
| 448 | chomp($host); | 
|---|
| 449 | } | 
|---|
| 450 |  | 
|---|
| 451 | if (!$server || !$host || $opt_help) { | 
|---|
| 452 | usage(); | 
|---|
| 453 | exit 1; | 
|---|
| 454 | } | 
|---|
| 455 |  | 
|---|
| 456 | my $ntver = sprintf("%c%c%c%c", 6,0,0,0); | 
|---|
| 457 |  | 
|---|
| 458 | $sock = connect_cldap($server); | 
|---|
| 459 | if (!$sock) { | 
|---|
| 460 | die("could not connect to $server"); | 
|---|
| 461 | } | 
|---|
| 462 |  | 
|---|
| 463 | $ret = send_cldap_netlogon($sock, $domain, $host, $ntver); | 
|---|
| 464 | if (!$ret) { | 
|---|
| 465 | close($sock); | 
|---|
| 466 | die("failed to send CLDAP request to $server"); | 
|---|
| 467 | } | 
|---|
| 468 |  | 
|---|
| 469 | $ret = recv_cldap_netlogon($sock, $reply); | 
|---|
| 470 | if (!$ret) { | 
|---|
| 471 | close($sock); | 
|---|
| 472 | die("failed to receive CLDAP reply from $server"); | 
|---|
| 473 | } | 
|---|
| 474 | close($sock); | 
|---|
| 475 |  | 
|---|
| 476 | if (!$reply) { | 
|---|
| 477 | printf("no 'NetLogon' attribute received\n"); | 
|---|
| 478 | exit 0; | 
|---|
| 479 | } | 
|---|
| 480 |  | 
|---|
| 481 | %cldap_netlogon_reply = parse_cldap_reply($reply); | 
|---|
| 482 | if (!%cldap_netlogon_reply) { | 
|---|
| 483 | die("failed to parse CLDAP reply from $server"); | 
|---|
| 484 | } | 
|---|
| 485 |  | 
|---|
| 486 | display_cldap_reply($server, %cldap_netlogon_reply); | 
|---|
| 487 |  | 
|---|
| 488 | exit 0; | 
|---|
| 489 | } | 
|---|
| 490 |  | 
|---|
| 491 | main(); | 
|---|