server.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. $socket = null;
  3. $errno = 0;
  4. $context = stream_context_create(array('ssl' => array('local_cert' => dirname(__FILE__).'/cert.pem')));
  5. for ($i=0; $i<10 && !$socket; ++$i) {
  6. $port = rand(50000, 65535);
  7. $socket = stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
  8. }
  9. //set anther random port that is not the same as $port
  10. do{
  11. $pasv_port = rand(50000, 65535);
  12. }while($pasv_port == $port);
  13. if (!$socket) {
  14. die("could not start/bind the ftp server\n");
  15. }
  16. $pid = pcntl_fork();
  17. function pasv_listen($action){
  18. global $pasv_port, $tmp_file;
  19. $tmp_file = 'nm2.php';
  20. $pid = pcntl_fork();
  21. if($pid === 0){
  22. $soc = stream_socket_server("tcp://127.0.0.1:$pasv_port");
  23. $fs = stream_socket_accept($soc, 3);
  24. switch ($action) {
  25. case 'fget':
  26. case 'get':
  27. //listen for 3 seconds 3 seconds
  28. fputs($fs, "I am passive.\r\n");
  29. break;
  30. case 'put':
  31. file_put_contents($tmp_file, stream_get_contents($fs));
  32. break;
  33. case 'list':
  34. fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 .\r\n");
  35. fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 ..\r\n");
  36. fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 public_ftp\r\n");
  37. break;
  38. case 'list_null':
  39. fputs($fs, "\r\n");
  40. break;
  41. }
  42. fclose($fs);
  43. exit;
  44. }
  45. }
  46. if ($pid) {
  47. function dump_and_exit($buf)
  48. {
  49. var_dump($buf);
  50. fclose($GLOBALS['s']);
  51. exit;
  52. }
  53. function anonymous()
  54. {
  55. return $GLOBALS['user'] === 'anonymous';
  56. }
  57. /* quick&dirty realpath() like function */
  58. function change_dir($dir)
  59. {
  60. global $cwd;
  61. if ($dir[0] == '/') {
  62. $cwd = $dir;
  63. return;
  64. }
  65. $cwd = "$cwd/$dir";
  66. do {
  67. $old = $cwd;
  68. $cwd = preg_replace('@/?[^/]+/\.\.@', '', $cwd);
  69. } while ($old != $cwd);
  70. $cwd = strtr($cwd, array('//' => '/'));
  71. if (!$cwd) $cwd = '/';
  72. }
  73. $s = stream_socket_accept($socket);
  74. if (!$s) die("Error accepting a new connection\n");
  75. fputs($s, "220----- PHP FTP server 0.3 -----\r\n220 Service ready\r\n");
  76. $buf = fread($s, 2048);
  77. function user_auth($buf) {
  78. global $user, $s, $ssl, $bug37799;
  79. if (!empty($ssl)) {
  80. if ($buf !== "AUTH TLS\r\n") {
  81. fputs($s, "500 Syntax error, command unrecognized.\r\n");
  82. dump_and_exit($buf);
  83. }
  84. if (empty($bug37799)) {
  85. fputs($s, "234 auth type accepted\r\n");
  86. } else {
  87. fputs($s, "666 dummy\r\n");
  88. fputs($s, "666 bogus msg\r\n");
  89. exit;
  90. }
  91. if (!stream_socket_enable_crypto($s, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER)) {
  92. die("SSLv23 handshake failed.\n");
  93. }
  94. if (!preg_match('/^PBSZ \d+\r\n$/', $buf = fread($s, 2048))) {
  95. fputs($s, "501 bogus data\r\n");
  96. dump_and_exit($buf);
  97. }
  98. fputs($s, "200 OK\r\n");
  99. $buf = fread($s, 2048);
  100. if ($buf !== "PROT P\r\n") {
  101. fputs($s, "504 Wrong protection.\r\n");
  102. dump_and_exit($buf);
  103. }
  104. fputs($s, "200 OK\r\n");
  105. $buf = fread($s, 2048);
  106. }
  107. if ($buf == "AUTH TLS\r\n") {
  108. fputs($s, "500 not supported.\r\n");
  109. return ;
  110. } else if (!preg_match('/^USER (\w+)\r\n$/', $buf, $m)) {
  111. fputs($s, "500 Syntax error, command unrecognized.\r\n");
  112. dump_and_exit($buf);
  113. }
  114. $user = $m[1];
  115. if ($user !== 'user' && $user !== 'anonymous') {
  116. fputs($s, "530 Not logged in.\r\n");
  117. fclose($s);
  118. exit;
  119. }
  120. if (anonymous()) {
  121. fputs($s, "230 Anonymous user logged in\r\n");
  122. } else {
  123. fputs($s, "331 User name ok, need password\r\n");
  124. if (!preg_match('/^PASS (\w+)\r\n$/', $buf = fread($s, 100), $m)) {
  125. fputs($s, "500 Syntax error, command unrecognized.\r\n");
  126. dump_and_exit($buf);
  127. }
  128. $pass = $m[1];
  129. if ($pass === 'pass') {
  130. fputs($s, "230 User logged in\r\n");
  131. } else {
  132. fputs($s, "530 Not logged in.\r\n");
  133. fclose($s);
  134. exit;
  135. }
  136. }
  137. }
  138. user_auth($buf);
  139. $cwd = '/';
  140. $num_bogus_cmds = 0;
  141. while($buf = fread($s, 4098)) {
  142. if (!empty($bogus)) {
  143. fputs($s, "502 Command not implemented (".$num_bogus_cmds++.").\r\n");
  144. } else if ($buf === "HELP\r\n") {
  145. fputs($s, "214-There is help available for the following commands:\r\n");
  146. fputs($s, " USER\r\n");
  147. fputs($s, " HELP\r\n");
  148. fputs($s, "214 end of list\r\n");
  149. } elseif ($buf === "HELP HELP\r\n") {
  150. fputs($s, "214 Syntax: HELP [<SP> <string>] <CRLF>\r\n");
  151. } elseif ($buf === "PWD\r\n") {
  152. fputs($s, "257 \"$cwd\" is current directory.\r\n");
  153. } elseif ($buf === "CDUP\r\n") {
  154. change_dir('..');
  155. fputs($s, "250 CDUP command successful.\r\n");
  156. } elseif ($buf === "SYST\r\n") {
  157. if (isset($bug27809)) {
  158. fputs($s, "215 OS/400 is the remote operating system. The TCP/IP version is \"V5R2M0\"\r\n");
  159. } else {
  160. fputs($s, "215 UNIX Type: L8.\r\n");
  161. }
  162. } elseif ($buf === "TYPE A\r\n") {
  163. $ascii = true;
  164. fputs($s, "200 OK\r\n");
  165. } elseif ($buf === "AUTH SSL\r\n") {
  166. $ascii = true;
  167. fputs($s, "500 not supported\r\n");
  168. } elseif ($buf === "TYPE I\r\n") {
  169. $ascii = false;
  170. fputs($s, "200 OK\r\n");
  171. } elseif ($buf === "QUIT\r\n") {
  172. break;
  173. } elseif (preg_match("~^PORT (\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\r\n$~", $buf, $m)) {
  174. $host = "$m[1].$m[2].$m[3].$m[4]";
  175. $port = ((int)$m[5] << 8) + (int)$m[6];
  176. fputs($s, "200 OK.\r\n");
  177. } elseif (preg_match("~^STOR ([\w/.-]+)\r\n$~", $buf, $m)) {
  178. fputs($s, "150 File status okay; about to open data connection\r\n");
  179. if(empty($pasv))
  180. {
  181. if (!$fs = stream_socket_client("tcp://$host:$port")) {
  182. fputs($s, "425 Can't open data connection\r\n");
  183. continue;
  184. }
  185. $data = stream_get_contents($fs);
  186. $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]);
  187. if (isset($ascii) && !$ascii && $orig === $data) {
  188. fputs($s, "226 Closing data Connection.\r\n");
  189. } elseif ((!empty($ascii) || isset($bug39583)) && $data === strtr($orig, array("\r\n" => "\n", "\r" => "\n", "\n" => "\r\n"))) {
  190. fputs($s, "226 Closing data Connection.\r\n");
  191. } else {
  192. var_dump($data);
  193. var_dump($orig);
  194. fputs($s, "552 Requested file action aborted.\r\n");
  195. }
  196. fclose($fs);
  197. }else{
  198. $data = file_get_contents('nm2.php');
  199. $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]);
  200. if ( $orig === $data) {
  201. fputs($s, "226 Closing data Connection.\r\n");
  202. } else {
  203. var_dump($data);
  204. var_dump($orig);
  205. fputs($s, "552 Requested file action aborted.\r\n");
  206. }
  207. }
  208. } elseif (preg_match("~^CWD ([A-Za-z./]+)\r\n$~", $buf, $m)) {
  209. change_dir($m[1]);
  210. fputs($s, "250 CWD command successful.\r\n");
  211. } elseif (preg_match("~^NLST(?: ([A-Za-z./]+))?\r\n$~", $buf, $m)) {
  212. if (isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) {
  213. fputs($s, "250 $m[1]: No such file or directory\r\n");
  214. continue;
  215. }
  216. // there are some servers that don't open the ftp-data socket if there's nothing to send
  217. if (isset($bug39458) && isset($m[1]) && $m[1] === 'emptydir') {
  218. fputs($s, "226 Transfer complete.\r\n");
  219. continue;
  220. }
  221. if (empty($pasv)) {
  222. fputs($s, "150 File status okay; about to open data connection\r\n");
  223. if (!$fs = stream_socket_client("tcp://$host:$port")) {
  224. fputs($s, "425 Can't open data connection\r\n");
  225. continue;
  226. }
  227. } else {
  228. fputs($s, "125 Data connection already open; transfer starting.\r\n");
  229. $fs=$pasvs;
  230. }
  231. if ((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) {
  232. die("SSLv23 handshake failed.\n");
  233. }
  234. if (empty($m[1]) || $m[1] !== 'emptydir') {
  235. fputs($fs, "file1\r\nfile1\r\nfile\nb0rk\r\n");
  236. }
  237. fputs($s, "226 Closing data Connection.\r\n");
  238. fclose($fs);
  239. } elseif (preg_match("~^MKD ([A-Za-z./]+)\r\n$~", $buf, $m)) {
  240. if (isset($bug7216)) {
  241. fputs($s, "257 OK.\r\n");
  242. } else {
  243. fputs($s, "257 \"/path/to/ftproot$cwd$m[1]\" created.\r\n");
  244. }
  245. } elseif (preg_match('/^USER /', $buf)) {
  246. user_auth($buf);
  247. } elseif (preg_match('/^MDTM ([\w\h]+)/', $buf, $matches)) {
  248. switch ($matches [1]){
  249. case "A":
  250. fputs($s, "213 19980615100045.014\r\n");
  251. break;
  252. case "B":
  253. fputs($s, "213 19980615100045.014\r\n");
  254. break;
  255. case "C":
  256. fputs($s, "213 19980705132316\r\n");
  257. break;
  258. case "19990929043300 File6":
  259. fputs($s, "213 19991005213102\r\n");
  260. break;
  261. default :
  262. fputs($s, "550 No file named \"{$matches [1]}\"\r\n");
  263. break;
  264. }
  265. }elseif (preg_match('/^RETR ([\w\h]+)/', $buf, $matches)) {
  266. if(!empty($pasv)){
  267. ;
  268. }
  269. else if (!$fs = stream_socket_client("tcp://$host:$port")) {
  270. fputs($s, "425 Can't open data connection\r\n");
  271. continue;
  272. }
  273. switch($matches[1]){
  274. case "pasv":
  275. fputs($s, "150 File status okay; about to open data connection.\r\n");
  276. //the data connection is handled in another forked process
  277. // called from outside this while loop
  278. fputs($s, "226 Closing data Connection.\r\n");
  279. break;
  280. case "a story":
  281. fputs($s, "150 File status okay; about to open data connection.\r\n");
  282. fputs($fs, "For sale: baby shoes, never worn.\r\n");
  283. fputs($s, "226 Closing data Connection.\r\n");
  284. break;
  285. case "binary data":
  286. fputs($s, "150 File status okay; about to open data connection.\r\n");
  287. $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
  288. fputs($fs, $transfer_type."Foo\0Bar\r\n");
  289. fputs($s, "226 Closing data Connection.\r\n");
  290. break;
  291. case "fget":
  292. fputs($s, "150 File status okay; about to open data connection.\r\n");
  293. $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
  294. fputs($fs, $transfer_type."FooBar\r\n");
  295. fputs($s, "226 Closing data Connection.\r\n");
  296. break;
  297. case "fgetresume":
  298. fputs($s, "150 File status okay; about to open data connection.\r\n");
  299. $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
  300. fputs($fs, "Bar\r\n");
  301. fputs($s, "226 Closing data Connection.\r\n");
  302. break;
  303. case "fget_large":
  304. fputs($s, "150 File status okay; about to open data connection.\r\n");
  305. $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
  306. if ($GLOBALS['rest_pos'] == '5368709119') {
  307. fputs($fs, "X");
  308. } else {
  309. fputs($fs, "Y");
  310. }
  311. fputs($s, "226 Closing data Connection.\r\n");
  312. break;
  313. case "mediumfile":
  314. fputs($s, "150 File status okay; about to open data connection.\r\n");
  315. for($i = 0; $i < 150; $i++){
  316. fputs($fs, "This is line $i of the test data.\n");
  317. }
  318. fputs($s, "226 Closing data Connection.\r\n");
  319. default:
  320. fputs($s, "550 {$matches[1]}: No such file or directory \r\n");
  321. break;
  322. }
  323. if(isset($fs))
  324. fclose($fs);
  325. }elseif (preg_match('/^PASV/', $buf, $matches)) {
  326. $pasv=true;
  327. $p2 = $pasv_port % ((int) 1 << 8);
  328. $p1 = ($pasv_port-$p2)/((int) 1 << 8);
  329. $host = "127.0.0.1";
  330. if (!empty($ssl)) {
  331. $soc = stream_socket_server("tcp://127.0.0.1:$pasv_port", $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
  332. } else {
  333. $soc = stream_socket_server("tcp://127.0.0.1:$pasv_port");
  334. }
  335. fputs($s, "227 Entering Passive Mode. (127,0,0,1,{$p1},{$p2})\r\n");
  336. $pasvs = stream_socket_accept($soc,10);
  337. }elseif (preg_match('/^EPSV/', $buf, $matches)) {
  338. fputs($s, "550 Extended passsive mode not supported.\r\n");
  339. } elseif (preg_match('/^SITE EXEC/', $buf, $matches)) {
  340. fputs($s, "200 OK\r\n");
  341. } elseif (preg_match('/^RMD/', $buf, $matches)) {
  342. fputs($s, "250 OK\r\n");
  343. } elseif (preg_match('/^SITE CHMOD/', $buf, $matches)) {
  344. fputs($s, "200 OK\r\n");
  345. } elseif (preg_match('/^ALLO (\d+)/', $buf, $matches)) {
  346. fputs($s, "200 " . $matches[1] . " bytes allocated\r\n");
  347. }elseif (preg_match('/^LIST www\//', $buf, $matches)) {
  348. fputs($s, "150 Opening ASCII mode data connection for file list\r\n");
  349. fputs($s, "226 Transfer complete\r\n");
  350. }elseif (preg_match('/^LIST no_exists\//', $buf, $matches)) {
  351. fputs($s, "425 Error establishing connection\r\n");
  352. }elseif (preg_match('/^REST (\d+)/', $buf, $matches)) {
  353. $GLOBALS['rest_pos'] = $matches[1];
  354. fputs($s, "350 OK\r\n");
  355. }elseif (preg_match('/^SIZE largefile/', $buf)) {
  356. fputs($s, "213 5368709120\r\n");
  357. }else {
  358. fputs($s, "500 Syntax error, command unrecognized.\r\n");
  359. dump_and_exit($buf);
  360. }
  361. }
  362. fclose($s);
  363. exit;
  364. }
  365. fclose($socket);
  366. ?>