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.

379 lines
10 KiB

  1. # chat.pl: chat with a server
  2. #
  3. # This library is no longer being maintained, and is included for backward
  4. # compatibility with Perl 4 programs which may require it.
  5. #
  6. # In particular, this should not be used as an example of modern Perl
  7. # programming techniques.
  8. #
  9. # Suggested alternative: Socket
  10. #
  11. # Based on: V2.01.alpha.7 91/06/16
  12. # Randal L. Schwartz (was <[email protected]>)
  13. # multihome additions by [email protected]
  14. # allow for /dev/pts based systems by Joe Doupnik <[email protected]>
  15. package chat;
  16. require 'sys/socket.ph';
  17. if( defined( &main'PF_INET ) ){
  18. $pf_inet = &main'PF_INET;
  19. $sock_stream = &main'SOCK_STREAM;
  20. local($name, $aliases, $proto) = getprotobyname( 'tcp' );
  21. $tcp_proto = $proto;
  22. }
  23. else {
  24. # XXX hardwired $PF_INET, $SOCK_STREAM, 'tcp'
  25. # but who the heck would change these anyway? (:-)
  26. $pf_inet = 2;
  27. $sock_stream = 1;
  28. $tcp_proto = 6;
  29. }
  30. $sockaddr = 'S n a4 x8';
  31. chop($thishost = `hostname`);
  32. # *S = symbol for current I/O, gets assigned *chatsymbol....
  33. $next = "chatsymbol000000"; # next one
  34. $nextpat = "^chatsymbol"; # patterns that match next++, ++, ++, ++
  35. ## $handle = &chat'open_port("server.address",$port_number);
  36. ## opens a named or numbered TCP server
  37. sub open_port { ## public
  38. local($server, $port) = @_;
  39. local($serveraddr,$serverproc);
  40. # We may be multi-homed, start with 0, fixup once connexion is made
  41. $thisaddr = "\0\0\0\0" ;
  42. $thisproc = pack($sockaddr, 2, 0, $thisaddr);
  43. *S = ++$next;
  44. if ($server =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) {
  45. $serveraddr = pack('C4', $1, $2, $3, $4);
  46. } else {
  47. local(@x) = gethostbyname($server);
  48. return undef unless @x;
  49. $serveraddr = $x[4];
  50. }
  51. $serverproc = pack($sockaddr, 2, $port, $serveraddr);
  52. unless (socket(S, $pf_inet, $sock_stream, $tcp_proto)) {
  53. ($!) = ($!, close(S)); # close S while saving $!
  54. return undef;
  55. }
  56. unless (bind(S, $thisproc)) {
  57. ($!) = ($!, close(S)); # close S while saving $!
  58. return undef;
  59. }
  60. unless (connect(S, $serverproc)) {
  61. ($!) = ($!, close(S)); # close S while saving $!
  62. return undef;
  63. }
  64. # We opened with the local address set to ANY, at this stage we know
  65. # which interface we are using. This is critical if our machine is
  66. # multi-homed, with IP forwarding off, so fix-up.
  67. local($fam,$lport);
  68. ($fam,$lport,$thisaddr) = unpack($sockaddr, getsockname(S));
  69. $thisproc = pack($sockaddr, 2, 0, $thisaddr);
  70. # end of post-connect fixup
  71. select((select(S), $| = 1)[0]);
  72. $next; # return symbol for switcharound
  73. }
  74. ## ($host, $port, $handle) = &chat'open_listen([$port_number]);
  75. ## opens a TCP port on the current machine, ready to be listened to
  76. ## if $port_number is absent or zero, pick a default port number
  77. ## process must be uid 0 to listen to a low port number
  78. sub open_listen { ## public
  79. *S = ++$next;
  80. local($thisport) = shift || 0;
  81. local($thisproc_local) = pack($sockaddr, 2, $thisport, $thisaddr);
  82. local(*NS) = "__" . time;
  83. unless (socket(NS, $pf_inet, $sock_stream, $tcp_proto)) {
  84. ($!) = ($!, close(NS));
  85. return undef;
  86. }
  87. unless (bind(NS, $thisproc_local)) {
  88. ($!) = ($!, close(NS));
  89. return undef;
  90. }
  91. unless (listen(NS, 1)) {
  92. ($!) = ($!, close(NS));
  93. return undef;
  94. }
  95. select((select(NS), $| = 1)[0]);
  96. local($family, $port, @myaddr) =
  97. unpack("S n C C C C x8", getsockname(NS));
  98. $S{"needs_accept"} = *NS; # so expect will open it
  99. (@myaddr, $port, $next); # returning this
  100. }
  101. ## $handle = &chat'open_proc("command","arg1","arg2",...);
  102. ## opens a /bin/sh on a pseudo-tty
  103. sub open_proc { ## public
  104. local(@cmd) = @_;
  105. *S = ++$next;
  106. local(*TTY) = "__TTY" . time;
  107. local($pty,$tty) = &_getpty(S,TTY);
  108. die "Cannot find a new pty" unless defined $pty;
  109. $pid = fork;
  110. die "Cannot fork: $!" unless defined $pid;
  111. unless ($pid) {
  112. close STDIN; close STDOUT; close STDERR;
  113. setpgrp(0,$$);
  114. if (open(DEVTTY, "/dev/tty")) {
  115. ioctl(DEVTTY,0x20007471,0); # XXX s/b &TIOCNOTTY
  116. close DEVTTY;
  117. }
  118. open(STDIN,"<&TTY");
  119. open(STDOUT,">&TTY");
  120. open(STDERR,">&STDOUT");
  121. die "Oops" unless fileno(STDERR) == 2; # sanity
  122. close(S);
  123. exec @cmd;
  124. die "Cannot exec @cmd: $!";
  125. }
  126. close(TTY);
  127. $next; # return symbol for switcharound
  128. }
  129. # $S is the read-ahead buffer
  130. ## $return = &chat'expect([$handle,] $timeout_time,
  131. ## $pat1, $body1, $pat2, $body2, ... )
  132. ## $handle is from previous &chat'open_*().
  133. ## $timeout_time is the time (either relative to the current time, or
  134. ## absolute, ala time(2)) at which a timeout event occurs.
  135. ## $pat1, $pat2, and so on are regexs which are matched against the input
  136. ## stream. If a match is found, the entire matched string is consumed,
  137. ## and the corresponding body eval string is evaled.
  138. ##
  139. ## Each pat is a regular-expression (probably enclosed in single-quotes
  140. ## in the invocation). ^ and $ will work, respecting the current value of $*.
  141. ## If pat is 'TIMEOUT', the body is executed if the timeout is exceeded.
  142. ## If pat is 'EOF', the body is executed if the process exits before
  143. ## the other patterns are seen.
  144. ##
  145. ## Pats are scanned in the order given, so later pats can contain
  146. ## general defaults that won't be examined unless the earlier pats
  147. ## have failed.
  148. ##
  149. ## The result of eval'ing body is returned as the result of
  150. ## the invocation. Recursive invocations are not thought
  151. ## through, and may work only accidentally. :-)
  152. ##
  153. ## undef is returned if either a timeout or an eof occurs and no
  154. ## corresponding body has been defined.
  155. ## I/O errors of any sort are treated as eof.
  156. $nextsubname = "expectloop000000"; # used for subroutines
  157. sub expect { ## public
  158. if ($_[0] =~ /$nextpat/) {
  159. *S = shift;
  160. }
  161. local($endtime) = shift;
  162. local($timeout,$eof) = (1,1);
  163. local($caller) = caller;
  164. local($rmask, $nfound, $timeleft, $thisbuf);
  165. local($cases, $pattern, $action, $subname);
  166. $endtime += time if $endtime < 600_000_000;
  167. if (defined $S{"needs_accept"}) { # is it a listen socket?
  168. local(*NS) = $S{"needs_accept"};
  169. delete $S{"needs_accept"};
  170. $S{"needs_close"} = *NS;
  171. unless(accept(S,NS)) {
  172. ($!) = ($!, close(S), close(NS));
  173. return undef;
  174. }
  175. select((select(S), $| = 1)[0]);
  176. }
  177. # now see whether we need to create a new sub:
  178. unless ($subname = $expect_subname{$caller,@_}) {
  179. # nope. make a new one:
  180. $expect_subname{$caller,@_} = $subname = $nextsubname++;
  181. $cases .= <<"EDQ"; # header is funny to make everything elsif's
  182. sub $subname {
  183. LOOP: {
  184. if (0) { ; }
  185. EDQ
  186. while (@_) {
  187. ($pattern,$action) = splice(@_,0,2);
  188. if ($pattern =~ /^eof$/i) {
  189. $cases .= <<"EDQ";
  190. elsif (\$eof) {
  191. package $caller;
  192. $action;
  193. }
  194. EDQ
  195. $eof = 0;
  196. } elsif ($pattern =~ /^timeout$/i) {
  197. $cases .= <<"EDQ";
  198. elsif (\$timeout) {
  199. package $caller;
  200. $action;
  201. }
  202. EDQ
  203. $timeout = 0;
  204. } else {
  205. $pattern =~ s#/#\\/#g;
  206. $cases .= <<"EDQ";
  207. elsif (\$S =~ /$pattern/) {
  208. \$S = \$';
  209. package $caller;
  210. $action;
  211. }
  212. EDQ
  213. }
  214. }
  215. $cases .= <<"EDQ" if $eof;
  216. elsif (\$eof) {
  217. undef;
  218. }
  219. EDQ
  220. $cases .= <<"EDQ" if $timeout;
  221. elsif (\$timeout) {
  222. undef;
  223. }
  224. EDQ
  225. $cases .= <<'ESQ';
  226. else {
  227. $rmask = "";
  228. vec($rmask,fileno(S),1) = 1;
  229. ($nfound, $rmask) =
  230. select($rmask, undef, undef, $endtime - time);
  231. if ($nfound) {
  232. $nread = sysread(S, $thisbuf, 1024);
  233. if ($nread > 0) {
  234. $S .= $thisbuf;
  235. } else {
  236. $eof++, redo LOOP; # any error is also eof
  237. }
  238. } else {
  239. $timeout++, redo LOOP; # timeout
  240. }
  241. redo LOOP;
  242. }
  243. }
  244. }
  245. ESQ
  246. eval $cases; die "$cases:\n$@" if $@;
  247. }
  248. $eof = $timeout = 0;
  249. do $subname();
  250. }
  251. ## &chat'print([$handle,] @data)
  252. ## $handle is from previous &chat'open().
  253. ## like print $handle @data
  254. sub print { ## public
  255. if ($_[0] =~ /$nextpat/) {
  256. *S = shift;
  257. }
  258. local $out = join $, , @_;
  259. syswrite(S, $out, length $out);
  260. if( $chat'debug ){
  261. print STDERR "printed:";
  262. print STDERR @_;
  263. }
  264. }
  265. ## &chat'close([$handle,])
  266. ## $handle is from previous &chat'open().
  267. ## like close $handle
  268. sub close { ## public
  269. if ($_[0] =~ /$nextpat/) {
  270. *S = shift;
  271. }
  272. close(S);
  273. if (defined $S{"needs_close"}) { # is it a listen socket?
  274. local(*NS) = $S{"needs_close"};
  275. delete $S{"needs_close"};
  276. close(NS);
  277. }
  278. }
  279. ## @ready_handles = &chat'select($timeout, @handles)
  280. ## select()'s the handles with a timeout value of $timeout seconds.
  281. ## Returns an array of handles that are ready for I/O.
  282. ## Both user handles and chat handles are supported (but beware of
  283. ## stdio's buffering for user handles).
  284. sub select { ## public
  285. local($timeout) = shift;
  286. local(@handles) = @_;
  287. local(%handlename) = ();
  288. local(%ready) = ();
  289. local($caller) = caller;
  290. local($rmask) = "";
  291. for (@handles) {
  292. if (/$nextpat/o) { # one of ours... see if ready
  293. local(*SYM) = $_;
  294. if (length($SYM)) {
  295. $timeout = 0; # we have a winner
  296. $ready{$_}++;
  297. }
  298. $handlename{fileno($_)} = $_;
  299. } else {
  300. $handlename{fileno(/'/ ? $_ : "$caller\'$_")} = $_;
  301. }
  302. }
  303. for (sort keys %handlename) {
  304. vec($rmask, $_, 1) = 1;
  305. }
  306. select($rmask, undef, undef, $timeout);
  307. for (sort keys %handlename) {
  308. $ready{$handlename{$_}}++ if vec($rmask,$_,1);
  309. }
  310. sort keys %ready;
  311. }
  312. # ($pty,$tty) = $chat'_getpty(PTY,TTY):
  313. # internal procedure to get the next available pty.
  314. # opens pty on handle PTY, and matching tty on handle TTY.
  315. # returns undef if can't find a pty.
  316. # Modify "/dev/pty" to "/dev/pts" for Dell Unix v2.2 (aka SVR4.04). Joe Doupnik.
  317. sub _getpty { ## private
  318. local($_PTY,$_TTY) = @_;
  319. $_PTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
  320. $_TTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
  321. local($pty, $tty, $kind);
  322. if( -e "/dev/pts000" ){ ## mods by Joe Doupnik Dec 1992
  323. $kind = "pts"; ## SVR4 Streams
  324. } else {
  325. $kind = "pty"; ## BSD Clist stuff
  326. }
  327. for $bank (112..127) {
  328. next unless -e sprintf("/dev/$kind%c0", $bank);
  329. for $unit (48..57) {
  330. $pty = sprintf("/dev/$kind%c%c", $bank, $unit);
  331. open($_PTY,"+>$pty") || next;
  332. select((select($_PTY), $| = 1)[0]);
  333. ($tty = $pty) =~ s/pty/tty/;
  334. open($_TTY,"+>$tty") || next;
  335. select((select($_TTY), $| = 1)[0]);
  336. system "stty nl>$tty";
  337. return ($pty,$tty);
  338. }
  339. }
  340. undef;
  341. }
  342. 1;