ftpserver.pl 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  1. #!/usr/bin/env perl
  2. #***************************************************************************
  3. # _ _ ____ _
  4. # Project ___| | | | _ \| |
  5. # / __| | | | |_) | |
  6. # | (__| |_| | _ <| |___
  7. # \___|\___/|_| \_\_____|
  8. #
  9. # Copyright (C) 1998 - 2010, 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. # $Id$
  23. ###########################################################################
  24. # This is a server designed for the curl test suite.
  25. #
  26. # In December 2009 we started remaking the server to support more protocols
  27. # that are similar in spirit. Like POP3, IMAP and SMTP in addition to the FTP
  28. # it already supported since a long time. Note that it still only supports one
  29. # protocol per invoke. You need to start mulitple servers to support multiple
  30. # protocols simultaneously.
  31. #
  32. # It is meant to exercise curl, it is not meant to be a fully working
  33. # or even very standard compliant server.
  34. #
  35. # You may optionally specify port on the command line, otherwise it'll
  36. # default to port 8921.
  37. #
  38. # All socket/network/TCP related stuff is done by the 'sockfilt' program.
  39. #
  40. BEGIN {
  41. @INC=(@INC, $ENV{'srcdir'}, '.');
  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. use serverhelp qw(
  55. servername_str
  56. server_pidfilename
  57. server_logfilename
  58. mainsockf_pidfilename
  59. mainsockf_logfilename
  60. datasockf_pidfilename
  61. datasockf_logfilename
  62. );
  63. #**********************************************************************
  64. # global vars...
  65. #
  66. my $verbose = 0; # set to 1 for debugging
  67. my $idstr = ""; # server instance string
  68. my $idnum = 1; # server instance number
  69. my $ipvnum = 4; # server IPv number (4 or 6)
  70. my $proto = 'ftp'; # default server protocol
  71. my $srcdir; # directory where ftpserver.pl is located
  72. my $srvrname; # server name for presentation purposes
  73. my $grok_eprt;
  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. #**********************************************************************
  108. # global vars which depend on server protocol selection
  109. #
  110. my %commandfunc; # protocol command specific function callbacks
  111. my %displaytext; # text returned to client before callback runs
  112. my @welcome; # text returned to client upon connection
  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 %customreply; #
  123. my %customcount; #
  124. my %delayreply; #
  125. #**********************************************************************
  126. # global vars used for signal handling
  127. #
  128. my $got_exit_signal = 0; # set if program should finish execution ASAP
  129. my $exit_signal; # first signal handled in exit_signal_handler
  130. #**********************************************************************
  131. # exit_signal_handler will be triggered to indicate that the program
  132. # should finish its execution in a controlled way as soon as possible.
  133. # For now, program will also terminate from within this handler.
  134. #
  135. sub exit_signal_handler {
  136. my $signame = shift;
  137. # For now, simply mimic old behavior.
  138. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  139. unlink($pidfile);
  140. if($serverlogslocked) {
  141. $serverlogslocked = 0;
  142. clear_advisor_read_lock($SERVERLOGS_LOCK);
  143. }
  144. exit;
  145. }
  146. #**********************************************************************
  147. # logmsg is general message logging subroutine for our test servers.
  148. #
  149. sub logmsg {
  150. my $now;
  151. # sub second timestamping needs Time::HiRes
  152. if($Time::HiRes::VERSION) {
  153. my ($seconds, $usec) = gettimeofday();
  154. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  155. localtime($seconds);
  156. $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec);
  157. }
  158. else {
  159. my $seconds = time();
  160. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  161. localtime($seconds);
  162. $now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec);
  163. }
  164. if(open(LOGFILEFH, ">>$logfile")) {
  165. print LOGFILEFH $now;
  166. print LOGFILEFH @_;
  167. close(LOGFILEFH);
  168. }
  169. }
  170. sub ftpmsg {
  171. # append to the server.input file
  172. open(INPUT, ">>log/server$idstr.input") ||
  173. logmsg "failed to open log/server$idstr.input\n";
  174. print INPUT @_;
  175. close(INPUT);
  176. # use this, open->print->close system only to make the file
  177. # open as little as possible, to make the test suite run
  178. # better on windows/cygwin
  179. }
  180. sub sysread_or_die {
  181. my $FH = shift;
  182. my $scalar = shift;
  183. my $length = shift;
  184. my $fcaller;
  185. my $lcaller;
  186. my $result;
  187. $result = sysread($$FH, $$scalar, $length);
  188. if(not defined $result) {
  189. ($fcaller, $lcaller) = (caller)[1,2];
  190. logmsg "Failed to read input\n";
  191. logmsg "Error: $srvrname server, sysread error: $!\n";
  192. logmsg "Exited from sysread_or_die() at $fcaller " .
  193. "line $lcaller. $srvrname server, sysread error: $!\n";
  194. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  195. unlink($pidfile);
  196. if($serverlogslocked) {
  197. $serverlogslocked = 0;
  198. clear_advisor_read_lock($SERVERLOGS_LOCK);
  199. }
  200. exit;
  201. }
  202. elsif($result == 0) {
  203. ($fcaller, $lcaller) = (caller)[1,2];
  204. logmsg "Failed to read input\n";
  205. logmsg "Error: $srvrname server, read zero\n";
  206. logmsg "Exited from sysread_or_die() at $fcaller " .
  207. "line $lcaller. $srvrname server, read zero\n";
  208. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  209. unlink($pidfile);
  210. if($serverlogslocked) {
  211. $serverlogslocked = 0;
  212. clear_advisor_read_lock($SERVERLOGS_LOCK);
  213. }
  214. exit;
  215. }
  216. return $result;
  217. }
  218. sub startsf {
  219. my $mainsockfcmd = "./server/sockfilt " .
  220. "--ipv$ipvnum --port $port " .
  221. "--pidfile \"$mainsockf_pidfile\" " .
  222. "--logfile \"$mainsockf_logfile\"";
  223. $sfpid = open2(*SFREAD, *SFWRITE, $mainsockfcmd);
  224. print STDERR "$mainsockfcmd\n" if($verbose);
  225. print SFWRITE "PING\n";
  226. my $pong;
  227. sysread_or_die(\*SFREAD, \$pong, 5);
  228. if($pong !~ /^PONG/) {
  229. logmsg "Failed sockfilt command: $mainsockfcmd\n";
  230. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  231. unlink($pidfile);
  232. if($serverlogslocked) {
  233. $serverlogslocked = 0;
  234. clear_advisor_read_lock($SERVERLOGS_LOCK);
  235. }
  236. die "Failed to start sockfilt!";
  237. }
  238. }
  239. sub sockfilt {
  240. my $l;
  241. foreach $l (@_) {
  242. printf SFWRITE "DATA\n%04x\n", length($l);
  243. print SFWRITE $l;
  244. }
  245. }
  246. sub sockfiltsecondary {
  247. my $l;
  248. foreach $l (@_) {
  249. printf DWRITE "DATA\n%04x\n", length($l);
  250. print DWRITE $l;
  251. }
  252. }
  253. # Send data to the client on the control stream, which happens to be plain
  254. # stdout.
  255. sub sendcontrol {
  256. if(!$ctrldelay) {
  257. # spit it all out at once
  258. sockfilt @_;
  259. }
  260. else {
  261. my $a = join("", @_);
  262. my @a = split("", $a);
  263. for(@a) {
  264. sockfilt $_;
  265. select(undef, undef, undef, 0.01);
  266. }
  267. }
  268. my $log;
  269. foreach $log (@_) {
  270. my $l = $log;
  271. $l =~ s/[\r\n]//g;
  272. logmsg "> \"$l\"\n";
  273. }
  274. }
  275. # Send data to the client on the data stream
  276. sub senddata {
  277. my $l;
  278. foreach $l (@_) {
  279. if(!$datadelay) {
  280. # spit it all out at once
  281. sockfiltsecondary $l;
  282. }
  283. else {
  284. # pause between each byte
  285. for (split(//,$l)) {
  286. sockfiltsecondary $_;
  287. select(undef, undef, undef, 0.01);
  288. }
  289. }
  290. }
  291. }
  292. #**********************************************************************
  293. # protocolsetup initializes the 'displaytext' and 'commandfunc' hashes
  294. # for the given protocol. References to protocol command callbacks are
  295. # stored in 'commandfunc' hash, and text which will be returned to the
  296. # client before the command callback runs is stored in 'displaytext'.
  297. #
  298. sub protocolsetup {
  299. my $proto = $_[0];
  300. if($proto eq 'ftp') {
  301. %commandfunc = (
  302. 'PORT' => \&PORT_ftp,
  303. 'EPRT' => \&PORT_ftp,
  304. 'LIST' => \&LIST_ftp,
  305. 'NLST' => \&NLST_ftp,
  306. 'PASV' => \&PASV_ftp,
  307. 'EPSV' => \&PASV_ftp,
  308. 'RETR' => \&RETR_ftp,
  309. 'SIZE' => \&SIZE_ftp,
  310. 'REST' => \&REST_ftp,
  311. 'STOR' => \&STOR_ftp,
  312. 'APPE' => \&STOR_ftp, # append looks like upload
  313. 'MDTM' => \&MDTM_ftp,
  314. );
  315. %displaytext = (
  316. 'USER' => '331 We are happy you popped in!',
  317. 'PASS' => '230 Welcome you silly person',
  318. 'PORT' => '200 You said PORT - I say FINE',
  319. 'TYPE' => '200 I modify TYPE as you wanted',
  320. 'LIST' => '150 here comes a directory',
  321. 'NLST' => '150 here comes a directory',
  322. 'CWD' => '250 CWD command successful.',
  323. 'SYST' => '215 UNIX Type: L8', # just fake something
  324. 'QUIT' => '221 bye bye baby', # just reply something
  325. 'PWD' => '257 "/nowhere/anywhere" is current directory',
  326. 'MKD' => '257 Created your requested directory',
  327. 'REST' => '350 Yeah yeah we set it there for you',
  328. 'DELE' => '200 OK OK OK whatever you say',
  329. 'RNFR' => '350 Received your order. Please provide more',
  330. 'RNTO' => '250 Ok, thanks. File renaming completed.',
  331. 'NOOP' => '200 Yes, I\'m very good at doing nothing.',
  332. 'PBSZ' => '500 PBSZ not implemented',
  333. 'PROT' => '500 PROT not implemented',
  334. );
  335. @welcome = (
  336. '220- _ _ ____ _ '."\r\n",
  337. '220- ___| | | | _ \| | '."\r\n",
  338. '220- / __| | | | |_) | | '."\r\n",
  339. '220- | (__| |_| | _ <| |___ '."\r\n",
  340. '220 \___|\___/|_| \_\_____|'."\r\n"
  341. );
  342. }
  343. elsif($proto eq 'pop3') {
  344. %commandfunc = (
  345. 'RETR' => \&RETR_pop3,
  346. );
  347. %displaytext = (
  348. 'USER' => '+OK We are happy you popped in!',
  349. 'PASS' => '+OK Access granted',
  350. 'QUIT' => '+OK byebye',
  351. );
  352. @welcome = (
  353. ' _ _ ____ _ '."\r\n",
  354. ' ___| | | | _ \| | '."\r\n",
  355. ' / __| | | | |_) | | '."\r\n",
  356. ' | (__| |_| | _ <| |___ '."\r\n",
  357. ' \___|\___/|_| \_\_____|'."\r\n",
  358. '+OK cURL POP3 server ready to serve'."\r\n"
  359. );
  360. }
  361. elsif($proto eq 'imap') {
  362. %commandfunc = (
  363. 'FETCH' => \&FETCH_imap,
  364. 'SELECT' => \&SELECT_imap,
  365. );
  366. %displaytext = (
  367. 'LOGIN' => ' OK We are happy you popped in!',
  368. 'SELECT' => ' OK selection done',
  369. 'LOGOUT' => ' OK thanks for the fish',
  370. );
  371. @welcome = (
  372. ' _ _ ____ _ '."\r\n",
  373. ' ___| | | | _ \| | '."\r\n",
  374. ' / __| | | | |_) | | '."\r\n",
  375. ' | (__| |_| | _ <| |___ '."\r\n",
  376. ' \___|\___/|_| \_\_____|'."\r\n",
  377. '* OK cURL IMAP server ready to serve'."\r\n"
  378. );
  379. }
  380. elsif($proto eq 'smtp') {
  381. %commandfunc = (
  382. 'DATA' => \&DATA_smtp,
  383. 'RCPT' => \&RCPT_smtp,
  384. );
  385. %displaytext = (
  386. 'EHLO' => '230 We are happy you popped in!',
  387. 'MAIL' => '200 Note taken',
  388. 'RCPT' => '200 Receivers accepted',
  389. 'QUIT' => '200 byebye',
  390. );
  391. @welcome = (
  392. '220- _ _ ____ _ '."\r\n",
  393. '220- ___| | | | _ \| | '."\r\n",
  394. '220- / __| | | | |_) | | '."\r\n",
  395. '220- | (__| |_| | _ <| |___ '."\r\n",
  396. '220 \___|\___/|_| \_\_____|'."\r\n"
  397. );
  398. }
  399. }
  400. sub close_dataconn {
  401. my ($closed)=@_; # non-zero if already disconnected
  402. my $datapid = processexists($datasockf_pidfile);
  403. if(!$closed) {
  404. logmsg "* disconnect data connection\n";
  405. if($datapid > 0) {
  406. print DWRITE "DISC\n";
  407. my $i;
  408. sysread DREAD, $i, 5;
  409. }
  410. }
  411. else {
  412. logmsg "data connection already disconnected\n";
  413. }
  414. logmsg "=====> Closed data connection\n";
  415. logmsg "* quit sockfilt for data (pid $datapid)\n";
  416. if($datapid > 0) {
  417. print DWRITE "QUIT\n";
  418. waitpid($datapid, 0);
  419. unlink($datasockf_pidfile) if(-f $datasockf_pidfile);
  420. }
  421. }
  422. ################
  423. ################ SMTP commands
  424. ################
  425. # what set by "RCPT"
  426. my $smtp_rcpt;
  427. sub DATA_smtp {
  428. my $testno;
  429. if($smtp_rcpt =~ /^TO:(.*)/) {
  430. $testno = $1;
  431. }
  432. else {
  433. return; # failure
  434. }
  435. if($testno eq "verifiedserver") {
  436. sendcontrol "554 WE ROOLZ: $$\r\n";
  437. return 0; # don't wait for data now
  438. }
  439. else {
  440. $testno =~ s/^([0-9]*).*/$1/;
  441. sendcontrol "354 Show me the mail\r\n";
  442. }
  443. logmsg "===> rcpt $testno was $smtp_rcpt\n";
  444. my $filename = "log/upload.$testno";
  445. logmsg "Store test number $testno in $filename\n";
  446. open(FILE, ">$filename") ||
  447. return 0; # failed to open output
  448. my $line;
  449. my $ulsize=0;
  450. my $disc=0;
  451. my $raw;
  452. while (5 == (sysread \*SFREAD, $line, 5)) {
  453. if($line eq "DATA\n") {
  454. my $i;
  455. my $eob;
  456. sysread \*SFREAD, $i, 5;
  457. my $size = 0;
  458. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  459. $size = hex($1);
  460. }
  461. sysread \*SFREAD, $line, $size;
  462. $ulsize += $size;
  463. print FILE $line if(!$nosave);
  464. $raw .= $line;
  465. if($raw =~ /\x0d\x0a\x2e\x0d\x0a/) {
  466. # end of data marker!
  467. $eob = 1;
  468. }
  469. logmsg "> Appending $size bytes to file\n";
  470. if($eob) {
  471. logmsg "Found SMTP EOB marker\n";
  472. last;
  473. }
  474. }
  475. elsif($line eq "DISC\n") {
  476. # disconnect!
  477. $disc=1;
  478. last;
  479. }
  480. else {
  481. logmsg "No support for: $line";
  482. last;
  483. }
  484. }
  485. if($nosave) {
  486. print FILE "$ulsize bytes would've been stored here\n";
  487. }
  488. close(FILE);
  489. logmsg "received $ulsize bytes upload\n";
  490. }
  491. sub RCPT_smtp {
  492. my ($args) = @_;
  493. $smtp_rcpt = $args;
  494. }
  495. ################
  496. ################ IMAP commands
  497. ################
  498. # global to allow the command functions to read it
  499. my $cmdid;
  500. # what was picked by SELECT
  501. my $selected;
  502. sub SELECT_imap {
  503. my ($testno) = @_;
  504. my @data;
  505. my $size;
  506. logmsg "SELECT_imap got test $testno\n";
  507. $selected = $testno;
  508. return 0;
  509. }
  510. sub FETCH_imap {
  511. my ($testno) = @_;
  512. my @data;
  513. my $size;
  514. logmsg "FETCH_imap got test $testno\n";
  515. $testno = $selected;
  516. if($testno =~ /^verifiedserver$/) {
  517. # this is the secret command that verifies that this actually is
  518. # the curl test server
  519. my $response = "WE ROOLZ: $$\r\n";
  520. if($verbose) {
  521. print STDERR "FTPD: We returned proof we are the test server\n";
  522. }
  523. $data[0] = $response;
  524. logmsg "return proof we are we\n";
  525. }
  526. else {
  527. logmsg "retrieve a mail\n";
  528. $testno =~ s/^([^0-9]*)//;
  529. my $testpart = "";
  530. if ($testno > 10000) {
  531. $testpart = $testno % 10000;
  532. $testno = int($testno / 10000);
  533. }
  534. # send mail content
  535. loadtest("$srcdir/data/test$testno");
  536. @data = getpart("reply", "data$testpart");
  537. }
  538. for (@data) {
  539. $size += length($_);
  540. }
  541. sendcontrol "* FETCH starts {$size}\r\n";
  542. for my $d (@data) {
  543. sendcontrol $d;
  544. }
  545. sendcontrol "$cmdid OK FETCH completed\r\n";
  546. return 0;
  547. }
  548. ################
  549. ################ POP3 commands
  550. ################
  551. sub RETR_pop3 {
  552. my ($testno) = @_;
  553. my @data;
  554. if($testno =~ /^verifiedserver$/) {
  555. # this is the secret command that verifies that this actually is
  556. # the curl test server
  557. my $response = "WE ROOLZ: $$\r\n";
  558. if($verbose) {
  559. print STDERR "FTPD: We returned proof we are the test server\n";
  560. }
  561. $data[0] = $response;
  562. logmsg "return proof we are we\n";
  563. }
  564. else {
  565. logmsg "retrieve a mail\n";
  566. $testno =~ s/^([^0-9]*)//;
  567. my $testpart = "";
  568. if ($testno > 10000) {
  569. $testpart = $testno % 10000;
  570. $testno = int($testno / 10000);
  571. }
  572. # send mail content
  573. loadtest("$srcdir/data/test$testno");
  574. @data = getpart("reply", "data$testpart");
  575. }
  576. sendcontrol "+OK Mail transfer starts\r\n";
  577. for my $d (@data) {
  578. sendcontrol $d;
  579. }
  580. # end with the magic 5-byte end of mail marker
  581. sendcontrol "\r\n.\r\n";
  582. return 0;
  583. }
  584. ################
  585. ################ FTP commands
  586. ################
  587. my $rest=0;
  588. sub REST_ftp {
  589. $rest = $_[0];
  590. logmsg "Set REST position to $rest\n"
  591. }
  592. sub LIST_ftp {
  593. # print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
  594. # this is a built-in fake-dir ;-)
  595. my @ftpdir=("total 20\r\n",
  596. "drwxr-xr-x 8 98 98 512 Oct 22 13:06 .\r\n",
  597. "drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..\r\n",
  598. "drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT\r\n",
  599. "-r--r--r-- 1 0 1 35 Jul 16 1996 README\r\n",
  600. "lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin\r\n",
  601. "dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev\r\n",
  602. "drwxrwxrwx 2 98 98 512 May 29 16:04 download.html\r\n",
  603. "dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc\r\n",
  604. "drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\r\n",
  605. "dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n");
  606. logmsg "pass LIST data on data connection\n";
  607. for(@ftpdir) {
  608. senddata $_;
  609. }
  610. close_dataconn(0);
  611. sendcontrol "226 ASCII transfer complete\r\n";
  612. return 0;
  613. }
  614. sub NLST_ftp {
  615. my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README");
  616. logmsg "pass NLST data on data connection\n";
  617. for(@ftpdir) {
  618. senddata "$_\r\n";
  619. }
  620. close_dataconn(0);
  621. sendcontrol "226 ASCII transfer complete\r\n";
  622. return 0;
  623. }
  624. sub MDTM_ftp {
  625. my $testno = $_[0];
  626. my $testpart = "";
  627. if ($testno > 10000) {
  628. $testpart = $testno % 10000;
  629. $testno = int($testno / 10000);
  630. }
  631. loadtest("$srcdir/data/test$testno");
  632. my @data = getpart("reply", "mdtm");
  633. my $reply = $data[0];
  634. chomp $reply if($reply);
  635. if($reply && ($reply =~ /^[+-]?\d+$/) && ($reply < 0)) {
  636. sendcontrol "550 $testno: no such file.\r\n";
  637. }
  638. elsif($reply) {
  639. sendcontrol "$reply\r\n";
  640. }
  641. else {
  642. sendcontrol "500 MDTM: no such command.\r\n";
  643. }
  644. return 0;
  645. }
  646. sub SIZE_ftp {
  647. my $testno = $_[0];
  648. if($testno =~ /^verifiedserver$/) {
  649. my $response = "WE ROOLZ: $$\r\n";
  650. my $size = length($response);
  651. sendcontrol "213 $size\r\n";
  652. return 0;
  653. }
  654. if($testno =~ /(\d+)\/?$/) {
  655. $testno = $1;
  656. }
  657. else {
  658. print STDERR "SIZE_ftp: invalid test number: $testno\n";
  659. return 1;
  660. }
  661. my $testpart = "";
  662. if($testno > 10000) {
  663. $testpart = $testno % 10000;
  664. $testno = int($testno / 10000);
  665. }
  666. loadtest("$srcdir/data/test$testno");
  667. my @data = getpart("reply", "size");
  668. my $size = $data[0];
  669. if($size) {
  670. if($size > -1) {
  671. sendcontrol "213 $size\r\n";
  672. }
  673. else {
  674. sendcontrol "550 $testno: No such file or directory.\r\n";
  675. }
  676. }
  677. else {
  678. $size=0;
  679. @data = getpart("reply", "data$testpart");
  680. for(@data) {
  681. $size += length($_);
  682. }
  683. if($size) {
  684. sendcontrol "213 $size\r\n";
  685. }
  686. else {
  687. sendcontrol "550 $testno: No such file or directory.\r\n";
  688. }
  689. }
  690. return 0;
  691. }
  692. sub RETR_ftp {
  693. my ($testno) = @_;
  694. if($testno =~ /^verifiedserver$/) {
  695. # this is the secret command that verifies that this actually is
  696. # the curl test server
  697. my $response = "WE ROOLZ: $$\r\n";
  698. my $len = length($response);
  699. sendcontrol "150 Binary junk ($len bytes).\r\n";
  700. senddata "WE ROOLZ: $$\r\n";
  701. close_dataconn(0);
  702. sendcontrol "226 File transfer complete\r\n";
  703. if($verbose) {
  704. print STDERR "FTPD: We returned proof we are the test server\n";
  705. }
  706. return 0;
  707. }
  708. $testno =~ s/^([^0-9]*)//;
  709. my $testpart = "";
  710. if ($testno > 10000) {
  711. $testpart = $testno % 10000;
  712. $testno = int($testno / 10000);
  713. }
  714. loadtest("$srcdir/data/test$testno");
  715. my @data = getpart("reply", "data$testpart");
  716. my $size=0;
  717. for(@data) {
  718. $size += length($_);
  719. }
  720. my %hash = getpartattr("reply", "data$testpart");
  721. if($size || $hash{'sendzero'}) {
  722. if($rest) {
  723. # move read pointer forward
  724. $size -= $rest;
  725. logmsg "REST $rest was removed from size, makes $size left\n";
  726. $rest = 0; # reset REST offset again
  727. }
  728. if($retrweirdo) {
  729. sendcontrol "150 Binary data connection for $testno () ($size bytes).\r\n",
  730. "226 File transfer complete\r\n";
  731. for(@data) {
  732. my $send = $_;
  733. senddata $send;
  734. }
  735. close_dataconn(0);
  736. $retrweirdo=0; # switch off the weirdo again!
  737. }
  738. else {
  739. my $sz = "($size bytes)";
  740. if($retrnosize) {
  741. $sz = "size?";
  742. }
  743. sendcontrol "150 Binary data connection for $testno () $sz.\r\n";
  744. for(@data) {
  745. my $send = $_;
  746. senddata $send;
  747. }
  748. close_dataconn(0);
  749. sendcontrol "226 File transfer complete\r\n";
  750. }
  751. }
  752. else {
  753. sendcontrol "550 $testno: No such file or directory.\r\n";
  754. }
  755. return 0;
  756. }
  757. sub STOR_ftp {
  758. my $testno=$_[0];
  759. my $filename = "log/upload.$testno";
  760. logmsg "STOR test number $testno in $filename\n";
  761. sendcontrol "125 Gimme gimme gimme!\r\n";
  762. open(FILE, ">$filename") ||
  763. return 0; # failed to open output
  764. my $line;
  765. my $ulsize=0;
  766. my $disc=0;
  767. while (5 == (sysread DREAD, $line, 5)) {
  768. if($line eq "DATA\n") {
  769. my $i;
  770. sysread DREAD, $i, 5;
  771. my $size = 0;
  772. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  773. $size = hex($1);
  774. }
  775. sysread DREAD, $line, $size;
  776. #print STDERR " GOT: $size bytes\n";
  777. $ulsize += $size;
  778. print FILE $line if(!$nosave);
  779. logmsg "> Appending $size bytes to file\n";
  780. }
  781. elsif($line eq "DISC\n") {
  782. # disconnect!
  783. $disc=1;
  784. last;
  785. }
  786. else {
  787. logmsg "No support for: $line";
  788. last;
  789. }
  790. }
  791. if($nosave) {
  792. print FILE "$ulsize bytes would've been stored here\n";
  793. }
  794. close(FILE);
  795. close_dataconn($disc);
  796. logmsg "received $ulsize bytes upload\n";
  797. sendcontrol "226 File transfer complete\r\n";
  798. return 0;
  799. }
  800. sub PASV_ftp {
  801. my ($arg, $cmd)=@_;
  802. my $pasvport;
  803. # kill previous data connection sockfilt when alive
  804. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  805. # We fire up a new sockfilt to do the data transfer for us.
  806. my $datasockfcmd = "./server/sockfilt " .
  807. "--ipv$ipvnum --port 0 " .
  808. "--pidfile \"$datasockf_pidfile\" " .
  809. "--logfile \"$datasockf_logfile\"";
  810. $slavepid = open2(\*DREAD, \*DWRITE, $datasockfcmd);
  811. print DWRITE "PING\n";
  812. my $pong;
  813. sysread_or_die(\*DREAD, \$pong, 5);
  814. if($pong !~ /^PONG/) {
  815. logmsg "failed to run sockfilt for data connection\n";
  816. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  817. sendcontrol "500 no free ports!\r\n";
  818. return 0;
  819. }
  820. logmsg "Run sockfilt for data on pid $slavepid\n";
  821. # Find out what port we listen on
  822. my $i;
  823. print DWRITE "PORT\n";
  824. # READ the response code
  825. sysread_or_die(\*DREAD, \$i, 5);
  826. # READ the response size
  827. sysread_or_die(\*DREAD, \$i, 5);
  828. my $size = 0;
  829. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  830. $size = hex($1);
  831. }
  832. # READ the response data
  833. sysread_or_die(\*DREAD, \$i, $size);
  834. # The data is in the format
  835. # IPvX/NNN
  836. if($i =~ /IPv(\d)\/(\d+)/) {
  837. # FIX: deal with IP protocol version
  838. $pasvport = $2;
  839. }
  840. if($cmd ne "EPSV") {
  841. # PASV reply
  842. my $p=$listenaddr;
  843. $p =~ s/\./,/g;
  844. if($pasvbadip) {
  845. $p="1,2,3,4";
  846. }
  847. sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\n",
  848. ($pasvport/256), ($pasvport%256));
  849. }
  850. else {
  851. # EPSV reply
  852. sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
  853. }
  854. eval {
  855. local $SIG{ALRM} = sub { die "alarm\n" };
  856. # assume swift operations unless explicitly slow
  857. alarm ($datadelay?20:10);
  858. # Wait for 'CNCT'
  859. my $input;
  860. while(sysread(DREAD, $input, 5)) {
  861. if($input !~ /^CNCT/) {
  862. # we wait for a connected client
  863. logmsg "Odd, we got $input from client\n";
  864. next;
  865. }
  866. logmsg "====> Client DATA connect\n";
  867. last;
  868. }
  869. alarm 0;
  870. };
  871. if ($@) {
  872. # timed out
  873. logmsg "$srvrname server timed out awaiting data connection ".
  874. "on port $pasvport\n";
  875. logmsg "accept failed or connection not even attempted\n";
  876. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  877. return;
  878. }
  879. else {
  880. logmsg "data connection setup on port $pasvport\n";
  881. }
  882. return;
  883. }
  884. # Support both PORT and EPRT here. Consider LPRT too.
  885. sub PORT_ftp {
  886. my ($arg, $cmd) = @_;
  887. my $port;
  888. my $addr;
  889. # We always ignore the given IP and use localhost.
  890. if($cmd eq "PORT") {
  891. if($arg !~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/) {
  892. logmsg "bad PORT-line: $arg\n";
  893. sendcontrol "500 silly you, go away\r\n";
  894. return 0;
  895. }
  896. $port = ($5<<8)+$6;
  897. $addr = "$1.$2.$3.$4";
  898. }
  899. # EPRT |2|::1|49706|
  900. elsif(($cmd eq "EPRT") && ($grok_eprt)) {
  901. if($arg !~ /(\d+)\|([^\|]+)\|(\d+)/) {
  902. sendcontrol "500 silly you, go away\r\n";
  903. return 0;
  904. }
  905. sendcontrol "200 Thanks for dropping by. We contact you later\r\n";
  906. $port = $3;
  907. $addr = $2;
  908. }
  909. else {
  910. sendcontrol "500 we don't like $cmd now\r\n";
  911. return 0;
  912. }
  913. if(!$port || $port > 65535) {
  914. print STDERR "very illegal PORT number: $port\n";
  915. return 1;
  916. }
  917. # We fire up a new sockfilt to do the data transfer for us.
  918. my $datasockfcmd = "./server/sockfilt " .
  919. "--ipv$ipvnum --connect $port --addr \"$addr\" " .
  920. "--pidfile \"$datasockf_pidfile\" " .
  921. "--logfile \"$datasockf_logfile\"";
  922. $slavepid = open2(\*DREAD, \*DWRITE, $datasockfcmd);
  923. print STDERR "$datasockfcmd\n" if($verbose);
  924. print DWRITE "PING\n";
  925. my $pong;
  926. sysread_or_die(\*DREAD, \$pong, 5);
  927. if($pong !~ /^PONG/) {
  928. logmsg "Failed sockfilt for data connection\n";
  929. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  930. }
  931. logmsg "====> Client DATA connect to port $port\n";
  932. return;
  933. }
  934. #**********************************************************************
  935. # customize configures test server operation for each curl test, reading
  936. # configuration commands/parameters from server commands file each time
  937. # a new client control connection is established with the test server.
  938. # On success returns 1, otherwise zero.
  939. #
  940. sub customize {
  941. $ctrldelay = 0; # default is no throttling of the ctrl stream
  942. $datadelay = 0; # default is no throttling of the data stream
  943. $retrweirdo = 0; # default is no use of RETRWEIRDO
  944. $retrnosize = 0; # default is no use of RETRNOSIZE
  945. $pasvbadip = 0; # default is no use of PASVBADIP
  946. $nosave = 0; # default is to actually save uploaded data to file
  947. %customreply = (); #
  948. %customcount = (); #
  949. %delayreply = (); #
  950. open(CUSTOM, "<log/ftpserver.cmd") ||
  951. return 1;
  952. logmsg "FTPD: Getting commands from log/ftpserver.cmd\n";
  953. while(<CUSTOM>) {
  954. if($_ =~ /REPLY ([A-Z]+) (.*)/) {
  955. $customreply{$1}=eval "qq{$2}";
  956. logmsg "FTPD: set custom reply for $1\n";
  957. }
  958. if($_ =~ /COUNT ([A-Z]+) (.*)/) {
  959. # we blank the customreply for this command when having
  960. # been used this number of times
  961. $customcount{$1}=$2;
  962. logmsg "FTPD: blank custom reply for $1 after $2 uses\n";
  963. }
  964. elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) {
  965. $delayreply{$1}=$2;
  966. logmsg "FTPD: delay reply for $1 with $2 seconds\n";
  967. }
  968. elsif($_ =~ /SLOWDOWN/) {
  969. $ctrldelay=1;
  970. $datadelay=1;
  971. logmsg "FTPD: send response with 0.01 sec delay between each byte\n";
  972. }
  973. elsif($_ =~ /RETRWEIRDO/) {
  974. logmsg "FTPD: instructed to use RETRWEIRDO\n";
  975. $retrweirdo=1;
  976. }
  977. elsif($_ =~ /RETRNOSIZE/) {
  978. logmsg "FTPD: instructed to use RETRNOSIZE\n";
  979. $retrnosize=1;
  980. }
  981. elsif($_ =~ /PASVBADIP/) {
  982. logmsg "FTPD: instructed to use PASVBADIP\n";
  983. $pasvbadip=1;
  984. }
  985. elsif($_ =~ /NOSAVE/) {
  986. # don't actually store the file we upload - to be used when
  987. # uploading insanely huge amounts
  988. $nosave = 1;
  989. logmsg "FTPD: NOSAVE prevents saving of uploaded data\n";
  990. }
  991. }
  992. close(CUSTOM);
  993. }
  994. #----------------------------------------------------------------------
  995. #----------------------------------------------------------------------
  996. #--------------------------- END OF SUBS ----------------------------
  997. #----------------------------------------------------------------------
  998. #----------------------------------------------------------------------
  999. #**********************************************************************
  1000. # Parse command line options
  1001. #
  1002. # Options:
  1003. #
  1004. # --verbose # verbose
  1005. # --srcdir # source directory
  1006. # --id # server instance number
  1007. # --proto # server protocol
  1008. # --pidfile # server pid file
  1009. # --logfile # server log file
  1010. # --ipv4 # server IP version 4
  1011. # --ipv6 # server IP version 6
  1012. # --port # server listener port
  1013. # --addr # server address for listener port binding
  1014. #
  1015. while(@ARGV) {
  1016. if($ARGV[0] eq '--verbose') {
  1017. $verbose = 1;
  1018. }
  1019. elsif($ARGV[0] eq '--srcdir') {
  1020. if($ARGV[1]) {
  1021. $srcdir = $ARGV[1];
  1022. shift @ARGV;
  1023. }
  1024. }
  1025. elsif($ARGV[0] eq '--id') {
  1026. if($ARGV[1] && ($ARGV[1] =~ /^(\d+)$/)) {
  1027. $idnum = $1 if($1 > 0);
  1028. shift @ARGV;
  1029. }
  1030. }
  1031. elsif($ARGV[0] eq '--proto') {
  1032. if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp)$/)) {
  1033. $proto = $1;
  1034. shift @ARGV;
  1035. }
  1036. else {
  1037. die "unsupported protocol $ARGV[1]";
  1038. }
  1039. }
  1040. elsif($ARGV[0] eq '--pidfile') {
  1041. if($ARGV[1]) {
  1042. $pidfile = $ARGV[1];
  1043. shift @ARGV;
  1044. }
  1045. }
  1046. elsif($ARGV[0] eq '--logfile') {
  1047. if($ARGV[1]) {
  1048. $logfile = $ARGV[1];
  1049. shift @ARGV;
  1050. }
  1051. }
  1052. elsif($ARGV[0] eq '--ipv4') {
  1053. $ipvnum = 4;
  1054. $listenaddr = '127.0.0.1' if($listenaddr eq '::1');
  1055. $grok_eprt = 0;
  1056. }
  1057. elsif($ARGV[0] eq '--ipv6') {
  1058. $ipvnum = 6;
  1059. $listenaddr = '::1' if($listenaddr eq '127.0.0.1');
  1060. $grok_eprt = 1;
  1061. }
  1062. elsif($ARGV[0] eq '--port') {
  1063. if($ARGV[1] && ($ARGV[1] =~ /^(\d+)$/)) {
  1064. $port = $1 if($1 > 1024);
  1065. shift @ARGV;
  1066. }
  1067. }
  1068. elsif($ARGV[0] eq '--addr') {
  1069. if($ARGV[1]) {
  1070. my $tmpstr = $ARGV[1];
  1071. if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) {
  1072. $listenaddr = "$1.$2.$3.$4" if($ipvnum == 4);
  1073. }
  1074. elsif($ipvnum == 6) {
  1075. $listenaddr = $tmpstr;
  1076. $listenaddr =~ s/^\[(.*)\]$/$1/;
  1077. }
  1078. shift @ARGV;
  1079. }
  1080. }
  1081. else {
  1082. print STDERR "\nWarning: ftpserver.pl unknown parameter: $ARGV[0]\n";
  1083. }
  1084. shift @ARGV;
  1085. }
  1086. #***************************************************************************
  1087. # Initialize command line option dependant variables
  1088. #
  1089. if(!$srcdir) {
  1090. $srcdir = $ENV{'srcdir'} || '.';
  1091. }
  1092. if(!$pidfile) {
  1093. $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum);
  1094. }
  1095. if(!$logfile) {
  1096. $logfile = server_logfilename($logdir, $proto, $ipvnum, $idnum);
  1097. }
  1098. $mainsockf_pidfile = "$path/".
  1099. mainsockf_pidfilename($proto, $ipvnum, $idnum);
  1100. $mainsockf_logfile =
  1101. mainsockf_logfilename($logdir, $proto, $ipvnum, $idnum);
  1102. if($proto eq 'ftp') {
  1103. $datasockf_pidfile = "$path/".
  1104. datasockf_pidfilename($proto, $ipvnum, $idnum);
  1105. $datasockf_logfile =
  1106. datasockf_logfilename($logdir, $proto, $ipvnum, $idnum);
  1107. }
  1108. $srvrname = servername_str($proto, $ipvnum, $idnum);
  1109. $idstr = "$idnum" if($idnum > 1);
  1110. protocolsetup($proto);
  1111. $SIG{INT} = \&exit_signal_handler;
  1112. $SIG{TERM} = \&exit_signal_handler;
  1113. startsf();
  1114. logmsg sprintf("%s server listens on port IPv${ipvnum}/${port}\n", uc($proto));
  1115. open(PID, ">$pidfile");
  1116. print PID $$."\n";
  1117. close(PID);
  1118. logmsg("logged pid $$ in $pidfile\n");
  1119. while(1) {
  1120. #
  1121. # We read 'sockfilt' commands.
  1122. #
  1123. my $input;
  1124. logmsg "Awaiting input\n";
  1125. sysread_or_die(\*SFREAD, \$input, 5);
  1126. if($input !~ /^CNCT/) {
  1127. # we wait for a connected client
  1128. logmsg "sockfilt said: $input";
  1129. next;
  1130. }
  1131. logmsg "====> Client connect\n";
  1132. set_advisor_read_lock($SERVERLOGS_LOCK);
  1133. $serverlogslocked = 1;
  1134. # flush data:
  1135. $| = 1;
  1136. killsockfilters($proto, $ipvnum, $idnum, $verbose, 'data');
  1137. &customize(); # read test control instructions
  1138. sendcontrol @welcome;
  1139. if($verbose) {
  1140. for(@welcome) {
  1141. print STDERR "OUT: $_";
  1142. }
  1143. }
  1144. while(1) {
  1145. my $i;
  1146. # Now we expect to read DATA\n[hex size]\n[prot], where the [prot]
  1147. # part only is FTP lingo.
  1148. # COMMAND
  1149. sysread_or_die(\*SFREAD, \$i, 5);
  1150. if($i !~ /^DATA/) {
  1151. logmsg "sockfilt said $i";
  1152. if($i =~ /^DISC/) {
  1153. # disconnect
  1154. last;
  1155. }
  1156. next;
  1157. }
  1158. # SIZE of data
  1159. sysread_or_die(\*SFREAD, \$i, 5);
  1160. my $size = 0;
  1161. if($i =~ /^([0-9a-fA-F]{4})\n/) {
  1162. $size = hex($1);
  1163. }
  1164. # data
  1165. sysread SFREAD, $_, $size;
  1166. ftpmsg $_;
  1167. # Remove trailing CRLF.
  1168. s/[\n\r]+$//;
  1169. my $FTPCMD;
  1170. my $FTPARG;
  1171. my $full=$_;
  1172. if($proto eq "imap") {
  1173. # IMAP is different with its identifier first on the command line
  1174. unless (m/^([^ ]+) ([^ ]+) (.*)/ ||
  1175. m/^([^ ]+) ([^ ]+)/) {
  1176. sendcontrol "$1 '$_': command not understood.\r\n";
  1177. last;
  1178. }
  1179. $cmdid=$1; # set the global variable
  1180. $FTPCMD=$2;
  1181. $FTPARG=$3;
  1182. }
  1183. else {
  1184. unless (m/^([A-Z]{3,4})\s?(.*)/i) {
  1185. sendcontrol "500 '$_': command not understood.\r\n";
  1186. last;
  1187. }
  1188. $FTPCMD=$1;
  1189. $FTPARG=$2;
  1190. }
  1191. logmsg "< \"$full\"\n";
  1192. if($verbose) {
  1193. print STDERR "IN: $full\n";
  1194. }
  1195. my $delay = $delayreply{$FTPCMD};
  1196. if($delay) {
  1197. # just go sleep this many seconds!
  1198. logmsg("Sleep for $delay seconds\n");
  1199. my $twentieths = $delay * 20;
  1200. while($twentieths--) {
  1201. select(undef, undef, undef, 0.05) unless($got_exit_signal);
  1202. }
  1203. }
  1204. my $text;
  1205. $text = $customreply{$FTPCMD};
  1206. my $fake = $text;
  1207. if($text && ($text ne "")) {
  1208. if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) {
  1209. # used enough number of times, now blank the customreply
  1210. $customreply{$FTPCMD}="";
  1211. }
  1212. }
  1213. else {
  1214. $text = $displaytext{$FTPCMD};
  1215. }
  1216. my $check;
  1217. if($text && ($text ne "")) {
  1218. if($cmdid && ($cmdid ne "")) {
  1219. sendcontrol "$cmdid$text\r\n";
  1220. }
  1221. else {
  1222. sendcontrol "$text\r\n";
  1223. }
  1224. }
  1225. else {
  1226. $check=1; # no response yet
  1227. }
  1228. unless($fake && ($fake ne "")) {
  1229. # only perform this if we're not faking a reply
  1230. my $func = $commandfunc{$FTPCMD};
  1231. if($func) {
  1232. &$func($FTPARG, $FTPCMD);
  1233. $check=0; # taken care of
  1234. }
  1235. }
  1236. if($check) {
  1237. logmsg "$FTPCMD wasn't handled!\n";
  1238. sendcontrol "500 $FTPCMD is not dealt with!\r\n";
  1239. }
  1240. } # while(1)
  1241. logmsg "====> Client disconnected\n";
  1242. if($serverlogslocked) {
  1243. $serverlogslocked = 0;
  1244. clear_advisor_read_lock($SERVERLOGS_LOCK);
  1245. }
  1246. }
  1247. killsockfilters($proto, $ipvnum, $idnum, $verbose);
  1248. unlink($pidfile);
  1249. if($serverlogslocked) {
  1250. $serverlogslocked = 0;
  1251. clear_advisor_read_lock($SERVERLOGS_LOCK);
  1252. }
  1253. exit;