Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

784 lines
18 KiB

  1. #
  2. # Copyright (c) 1995-1997 Graham Barr <[email protected]> and
  3. # Alex Hristov <[email protected]>. All rights reserved. This program is free
  4. # software; you can redistribute it and/or modify it under the same terms
  5. # as Perl itself.
  6. package Net::PH;
  7. require 5.001;
  8. use strict;
  9. use vars qw(@ISA $VERSION);
  10. use Carp;
  11. use Socket 1.3;
  12. use IO::Socket;
  13. use Net::Cmd;
  14. use Net::Config;
  15. $VERSION = "2.20"; # $Id: //depot/libnet/Net/PH.pm#7$
  16. @ISA = qw(Exporter Net::Cmd IO::Socket::INET);
  17. sub new
  18. {
  19. my $pkg = shift;
  20. my $host = shift if @_ % 2;
  21. my %arg = @_;
  22. my $hosts = defined $host ? [ $host ] : $NetConfig{ph_hosts};
  23. my $ph;
  24. my $h;
  25. foreach $h (@{$hosts})
  26. {
  27. $ph = $pkg->SUPER::new(PeerAddr => ($host = $h),
  28. PeerPort => $arg{Port} || 'csnet-ns(105)',
  29. Proto => 'tcp',
  30. Timeout => defined $arg{Timeout}
  31. ? $arg{Timeout}
  32. : 120
  33. ) and last;
  34. }
  35. return undef
  36. unless defined $ph;
  37. ${*$ph}{'net_ph_host'} = $host;
  38. $ph->autoflush(1);
  39. $ph->debug(exists $arg{Debug} ? $arg{Debug} : undef);
  40. $ph;
  41. }
  42. sub status
  43. {
  44. my $ph = shift;
  45. $ph->command('status')->response;
  46. $ph->code;
  47. }
  48. sub login
  49. {
  50. my $ph = shift;
  51. my($user,$pass,$encrypted) = @_;
  52. my $resp;
  53. $resp = $ph->command("login",$user)->response;
  54. if(defined($pass) && $resp == CMD_MORE)
  55. {
  56. if($encrypted)
  57. {
  58. my $challenge_str = $ph->message;
  59. chomp($challenge_str);
  60. Net::PH::crypt::crypt_start($pass);
  61. my $cryptstr = Net::PH::crypt::encryptit($challenge_str);
  62. $ph->command("answer", $cryptstr);
  63. }
  64. else
  65. {
  66. $ph->command("clear", $pass);
  67. }
  68. $resp = $ph->response;
  69. }
  70. $resp == CMD_OK;
  71. }
  72. sub logout
  73. {
  74. my $ph = shift;
  75. $ph->command("logout")->response == CMD_OK;
  76. }
  77. sub id
  78. {
  79. my $ph = shift;
  80. my $id = @_ ? shift : $<;
  81. $ph->command("id",$id)->response == CMD_OK;
  82. }
  83. sub siteinfo
  84. {
  85. my $ph = shift;
  86. $ph->command("siteinfo");
  87. my $ln;
  88. my %resp;
  89. my $cur_num = 0;
  90. while(defined($ln = $ph->getline))
  91. {
  92. $ph->debug_print(0,$ln)
  93. if ($ph->debug & 2);
  94. chomp($ln);
  95. my($code,$num,$tag,$data);
  96. if($ln =~ /^-(\d+):(\d+):(?:\s*([^:]+):)?\s*(.*)/o)
  97. {
  98. ($code,$num,$tag,$data) = ($1, $2, $3 || "",$4);
  99. $resp{$tag} = bless [$code, $num, $tag, $data], "Net::PH::Result";
  100. }
  101. else
  102. {
  103. $ph->set_status($ph->parse_response($ln));
  104. return \%resp;
  105. }
  106. }
  107. return undef;
  108. }
  109. sub query
  110. {
  111. my $ph = shift;
  112. my $search = shift;
  113. my($k,$v);
  114. my @args = ('query', _arg_hash($search));
  115. push(@args,'return',_arg_list( shift ))
  116. if @_;
  117. unless($ph->command(@args)->response == CMD_INFO)
  118. {
  119. return $ph->code == 501
  120. ? []
  121. : undef;
  122. }
  123. my $ln;
  124. my @resp;
  125. my $cur_num = 0;
  126. my($last_tag);
  127. while(defined($ln = $ph->getline))
  128. {
  129. $ph->debug_print(0,$ln)
  130. if ($ph->debug & 2);
  131. chomp($ln);
  132. my($code,$idx,$num,$tag,$data);
  133. if($ln =~ /^-(\d+):(\d+):\s*([^:]*):\s*(.*)/o)
  134. {
  135. ($code,$idx,$tag,$data) = ($1,$2,$3,$4);
  136. my $num = $idx - 1;
  137. $resp[$num] ||= {};
  138. $tag = $last_tag
  139. unless(length($tag));
  140. $last_tag = $tag;
  141. if(exists($resp[$num]->{$tag}))
  142. {
  143. $resp[$num]->{$tag}->[3] .= "\n" . $data;
  144. }
  145. else
  146. {
  147. $resp[$num]->{$tag} = bless [$code, $idx, $tag, $data], "Net::PH::Result";
  148. }
  149. }
  150. else
  151. {
  152. $ph->set_status($ph->parse_response($ln));
  153. return \@resp;
  154. }
  155. }
  156. return undef;
  157. }
  158. sub change
  159. {
  160. my $ph = shift;
  161. my $search = shift;
  162. my $make = shift;
  163. $ph->command(
  164. "change", _arg_hash($search),
  165. "make", _arg_hash($make)
  166. )->response == CMD_OK;
  167. }
  168. sub _arg_hash
  169. {
  170. my $hash = shift;
  171. return $hash
  172. unless(ref($hash));
  173. my($k,$v);
  174. my @r;
  175. while(($k,$v) = each %$hash)
  176. {
  177. my $a = $v;
  178. $a =~ s/\n/\\n/sog;
  179. $a =~ s/\t/\\t/sog;
  180. $a = '"' . $a . '"'
  181. if $a =~ /\W/;
  182. $a = '""'
  183. unless length $a;
  184. push(@r, "$k=$a");
  185. }
  186. join(" ", @r);
  187. }
  188. sub _arg_list
  189. {
  190. my $arr = shift;
  191. return $arr
  192. unless(ref($arr));
  193. my $v;
  194. my @r;
  195. foreach $v (@$arr)
  196. {
  197. my $a = $v;
  198. $a =~ s/\n/\\n/sog;
  199. $a =~ s/\t/\\t/sog;
  200. $a = '"' . $a . '"'
  201. if $a =~ /\W/;
  202. push(@r, $a);
  203. }
  204. join(" ",@r);
  205. }
  206. sub add
  207. {
  208. my $ph = shift;
  209. my $arg = @_ > 1 ? { @_ } : shift;
  210. $ph->command('add', _arg_hash($arg))->response == CMD_OK;
  211. }
  212. sub delete
  213. {
  214. my $ph = shift;
  215. my $arg = @_ > 1 ? { @_ } : shift;
  216. $ph->command('delete', _arg_hash($arg))->response == CMD_OK;
  217. }
  218. sub force
  219. {
  220. my $ph = shift;
  221. my $search = shift;
  222. my $force = shift;
  223. $ph->command(
  224. "change", _arg_hash($search),
  225. "force", _arg_hash($force)
  226. )->response == CMD_OK;
  227. }
  228. sub fields
  229. {
  230. my $ph = shift;
  231. $ph->command("fields", _arg_list(\@_));
  232. my $ln;
  233. my %resp;
  234. my $cur_num = 0;
  235. my @tags = ();
  236. while(defined($ln = $ph->getline))
  237. {
  238. $ph->debug_print(0,$ln)
  239. if ($ph->debug & 2);
  240. chomp($ln);
  241. my($code,$num,$tag,$data,$last_tag);
  242. if($ln =~ /^-(\d+):(\d+):\s*([^:]*):\s*(.*)/o)
  243. {
  244. ($code,$num,$tag,$data) = ($1,$2,$3,$4);
  245. $tag = $last_tag
  246. unless(length($tag));
  247. $last_tag = $tag;
  248. if(exists $resp{$tag})
  249. {
  250. $resp{$tag}->[3] .= "\n" . $data;
  251. }
  252. else
  253. {
  254. $resp{$tag} = bless [$code, $num, $tag, $data], "Net::PH::Result";
  255. push @tags, $tag;
  256. }
  257. }
  258. else
  259. {
  260. $ph->set_status($ph->parse_response($ln));
  261. return wantarray ? (\%resp, \@tags) : \%resp;
  262. }
  263. }
  264. return;
  265. }
  266. sub quit
  267. {
  268. my $ph = shift;
  269. $ph->close
  270. if $ph->command("quit")->response == CMD_OK;
  271. }
  272. ##
  273. ## Net::Cmd overrides
  274. ##
  275. sub parse_response
  276. {
  277. return ()
  278. unless $_[1] =~ s/^(-?)(\d\d\d):?//o;
  279. ($2, $1 eq "-");
  280. }
  281. sub debug_text { $_[2] =~ /^(clear)/i ? "$1 ....\n" : $_[2]; }
  282. package Net::PH::Result;
  283. sub code { shift->[0] }
  284. sub value { shift->[1] }
  285. sub field { shift->[2] }
  286. sub text { shift->[3] }
  287. package Net::PH::crypt;
  288. # The code in this package is based upon 'cryptit.c', Copyright (C) 1988 by
  289. # Steven Dorner, and Paul Pomes, and the University of Illinois Board
  290. # of Trustees, and by CSNET.
  291. use integer;
  292. use strict;
  293. sub ROTORSZ () { 256 }
  294. sub MASK () { 255 }
  295. my(@t1,@t2,@t3,$n1,$n2);
  296. sub crypt_start {
  297. my $pass = shift;
  298. $n1 = 0;
  299. $n2 = 0;
  300. crypt_init($pass);
  301. }
  302. sub crypt_init {
  303. my $pw = shift;
  304. my $i;
  305. @t2 = @t3 = (0) x ROTORSZ;
  306. my $buf = crypt($pw,$pw);
  307. return -1 unless length($buf) > 0;
  308. $buf = substr($buf . "\0" x 13,0,13);
  309. my @buf = map { ord $_ } split(//, $buf);
  310. my $seed = 123;
  311. for($i = 0 ; $i < 13 ; $i++) {
  312. $seed = $seed * $buf[$i] + $i;
  313. }
  314. @t1 = (0 .. ROTORSZ-1);
  315. for($i = 0 ; $i < ROTORSZ ; $i++) {
  316. $seed = 5 * $seed + $buf[$i % 13];
  317. my $random = $seed % 65521;
  318. my $k = ROTORSZ - 1 - $i;
  319. my $ic = ($random & MASK) % ($k + 1);
  320. $random >>= 8;
  321. @t1[$k,$ic] = @t1[$ic,$k];
  322. next if $t3[$k] != 0;
  323. $ic = ($random & MASK) % $k;
  324. while($t3[$ic] != 0) {
  325. $ic = ($ic + 1) % $k;
  326. }
  327. $t3[$k] = $ic;
  328. $t3[$ic] = $k;
  329. }
  330. for($i = 0 ; $i < ROTORSZ ; $i++) {
  331. $t2[$t1[$i] & MASK] = $i
  332. }
  333. }
  334. sub encode {
  335. my $sp = shift;
  336. my $ch;
  337. my $n = scalar(@$sp);
  338. my @out = ($n);
  339. my $i;
  340. for($i = 0 ; $i < $n ; ) {
  341. my($f0,$f1,$f2) = splice(@$sp,0,3);
  342. push(@out,
  343. $f0 >> 2,
  344. ($f0 << 4) & 060 | ($f1 >> 4) & 017,
  345. ($f1 << 2) & 074 | ($f2 >> 6) & 03,
  346. $f2 & 077);
  347. $i += 3;
  348. }
  349. join("", map { chr((($_ & 077) + 35) & 0xff) } @out); # ord('#') == 35
  350. }
  351. sub encryptit {
  352. my $from = shift;
  353. my @from = map { ord $_ } split(//, $from);
  354. my @sp = ();
  355. my $ch;
  356. while(defined($ch = shift @from)) {
  357. push(@sp,
  358. $t2[($t3[($t1[($ch + $n1) & MASK] + $n2) & MASK] - $n2) & MASK] - $n1);
  359. $n1++;
  360. if($n1 == ROTORSZ) {
  361. $n1 = 0;
  362. $n2++;
  363. $n2 = 0 if $n2 == ROTORSZ;
  364. }
  365. }
  366. encode(\@sp);
  367. }
  368. 1;
  369. __END__
  370. =head1 NAME
  371. Net::PH - CCSO Nameserver Client class
  372. =head1 SYNOPSIS
  373. use Net::PH;
  374. $ph = Net::PH->new("some.host.name",
  375. Port => 105,
  376. Timeout => 120,
  377. Debug => 0);
  378. if($ph) {
  379. $q = $ph->query({ field1 => "value1" },
  380. [qw(name address pobox)]);
  381. if($q) {
  382. }
  383. }
  384. # Alternative syntax
  385. if($ph) {
  386. $q = $ph->query('field1=value1',
  387. 'name address pobox');
  388. if($q) {
  389. }
  390. }
  391. =head1 DESCRIPTION
  392. C<Net::PH> is a class implementing a simple Nameserver/PH client in Perl
  393. as described in the CCSO Nameserver -- Server-Client Protocol. Like other
  394. modules in the Net:: family the C<Net::PH> object inherits methods from
  395. C<Net::Cmd>.
  396. =head1 CONSTRUCTOR
  397. =over 4
  398. =item new ( [ HOST ] [, OPTIONS ])
  399. $ph = Net::PH->new("some.host.name",
  400. Port => 105,
  401. Timeout => 120,
  402. Debug => 0
  403. );
  404. This is the constructor for a new Net::PH object. C<HOST> is the
  405. name of the remote host to which a PH connection is required.
  406. If C<HOST> is not given, then the C<SNPP_Host> specified in C<Net::Config>
  407. will be used.
  408. C<OPTIONS> is an optional list of named options which are passed in
  409. a hash like fashion, using key and value pairs. Possible options are:-
  410. B<Port> - Port number to connect to on remote host.
  411. B<Timeout> - Maximum time, in seconds, to wait for a response from the
  412. Nameserver, a value of zero will cause all IO operations to block.
  413. (default: 120)
  414. B<Debug> - Enable the printing of debugging information to STDERR
  415. =back
  416. =head1 METHODS
  417. Unless otherwise stated all methods return either a I<true> or I<false>
  418. value, with I<true> meaning that the operation was a success. When a method
  419. states that it returns a value, failure will be returned as I<undef> or an
  420. empty list.
  421. =over 4
  422. =item query( SEARCH [, RETURN ] )
  423. $q = $ph->query({ name => $myname },
  424. [qw(name email schedule)]);
  425. foreach $handle (@{$q}) {
  426. foreach $field (keys %{$handle}) {
  427. $c = ${$handle}{$field}->code;
  428. $v = ${$handle}{$field}->value;
  429. $f = ${$handle}{$field}->field;
  430. $t = ${$handle}{$field}->text;
  431. print "field:[$field] [$c][$v][$f][$t]\n" ;
  432. }
  433. }
  434. Search the database and return fields from all matching entries.
  435. The C<SEARCH> argument is a reference to a HASH which contains field/value
  436. pairs which will be passed to the Nameserver as the search criteria.
  437. C<RETURN> is optional, but if given it should be a reference to a list which
  438. contains field names to be returned.
  439. The alternative syntax is to pass strings instead of references, for example
  440. $q = $ph->query('name=myname',
  441. 'name email schedule');
  442. The C<SEARCH> argument is a string that is passed to the Nameserver as the
  443. search criteria. The strings being passed should B<not> contain any carriage
  444. returns, or else the query command might fail or return invalid data.
  445. C<RETURN> is optional, but if given it should be a string which will
  446. contain field names to be returned.
  447. Each match from the server will be returned as a HASH where the keys are the
  448. field names and the values are C<Net::PH:Result> objects (I<code>, I<value>,
  449. I<field>, I<text>).
  450. Returns a reference to an ARRAY which contains references to HASHs, one
  451. per match from the server.
  452. =item change( SEARCH , MAKE )
  453. $r = $ph->change({ email => "*.domain.name" },
  454. { schedule => "busy");
  455. Change field values for matching entries.
  456. The C<SEARCH> argument is a reference to a HASH which contains field/value
  457. pairs which will be passed to the Nameserver as the search criteria.
  458. The C<MAKE> argument is a reference to a HASH which contains field/value
  459. pairs which will be passed to the Nameserver that
  460. will set new values to designated fields.
  461. The alternative syntax is to pass strings instead of references, for example
  462. $r = $ph->change('email="*.domain.name"',
  463. 'schedule="busy"');
  464. The C<SEARCH> argument is a string to be passed to the Nameserver as the
  465. search criteria. The strings being passed should B<not> contain any carriage
  466. returns, or else the query command might fail or return invalid data.
  467. The C<MAKE> argument is a string to be passed to the Nameserver that
  468. will set new values to designated fields.
  469. Upon success all entries that match the search criteria will have
  470. the field values, given in the Make argument, changed.
  471. =item login( USER, PASS [, ENCRYPT ])
  472. $r = $ph->login('username','password',1);
  473. Enter login mode using C<USER> and C<PASS>. If C<ENCRYPT> is given and
  474. is I<true> then the password will be used to encrypt a challenge text
  475. string provided by the server, and the encrypted string will be sent back
  476. to the server. If C<ENCRYPT> is not given, or I<false> then the password
  477. will be sent in clear text (I<this is not recommended>)
  478. =item logout()
  479. $r = $ph->logout();
  480. Exit login mode and return to anonymous mode.
  481. =item fields( [ FIELD_LIST ] )
  482. $fields = $ph->fields();
  483. foreach $field (keys %{$fields}) {
  484. $c = ${$fields}{$field}->code;
  485. $v = ${$fields}{$field}->value;
  486. $f = ${$fields}{$field}->field;
  487. $t = ${$fields}{$field}->text;
  488. print "field:[$field] [$c][$v][$f][$t]\n";
  489. }
  490. In a scalar context, returns a reference to a HASH. The keys of the HASH are
  491. the field names and the values are C<Net::PH:Result> objects (I<code>,
  492. I<value>, I<field>, I<text>).
  493. In an array context, returns a two element array. The first element is a
  494. reference to a HASH as above, the second element is a reference to an array
  495. which contains the tag names in the order that they were returned from the
  496. server.
  497. C<FIELD_LIST> is a string that lists the fields for which info will be
  498. returned.
  499. =item add( FIELD_VALUES )
  500. $r = $ph->add( { name => $name, phone => $phone });
  501. This method is used to add new entries to the Nameserver database. You
  502. must successfully call L<login> before this method can be used.
  503. B<Note> that this method adds new entries to the database. To modify
  504. an existing entry use L<change>.
  505. C<FIELD_VALUES> is a reference to a HASH which contains field/value
  506. pairs which will be passed to the Nameserver and will be used to
  507. initialize the new entry.
  508. The alternative syntax is to pass a string instead of a reference, for example
  509. $r = $ph->add('name=myname phone=myphone');
  510. C<FIELD_VALUES> is a string that consists of field/value pairs which the
  511. new entry will contain. The strings being passed should B<not> contain any
  512. carriage returns, or else the query command might fail or return invalid data.
  513. =item delete( FIELD_VALUES )
  514. $r = $ph->delete('name=myname phone=myphone');
  515. This method is used to delete existing entries from the Nameserver database.
  516. You must successfully call L<login> before this method can be used.
  517. B<Note> that this method deletes entries to the database. To modify
  518. an existing entry use L<change>.
  519. C<FIELD_VALUES> is a string that serves as the search criteria for the
  520. records to be deleted. Any entry in the database which matches this search
  521. criteria will be deleted.
  522. =item id( [ ID ] )
  523. $r = $ph->id('709');
  524. Sends C<ID> to the Nameserver, which will enter this into its
  525. logs. If C<ID> is not given then the UID of the user running the
  526. process will be sent.
  527. =item status()
  528. Returns the current status of the Nameserver.
  529. =item siteinfo()
  530. $siteinfo = $ph->siteinfo();
  531. foreach $field (keys %{$siteinfo}) {
  532. $c = ${$siteinfo}{$field}->code;
  533. $v = ${$siteinfo}{$field}->value;
  534. $f = ${$siteinfo}{$field}->field;
  535. $t = ${$siteinfo}{$field}->text;
  536. print "field:[$field] [$c][$v][$f][$t]\n";
  537. }
  538. Returns a reference to a HASH containing information about the server's
  539. site. The keys of the HASH are the field names and values are
  540. C<Net::PH:Result> objects (I<code>, I<value>, I<field>, I<text>).
  541. =item quit()
  542. $r = $ph->quit();
  543. Quit the connection
  544. =back
  545. =head1 Q&A
  546. How do I get the values of a Net::PH::Result object?
  547. foreach $handle (@{$q}) {
  548. foreach $field (keys %{$handle}) {
  549. $my_code = ${$q}{$field}->code;
  550. $my_value = ${$q}{$field}->value;
  551. $my_field = ${$q}{$field}->field;
  552. $my_text = ${$q}{$field}->text;
  553. }
  554. }
  555. How do I get a count of the returned matches to my query?
  556. $my_count = scalar(@{$query_result});
  557. How do I get the status code and message of the last C<$ph> command?
  558. $status_code = $ph->code;
  559. $status_message = $ph->message;
  560. =head1 SEE ALSO
  561. L<Net::Cmd>
  562. =head1 AUTHORS
  563. Graham Barr <[email protected]>
  564. Alex Hristov <[email protected]>
  565. =head1 ACKNOWLEDGMENTS
  566. Password encryption code ported to perl by Broc Seib <[email protected]>,
  567. Purdue University Computing Center.
  568. Otis Gospodnetic <[email protected]> suggested
  569. passing parameters as string constants. Some queries cannot be
  570. executed when passing parameters as string references.
  571. Example: query first_name last_name email="*.domain"
  572. =head1 COPYRIGHT
  573. The encryption code is based upon cryptit.c, Copyright (C) 1988 by
  574. Steven Dorner, and Paul Pomes, and the University of Illinois Board
  575. of Trustees, and by CSNET.
  576. All other code is Copyright (c) 1996-1997 Graham Barr <[email protected]>
  577. and Alex Hristov <[email protected]>. All rights reserved. This program is
  578. free software; you can redistribute it and/or modify it under the same
  579. terms as Perl itself.
  580. =cut