诸暨麻将添加redis
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2365 lines
67 KiB

  1. #!/usr/bin/env perl
  2. #***************************************************************************
  3. # _ _ ____ _
  4. # Project ___| | | | _ \| |
  5. # / __| | | | |_) | |
  6. # | (__| |_| | _ <| |___
  7. # \___|\___/|_| \_\_____|
  8. #
  9. # Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
  10. #
  11. # This software is licensed as described in the file COPYING, which
  12. # you should have received as part of this distribution. The terms
  13. # are also available at http://curl.haxx.se/docs/copyright.html.
  14. #
  15. # You may opt to use, copy, modify, merge, publish, distribute and/or sell
  16. # copies of the Software, and permit persons to whom the Software is
  17. # furnished to do so, under the terms of the COPYING file.
  18. #
  19. # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  20. # KIND, either express or implied.
  21. #
  22. ###########################################################################
  23. # This is a server designed for the curl test suite.
  24. #
  25. # In December 2009 we started remaking the server to support more protocols
  26. # that are similar in spirit. Like POP3, IMAP and SMTP in addition to the FTP
  27. # it already supported since a long time. Note that it still only supports one
  28. # protocol per invoke. You need to start multiple servers to support multiple
  29. # protocols simultaneously.
  30. #
  31. # It is meant to exercise curl, it is not meant to be a fully working
  32. # or even very standard compliant server.
  33. #
  34. # You may optionally specify port on the command line, otherwise it'll
  35. # default to port 8921.
  36. #
  37. # All socket/network/TCP related stuff is done by the 'sockfilt' program.
  38. #
  39. BEGIN {
  40. push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'});
  41. push(@INC, ".");
  42. # sub second timestamping needs Time::HiRes
  43. eval {
  44. no warnings "all";
  45. require Time::HiRes;
  46. import Time::HiRes qw( gettimeofday );
  47. }
  48. }
  49. use strict;
  50. use warnings;
  51. use IPC::Open2;
  52. require "getpart.pm";
  53. require "ftp.pm";
  54. require "directories.pm";
  55. use serverhelp qw(
  56. servername_str
  57. server_pidfilename
  58. server_logfilename
  59. mainsockf_pidfilename
  60. mainsockf_logfilename
  61. datasockf_pidfilename
  62. datasockf_logfilename
  63. );
  64. #**********************************************************************
  65. # global vars...
  66. #
  67. my $verbose = 0; # set to 1 for debugging
  68. my $idstr = ""; # server instance string
  69. my $idnum = 1; # server instance number
  70. my $ipvnum = 4; # server IPv number (4 or 6)
  71. my $proto = 'ftp'; # default server protocol
  72. my $srcdir; # directory where ftpserver.pl is located
  73. my $srvrname; # server name for presentation purposes
  74. my $path = '.';
  75. my $logdir = $path .'/log';
  76. #**********************************************************************
  77. # global vars used for server address and primary listener port
  78. #
  79. my $port = 8921; # default primary listener port
  80. my $listenaddr = '127.0.0.1'; # default address for listener port
  81. #**********************************************************************
  82. # global vars used for file names
  83. #
  84. my $pidfile; # server pid file name
  85. my $logfile; # server log file name
  86. my $mainsockf_pidfile; # pid file for primary connection sockfilt process
  87. my $mainsockf_logfile; # log file for primary connection sockfilt process
  88. my $datasockf_pidfile; # pid file for secondary connection sockfilt process
  89. my $datasockf_logfile; # log file for secondary connection sockfilt process
  90. #**********************************************************************
  91. # global vars used for server logs advisor read lock handling
  92. #
  93. my $SERVERLOGS_LOCK = 'log/serverlogs.lock';
  94. my $serverlogslocked = 0;
  95. #**********************************************************************
  96. # global vars used for child processes PID tracking
  97. #
  98. my $sfpid; # PID for primary connection sockfilt process
  99. my $slavepid; # PID for secondary connection sockfilt process
  100. #**********************************************************************
  101. # global typeglob filehandle vars to read/write from/to sockfilters
  102. #
  103. local *SFREAD; # used to read from primary connection
  104. local *SFWRITE; # used to write to primary connection
  105. local *DREAD; # used to read from secondary connection
  106. local *DWRITE; # used to write to secondary connection
  107. my $sockfilt_timeout = 5; # default timeout for sockfilter eXsysreads
  108. #**********************************************************************
  109. # global vars which depend on server protocol selection
  110. #
  111. my %commandfunc; # protocol command specific function callbacks
  112. my %displaytext; # text returned to client before callback runs
  113. #**********************************************************************
  114. # global vars customized for each test from the server commands file
  115. #
  116. my $ctrldelay; # set if server should throttle ctrl stream
  117. my $datadelay; # set if server should throttle data stream
  118. my $retrweirdo; # set if ftp server should use RETRWEIRDO
  119. my $retrnosize; # set if ftp server should use RETRNOSIZE
  120. my $pasvbadip; # set if ftp server should use PASVBADIP
  121. my $nosave; # set if ftp server should not save uploaded data
  122. my $nodataconn; # set if ftp srvr doesn't establish or accepts data channel
  123. my $nodataconn425; # set if ftp srvr doesn't establish data ch and replies 425
  124. my $nodataconn421; # set if ftp srvr doesn't establish data ch and replies 421
  125. my $nodataconn150; # set if ftp srvr doesn't establish data ch and replies 150
  126. my $support_capa; # set if server supports capability command
  127. my $support_auth; # set if server supports authentication command
  128. my %customreply; #
  129. my %customcount; #
  130. my %delayreply; #
  131. #**********************************************************************
  132. # global variables for to test ftp wildcardmatching or other test that
  133. # need flexible LIST responses.. and corresponding files.
  134. # $ftptargetdir is keeping the fake "name" of LIST directory.
  135. #
  136. my $ftplistparserstate;
  137. my $ftptargetdir;
  138. #**********************************************************************
  139. # global variables used when running a ftp server to keep state info
  140. # relative to the secondary or data sockfilt process. Values of these
  141. # variables should only be modified using datasockf_state() sub, given
  142. # that they are closely related and relationship is a bit awkward.
  143. #
  144. my $datasockf_state = 'STOPPED'; # see datasockf_state() sub
  145. my $datasockf_mode = 'none'; # ['none','active','passive']
  146. my $datasockf_runs = 'no'; # ['no','yes']
  147. my $datasockf_conn = 'no'; # ['no','yes']
  148. #**********************************************************************
  149. # global vars used for signal handling
  150. #
  151. my $got_exit_signal = 0; # set if program should finish execution ASAP
  152. my $exit_signal; # first signal handled in exit_signal_handler
  153. #**********************************************************************
  154. # exit_signal_handler will be triggered to indicate that the program
  155. # should finish its execution in a controlled way as soon as possible.
  156. # For now, program will also terminate from within this handler.
  157. #
  158. sub exit_signal_handler {
  159. my $signame = shift;
  160. # For now, simply mimic old behavior.
  161. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  162. unlink($pidfile);
  163. if($serverlogslocked) {
  164. $serverlogslocked = 0;
  165. clear_advisor_read_lock($SERVERLOGS_LOCK);
  166. }
  167. exit;
  168. }
  169. #**********************************************************************
  170. # logmsg is general message logging subroutine for our test servers.
  171. #
  172. sub logmsg {
  173. my $now;
  174. # sub second timestamping needs Time::HiRes
  175. if($Time::HiRes::VERSION) {
  176. my ($seconds, $usec) = gettimeofday();
  177. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  178. localtime($seconds);
  179. $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec);
  180. }
  181. else {
  182. my $seconds = time();
  183. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  184. localtime($seconds);
  185. $now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec);
  186. }
  187. if(open(LOGFILEFH, ">>$logfile")) {
  188. print LOGFILEFH $now;
  189. print LOGFILEFH @_;
  190. close(LOGFILEFH);
  191. }
  192. }
  193. sub ftpmsg {
  194. # append to the server.input file
  195. open(INPUT, ">>log/server$idstr.input") ||
  196. logmsg "failed to open log/server$idstr.input\n";
  197. print INPUT @_;
  198. close(INPUT);
  199. # use this, open->print->close system only to make the file
  200. # open as little as possible, to make the test suite run
  201. # better on windows/cygwin
  202. }
  203. #**********************************************************************
  204. # eXsysread is a wrapper around perl's sysread() function. This will
  205. # repeat the call to sysread() until it has actually read the complete
  206. # number of requested bytes or an unrecoverable condition occurs.
  207. # On success returns a positive value, the number of bytes requested.
  208. # On failure or timeout returns zero.
  209. #
  210. sub eXsysread {
  211. my $FH = shift;
  212. my $scalar = shift;
  213. my $nbytes = shift;
  214. my $timeout = shift; # A zero timeout disables eXsysread() time limit
  215. #
  216. my $time_limited = 0;
  217. my $timeout_rest = 0;
  218. my $start_time = 0;
  219. my $nread = 0;
  220. my $rc;
  221. $$scalar = "";
  222. if((not defined $nbytes) || ($nbytes < 1)) {
  223. logmsg "Error: eXsysread() failure: " .
  224. "length argument must be positive\n";
  225. return 0;
  226. }
  227. if((not defined $timeout) || ($timeout < 0)) {
  228. logmsg "Error: eXsysread() failure: " .
  229. "timeout argument must be zero or positive\n";
  230. return 0;
  231. }
  232. if($timeout > 0) {
  233. # caller sets eXsysread() time limit
  234. $time_limited = 1;
  235. $timeout_rest = $timeout;
  236. $start_time = int(time());
  237. }
  238. while($nread < $nbytes) {
  239. if($time_limited) {
  240. eval {
  241. local $SIG{ALRM} = sub { die "alarm\n"; };
  242. alarm $timeout_rest;
  243. $rc = sysread($FH, $$scalar, $nbytes - $nread, $nread);
  244. alarm 0;
  245. };
  246. $timeout_rest = $timeout - (int(time()) - $start_time);
  247. if($timeout_rest < 1) {
  248. logmsg "Error: eXsysread() failure: timed out\n";
  249. return 0;
  250. }
  251. }
  252. else {
  253. $rc = sysread($FH, $$scalar, $nbytes - $nread, $nread);
  254. }
  255. if($got_exit_signal) {
  256. logmsg "Error: eXsysread() failure: signalled to die\n";
  257. return 0;
  258. }
  259. if(not defined $rc) {
  260. if($!{EINTR}) {
  261. logmsg "Warning: retrying sysread() interrupted system call\n";
  262. next;
  263. }
  264. if($!{EAGAIN}) {
  265. logmsg "Warning: retrying sysread() due to EAGAIN\n";
  266. next;
  267. }
  268. if($!{EWOULDBLOCK}) {
  269. logmsg "Warning: retrying sysread() due to EWOULDBLOCK\n";
  270. next;
  271. }
  272. logmsg "Error: sysread() failure: $!\n";
  273. return 0;
  274. }
  275. if($rc < 0) {
  276. logmsg "Error: sysread() failure: returned negative value $rc\n";
  277. return 0;
  278. }
  279. if($rc == 0) {
  280. logmsg "Error: sysread() failure: read zero bytes\n";
  281. return 0;
  282. }
  283. $nread += $rc;
  284. }
  285. return $nread;
  286. }
  287. #**********************************************************************
  288. # read_mainsockf attempts to read the given amount of output from the
  289. # sockfilter which is in use for the main or primary connection. This
  290. # reads untranslated sockfilt lingo which may hold data read from the
  291. # main or primary socket. On success returns 1, otherwise zero.
  292. #
  293. sub read_mainsockf {
  294. my $scalar = shift;
  295. my $nbytes = shift;
  296. my $timeout = shift; # Optional argument, if zero blocks indefinitively
  297. my $FH = \*SFREAD;
  298. if(not defined $timeout) {
  299. $timeout = $sockfilt_timeout + ($nbytes >> 12);
  300. }
  301. if(eXsysread($FH, $scalar, $nbytes, $timeout) != $nbytes) {
  302. my ($fcaller, $lcaller) = (caller)[1,2];
  303. logmsg "Error: read_mainsockf() failure at $fcaller " .
  304. "line $lcaller. Due to eXsysread() failure\n";
  305. return 0;
  306. }
  307. return 1;
  308. }
  309. #**********************************************************************
  310. # read_datasockf attempts to read the given amount of output from the
  311. # sockfilter which is in use for the data or secondary connection. This
  312. # reads untranslated sockfilt lingo which may hold data read from the
  313. # data or secondary socket. On success returns 1, otherwise zero.
  314. #
  315. sub read_datasockf {
  316. my $scalar = shift;
  317. my $nbytes = shift;
  318. my $timeout = shift; # Optional argument, if zero blocks indefinitively
  319. my $FH = \*DREAD;
  320. if(not defined $timeout) {
  321. $timeout = $sockfilt_timeout + ($nbytes >> 12);
  322. }
  323. if(eXsysread($FH, $scalar, $nbytes, $timeout) != $nbytes) {
  324. my ($fcaller, $lcaller) = (caller)[1,2];
  325. logmsg "Error: read_datasockf() failure at $fcaller " .
  326. "line $lcaller. Due to eXsysread() failure\n";
  327. return 0;
  328. }
  329. return 1;
  330. }
  331. sub sysread_or_die {
  332. my $FH = shift;
  333. my $scalar = shift;
  334. my $length = shift;
  335. my $fcaller;
  336. my $lcaller;
  337. my $result;
  338. $result = sysread($$FH, $$scalar, $length);
  339. if(not defined $result) {
  340. ($fcaller, $lcaller) = (caller)[1,2];
  341. logmsg "Failed to read input\n";
  342. logmsg "Error: $srvrname server, sysread error: $!\n";
  343. logmsg "Exited from sysread_or_die() at $fcaller " .
  344. "line $lcaller. $srvrname server, sysread error: $!\n";
  345. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  346. unlink($pidfile);
  347. if($serverlogslocked) {
  348. $serverlogslocked = 0;
  349. clear_advisor_read_lock($SERVERLOGS_LOCK);
  350. }
  351. exit;
  352. }
  353. elsif($result == 0) {
  354. ($fcaller, $lcaller) = (caller)[1,2];
  355. logmsg "Failed to read input\n";
  356. logmsg "Error: $srvrname server, read zero\n";
  357. logmsg "Exited from sysread_or_die() at $fcaller " .
  358. "line $lcaller. $srvrname server, read zero\n";
  359. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  360. unlink($pidfile);
  361. if($serverlogslocked) {
  362. $serverlogslocked = 0;
  363. clear_advisor_read_lock($SERVERLOGS_LOCK);
  364. }
  365. exit;
  366. }
  367. return $result;
  368. }
  369. sub startsf {
  370. my $mainsockfcmd = "./server/sockfilt " .
  371. "--ipv$ipvnum --port $port " .
  372. "--pidfile \"$mainsockf_pidfile\" " .
  373. "--logfile \"$mainsockf_logfile\"";
  374. $sfpid = open2(*SFREAD, *SFWRITE, $mainsockfcmd);
  375. print STDERR "$mainsockfcmd\n" if($verbose);
  376. print SFWRITE "PING\n";
  377. my $pong;
  378. sysread_or_die(\*SFREAD, \$pong, 5);
  379. if($pong !~ /^PONG/) {
  380. logmsg "Failed sockfilt command: $mainsockfcmd\n";
  381. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  382. unlink($pidfile);
  383. if($serverlogslocked) {
  384. $serverlogslocked = 0;
  385. clear_advisor_read_lock($SERVERLOGS_LOCK);
  386. }
  387. die "Failed to start sockfilt!";
  388. }
  389. }
  390. sub sockfilt {
  391. my $l;
  392. foreach $l (@_) {
  393. printf SFWRITE "DATA\n%04x\n", length($l);
  394. print SFWRITE $l;
  395. }
  396. }
  397. sub sockfiltsecondary {
  398. my $l;
  399. foreach $l (@_) {
  400. printf DWRITE "DATA\n%04x\n", length($l);
  401. print DWRITE $l;
  402. }
  403. }
  404. # Send data to the client on the control stream, which happens to be plain
  405. # stdout.
  406. sub sendcontrol {
  407. if(!$ctrldelay) {
  408. # spit it all out at once
  409. sockfilt @_;
  410. }
  411. else {
  412. my $a = join("", @_);
  413. my @a = split("", $a);
  414. for(@a) {
  415. sockfilt $_;
  416. select(undef, undef, undef, 0.01);
  417. }
  418. }
  419. my $log;
  420. foreach $log (@_) {
  421. my $l = $log;
  422. $l =~ s/\r/[CR]/g;
  423. $l =~ s/\n/[LF]/g;
  424. logmsg "> \"$l\"\n";
  425. }
  426. }
  427. #**********************************************************************
  428. # Send data to the FTP client on the data stream when data connection
  429. # is actually established. Given that this sub should only be called
  430. # when a data connection is supposed to be established, calling this
  431. # without a data connection is an indication of weak logic somewhere.
  432. #
  433. sub senddata {
  434. my $l;
  435. if($datasockf_conn eq 'no') {
  436. logmsg "WARNING: Detected data sending attempt without DATA channel\n";
  437. foreach $l (@_) {
  438. logmsg "WARNING: Data swallowed: $l\n"
  439. }
  440. return;
  441. }
  442. foreach $l (@_) {
  443. if(!$datadelay) {
  444. # spit it all out at once
  445. sockfiltsecondary $l;
  446. }
  447. else {
  448. # pause between each byte
  449. for (split(//,$l)) {
  450. sockfiltsecondary $_;
  451. select(undef, undef, undef, 0.01);
  452. }
  453. }
  454. }
  455. }
  456. #**********************************************************************
  457. # protocolsetup initializes the 'displaytext' and 'commandfunc' hashes
  458. # for the given protocol. References to protocol command callbacks are
  459. # stored in 'commandfunc' hash, and text which will be returned to the
  460. # client before the command callback runs is stored in 'displaytext'.
  461. #
  462. sub protocolsetup {
  463. my $proto = $_[0];
  464. if($proto eq 'ftp') {
  465. %commandfunc = (
  466. 'PORT' => \&PORT_ftp,
  467. 'EPRT' => \&PORT_ftp,
  468. 'LIST' => \&LIST_ftp,
  469. 'NLST' => \&NLST_ftp,
  470. 'PASV' => \&PASV_ftp,
  471. 'CWD' => \&CWD_ftp,
  472. 'PWD' => \&PWD_ftp,
  473. 'EPSV' => \&PASV_ftp,
  474. 'RETR' => \&RETR_ftp,
  475. 'SIZE' => \&SIZE_ftp,
  476. 'REST' => \&REST_ftp,
  477. 'STOR' => \&STOR_ftp,
  478. 'APPE' => \&STOR_ftp, # append looks like upload
  479. 'MDTM' => \&MDTM_ftp,
  480. );
  481. %displaytext = (
  482. 'USER' => '331 We are happy you popped in!',
  483. 'PASS' => '230 Welcome you silly person',
  484. 'PORT' => '200 You said PORT - I say FINE',
  485. 'TYPE' => '200 I modify TYPE as you wanted',
  486. 'LIST' => '150 here comes a directory',
  487. 'NLST' => '150 here comes a directory',
  488. 'CWD' => '250 CWD command successful.',
  489. 'SYST' => '215 UNIX Type: L8', # just fake something
  490. 'QUIT' => '221 bye bye baby', # just reply something
  491. 'MKD' => '257 Created your requested directory',
  492. 'REST' => '350 Yeah yeah we set it there for you',
  493. 'DELE' => '200 OK OK OK whatever you say',
  494. 'RNFR' => '350 Received your order. Please provide more',
  495. 'RNTO' => '250 Ok, thanks. File renaming completed.',
  496. 'NOOP' => '200 Yes, I\'m very good at doing nothing.',
  497. 'PBSZ' => '500 PBSZ not implemented',
  498. 'PROT' => '500 PROT not implemented',
  499. 'welcome' => join("",
  500. '220- _ _ ____ _ '."\r\n",
  501. '220- ___| | | | _ \| | '."\r\n",
  502. '220- / __| | | | |_) | | '."\r\n",
  503. '220- | (__| |_| | _ <| |___ '."\r\n",
  504. '220 \___|\___/|_| \_\_____|'."\r\n")
  505. );
  506. }
  507. elsif($proto eq 'pop3') {
  508. %commandfunc = (
  509. 'CAPA' => \&CAPA_pop3,
  510. 'AUTH' => \&AUTH_pop3,
  511. 'RETR' => \&RETR_pop3,
  512. 'LIST' => \&LIST_pop3,
  513. );
  514. %displaytext = (
  515. 'USER' => '+OK We are happy you popped in!',
  516. 'PASS' => '+OK Access granted',
  517. 'QUIT' => '+OK byebye',
  518. 'welcome' => join("",
  519. ' _ _ ____ _ '."\r\n",
  520. ' ___| | | | _ \| | '."\r\n",
  521. ' / __| | | | |_) | | '."\r\n",
  522. ' | (__| |_| | _ <| |___ '."\r\n",
  523. ' \___|\___/|_| \_\_____|'."\r\n",
  524. '+OK cURL POP3 server ready to serve'."\r\n")
  525. );
  526. }
  527. elsif($proto eq 'imap') {
  528. %commandfunc = (
  529. 'APPEND' => \&APPEND_imap,
  530. 'CAPABILITY' => \&CAPABILITY_imap,
  531. 'EXAMINE' => \&EXAMINE_imap,
  532. 'FETCH' => \&FETCH_imap,
  533. 'LIST' => \&LIST_imap,
  534. 'LOGOUT' => \&LOGOUT_imap,
  535. 'SELECT' => \&SELECT_imap,
  536. 'STATUS' => \&STATUS_imap,
  537. 'STORE' => \&STORE_imap
  538. );
  539. %displaytext = (
  540. 'LOGIN' => ' OK LOGIN completed',
  541. 'welcome' => join("",
  542. ' _ _ ____ _ '."\r\n",
  543. ' ___| | | | _ \| | '."\r\n",
  544. ' / __| | | | |_) | | '."\r\n",
  545. ' | (__| |_| | _ <| |___ '."\r\n",
  546. ' \___|\___/|_| \_\_____|'."\r\n",
  547. '* OK cURL IMAP server ready to serve'."\r\n")
  548. );
  549. }
  550. elsif($proto eq 'smtp') {
  551. %commandfunc = (
  552. 'DATA' => \&DATA_smtp,
  553. 'RCPT' => \&RCPT_smtp,
  554. );
  555. %displaytext = (
  556. 'EHLO' => "250-SIZE\r\n250 Welcome visitor, stay a while staaaaaay forever",
  557. 'MAIL' => '200 Note taken',
  558. 'RCPT' => '200 Receivers accepted',
  559. 'QUIT' => '200 byebye',
  560. 'welcome' => join("",
  561. '220- _ _ ____ _ '."\r\n",
  562. '220- ___| | | | _ \| | '."\r\n",
  563. '220- / __| | | | |_) | | '."\r\n",
  564. '220- | (__| |_| | _ <| |___ '."\r\n",
  565. '220 \___|\___/|_| \_\_____|'."\r\n")
  566. );
  567. }
  568. }
  569. sub close_dataconn {
  570. my ($closed)=@_; # non-zero if already disconnected
  571. my $datapid = processexists($datasockf_pidfile);
  572. logmsg "=====> Closing $datasockf_mode DATA connection...\n";
  573. if(!$closed) {
  574. if($datapid > 0) {
  575. logmsg "Server disconnects $datasockf_mode DATA connection\n";
  576. print DWRITE "DISC\n";
  577. my $i;
  578. sysread DREAD, $i, 5;
  579. }
  580. else {
  581. logmsg "Server finds $datasockf_mode DATA connection already ".
  582. "disconnected\n";
  583. }
  584. }
  585. else {
  586. logmsg "Server knows $datasockf_mode DATA connection is already ".
  587. "disconnected\n";
  588. }
  589. if($datapid > 0) {
  590. print DWRITE "QUIT\n";
  591. waitpid($datapid, 0);
  592. unlink($datasockf_pidfile) if(-f $datasockf_pidfile);
  593. logmsg "DATA sockfilt for $datasockf_mode data channel quits ".
  594. "(pid $datapid)\n";
  595. }
  596. else {
  597. logmsg "DATA sockfilt for $datasockf_mode data channel already ".
  598. "dead\n";
  599. }
  600. logmsg "=====> Closed $datasockf_mode DATA connection\n";
  601. datasockf_state('STOPPED');
  602. }
  603. ################
  604. ################ SMTP commands
  605. ################
  606. # what set by "RCPT"
  607. my $smtp_rcpt;
  608. sub DATA_smtp {
  609. my $testno;
  610. if($smtp_rcpt =~ /^TO:(.*)/) {
  611. $testno = $1;
  612. }
  613. else {
  614. return; # failure
  615. }
  616. if($testno eq "<verifiedserver>") {
  617. sendcontrol "554 WE ROOLZ: $$\r\n";
  618. return 0; # don't wait for data now
  619. }
  620. else {
  621. $testno =~ s/^([^0-9]*)([0-9]+).*/$2/;
  622. sendcontrol "354 Show me the mail\r\n";
  623. }
  624. logmsg "===> rcpt $testno was $smtp_rcpt\n";
  625. my $filename = "log/upload.$testno";
  626. logmsg "Store test number $testno in $filename\n";
  627. open(FILE, ">$filename") ||
  628. return 0; # failed to open output
  629. my $line;
  630. my $ulsize=0;
  631. my $disc=0;
  632. my $raw;
  633. while (5 == (sysread \*SFREAD, $line, 5)) {
  634. if($line eq "DATA\n") {
  635. my $i;
  636. my $eob;
  637. sysread \*SFREAD, $i, 5;
  638. my $size = 0;
  639. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  640. $size = hex($1);
  641. }
  642. read_mainsockf(\$line, $size);
  643. $ulsize += $size;
  644. print FILE $line if(!$nosave);
  645. $raw .= $line;
  646. if($raw =~ /\x0d\x0a\x2e\x0d\x0a/) {
  647. # end of data marker!
  648. $eob = 1;
  649. }
  650. logmsg "> Appending $size bytes to file\n";
  651. if($eob) {
  652. logmsg "Found SMTP EOB marker\n";
  653. last;
  654. }
  655. }
  656. elsif($line eq "DISC\n") {
  657. # disconnect!
  658. $disc=1;
  659. last;
  660. }
  661. else {
  662. logmsg "No support for: $line";
  663. last;
  664. }
  665. }
  666. if($nosave) {
  667. print FILE "$ulsize bytes would've been stored here\n";
  668. }
  669. close(FILE);
  670. sendcontrol "250 OK, data received!\r\n";
  671. logmsg "received $ulsize bytes upload\n";
  672. }
  673. sub RCPT_smtp {
  674. my ($args) = @_;
  675. $smtp_rcpt = $args;
  676. }
  677. ################
  678. ################ IMAP commands
  679. ################
  680. # global to allow the command functions to read it
  681. my $cmdid;
  682. # what was picked by SELECT
  683. my $selected;
  684. # Any IMAP parameter can come in escaped and in double quotes.
  685. # This function is dumb (so far) and just removes the quotes if present.
  686. sub fix_imap_params {
  687. foreach (@_) {
  688. $_ = $1 if /^"(.*)"$/;
  689. }
  690. }
  691. sub CAPABILITY_imap {
  692. my ($testno) = @_;
  693. my $data;
  694. if(!$support_capa) {
  695. sendcontrol "$cmdid BAD Command\r\n";
  696. }
  697. else {
  698. $data = "* CAPABILITY IMAP4";
  699. if($support_auth) {
  700. $data .= " AUTH=UNKNOWN";
  701. }
  702. $data .= " pingpong test server\r\n";
  703. sendcontrol $data;
  704. sendcontrol "$cmdid OK CAPABILITY completed\r\n";
  705. }
  706. return 0;
  707. }
  708. sub SELECT_imap {
  709. my ($testno) = @_;
  710. fix_imap_params($testno);
  711. logmsg "SELECT_imap got test $testno\n";
  712. # Example from RFC 3501, 6.3.1. SELECT Command
  713. sendcontrol "* 172 EXISTS\r\n";
  714. sendcontrol "* 1 RECENT\r\n";
  715. sendcontrol "* OK [UNSEEN 12] Message 12 is first unseen\r\n";
  716. sendcontrol "* OK [UIDVALIDITY 3857529045] UIDs valid\r\n";
  717. sendcontrol "* OK [UIDNEXT 4392] Predicted next UID\r\n";
  718. sendcontrol "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n";
  719. sendcontrol "* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\r\n";
  720. sendcontrol "$cmdid OK [READ-WRITE] SELECT completed\r\n";
  721. $selected = $testno;
  722. return 0;
  723. }
  724. sub FETCH_imap {
  725. my ($args) = @_;
  726. my ($uid, $how) = split(/ /, $args, 2);
  727. my @data;
  728. my $size;
  729. fix_imap_params($uid, $how);
  730. logmsg "FETCH_imap got $args\n";
  731. if($selected eq "verifiedserver") {
  732. # this is the secret command that verifies that this actually is
  733. # the curl test server
  734. my $response = "WE ROOLZ: $$\r\n";
  735. if($verbose) {
  736. print STDERR "FTPD: We returned proof we are the test server\n";
  737. }
  738. $data[0] = $response;
  739. logmsg "return proof we are we\n";
  740. }
  741. else {
  742. logmsg "retrieve a mail\n";
  743. my $testno = $selected;
  744. $testno =~ s/^([^0-9]*)//;
  745. my $testpart = "";
  746. if ($testno > 10000) {
  747. $testpart = $testno % 10000;
  748. $testno = int($testno / 10000);
  749. }
  750. # send mail content
  751. loadtest("$srcdir/data/test$testno");
  752. @data = getpart("reply", "data$testpart");
  753. }
  754. for (@data) {
  755. $size += length($_);
  756. }
  757. sendcontrol "* $uid FETCH ($how {$size}\r\n";
  758. for my $d (@data) {
  759. sendcontrol $d;
  760. }
  761. sendcontrol ")\r\n";
  762. sendcontrol "$cmdid OK FETCH completed\r\n";
  763. return 0;
  764. }
  765. sub APPEND_imap {
  766. my ($args) = @_;
  767. logmsg "APPEND_imap got $args\r\n";
  768. $args =~ /^([^ ]+) [^{]*\{(\d+)\}$/;
  769. my ($folder, $size) = ($1, $2);
  770. fix_imap_params($folder);
  771. sendcontrol "+ Ready for literal data\r\n";
  772. my $testno = $folder;
  773. my $filename = "log/upload.$testno";
  774. logmsg "Store test number $testno in $filename\n";
  775. open(FILE, ">$filename") ||
  776. return 0; # failed to open output
  777. my $received = 0;
  778. my $line;
  779. while(5 == (sysread \*SFREAD, $line, 5)) {
  780. if($line eq "DATA\n") {
  781. sysread \*SFREAD, $line, 5;
  782. my $chunksize = 0;
  783. if($line =~ /^([0-9a-fA-F]{4})\n/) {
  784. $chunksize = hex($1);
  785. }
  786. read_mainsockf(\$line, $chunksize);
  787. my $left = $size - $received;
  788. my $datasize = ($left > $chunksize) ? $chunksize : $left;
  789. if($datasize > 0) {
  790. logmsg "> Appending $datasize bytes to file\n";
  791. print FILE substr($line, 0, $datasize) if(!$nosave);
  792. $line = substr($line, $datasize);
  793. $received += $datasize;
  794. if($received == $size) {
  795. logmsg "Received all data, waiting for final CRLF.\n";
  796. }
  797. }
  798. if($received == $size && $line eq "\r\n") {
  799. last;
  800. }
  801. }
  802. elsif($line eq "DISC\n") {
  803. logmsg "Unexpected disconnect!\n";
  804. last;
  805. }
  806. else {
  807. logmsg "No support for: $line";
  808. last;
  809. }
  810. }
  811. if($nosave) {
  812. print FILE "$size bytes would've been stored here\n";
  813. }
  814. close(FILE);
  815. logmsg "received $size bytes upload\n";
  816. sendcontrol "$cmdid OK APPEND completed\r\n";
  817. return 0;
  818. }
  819. sub STORE_imap {
  820. my ($args) = @_;
  821. my ($uid, $what) = split(/ /, $args, 2);
  822. fix_imap_params($uid);
  823. logmsg "STORE_imap got $args\n";
  824. sendcontrol "* $uid FETCH (FLAGS (\\Seen \\Deleted))\r\n";
  825. sendcontrol "$cmdid OK STORE completed\r\n";
  826. return 0;
  827. }
  828. sub LIST_imap {
  829. my ($args) = @_;
  830. my ($reference, $mailbox) = split(/ /, $args, 2);
  831. my @data;
  832. fix_imap_params($reference, $mailbox);
  833. logmsg "LIST_imap got $args\n";
  834. if ($reference eq "verifiedserver") {
  835. # this is the secret command that verifies that this actually is
  836. # the curl test server
  837. @data = ("* LIST () \"/\" \"WE ROOLZ: $$\"\r\n");
  838. if($verbose) {
  839. print STDERR "FTPD: We returned proof we are the test server\n";
  840. }
  841. logmsg "return proof we are we\n";
  842. }
  843. else {
  844. my $testno = $reference;
  845. $testno =~ s/^([^0-9]*)//;
  846. my $testpart = "";
  847. if ($testno > 10000) {
  848. $testpart = $testno % 10000;
  849. $testno = int($testno / 10000);
  850. }
  851. loadtest("$srcdir/data/test$testno");
  852. @data = getpart("reply", "data$testpart");
  853. }
  854. for my $d (@data) {
  855. sendcontrol $d;
  856. }
  857. sendcontrol "$cmdid OK LIST Completed\r\n";
  858. return 0;
  859. }
  860. sub EXAMINE_imap {
  861. my ($testno) = @_;
  862. fix_imap_params($testno);
  863. logmsg "EXAMINE_imap got test $testno\n";
  864. # Example from RFC 3501, 6.3.2. EXAMINE Command
  865. sendcontrol "* 17 EXISTS\r\n";
  866. sendcontrol "* 2 RECENT\r\n";
  867. sendcontrol "* OK [UNSEEN 8] Message 8 is first unseen\r\n";
  868. sendcontrol "* OK [UIDVALIDITY 3857529045] UIDs valid\r\n";
  869. sendcontrol "* OK [UIDNEXT 4392] Predicted next UID\r\n";
  870. sendcontrol "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n";
  871. sendcontrol "* OK [PERMANENTFLAGS ()] No permanent flags permitted\r\n";
  872. sendcontrol "$cmdid OK [READ-ONLY] EXAMINE completed\r\n";
  873. return 0;
  874. }
  875. sub STATUS_imap {
  876. my ($testno) = @_;
  877. fix_imap_params($testno);
  878. logmsg "STATUS_imap got test $testno\n";
  879. $testno =~ s/[^0-9]//g;
  880. my $testpart = "";
  881. if ($testno > 10000) {
  882. $testpart = $testno % 10000;
  883. $testno = int($testno / 10000);
  884. }
  885. loadtest("$srcdir/data/test$testno");
  886. my @data = getpart("reply", "data$testpart");
  887. for my $d (@data) {
  888. sendcontrol $d;
  889. }
  890. sendcontrol "$cmdid OK STATUS completed\r\n";
  891. return 0;
  892. }
  893. sub LOGOUT_imap {
  894. sendcontrol "* BYE cURL IMAP server signing off\r\n";
  895. sendcontrol "$cmdid OK LOGOUT completed\r\n";
  896. return 0;
  897. }
  898. ################
  899. ################ POP3 commands
  900. ################
  901. sub CAPA_pop3 {
  902. my ($testno) = @_;
  903. my @data = ();
  904. if(!$support_capa) {
  905. push @data, "-ERR Unsupported command: 'CAPA'\r\n";
  906. }
  907. else {
  908. push @data, "+OK List of capabilities follows\r\n";
  909. push @data, "USER\r\n";
  910. if($support_auth) {
  911. push @data, "SASL UNKNOWN\r\n";
  912. }
  913. push @data, "IMPLEMENTATION POP3 pingpong test server\r\n";
  914. push @data, ".\r\n";
  915. }
  916. for my $d (@data) {
  917. sendcontrol $d;
  918. }
  919. return 0;
  920. }
  921. sub AUTH_pop3 {
  922. my ($testno) = @_;
  923. my @data = ();
  924. if(!$support_auth) {
  925. push @data, "-ERR Unsupported command: 'AUTH'\r\n";
  926. }
  927. else {
  928. push @data, "+OK List of supported mechanisms follows\r\n";
  929. push @data, "UNKNOWN\r\n";
  930. push @data, ".\r\n";
  931. }
  932. for my $d (@data) {
  933. sendcontrol $d;
  934. }
  935. return 0;
  936. }
  937. sub RETR_pop3 {
  938. my ($testno) = @_;
  939. my @data;
  940. if($testno =~ /^verifiedserver$/) {
  941. # this is the secret command that verifies that this actually is
  942. # the curl test server
  943. my $response = "WE ROOLZ: $$\r\n";
  944. if($verbose) {
  945. print STDERR "FTPD: We returned proof we are the test server\n";
  946. }
  947. $data[0] = $response;
  948. logmsg "return proof we are we\n";
  949. }
  950. else {
  951. logmsg "retrieve a mail\n";
  952. $testno =~ s/^([^0-9]*)//;
  953. my $testpart = "";
  954. if ($testno > 10000) {
  955. $testpart = $testno % 10000;
  956. $testno = int($testno / 10000);
  957. }
  958. # send mail content
  959. loadtest("$srcdir/data/test$testno");
  960. @data = getpart("reply", "data$testpart");
  961. }
  962. sendcontrol "+OK Mail transfer starts\r\n";
  963. for my $d (@data) {
  964. sendcontrol $d;
  965. }
  966. # end with the magic 3-byte end of mail marker, assumes that the
  967. # mail body ends with a CRLF!
  968. sendcontrol ".\r\n";
  969. return 0;
  970. }
  971. sub LIST_pop3 {
  972. # this is a built-in fake-message list
  973. my @pop3list=(
  974. "1 100\r\n",
  975. "2 4294967400\r\n", # > 4 GB
  976. "4 200\r\n", # Note that message 3 is a simulated "deleted" message
  977. );
  978. logmsg "retrieve a message list\n";
  979. sendcontrol "+OK Listing starts\r\n";
  980. for my $d (@pop3list) {
  981. sendcontrol $d;
  982. }
  983. # end with the magic 3-byte end of listing marker
  984. sendcontrol ".\r\n";
  985. return 0;
  986. }
  987. ################
  988. ################ FTP commands
  989. ################
  990. my $rest=0;
  991. sub REST_ftp {
  992. $rest = $_[0];
  993. logmsg "Set REST position to $rest\n"
  994. }
  995. sub switch_directory_goto {
  996. my $target_dir = $_;
  997. if(!$ftptargetdir) {
  998. $ftptargetdir = "/";
  999. }
  1000. if($target_dir eq "") {
  1001. $ftptargetdir = "/";
  1002. }
  1003. elsif($target_dir eq "..") {
  1004. if($ftptargetdir eq "/") {
  1005. $ftptargetdir = "/";
  1006. }
  1007. else {
  1008. $ftptargetdir =~ s/[[:alnum:]]+\/$//;
  1009. }
  1010. }
  1011. else {
  1012. $ftptargetdir .= $target_dir . "/";
  1013. }
  1014. }
  1015. sub switch_directory {
  1016. my $target_dir = $_[0];
  1017. if($target_dir eq "/") {
  1018. $ftptargetdir = "/";
  1019. }
  1020. else {
  1021. my @dirs = split("/", $target_dir);
  1022. for(@dirs) {
  1023. switch_directory_goto($_);
  1024. }
  1025. }
  1026. }
  1027. sub CWD_ftp {
  1028. my ($folder, $fullcommand) = $_[0];
  1029. switch_directory($folder);
  1030. if($ftptargetdir =~ /^\/fully_simulated/) {
  1031. $ftplistparserstate = "enabled";
  1032. }
  1033. else {
  1034. undef $ftplistparserstate;
  1035. }
  1036. }
  1037. sub PWD_ftp {
  1038. my $mydir;
  1039. $mydir = $ftptargetdir ? $ftptargetdir : "/";
  1040. if($mydir ne "/") {
  1041. $mydir =~ s/\/$//;
  1042. }
  1043. sendcontrol "257 \"$mydir\" is current directory\r\n";
  1044. }
  1045. sub LIST_ftp {
  1046. # print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
  1047. # this is a built-in fake-dir ;-)
  1048. my @ftpdir=("total 20\r\n",
  1049. "drwxr-xr-x 8 98 98 512 Oct 22 13:06 .\r\n",
  1050. "drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..\r\n",
  1051. "drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT\r\n",
  1052. "-r--r--r-- 1 0 1 35 Jul 16 1996 README\r\n",
  1053. "lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin\r\n",
  1054. "dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev\r\n",
  1055. "drwxrwxrwx 2 98 98 512 May 29 16:04 download.html\r\n",
  1056. "dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc\r\n",
  1057. "drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\r\n",
  1058. "dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n");
  1059. if($datasockf_conn eq 'no') {
  1060. if($nodataconn425) {
  1061. sendcontrol "150 Opening data connection\r\n";
  1062. sendcontrol "425 Can't open data connection\r\n";
  1063. }
  1064. elsif($nodataconn421) {
  1065. sendcontrol "150 Opening data connection\r\n";
  1066. sendcontrol "421 Connection timed out\r\n";
  1067. }
  1068. elsif($nodataconn150) {
  1069. sendcontrol "150 Opening data connection\r\n";
  1070. # client shall timeout
  1071. }
  1072. else {
  1073. # client shall timeout
  1074. }
  1075. return 0;
  1076. }
  1077. if($ftplistparserstate) {
  1078. @ftpdir = ftp_contentlist($ftptargetdir);
  1079. }
  1080. logmsg "pass LIST data on data connection\n";
  1081. for(@ftpdir) {
  1082. senddata $_;
  1083. }
  1084. close_dataconn(0);
  1085. sendcontrol "226 ASCII transfer complete\r\n";
  1086. return 0;
  1087. }
  1088. sub NLST_ftp {
  1089. my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README");
  1090. if($datasockf_conn eq 'no') {
  1091. if($nodataconn425) {
  1092. sendcontrol "150 Opening data connection\r\n";
  1093. sendcontrol "425 Can't open data connection\r\n";
  1094. }
  1095. elsif($nodataconn421) {
  1096. sendcontrol "150 Opening data connection\r\n";
  1097. sendcontrol "421 Connection timed out\r\n";
  1098. }
  1099. elsif($nodataconn150) {
  1100. sendcontrol "150 Opening data connection\r\n";
  1101. # client shall timeout
  1102. }
  1103. else {
  1104. # client shall timeout
  1105. }
  1106. return 0;
  1107. }
  1108. logmsg "pass NLST data on data connection\n";
  1109. for(@ftpdir) {
  1110. senddata "$_\r\n";
  1111. }
  1112. close_dataconn(0);
  1113. sendcontrol "226 ASCII transfer complete\r\n";
  1114. return 0;
  1115. }
  1116. sub MDTM_ftp {
  1117. my $testno = $_[0];
  1118. my $testpart = "";
  1119. if ($testno > 10000) {
  1120. $testpart = $testno % 10000;
  1121. $testno = int($testno / 10000);
  1122. }
  1123. loadtest("$srcdir/data/test$testno");
  1124. my @data = getpart("reply", "mdtm");
  1125. my $reply = $data[0];
  1126. chomp $reply if($reply);
  1127. if($reply && ($reply =~ /^[+-]?\d+$/) && ($reply < 0)) {
  1128. sendcontrol "550 $testno: no such file.\r\n";
  1129. }
  1130. elsif($reply) {
  1131. sendcontrol "$reply\r\n";
  1132. }
  1133. else {
  1134. sendcontrol "500 MDTM: no such command.\r\n";
  1135. }
  1136. return 0;
  1137. }
  1138. sub SIZE_ftp {
  1139. my $testno = $_[0];
  1140. if($ftplistparserstate) {
  1141. my $size = wildcard_filesize($ftptargetdir, $testno);
  1142. if($size == -1) {
  1143. sendcontrol "550 $testno: No such file or directory.\r\n";
  1144. }
  1145. else {
  1146. sendcontrol "213 $size\r\n";
  1147. }
  1148. return 0;
  1149. }
  1150. if($testno =~ /^verifiedserver$/) {
  1151. my $response = "WE ROOLZ: $$\r\n";
  1152. my $size = length($response);
  1153. sendcontrol "213 $size\r\n";
  1154. return 0;
  1155. }
  1156. if($testno =~ /(\d+)\/?$/) {
  1157. $testno = $1;
  1158. }
  1159. else {
  1160. print STDERR "SIZE_ftp: invalid test number: $testno\n";
  1161. return 1;
  1162. }
  1163. my $testpart = "";
  1164. if($testno > 10000) {
  1165. $testpart = $testno % 10000;
  1166. $testno = int($testno / 10000);
  1167. }
  1168. loadtest("$srcdir/data/test$testno");
  1169. my @data = getpart("reply", "size");
  1170. my $size = $data[0];
  1171. if($size) {
  1172. if($size > -1) {
  1173. sendcontrol "213 $size\r\n";
  1174. }
  1175. else {
  1176. sendcontrol "550 $testno: No such file or directory.\r\n";
  1177. }
  1178. }
  1179. else {
  1180. $size=0;
  1181. @data = getpart("reply", "data$testpart");
  1182. for(@data) {
  1183. $size += length($_);
  1184. }
  1185. if($size) {
  1186. sendcontrol "213 $size\r\n";
  1187. }
  1188. else {
  1189. sendcontrol "550 $testno: No such file or directory.\r\n";
  1190. }
  1191. }
  1192. return 0;
  1193. }
  1194. sub RETR_ftp {
  1195. my ($testno) = @_;
  1196. if($datasockf_conn eq 'no') {
  1197. if($nodataconn425) {
  1198. sendcontrol "150 Opening data connection\r\n";
  1199. sendcontrol "425 Can't open data connection\r\n";
  1200. }
  1201. elsif($nodataconn421) {
  1202. sendcontrol "150 Opening data connection\r\n";
  1203. sendcontrol "421 Connection timed out\r\n";
  1204. }
  1205. elsif($nodataconn150) {
  1206. sendcontrol "150 Opening data connection\r\n";
  1207. # client shall timeout
  1208. }
  1209. else {
  1210. # client shall timeout
  1211. }
  1212. return 0;
  1213. }
  1214. if($ftplistparserstate) {
  1215. my @content = wildcard_getfile($ftptargetdir, $testno);
  1216. if($content[0] == -1) {
  1217. #file not found
  1218. }
  1219. else {
  1220. my $size = length $content[1];
  1221. sendcontrol "150 Binary data connection for $testno ($size bytes).\r\n",
  1222. senddata $content[1];
  1223. close_dataconn(0);
  1224. sendcontrol "226 File transfer complete\r\n";
  1225. }
  1226. return 0;
  1227. }
  1228. if($testno =~ /^verifiedserver$/) {
  1229. # this is the secret command that verifies that this actually is
  1230. # the curl test server
  1231. my $response = "WE ROOLZ: $$\r\n";
  1232. my $len = length($response);
  1233. sendcontrol "150 Binary junk ($len bytes).\r\n";
  1234. senddata "WE ROOLZ: $$\r\n";
  1235. close_dataconn(0);
  1236. sendcontrol "226 File transfer complete\r\n";
  1237. if($verbose) {
  1238. print STDERR "FTPD: We returned proof we are the test server\n";
  1239. }
  1240. return 0;
  1241. }
  1242. $testno =~ s/^([^0-9]*)//;
  1243. my $testpart = "";
  1244. if ($testno > 10000) {
  1245. $testpart = $testno % 10000;
  1246. $testno = int($testno / 10000);
  1247. }
  1248. loadtest("$srcdir/data/test$testno");
  1249. my @data = getpart("reply", "data$testpart");
  1250. my $size=0;
  1251. for(@data) {
  1252. $size += length($_);
  1253. }
  1254. my %hash = getpartattr("reply", "data$testpart");
  1255. if($size || $hash{'sendzero'}) {
  1256. if($rest) {
  1257. # move read pointer forward
  1258. $size -= $rest;
  1259. logmsg "REST $rest was removed from size, makes $size left\n";
  1260. $rest = 0; # reset REST offset again
  1261. }
  1262. if($retrweirdo) {
  1263. sendcontrol "150 Binary data connection for $testno () ($size bytes).\r\n",
  1264. "226 File transfer complete\r\n";
  1265. for(@data) {
  1266. my $send = $_;
  1267. senddata $send;
  1268. }
  1269. close_dataconn(0);
  1270. $retrweirdo=0; # switch off the weirdo again!
  1271. }
  1272. else {
  1273. my $sz = "($size bytes)";
  1274. if($retrnosize) {
  1275. $sz = "size?";
  1276. }
  1277. sendcontrol "150 Binary data connection for $testno () $sz.\r\n";
  1278. for(@data) {
  1279. my $send = $_;
  1280. senddata $send;
  1281. }
  1282. close_dataconn(0);
  1283. sendcontrol "226 File transfer complete\r\n";
  1284. }
  1285. }
  1286. else {
  1287. sendcontrol "550 $testno: No such file or directory.\r\n";
  1288. }
  1289. return 0;
  1290. }
  1291. sub STOR_ftp {
  1292. my $testno=$_[0];
  1293. my $filename = "log/upload.$testno";
  1294. if($datasockf_conn eq 'no') {
  1295. if($nodataconn425) {
  1296. sendcontrol "150 Opening data connection\r\n";
  1297. sendcontrol "425 Can't open data connection\r\n";
  1298. }
  1299. elsif($nodataconn421) {
  1300. sendcontrol "150 Opening data connection\r\n";
  1301. sendcontrol "421 Connection timed out\r\n";
  1302. }
  1303. elsif($nodataconn150) {
  1304. sendcontrol "150 Opening data connection\r\n";
  1305. # client shall timeout
  1306. }
  1307. else {
  1308. # client shall timeout
  1309. }
  1310. return 0;
  1311. }
  1312. logmsg "STOR test number $testno in $filename\n";
  1313. sendcontrol "125 Gimme gimme gimme!\r\n";
  1314. open(FILE, ">$filename") ||
  1315. return 0; # failed to open output
  1316. my $line;
  1317. my $ulsize=0;
  1318. my $disc=0;
  1319. while (5 == (sysread DREAD, $line, 5)) {
  1320. if($line eq "DATA\n") {
  1321. my $i;
  1322. sysread DREAD, $i, 5;
  1323. my $size = 0;
  1324. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  1325. $size = hex($1);
  1326. }
  1327. read_datasockf(\$line, $size);
  1328. #print STDERR " GOT: $size bytes\n";
  1329. $ulsize += $size;
  1330. print FILE $line if(!$nosave);
  1331. logmsg "> Appending $size bytes to file\n";
  1332. }
  1333. elsif($line eq "DISC\n") {
  1334. # disconnect!
  1335. $disc=1;
  1336. last;
  1337. }
  1338. else {
  1339. logmsg "No support for: $line";
  1340. last;
  1341. }
  1342. }
  1343. if($nosave) {
  1344. print FILE "$ulsize bytes would've been stored here\n";
  1345. }
  1346. close(FILE);
  1347. close_dataconn($disc);
  1348. logmsg "received $ulsize bytes upload\n";
  1349. sendcontrol "226 File transfer complete\r\n";
  1350. return 0;
  1351. }
  1352. sub PASV_ftp {
  1353. my ($arg, $cmd)=@_;
  1354. my $pasvport;
  1355. my $bindonly = ($nodataconn) ? '--bindonly' : '';
  1356. # kill previous data connection sockfilt when alive
  1357. if($datasockf_runs eq 'yes') {
  1358. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1359. logmsg "DATA sockfilt for $datasockf_mode data channel killed\n";
  1360. }
  1361. datasockf_state('STOPPED');
  1362. logmsg "====> Passive DATA channel requested by client\n";
  1363. logmsg "DATA sockfilt for passive data channel starting...\n";
  1364. # We fire up a new sockfilt to do the data transfer for us.
  1365. my $datasockfcmd = "./server/sockfilt " .
  1366. "--ipv$ipvnum $bindonly --port 0 " .
  1367. "--pidfile \"$datasockf_pidfile\" " .
  1368. "--logfile \"$datasockf_logfile\"";
  1369. $slavepid = open2(\*DREAD, \*DWRITE, $datasockfcmd);
  1370. if($nodataconn) {
  1371. datasockf_state('PASSIVE_NODATACONN');
  1372. }
  1373. else {
  1374. datasockf_state('PASSIVE');
  1375. }
  1376. print STDERR "$datasockfcmd\n" if($verbose);
  1377. print DWRITE "PING\n";
  1378. my $pong;
  1379. sysread_or_die(\*DREAD, \$pong, 5);
  1380. if($pong =~ /^FAIL/) {
  1381. logmsg "DATA sockfilt said: FAIL\n";
  1382. logmsg "DATA sockfilt for passive data channel failed\n";
  1383. logmsg "DATA sockfilt not running\n";
  1384. datasockf_state('STOPPED');
  1385. sendcontrol "500 no free ports!\r\n";
  1386. return;
  1387. }
  1388. elsif($pong !~ /^PONG/) {
  1389. logmsg "DATA sockfilt unexpected response: $pong\n";
  1390. logmsg "DATA sockfilt for passive data channel failed\n";
  1391. logmsg "DATA sockfilt killed now\n";
  1392. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1393. logmsg "DATA sockfilt not running\n";
  1394. datasockf_state('STOPPED');
  1395. sendcontrol "500 no free ports!\r\n";
  1396. return;
  1397. }
  1398. logmsg "DATA sockfilt for passive data channel started (pid $slavepid)\n";
  1399. # Find out on what port we listen on or have bound
  1400. my $i;
  1401. print DWRITE "PORT\n";
  1402. # READ the response code
  1403. sysread_or_die(\*DREAD, \$i, 5);
  1404. # READ the response size
  1405. sysread_or_die(\*DREAD, \$i, 5);
  1406. my $size = 0;
  1407. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  1408. $size = hex($1);
  1409. }
  1410. # READ the response data
  1411. read_datasockf(\$i, $size);
  1412. # The data is in the format
  1413. # IPvX/NNN
  1414. if($i =~ /IPv(\d)\/(\d+)/) {
  1415. # FIX: deal with IP protocol version
  1416. $pasvport = $2;
  1417. }
  1418. if(!$pasvport) {
  1419. logmsg "DATA sockfilt unknown listener port\n";
  1420. logmsg "DATA sockfilt for passive data channel failed\n";
  1421. logmsg "DATA sockfilt killed now\n";
  1422. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1423. logmsg "DATA sockfilt not running\n";
  1424. datasockf_state('STOPPED');
  1425. sendcontrol "500 no free ports!\r\n";
  1426. return;
  1427. }
  1428. if($nodataconn) {
  1429. my $str = nodataconn_str();
  1430. logmsg "DATA sockfilt for passive data channel ($str) bound on port ".
  1431. "$pasvport\n";
  1432. }
  1433. else {
  1434. logmsg "DATA sockfilt for passive data channel listens on port ".
  1435. "$pasvport\n";
  1436. }
  1437. if($cmd ne "EPSV") {
  1438. # PASV reply
  1439. my $p=$listenaddr;
  1440. $p =~ s/\./,/g;
  1441. if($pasvbadip) {
  1442. $p="1,2,3,4";
  1443. }
  1444. sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\n",
  1445. int($pasvport/256), int($pasvport%256));
  1446. }
  1447. else {
  1448. # EPSV reply
  1449. sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
  1450. }
  1451. logmsg "Client has been notified that DATA conn ".
  1452. "will be accepted on port $pasvport\n";
  1453. if($nodataconn) {
  1454. my $str = nodataconn_str();
  1455. logmsg "====> Client fooled ($str)\n";
  1456. return;
  1457. }
  1458. eval {
  1459. local $SIG{ALRM} = sub { die "alarm\n" };
  1460. # assume swift operations unless explicitly slow
  1461. alarm ($datadelay?20:10);
  1462. # Wait for 'CNCT'
  1463. my $input;
  1464. # FIX: Monitor ctrl conn for disconnect
  1465. while(sysread(DREAD, $input, 5)) {
  1466. if($input !~ /^CNCT/) {
  1467. # we wait for a connected client
  1468. logmsg "Odd, we got $input from client\n";
  1469. next;
  1470. }
  1471. logmsg "Client connects to port $pasvport\n";
  1472. last;
  1473. }
  1474. alarm 0;
  1475. };
  1476. if ($@) {
  1477. # timed out
  1478. logmsg "$srvrname server timed out awaiting data connection ".
  1479. "on port $pasvport\n";
  1480. logmsg "accept failed or connection not even attempted\n";
  1481. logmsg "DATA sockfilt killed now\n";
  1482. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1483. logmsg "DATA sockfilt not running\n";
  1484. datasockf_state('STOPPED');
  1485. return;
  1486. }
  1487. else {
  1488. logmsg "====> Client established passive DATA connection ".
  1489. "on port $pasvport\n";
  1490. }
  1491. return;
  1492. }
  1493. #
  1494. # Support both PORT and EPRT here.
  1495. #
  1496. sub PORT_ftp {
  1497. my ($arg, $cmd) = @_;
  1498. my $port;
  1499. my $addr;
  1500. # kill previous data connection sockfilt when alive
  1501. if($datasockf_runs eq 'yes') {
  1502. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1503. logmsg "DATA sockfilt for $datasockf_mode data channel killed\n";
  1504. }
  1505. datasockf_state('STOPPED');
  1506. logmsg "====> Active DATA channel requested by client\n";
  1507. # We always ignore the given IP and use localhost.
  1508. if($cmd eq "PORT") {
  1509. if($arg !~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/) {
  1510. logmsg "DATA sockfilt for active data channel not started ".
  1511. "(bad PORT-line: $arg)\n";
  1512. sendcontrol "500 silly you, go away\r\n";
  1513. return;
  1514. }
  1515. $port = ($5<<8)+$6;
  1516. $addr = "$1.$2.$3.$4";
  1517. }
  1518. # EPRT |2|::1|49706|
  1519. elsif($cmd eq "EPRT") {
  1520. if($arg !~ /(\d+)\|([^\|]+)\|(\d+)/) {
  1521. logmsg "DATA sockfilt for active data channel not started ".
  1522. "(bad EPRT-line: $arg)\n";
  1523. sendcontrol "500 silly you, go away\r\n";
  1524. return;
  1525. }
  1526. sendcontrol "200 Thanks for dropping by. We contact you later\r\n";
  1527. $port = $3;
  1528. $addr = $2;
  1529. }
  1530. else {
  1531. logmsg "DATA sockfilt for active data channel not started ".
  1532. "(invalid command: $cmd)\n";
  1533. sendcontrol "500 we don't like $cmd now\r\n";
  1534. return;
  1535. }
  1536. if(!$port || $port > 65535) {
  1537. logmsg "DATA sockfilt for active data channel not started ".
  1538. "(illegal PORT number: $port)\n";
  1539. return;
  1540. }
  1541. if($nodataconn) {
  1542. my $str = nodataconn_str();
  1543. logmsg "DATA sockfilt for active data channel not started ($str)\n";
  1544. datasockf_state('ACTIVE_NODATACONN');
  1545. logmsg "====> Active DATA channel not established\n";
  1546. return;
  1547. }
  1548. logmsg "DATA sockfilt for active data channel starting...\n";
  1549. # We fire up a new sockfilt to do the data transfer for us.
  1550. my $datasockfcmd = "./server/sockfilt " .
  1551. "--ipv$ipvnum --connect $port --addr \"$addr\" " .
  1552. "--pidfile \"$datasockf_pidfile\" " .
  1553. "--logfile \"$datasockf_logfile\"";
  1554. $slavepid = open2(\*DREAD, \*DWRITE, $datasockfcmd);
  1555. datasockf_state('ACTIVE');
  1556. print STDERR "$datasockfcmd\n" if($verbose);
  1557. print DWRITE "PING\n";
  1558. my $pong;
  1559. sysread_or_die(\*DREAD, \$pong, 5);
  1560. if($pong =~ /^FAIL/) {
  1561. logmsg "DATA sockfilt said: FAIL\n";
  1562. logmsg "DATA sockfilt for active data channel failed\n";
  1563. logmsg "DATA sockfilt not running\n";
  1564. datasockf_state('STOPPED');
  1565. # client shall timeout awaiting connection from server
  1566. return;
  1567. }
  1568. elsif($pong !~ /^PONG/) {
  1569. logmsg "DATA sockfilt unexpected response: $pong\n";
  1570. logmsg "DATA sockfilt for active data channel failed\n";
  1571. logmsg "DATA sockfilt killed now\n";
  1572. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1573. logmsg "DATA sockfilt not running\n";
  1574. datasockf_state('STOPPED');
  1575. # client shall timeout awaiting connection from server
  1576. return;
  1577. }
  1578. logmsg "DATA sockfilt for active data channel started (pid $slavepid)\n";
  1579. logmsg "====> Active DATA channel connected to client port $port\n";
  1580. return;
  1581. }
  1582. #**********************************************************************
  1583. # datasockf_state is used to change variables that keep state info
  1584. # relative to the FTP secondary or data sockfilt process as soon as
  1585. # one of the five possible stable states is reached. Variables that
  1586. # are modified by this sub may be checked independently but should
  1587. # not be changed except by calling this sub.
  1588. #
  1589. sub datasockf_state {
  1590. my $state = $_[0];
  1591. if($state eq 'STOPPED') {
  1592. # Data sockfilter initial state, not running,
  1593. # not connected and not used.
  1594. $datasockf_state = $state;
  1595. $datasockf_mode = 'none';
  1596. $datasockf_runs = 'no';
  1597. $datasockf_conn = 'no';
  1598. }
  1599. elsif($state eq 'PASSIVE') {
  1600. # Data sockfilter accepted connection from client.
  1601. $datasockf_state = $state;
  1602. $datasockf_mode = 'passive';
  1603. $datasockf_runs = 'yes';
  1604. $datasockf_conn = 'yes';
  1605. }
  1606. elsif($state eq 'ACTIVE') {
  1607. # Data sockfilter has connected to client.
  1608. $datasockf_state = $state;
  1609. $datasockf_mode = 'active';
  1610. $datasockf_runs = 'yes';
  1611. $datasockf_conn = 'yes';
  1612. }
  1613. elsif($state eq 'PASSIVE_NODATACONN') {
  1614. # Data sockfilter bound port without listening,
  1615. # client won't be able to establish data connection.
  1616. $datasockf_state = $state;
  1617. $datasockf_mode = 'passive';
  1618. $datasockf_runs = 'yes';
  1619. $datasockf_conn = 'no';
  1620. }
  1621. elsif($state eq 'ACTIVE_NODATACONN') {
  1622. # Data sockfilter does not even run,
  1623. # client awaits data connection from server in vain.
  1624. $datasockf_state = $state;
  1625. $datasockf_mode = 'active';
  1626. $datasockf_runs = 'no';
  1627. $datasockf_conn = 'no';
  1628. }
  1629. else {
  1630. die "Internal error. Unknown datasockf state: $state!";
  1631. }
  1632. }
  1633. #**********************************************************************
  1634. # nodataconn_str returns string of efective nodataconn command. Notice
  1635. # that $nodataconn may be set alone or in addition to a $nodataconnXXX.
  1636. #
  1637. sub nodataconn_str {
  1638. my $str;
  1639. # order matters
  1640. $str = 'NODATACONN' if($nodataconn);
  1641. $str = 'NODATACONN425' if($nodataconn425);
  1642. $str = 'NODATACONN421' if($nodataconn421);
  1643. $str = 'NODATACONN150' if($nodataconn150);
  1644. return "$str";
  1645. }
  1646. #**********************************************************************
  1647. # customize configures test server operation for each curl test, reading
  1648. # configuration commands/parameters from server commands file each time
  1649. # a new client control connection is established with the test server.
  1650. # On success returns 1, otherwise zero.
  1651. #
  1652. sub customize {
  1653. $ctrldelay = 0; # default is no throttling of the ctrl stream
  1654. $datadelay = 0; # default is no throttling of the data stream
  1655. $retrweirdo = 0; # default is no use of RETRWEIRDO
  1656. $retrnosize = 0; # default is no use of RETRNOSIZE
  1657. $pasvbadip = 0; # default is no use of PASVBADIP
  1658. $nosave = 0; # default is to actually save uploaded data to file
  1659. $nodataconn = 0; # default is to establish or accept data channel
  1660. $nodataconn425 = 0; # default is to not send 425 without data channel
  1661. $nodataconn421 = 0; # default is to not send 421 without data channel
  1662. $nodataconn150 = 0; # default is to not send 150 without data channel
  1663. $support_capa = 0; # default is to not support capability command
  1664. $support_auth = 0; # default is to not support authentication command
  1665. %customreply = (); #
  1666. %customcount = (); #
  1667. %delayreply = (); #
  1668. open(CUSTOM, "<log/ftpserver.cmd") ||
  1669. return 1;
  1670. logmsg "FTPD: Getting commands from log/ftpserver.cmd\n";
  1671. while(<CUSTOM>) {
  1672. if($_ =~ /REPLY ([A-Za-z0-9+\/=]+) (.*)/) {
  1673. $customreply{$1}=eval "qq{$2}";
  1674. logmsg "FTPD: set custom reply for $1\n";
  1675. }
  1676. elsif($_ =~ /COUNT ([A-Z]+) (.*)/) {
  1677. # we blank the customreply for this command when having
  1678. # been used this number of times
  1679. $customcount{$1}=$2;
  1680. logmsg "FTPD: blank custom reply for $1 after $2 uses\n";
  1681. }
  1682. elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) {
  1683. $delayreply{$1}=$2;
  1684. logmsg "FTPD: delay reply for $1 with $2 seconds\n";
  1685. }
  1686. elsif($_ =~ /SLOWDOWN/) {
  1687. $ctrldelay=1;
  1688. $datadelay=1;
  1689. logmsg "FTPD: send response with 0.01 sec delay between each byte\n";
  1690. }
  1691. elsif($_ =~ /RETRWEIRDO/) {
  1692. logmsg "FTPD: instructed to use RETRWEIRDO\n";
  1693. $retrweirdo=1;
  1694. }
  1695. elsif($_ =~ /RETRNOSIZE/) {
  1696. logmsg "FTPD: instructed to use RETRNOSIZE\n";
  1697. $retrnosize=1;
  1698. }
  1699. elsif($_ =~ /PASVBADIP/) {
  1700. logmsg "FTPD: instructed to use PASVBADIP\n";
  1701. $pasvbadip=1;
  1702. }
  1703. elsif($_ =~ /NODATACONN425/) {
  1704. # applies to both active and passive FTP modes
  1705. logmsg "FTPD: instructed to use NODATACONN425\n";
  1706. $nodataconn425=1;
  1707. $nodataconn=1;
  1708. }
  1709. elsif($_ =~ /NODATACONN421/) {
  1710. # applies to both active and passive FTP modes
  1711. logmsg "FTPD: instructed to use NODATACONN421\n";
  1712. $nodataconn421=1;
  1713. $nodataconn=1;
  1714. }
  1715. elsif($_ =~ /NODATACONN150/) {
  1716. # applies to both active and passive FTP modes
  1717. logmsg "FTPD: instructed to use NODATACONN150\n";
  1718. $nodataconn150=1;
  1719. $nodataconn=1;
  1720. }
  1721. elsif($_ =~ /NODATACONN/) {
  1722. # applies to both active and passive FTP modes
  1723. logmsg "FTPD: instructed to use NODATACONN\n";
  1724. $nodataconn=1;
  1725. }
  1726. elsif($_ =~ /SUPPORTCAPA/) {
  1727. logmsg "FTPD: instructed to support CAPABILITY command\n";
  1728. $support_capa=1;
  1729. }
  1730. elsif($_ =~ /SUPPORTAUTH/) {
  1731. logmsg "FTPD: instructed to support AUTHENTICATION command\n";
  1732. $support_auth=1;
  1733. }
  1734. elsif($_ =~ /NOSAVE/) {
  1735. # don't actually store the file we upload - to be used when
  1736. # uploading insanely huge amounts
  1737. $nosave = 1;
  1738. logmsg "FTPD: NOSAVE prevents saving of uploaded data\n";
  1739. }
  1740. }
  1741. close(CUSTOM);
  1742. }
  1743. #----------------------------------------------------------------------
  1744. #----------------------------------------------------------------------
  1745. #--------------------------- END OF SUBS ----------------------------
  1746. #----------------------------------------------------------------------
  1747. #----------------------------------------------------------------------
  1748. #**********************************************************************
  1749. # Parse command line options
  1750. #
  1751. # Options:
  1752. #
  1753. # --verbose # verbose
  1754. # --srcdir # source directory
  1755. # --id # server instance number
  1756. # --proto # server protocol
  1757. # --pidfile # server pid file
  1758. # --logfile # server log file
  1759. # --ipv4 # server IP version 4
  1760. # --ipv6 # server IP version 6
  1761. # --port # server listener port
  1762. # --addr # server address for listener port binding
  1763. #
  1764. while(@ARGV) {
  1765. if($ARGV[0] eq '--verbose') {
  1766. $verbose = 1;
  1767. }
  1768. elsif($ARGV[0] eq '--srcdir') {
  1769. if($ARGV[1]) {
  1770. $srcdir = $ARGV[1];
  1771. shift @ARGV;
  1772. }
  1773. }
  1774. elsif($ARGV[0] eq '--id') {
  1775. if($ARGV[1] && ($ARGV[1] =~ /^(\d+)$/)) {
  1776. $idnum = $1 if($1 > 0);
  1777. shift @ARGV;
  1778. }
  1779. }
  1780. elsif($ARGV[0] eq '--proto') {
  1781. if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp)$/)) {
  1782. $proto = $1;
  1783. shift @ARGV;
  1784. }
  1785. else {
  1786. die "unsupported protocol $ARGV[1]";
  1787. }
  1788. }
  1789. elsif($ARGV[0] eq '--pidfile') {
  1790. if($ARGV[1]) {
  1791. $pidfile = $ARGV[1];
  1792. shift @ARGV;
  1793. }
  1794. }
  1795. elsif($ARGV[0] eq '--logfile') {
  1796. if($ARGV[1]) {
  1797. $logfile = $ARGV[1];
  1798. shift @ARGV;
  1799. }
  1800. }
  1801. elsif($ARGV[0] eq '--ipv4') {
  1802. $ipvnum = 4;
  1803. $listenaddr = '127.0.0.1' if($listenaddr eq '::1');
  1804. }
  1805. elsif($ARGV[0] eq '--ipv6') {
  1806. $ipvnum = 6;
  1807. $listenaddr = '::1' if($listenaddr eq '127.0.0.1');
  1808. }
  1809. elsif($ARGV[0] eq '--port') {
  1810. if($ARGV[1] && ($ARGV[1] =~ /^(\d+)$/)) {
  1811. $port = $1 if($1 > 1024);
  1812. shift @ARGV;
  1813. }
  1814. }
  1815. elsif($ARGV[0] eq '--addr') {
  1816. if($ARGV[1]) {
  1817. my $tmpstr = $ARGV[1];
  1818. if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) {
  1819. $listenaddr = "$1.$2.$3.$4" if($ipvnum == 4);
  1820. }
  1821. elsif($ipvnum == 6) {
  1822. $listenaddr = $tmpstr;
  1823. $listenaddr =~ s/^\[(.*)\]$/$1/;
  1824. }
  1825. shift @ARGV;
  1826. }
  1827. }
  1828. else {
  1829. print STDERR "\nWarning: ftpserver.pl unknown parameter: $ARGV[0]\n";
  1830. }
  1831. shift @ARGV;
  1832. }
  1833. #***************************************************************************
  1834. # Initialize command line option dependant variables
  1835. #
  1836. if(!$srcdir) {
  1837. $srcdir = $ENV{'srcdir'} || '.';
  1838. }
  1839. if(!$pidfile) {
  1840. $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum);
  1841. }
  1842. if(!$logfile) {
  1843. $logfile = server_logfilename($logdir, $proto, $ipvnum, $idnum);
  1844. }
  1845. $mainsockf_pidfile = "$path/".
  1846. mainsockf_pidfilename($proto, $ipvnum, $idnum);
  1847. $mainsockf_logfile =
  1848. mainsockf_logfilename($logdir, $proto, $ipvnum, $idnum);
  1849. if($proto eq 'ftp') {
  1850. $datasockf_pidfile = "$path/".
  1851. datasockf_pidfilename($proto, $ipvnum, $idnum);
  1852. $datasockf_logfile =
  1853. datasockf_logfilename($logdir, $proto, $ipvnum, $idnum);
  1854. }
  1855. $srvrname = servername_str($proto, $ipvnum, $idnum);
  1856. $idstr = "$idnum" if($idnum > 1);
  1857. protocolsetup($proto);
  1858. $SIG{INT} = \&exit_signal_handler;
  1859. $SIG{TERM} = \&exit_signal_handler;
  1860. startsf();
  1861. logmsg sprintf("%s server listens on port IPv${ipvnum}/${port}\n", uc($proto));
  1862. open(PID, ">$pidfile");
  1863. print PID $$."\n";
  1864. close(PID);
  1865. logmsg("logged pid $$ in $pidfile\n");
  1866. while(1) {
  1867. # kill previous data connection sockfilt when alive
  1868. if($datasockf_runs eq 'yes') {
  1869. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1870. logmsg "DATA sockfilt for $datasockf_mode data channel killed now\n";
  1871. }
  1872. datasockf_state('STOPPED');
  1873. #
  1874. # We read 'sockfilt' commands.
  1875. #
  1876. my $input;
  1877. logmsg "Awaiting input\n";
  1878. sysread_or_die(\*SFREAD, \$input, 5);
  1879. if($input !~ /^CNCT/) {
  1880. # we wait for a connected client
  1881. logmsg "MAIN sockfilt said: $input";
  1882. next;
  1883. }
  1884. logmsg "====> Client connect\n";
  1885. set_advisor_read_lock($SERVERLOGS_LOCK);
  1886. $serverlogslocked = 1;
  1887. # flush data:
  1888. $| = 1;
  1889. &customize(); # read test control instructions
  1890. my $welcome = $customreply{"welcome"};
  1891. if(!$welcome) {
  1892. $welcome = $displaytext{"welcome"};
  1893. }
  1894. else {
  1895. # clear it after use
  1896. $customreply{"welcome"}="";
  1897. if($welcome !~ /\r\n\z/) {
  1898. $welcome .= "\r\n";
  1899. }
  1900. }
  1901. sendcontrol $welcome;
  1902. #remove global variables from last connection
  1903. if($ftplistparserstate) {
  1904. undef $ftplistparserstate;
  1905. }
  1906. if($ftptargetdir) {
  1907. undef $ftptargetdir;
  1908. }
  1909. if($verbose) {
  1910. print STDERR "OUT: $welcome";
  1911. }
  1912. my $full = "";
  1913. while(1) {
  1914. my $i;
  1915. # Now we expect to read DATA\n[hex size]\n[prot], where the [prot]
  1916. # part only is FTP lingo.
  1917. # COMMAND
  1918. sysread_or_die(\*SFREAD, \$i, 5);
  1919. if($i !~ /^DATA/) {
  1920. logmsg "MAIN sockfilt said $i";
  1921. if($i =~ /^DISC/) {
  1922. # disconnect
  1923. last;
  1924. }
  1925. next;
  1926. }
  1927. # SIZE of data
  1928. sysread_or_die(\*SFREAD, \$i, 5);
  1929. my $size = 0;
  1930. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  1931. $size = hex($1);
  1932. }
  1933. # data
  1934. read_mainsockf(\$input, $size);
  1935. ftpmsg $input;
  1936. $full .= $input;
  1937. # Loop until command completion
  1938. next unless($full =~ /\r\n$/);
  1939. # Remove trailing CRLF.
  1940. $full =~ s/[\n\r]+$//;
  1941. my $FTPCMD;
  1942. my $FTPARG;
  1943. if($proto eq "imap") {
  1944. # IMAP is different with its identifier first on the command line
  1945. unless(($full =~ /^([^ ]+) ([^ ]+) (.*)/) ||
  1946. ($full =~ /^([^ ]+) ([^ ]+)/)) {
  1947. sendcontrol "$1 '$full': command not understood.\r\n";
  1948. last;
  1949. }
  1950. $cmdid=$1; # set the global variable
  1951. $FTPCMD=$2;
  1952. $FTPARG=$3;
  1953. }
  1954. elsif($full =~ /^([A-Z]{3,4})(\s(.*))?$/i) {
  1955. $FTPCMD=$1;
  1956. $FTPARG=$3;
  1957. }
  1958. elsif(($proto eq "smtp") && ($full =~ /^[A-Z0-9+\/]{0,512}={0,2}$/i)) {
  1959. # SMTP long "commands" are base64 authentication data.
  1960. $FTPCMD=$full;
  1961. $FTPARG="";
  1962. }
  1963. else {
  1964. sendcontrol "500 '$full': command not understood.\r\n";
  1965. last;
  1966. }
  1967. logmsg "< \"$full\"\n";
  1968. if($verbose) {
  1969. print STDERR "IN: $full\n";
  1970. }
  1971. $full = "";
  1972. my $delay = $delayreply{$FTPCMD};
  1973. if($delay) {
  1974. # just go sleep this many seconds!
  1975. logmsg("Sleep for $delay seconds\n");
  1976. my $twentieths = $delay * 20;
  1977. while($twentieths--) {
  1978. select(undef, undef, undef, 0.05) unless($got_exit_signal);
  1979. }
  1980. }
  1981. my $text;
  1982. $text = $customreply{$FTPCMD};
  1983. my $fake = $text;
  1984. if($text && ($text ne "")) {
  1985. if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) {
  1986. # used enough number of times, now blank the customreply
  1987. $customreply{$FTPCMD}="";
  1988. }
  1989. }
  1990. else {
  1991. $text = $displaytext{$FTPCMD};
  1992. }
  1993. my $check;
  1994. if($text && ($text ne "")) {
  1995. if($cmdid && ($cmdid ne "")) {
  1996. sendcontrol "$cmdid$text\r\n";
  1997. }
  1998. else {
  1999. sendcontrol "$text\r\n";
  2000. }
  2001. }
  2002. else {
  2003. $check=1; # no response yet
  2004. }
  2005. unless($fake && ($fake ne "")) {
  2006. # only perform this if we're not faking a reply
  2007. my $func = $commandfunc{$FTPCMD};
  2008. if($func) {
  2009. &$func($FTPARG, $FTPCMD);
  2010. $check=0; # taken care of
  2011. }
  2012. }
  2013. if($check) {
  2014. logmsg "$FTPCMD wasn't handled!\n";
  2015. if($proto eq 'pop3') {
  2016. sendcontrol "-ERR $FTPCMD is not dealt with!\r\n";
  2017. }
  2018. elsif($proto eq 'imap') {
  2019. sendcontrol "$cmdid BAD $FTPCMD is not dealt with!\r\n";
  2020. }
  2021. else {
  2022. sendcontrol "500 $FTPCMD is not dealt with!\r\n";
  2023. }
  2024. }
  2025. } # while(1)
  2026. logmsg "====> Client disconnected\n";
  2027. if($serverlogslocked) {
  2028. $serverlogslocked = 0;
  2029. clear_advisor_read_lock($SERVERLOGS_LOCK);
  2030. }
  2031. }
  2032. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  2033. unlink($pidfile);
  2034. if($serverlogslocked) {
  2035. $serverlogslocked = 0;
  2036. clear_advisor_read_lock($SERVERLOGS_LOCK);
  2037. }
  2038. exit;