ftpserver.pl 61 KB

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