1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andrew Skalski <askalski@chek.com> |
16 | Stefan Esser <sesser@php.net> (resume functions) |
17 +----------------------------------------------------------------------+
18 */
19
20 /* $Id$ */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "php.h"
27
28 #if HAVE_FTP
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <fcntl.h>
37 #include <string.h>
38 #include <time.h>
39 #ifdef PHP_WIN32
40 #include <winsock2.h>
41 #elif defined(NETWARE)
42 #ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), at least for now */
43 #include <novsock2.h>
44 #else
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <netdb.h>
48 #endif
49 #else
50 #ifdef HAVE_SYS_TYPES_H
51 #include <sys/types.h>
52 #endif
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57 #endif
58 #include <errno.h>
59
60 #if HAVE_SYS_TIME_H
61 #include <sys/time.h>
62 #endif
63
64 #ifdef HAVE_SYS_SELECT_H
65 #include <sys/select.h>
66 #endif
67
68 #ifdef HAVE_FTP_SSL
69 #include <openssl/ssl.h>
70 #include <openssl/err.h>
71 #endif
72
73 #include "ftp.h"
74 #include "ext/standard/fsock.h"
75
76 /* Additional headers for NetWare */
77 #if defined(NETWARE) && !defined(USE_WINSOCK)
78 #include <sys/select.h>
79 #endif
80
81 /* sends an ftp command, returns true on success, false on error.
82 * it sends the string "cmd args\r\n" if args is non-null, or
83 * "cmd\r\n" if args is null
84 */
85 static int ftp_putcmd( ftpbuf_t *ftp,
86 const char *cmd,
87 const char *args);
88
89 /* wrapper around send/recv to handle timeouts */
90 static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
91 static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
92 static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
93
94 /* reads a line the socket , returns true on success, false on error */
95 static int ftp_readline(ftpbuf_t *ftp);
96
97 /* reads an ftp response, returns true on success, false on error */
98 static int ftp_getresp(ftpbuf_t *ftp);
99
100 /* sets the ftp transfer type */
101 static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
102
103 /* opens up a data stream */
104 static databuf_t* ftp_getdata(ftpbuf_t *ftp);
105
106 /* accepts the data connection, returns updated data buffer */
107 static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp);
108
109 /* closes the data connection, returns NULL */
110 static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
111
112 /* generic file lister */
113 static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path);
114
115 #ifdef HAVE_FTP_SSL
116 /* shuts down a TLS/SSL connection */
117 static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle);
118 #endif
119
120 /* IP and port conversion box */
121 union ipbox {
122 struct in_addr ia[2];
123 unsigned short s[4];
124 unsigned char c[8];
125 };
126
127 /* {{{ ftp_open
128 */
129 ftpbuf_t*
ftp_open(const char * host,short port,zend_long timeout_sec)130 ftp_open(const char *host, short port, zend_long timeout_sec)
131 {
132 ftpbuf_t *ftp;
133 socklen_t size;
134 struct timeval tv;
135
136
137 /* alloc the ftp structure */
138 ftp = ecalloc(1, sizeof(*ftp));
139
140 tv.tv_sec = timeout_sec;
141 tv.tv_usec = 0;
142
143 ftp->fd = php_network_connect_socket_to_host(host,
144 (unsigned short) (port ? port : 21), SOCK_STREAM,
145 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);
146 if (ftp->fd == -1) {
147 goto bail;
148 }
149
150 /* Default Settings */
151 ftp->timeout_sec = timeout_sec;
152 ftp->nb = 0;
153
154 size = sizeof(ftp->localaddr);
155 memset(&ftp->localaddr, 0, size);
156 if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
157 php_error_docref(NULL, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
158 goto bail;
159 }
160
161 if (!ftp_getresp(ftp) || ftp->resp != 220) {
162 goto bail;
163 }
164
165 return ftp;
166
167 bail:
168 if (ftp->fd != -1) {
169 closesocket(ftp->fd);
170 }
171 efree(ftp);
172 return NULL;
173 }
174 /* }}} */
175
176 /* {{{ ftp_close
177 */
178 ftpbuf_t*
ftp_close(ftpbuf_t * ftp)179 ftp_close(ftpbuf_t *ftp)
180 {
181 if (ftp == NULL) {
182 return NULL;
183 }
184 if (ftp->data) {
185 data_close(ftp, ftp->data);
186 }
187 if (ftp->stream && ftp->closestream) {
188 php_stream_close(ftp->stream);
189 }
190 if (ftp->fd != -1) {
191 #ifdef HAVE_FTP_SSL
192 if (ftp->ssl_active) {
193 ftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle);
194 }
195 #endif
196 closesocket(ftp->fd);
197 }
198 ftp_gc(ftp);
199 efree(ftp);
200 return NULL;
201 }
202 /* }}} */
203
204 /* {{{ ftp_gc
205 */
206 void
ftp_gc(ftpbuf_t * ftp)207 ftp_gc(ftpbuf_t *ftp)
208 {
209 if (ftp == NULL) {
210 return;
211 }
212 if (ftp->pwd) {
213 efree(ftp->pwd);
214 ftp->pwd = NULL;
215 }
216 if (ftp->syst) {
217 efree(ftp->syst);
218 ftp->syst = NULL;
219 }
220 }
221 /* }}} */
222
223 /* {{{ ftp_quit
224 */
225 int
ftp_quit(ftpbuf_t * ftp)226 ftp_quit(ftpbuf_t *ftp)
227 {
228 if (ftp == NULL) {
229 return 0;
230 }
231
232 if (!ftp_putcmd(ftp, "QUIT", NULL)) {
233 return 0;
234 }
235 if (!ftp_getresp(ftp) || ftp->resp != 221) {
236 return 0;
237 }
238
239 if (ftp->pwd) {
240 efree(ftp->pwd);
241 ftp->pwd = NULL;
242 }
243
244 return 1;
245 }
246 /* }}} */
247
248 /* {{{ ftp_login
249 */
250 int
ftp_login(ftpbuf_t * ftp,const char * user,const char * pass)251 ftp_login(ftpbuf_t *ftp, const char *user, const char *pass)
252 {
253 #ifdef HAVE_FTP_SSL
254 SSL_CTX *ctx = NULL;
255 long ssl_ctx_options = SSL_OP_ALL;
256 int err, res;
257 zend_bool retry;
258 #endif
259 if (ftp == NULL) {
260 return 0;
261 }
262
263 #ifdef HAVE_FTP_SSL
264 if (ftp->use_ssl && !ftp->ssl_active) {
265 if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
266 return 0;
267 }
268 if (!ftp_getresp(ftp)) {
269 return 0;
270 }
271
272 if (ftp->resp != 234) {
273 if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
274 return 0;
275 }
276 if (!ftp_getresp(ftp)) {
277 return 0;
278 }
279
280 if (ftp->resp != 334) {
281 return 0;
282 } else {
283 ftp->old_ssl = 1;
284 ftp->use_ssl_for_data = 1;
285 }
286 }
287
288 ctx = SSL_CTX_new(SSLv23_client_method());
289 if (ctx == NULL) {
290 php_error_docref(NULL, E_WARNING, "failed to create the SSL context");
291 return 0;
292 }
293
294 #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
295 ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
296 #endif
297 SSL_CTX_set_options(ctx, ssl_ctx_options);
298
299 /* allow SSL to re-use sessions */
300 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
301
302 ftp->ssl_handle = SSL_new(ctx);
303 if (ftp->ssl_handle == NULL) {
304 php_error_docref(NULL, E_WARNING, "failed to create the SSL handle");
305 SSL_CTX_free(ctx);
306 return 0;
307 }
308
309 SSL_set_fd(ftp->ssl_handle, ftp->fd);
310
311 do {
312 res = SSL_connect(ftp->ssl_handle);
313 err = SSL_get_error(ftp->ssl_handle, res);
314
315 /* TODO check if handling other error codes would make sense */
316 switch (err) {
317 case SSL_ERROR_NONE:
318 retry = 0;
319 break;
320
321 case SSL_ERROR_ZERO_RETURN:
322 retry = 0;
323 SSL_shutdown(ftp->ssl_handle);
324 break;
325
326 case SSL_ERROR_WANT_READ:
327 case SSL_ERROR_WANT_WRITE: {
328 php_pollfd p;
329 int i;
330
331 p.fd = ftp->fd;
332 p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
333 p.revents = 0;
334
335 i = php_poll2(&p, 1, 300);
336
337 retry = i > 0;
338 }
339 break;
340
341 default:
342 php_error_docref(NULL, E_WARNING, "SSL/TLS handshake failed");
343 SSL_shutdown(ftp->ssl_handle);
344 SSL_free(ftp->ssl_handle);
345 return 0;
346 }
347 } while (retry);
348
349 ftp->ssl_active = 1;
350
351 if (!ftp->old_ssl) {
352
353 /* set protection buffersize to zero */
354 if (!ftp_putcmd(ftp, "PBSZ", "0")) {
355 return 0;
356 }
357 if (!ftp_getresp(ftp)) {
358 return 0;
359 }
360
361 /* enable data conn encryption */
362 if (!ftp_putcmd(ftp, "PROT", "P")) {
363 return 0;
364 }
365 if (!ftp_getresp(ftp)) {
366 return 0;
367 }
368
369 ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
370 }
371 }
372 #endif
373
374 if (!ftp_putcmd(ftp, "USER", user)) {
375 return 0;
376 }
377 if (!ftp_getresp(ftp)) {
378 return 0;
379 }
380 if (ftp->resp == 230) {
381 return 1;
382 }
383 if (ftp->resp != 331) {
384 return 0;
385 }
386 if (!ftp_putcmd(ftp, "PASS", pass)) {
387 return 0;
388 }
389 if (!ftp_getresp(ftp)) {
390 return 0;
391 }
392 return (ftp->resp == 230);
393 }
394 /* }}} */
395
396 /* {{{ ftp_reinit
397 */
398 int
ftp_reinit(ftpbuf_t * ftp)399 ftp_reinit(ftpbuf_t *ftp)
400 {
401 if (ftp == NULL) {
402 return 0;
403 }
404
405 ftp_gc(ftp);
406
407 ftp->nb = 0;
408
409 if (!ftp_putcmd(ftp, "REIN", NULL)) {
410 return 0;
411 }
412 if (!ftp_getresp(ftp) || ftp->resp != 220) {
413 return 0;
414 }
415
416 return 1;
417 }
418 /* }}} */
419
420 /* {{{ ftp_syst
421 */
422 const char*
ftp_syst(ftpbuf_t * ftp)423 ftp_syst(ftpbuf_t *ftp)
424 {
425 char *syst, *end;
426
427 if (ftp == NULL) {
428 return NULL;
429 }
430
431 /* default to cached value */
432 if (ftp->syst) {
433 return ftp->syst;
434 }
435 if (!ftp_putcmd(ftp, "SYST", NULL)) {
436 return NULL;
437 }
438 if (!ftp_getresp(ftp) || ftp->resp != 215) {
439 return NULL;
440 }
441 syst = ftp->inbuf;
442 while (*syst == ' ') {
443 syst++;
444 }
445 if ((end = strchr(syst, ' '))) {
446 *end = 0;
447 }
448 ftp->syst = estrdup(syst);
449 if (end) {
450 *end = ' ';
451 }
452 return ftp->syst;
453 }
454 /* }}} */
455
456 /* {{{ ftp_pwd
457 */
458 const char*
ftp_pwd(ftpbuf_t * ftp)459 ftp_pwd(ftpbuf_t *ftp)
460 {
461 char *pwd, *end;
462
463 if (ftp == NULL) {
464 return NULL;
465 }
466
467 /* default to cached value */
468 if (ftp->pwd) {
469 return ftp->pwd;
470 }
471 if (!ftp_putcmd(ftp, "PWD", NULL)) {
472 return NULL;
473 }
474 if (!ftp_getresp(ftp) || ftp->resp != 257) {
475 return NULL;
476 }
477 /* copy out the pwd from response */
478 if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
479 return NULL;
480 }
481 if ((end = strrchr(++pwd, '"')) == NULL) {
482 return NULL;
483 }
484 ftp->pwd = estrndup(pwd, end - pwd);
485
486 return ftp->pwd;
487 }
488 /* }}} */
489
490 /* {{{ ftp_exec
491 */
492 int
ftp_exec(ftpbuf_t * ftp,const char * cmd)493 ftp_exec(ftpbuf_t *ftp, const char *cmd)
494 {
495 if (ftp == NULL) {
496 return 0;
497 }
498 if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
499 return 0;
500 }
501 if (!ftp_getresp(ftp) || ftp->resp != 200) {
502 return 0;
503 }
504
505 return 1;
506 }
507 /* }}} */
508
509 /* {{{ ftp_raw
510 */
511 void
ftp_raw(ftpbuf_t * ftp,const char * cmd,zval * return_value)512 ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
513 {
514 if (ftp == NULL || cmd == NULL) {
515 RETURN_NULL();
516 }
517 if (!ftp_putcmd(ftp, cmd, NULL)) {
518 RETURN_NULL();
519 }
520 array_init(return_value);
521 while (ftp_readline(ftp)) {
522 add_next_index_string(return_value, ftp->inbuf);
523 if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
524 return;
525 }
526 }
527 }
528 /* }}} */
529
530 /* {{{ ftp_chdir
531 */
532 int
ftp_chdir(ftpbuf_t * ftp,const char * dir)533 ftp_chdir(ftpbuf_t *ftp, const char *dir)
534 {
535 if (ftp == NULL) {
536 return 0;
537 }
538
539 if (ftp->pwd) {
540 efree(ftp->pwd);
541 ftp->pwd = NULL;
542 }
543
544 if (!ftp_putcmd(ftp, "CWD", dir)) {
545 return 0;
546 }
547 if (!ftp_getresp(ftp) || ftp->resp != 250) {
548 return 0;
549 }
550 return 1;
551 }
552 /* }}} */
553
554 /* {{{ ftp_cdup
555 */
556 int
ftp_cdup(ftpbuf_t * ftp)557 ftp_cdup(ftpbuf_t *ftp)
558 {
559 if (ftp == NULL) {
560 return 0;
561 }
562
563 if (ftp->pwd) {
564 efree(ftp->pwd);
565 ftp->pwd = NULL;
566 }
567
568 if (!ftp_putcmd(ftp, "CDUP", NULL)) {
569 return 0;
570 }
571 if (!ftp_getresp(ftp) || ftp->resp != 250) {
572 return 0;
573 }
574 return 1;
575 }
576 /* }}} */
577
578 /* {{{ ftp_mkdir
579 */
580 zend_string*
ftp_mkdir(ftpbuf_t * ftp,const char * dir)581 ftp_mkdir(ftpbuf_t *ftp, const char *dir)
582 {
583 char *mkd, *end;
584 zend_string *ret;
585
586 if (ftp == NULL) {
587 return NULL;
588 }
589 if (!ftp_putcmd(ftp, "MKD", dir)) {
590 return NULL;
591 }
592 if (!ftp_getresp(ftp) || ftp->resp != 257) {
593 return NULL;
594 }
595 /* copy out the dir from response */
596 if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
597 return zend_string_init(dir, strlen(dir), 0);
598 }
599 if ((end = strrchr(++mkd, '"')) == NULL) {
600 return NULL;
601 }
602 *end = 0;
603 ret = zend_string_init(mkd, end - mkd, 0);
604 *end = '"';
605
606 return ret;
607 }
608 /* }}} */
609
610 /* {{{ ftp_rmdir
611 */
612 int
ftp_rmdir(ftpbuf_t * ftp,const char * dir)613 ftp_rmdir(ftpbuf_t *ftp, const char *dir)
614 {
615 if (ftp == NULL) {
616 return 0;
617 }
618 if (!ftp_putcmd(ftp, "RMD", dir)) {
619 return 0;
620 }
621 if (!ftp_getresp(ftp) || ftp->resp != 250) {
622 return 0;
623 }
624 return 1;
625 }
626 /* }}} */
627
628 /* {{{ ftp_chmod
629 */
630 int
ftp_chmod(ftpbuf_t * ftp,const int mode,const char * filename,const int filename_len)631 ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
632 {
633 char *buffer;
634
635 if (ftp == NULL || filename_len <= 0) {
636 return 0;
637 }
638
639 spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
640
641 if (!ftp_putcmd(ftp, "SITE", buffer)) {
642 efree(buffer);
643 return 0;
644 }
645
646 efree(buffer);
647
648 if (!ftp_getresp(ftp) || ftp->resp != 200) {
649 return 0;
650 }
651
652 return 1;
653 }
654 /* }}} */
655
656 /* {{{ ftp_alloc
657 */
658 int
ftp_alloc(ftpbuf_t * ftp,const zend_long size,zend_string ** response)659 ftp_alloc(ftpbuf_t *ftp, const zend_long size, zend_string **response)
660 {
661 char buffer[64];
662
663 if (ftp == NULL || size <= 0) {
664 return 0;
665 }
666
667 snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size);
668
669 if (!ftp_putcmd(ftp, "ALLO", buffer)) {
670 return 0;
671 }
672
673 if (!ftp_getresp(ftp)) {
674 return 0;
675 }
676
677 if (response) {
678 *response = zend_string_init(ftp->inbuf, strlen(ftp->inbuf), 0);
679 }
680
681 if (ftp->resp < 200 || ftp->resp >= 300) {
682 return 0;
683 }
684
685 return 1;
686 }
687 /* }}} */
688
689 /* {{{ ftp_nlist
690 */
691 char**
ftp_nlist(ftpbuf_t * ftp,const char * path)692 ftp_nlist(ftpbuf_t *ftp, const char *path)
693 {
694 return ftp_genlist(ftp, "NLST", path);
695 }
696 /* }}} */
697
698 /* {{{ ftp_list
699 */
700 char**
ftp_list(ftpbuf_t * ftp,const char * path,int recursive)701 ftp_list(ftpbuf_t *ftp, const char *path, int recursive)
702 {
703 return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path);
704 }
705 /* }}} */
706
707 /* {{{ ftp_type
708 */
709 int
ftp_type(ftpbuf_t * ftp,ftptype_t type)710 ftp_type(ftpbuf_t *ftp, ftptype_t type)
711 {
712 char typechar[2] = "?";
713
714 if (ftp == NULL) {
715 return 0;
716 }
717 if (type == ftp->type) {
718 return 1;
719 }
720 if (type == FTPTYPE_ASCII) {
721 typechar[0] = 'A';
722 } else if (type == FTPTYPE_IMAGE) {
723 typechar[0] = 'I';
724 } else {
725 return 0;
726 }
727 if (!ftp_putcmd(ftp, "TYPE", typechar)) {
728 return 0;
729 }
730 if (!ftp_getresp(ftp) || ftp->resp != 200) {
731 return 0;
732 }
733 ftp->type = type;
734
735 return 1;
736 }
737 /* }}} */
738
739 /* {{{ ftp_pasv
740 */
741 int
ftp_pasv(ftpbuf_t * ftp,int pasv)742 ftp_pasv(ftpbuf_t *ftp, int pasv)
743 {
744 char *ptr;
745 union ipbox ipbox;
746 unsigned long b[6];
747 socklen_t n;
748 struct sockaddr *sa;
749 struct sockaddr_in *sin;
750
751 if (ftp == NULL) {
752 return 0;
753 }
754 if (pasv && ftp->pasv == 2) {
755 return 1;
756 }
757 ftp->pasv = 0;
758 if (!pasv) {
759 return 1;
760 }
761 n = sizeof(ftp->pasvaddr);
762 memset(&ftp->pasvaddr, 0, n);
763 sa = (struct sockaddr *) &ftp->pasvaddr;
764
765 if (getpeername(ftp->fd, sa, &n) < 0) {
766 return 0;
767 }
768
769 #if HAVE_IPV6
770 if (sa->sa_family == AF_INET6) {
771 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
772 char *endptr, delimiter;
773
774 /* try EPSV first */
775 if (!ftp_putcmd(ftp, "EPSV", NULL)) {
776 return 0;
777 }
778 if (!ftp_getresp(ftp)) {
779 return 0;
780 }
781 if (ftp->resp == 229) {
782 /* parse out the port */
783 for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
784 if (!*ptr) {
785 return 0;
786 }
787 delimiter = *++ptr;
788 for (n = 0; *ptr && n < 3; ptr++) {
789 if (*ptr == delimiter) {
790 n++;
791 }
792 }
793
794 sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
795 if (ptr == endptr || *endptr != delimiter) {
796 return 0;
797 }
798 ftp->pasv = 2;
799 return 1;
800 }
801 }
802
803 /* fall back to PASV */
804 #endif
805
806 if (!ftp_putcmd(ftp, "PASV", NULL)) {
807 return 0;
808 }
809 if (!ftp_getresp(ftp) || ftp->resp != 227) {
810 return 0;
811 }
812 /* parse out the IP and port */
813 for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
814 n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
815 if (n != 6) {
816 return 0;
817 }
818 for (n = 0; n < 6; n++) {
819 ipbox.c[n] = (unsigned char) b[n];
820 }
821 sin = (struct sockaddr_in *) sa;
822 if (ftp->usepasvaddress) {
823 sin->sin_addr = ipbox.ia[0];
824 }
825 sin->sin_port = ipbox.s[2];
826
827 ftp->pasv = 2;
828
829 return 1;
830 }
831 /* }}} */
832
833 /* {{{ ftp_get
834 */
835 int
ftp_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,zend_long resumepos)836 ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, zend_long resumepos)
837 {
838 databuf_t *data = NULL;
839 size_t rcvd;
840 char arg[11];
841
842 if (ftp == NULL) {
843 return 0;
844 }
845 if (!ftp_type(ftp, type)) {
846 goto bail;
847 }
848
849 if ((data = ftp_getdata(ftp)) == NULL) {
850 goto bail;
851 }
852
853 ftp->data = data;
854
855 if (resumepos > 0) {
856 snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
857 if (!ftp_putcmd(ftp, "REST", arg)) {
858 goto bail;
859 }
860 if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
861 goto bail;
862 }
863 }
864
865 if (!ftp_putcmd(ftp, "RETR", path)) {
866 goto bail;
867 }
868 if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
869 goto bail;
870 }
871
872 if ((data = data_accept(data, ftp)) == NULL) {
873 goto bail;
874 }
875
876 while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
877 if (rcvd == (size_t)-1) {
878 goto bail;
879 }
880
881 if (type == FTPTYPE_ASCII) {
882 #ifndef PHP_WIN32
883 char *s;
884 #endif
885 char *ptr = data->buf;
886 char *e = ptr + rcvd;
887 /* logic depends on the OS EOL
888 * Win32 -> \r\n
889 * Everything Else \n
890 */
891 #ifdef PHP_WIN32
892 php_stream_write(outstream, ptr, (e - ptr));
893 ptr = e;
894 #else
895 while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
896 php_stream_write(outstream, ptr, (s - ptr));
897 if (*(s + 1) == '\n') {
898 s++;
899 php_stream_putc(outstream, '\n');
900 }
901 ptr = s + 1;
902 }
903 #endif
904 if (ptr < e) {
905 php_stream_write(outstream, ptr, (e - ptr));
906 }
907 } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
908 goto bail;
909 }
910 }
911
912 ftp->data = data = data_close(ftp, data);
913
914 if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
915 goto bail;
916 }
917
918 return 1;
919 bail:
920 ftp->data = data_close(ftp, data);
921 return 0;
922 }
923 /* }}} */
924
925 /* {{{ ftp_put
926 */
927 int
ftp_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,zend_long startpos)928 ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, zend_long startpos)
929 {
930 databuf_t *data = NULL;
931 zend_long size;
932 char *ptr;
933 int ch;
934 char arg[11];
935
936 if (ftp == NULL) {
937 return 0;
938 }
939 if (!ftp_type(ftp, type)) {
940 goto bail;
941 }
942 if ((data = ftp_getdata(ftp)) == NULL) {
943 goto bail;
944 }
945 ftp->data = data;
946
947 if (startpos > 0) {
948 snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
949 if (!ftp_putcmd(ftp, "REST", arg)) {
950 goto bail;
951 }
952 if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
953 goto bail;
954 }
955 }
956
957 if (!ftp_putcmd(ftp, "STOR", path)) {
958 goto bail;
959 }
960 if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
961 goto bail;
962 }
963 if ((data = data_accept(data, ftp)) == NULL) {
964 goto bail;
965 }
966
967 size = 0;
968 ptr = data->buf;
969 while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
970 /* flush if necessary */
971 if (FTP_BUFSIZE - size < 2) {
972 if (my_send(ftp, data->fd, data->buf, size) != size) {
973 goto bail;
974 }
975 ptr = data->buf;
976 size = 0;
977 }
978
979 if (ch == '\n' && type == FTPTYPE_ASCII) {
980 *ptr++ = '\r';
981 size++;
982 }
983
984 *ptr++ = ch;
985 size++;
986 }
987
988 if (size && my_send(ftp, data->fd, data->buf, size) != size) {
989 goto bail;
990 }
991 ftp->data = data = data_close(ftp, data);
992
993 if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
994 goto bail;
995 }
996 return 1;
997 bail:
998 ftp->data = data_close(ftp, data);
999 return 0;
1000 }
1001 /* }}} */
1002
1003 /* {{{ ftp_size
1004 */
1005 zend_long
ftp_size(ftpbuf_t * ftp,const char * path)1006 ftp_size(ftpbuf_t *ftp, const char *path)
1007 {
1008 if (ftp == NULL) {
1009 return -1;
1010 }
1011 if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
1012 return -1;
1013 }
1014 if (!ftp_putcmd(ftp, "SIZE", path)) {
1015 return -1;
1016 }
1017 if (!ftp_getresp(ftp) || ftp->resp != 213) {
1018 return -1;
1019 }
1020 return atol(ftp->inbuf);
1021 }
1022 /* }}} */
1023
1024 /* {{{ ftp_mdtm
1025 */
1026 time_t
ftp_mdtm(ftpbuf_t * ftp,const char * path)1027 ftp_mdtm(ftpbuf_t *ftp, const char *path)
1028 {
1029 time_t stamp;
1030 struct tm *gmt, tmbuf;
1031 struct tm tm;
1032 char *ptr;
1033 int n;
1034
1035 if (ftp == NULL) {
1036 return -1;
1037 }
1038 if (!ftp_putcmd(ftp, "MDTM", path)) {
1039 return -1;
1040 }
1041 if (!ftp_getresp(ftp) || ftp->resp != 213) {
1042 return -1;
1043 }
1044 /* parse out the timestamp */
1045 for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
1046 n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
1047 if (n != 6) {
1048 return -1;
1049 }
1050 tm.tm_year -= 1900;
1051 tm.tm_mon--;
1052 tm.tm_isdst = -1;
1053
1054 /* figure out the GMT offset */
1055 stamp = time(NULL);
1056 gmt = php_gmtime_r(&stamp, &tmbuf);
1057 if (!gmt) {
1058 return -1;
1059 }
1060 gmt->tm_isdst = -1;
1061
1062 /* apply the GMT offset */
1063 tm.tm_sec += stamp - mktime(gmt);
1064 tm.tm_isdst = gmt->tm_isdst;
1065
1066 stamp = mktime(&tm);
1067
1068 return stamp;
1069 }
1070 /* }}} */
1071
1072 /* {{{ ftp_delete
1073 */
1074 int
ftp_delete(ftpbuf_t * ftp,const char * path)1075 ftp_delete(ftpbuf_t *ftp, const char *path)
1076 {
1077 if (ftp == NULL) {
1078 return 0;
1079 }
1080 if (!ftp_putcmd(ftp, "DELE", path)) {
1081 return 0;
1082 }
1083 if (!ftp_getresp(ftp) || ftp->resp != 250) {
1084 return 0;
1085 }
1086
1087 return 1;
1088 }
1089 /* }}} */
1090
1091 /* {{{ ftp_rename
1092 */
1093 int
ftp_rename(ftpbuf_t * ftp,const char * src,const char * dest)1094 ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
1095 {
1096 if (ftp == NULL) {
1097 return 0;
1098 }
1099 if (!ftp_putcmd(ftp, "RNFR", src)) {
1100 return 0;
1101 }
1102 if (!ftp_getresp(ftp) || ftp->resp != 350) {
1103 return 0;
1104 }
1105 if (!ftp_putcmd(ftp, "RNTO", dest)) {
1106 return 0;
1107 }
1108 if (!ftp_getresp(ftp) || ftp->resp != 250) {
1109 return 0;
1110 }
1111 return 1;
1112 }
1113 /* }}} */
1114
1115 /* {{{ ftp_site
1116 */
1117 int
ftp_site(ftpbuf_t * ftp,const char * cmd)1118 ftp_site(ftpbuf_t *ftp, const char *cmd)
1119 {
1120 if (ftp == NULL) {
1121 return 0;
1122 }
1123 if (!ftp_putcmd(ftp, "SITE", cmd)) {
1124 return 0;
1125 }
1126 if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
1127 return 0;
1128 }
1129
1130 return 1;
1131 }
1132 /* }}} */
1133
1134 /* static functions */
1135
1136 /* {{{ ftp_putcmd
1137 */
1138 int
ftp_putcmd(ftpbuf_t * ftp,const char * cmd,const char * args)1139 ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
1140 {
1141 int size;
1142 char *data;
1143
1144 if (strpbrk(cmd, "\r\n")) {
1145 return 0;
1146 }
1147 /* build the output buffer */
1148 if (args && args[0]) {
1149 /* "cmd args\r\n\0" */
1150 if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
1151 return 0;
1152 }
1153 if (strpbrk(args, "\r\n")) {
1154 return 0;
1155 }
1156 size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
1157 } else {
1158 /* "cmd\r\n\0" */
1159 if (strlen(cmd) + 3 > FTP_BUFSIZE) {
1160 return 0;
1161 }
1162 size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
1163 }
1164
1165 data = ftp->outbuf;
1166
1167 /* Clear the extra-lines buffer */
1168 ftp->extra = NULL;
1169
1170 if (my_send(ftp, ftp->fd, data, size) != size) {
1171 return 0;
1172 }
1173 return 1;
1174 }
1175 /* }}} */
1176
1177 /* {{{ ftp_readline
1178 */
1179 int
ftp_readline(ftpbuf_t * ftp)1180 ftp_readline(ftpbuf_t *ftp)
1181 {
1182 long size, rcvd;
1183 char *data, *eol;
1184
1185 /* shift the extra to the front */
1186 size = FTP_BUFSIZE;
1187 rcvd = 0;
1188 if (ftp->extra) {
1189 memmove(ftp->inbuf, ftp->extra, ftp->extralen);
1190 rcvd = ftp->extralen;
1191 }
1192
1193 data = ftp->inbuf;
1194
1195 do {
1196 size -= rcvd;
1197 for (eol = data; rcvd; rcvd--, eol++) {
1198 if (*eol == '\r') {
1199 *eol = 0;
1200 ftp->extra = eol + 1;
1201 if (rcvd > 1 && *(eol + 1) == '\n') {
1202 ftp->extra++;
1203 rcvd--;
1204 }
1205 if ((ftp->extralen = --rcvd) == 0) {
1206 ftp->extra = NULL;
1207 }
1208 return 1;
1209 } else if (*eol == '\n') {
1210 *eol = 0;
1211 ftp->extra = eol + 1;
1212 if ((ftp->extralen = --rcvd) == 0) {
1213 ftp->extra = NULL;
1214 }
1215 return 1;
1216 }
1217 }
1218
1219 data = eol;
1220 if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
1221 return 0;
1222 }
1223 } while (size);
1224
1225 return 0;
1226 }
1227 /* }}} */
1228
1229 /* {{{ ftp_getresp
1230 */
1231 int
ftp_getresp(ftpbuf_t * ftp)1232 ftp_getresp(ftpbuf_t *ftp)
1233 {
1234 if (ftp == NULL) {
1235 return 0;
1236 }
1237 ftp->resp = 0;
1238
1239 while (1) {
1240
1241 if (!ftp_readline(ftp)) {
1242 return 0;
1243 }
1244
1245 /* Break out when the end-tag is found */
1246 if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
1247 break;
1248 }
1249 }
1250
1251 /* translate the tag */
1252 if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
1253 return 0;
1254 }
1255
1256 ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
1257
1258 memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
1259
1260 if (ftp->extra) {
1261 ftp->extra -= 4;
1262 }
1263 return 1;
1264 }
1265 /* }}} */
1266
1267 /* {{{ my_send
1268 */
1269 int
my_send(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)1270 my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1271 {
1272 zend_long size, sent;
1273 int n;
1274 #ifdef HAVE_FTP_SSL
1275 int err;
1276 zend_bool retry = 0;
1277 SSL *handle = NULL;
1278 php_socket_t fd;
1279 #endif
1280
1281
1282 size = len;
1283 while (size) {
1284 n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
1285
1286 if (n < 1) {
1287 #ifdef PHP_WIN32
1288 if (n == 0) {
1289 _set_errno(ETIMEDOUT);
1290 }
1291 #elif !(defined(NETWARE) && defined(USE_WINSOCK))
1292 if (n == 0) {
1293 errno = ETIMEDOUT;
1294 }
1295 #endif
1296 return -1;
1297 }
1298
1299 #ifdef HAVE_FTP_SSL
1300 if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1301 handle = ftp->ssl_handle;
1302 fd = ftp->fd;
1303 } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1304 handle = ftp->data->ssl_handle;
1305 fd = ftp->data->fd;
1306 }
1307
1308 if (handle) {
1309 do {
1310 sent = SSL_write(handle, buf, size);
1311 err = SSL_get_error(handle, sent);
1312
1313 switch (err) {
1314 case SSL_ERROR_NONE:
1315 retry = 0;
1316 break;
1317
1318 case SSL_ERROR_ZERO_RETURN:
1319 retry = 0;
1320 SSL_shutdown(handle);
1321 break;
1322
1323 case SSL_ERROR_WANT_READ:
1324 case SSL_ERROR_WANT_CONNECT: {
1325 php_pollfd p;
1326 int i;
1327
1328 p.fd = fd;
1329 p.events = POLLOUT;
1330 p.revents = 0;
1331
1332 i = php_poll2(&p, 1, 300);
1333
1334 retry = i > 0;
1335 }
1336 break;
1337
1338 default:
1339 php_error_docref(NULL, E_WARNING, "SSL write failed");
1340 return -1;
1341 }
1342 } while (retry);
1343 } else {
1344 #endif
1345 sent = send(s, buf, size, 0);
1346 #ifdef HAVE_FTP_SSL
1347 }
1348 #endif
1349 if (sent == -1) {
1350 return -1;
1351 }
1352
1353 buf = (char*) buf + sent;
1354 size -= sent;
1355 }
1356
1357 return len;
1358 }
1359 /* }}} */
1360
1361 /* {{{ my_recv
1362 */
1363 int
my_recv(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)1364 my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1365 {
1366 int n, nr_bytes;
1367 #ifdef HAVE_FTP_SSL
1368 int err;
1369 zend_bool retry = 0;
1370 SSL *handle = NULL;
1371 php_socket_t fd;
1372 #endif
1373
1374 n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1375 if (n < 1) {
1376 #ifdef PHP_WIN32
1377 if (n == 0) {
1378 _set_errno(ETIMEDOUT);
1379 }
1380 #elif !(defined(NETWARE) && defined(USE_WINSOCK))
1381 if (n == 0) {
1382 errno = ETIMEDOUT;
1383 }
1384 #endif
1385 return -1;
1386 }
1387
1388 #ifdef HAVE_FTP_SSL
1389 if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1390 handle = ftp->ssl_handle;
1391 fd = ftp->fd;
1392 } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1393 handle = ftp->data->ssl_handle;
1394 fd = ftp->data->fd;
1395 }
1396
1397 if (handle) {
1398 do {
1399 nr_bytes = SSL_read(handle, buf, len);
1400 err = SSL_get_error(handle, nr_bytes);
1401
1402 switch (err) {
1403 case SSL_ERROR_NONE:
1404 retry = 0;
1405 break;
1406
1407 case SSL_ERROR_ZERO_RETURN:
1408 retry = 0;
1409 SSL_shutdown(handle);
1410 break;
1411
1412 case SSL_ERROR_WANT_READ:
1413 case SSL_ERROR_WANT_CONNECT: {
1414 php_pollfd p;
1415 int i;
1416
1417 p.fd = fd;
1418 p.events = POLLIN|POLLPRI;
1419 p.revents = 0;
1420
1421 i = php_poll2(&p, 1, 300);
1422
1423 retry = i > 0;
1424 }
1425 break;
1426
1427 default:
1428 php_error_docref(NULL, E_WARNING, "SSL read failed");
1429 return -1;
1430 }
1431 } while (retry);
1432 } else {
1433 #endif
1434 nr_bytes = recv(s, buf, len, 0);
1435 #ifdef HAVE_FTP_SSL
1436 }
1437 #endif
1438 return (nr_bytes);
1439 }
1440 /* }}} */
1441
1442 /* {{{ data_available
1443 */
1444 int
data_available(ftpbuf_t * ftp,php_socket_t s)1445 data_available(ftpbuf_t *ftp, php_socket_t s)
1446 {
1447 int n;
1448
1449 n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
1450 if (n < 1) {
1451 #ifdef PHP_WIN32
1452 if (n == 0) {
1453 _set_errno(ETIMEDOUT);
1454 }
1455 #elif !(defined(NETWARE) && defined(USE_WINSOCK))
1456 if (n == 0) {
1457 errno = ETIMEDOUT;
1458 }
1459 #endif
1460 return 0;
1461 }
1462
1463 return 1;
1464 }
1465 /* }}} */
1466 /* {{{ data_writeable
1467 */
1468 int
data_writeable(ftpbuf_t * ftp,php_socket_t s)1469 data_writeable(ftpbuf_t *ftp, php_socket_t s)
1470 {
1471 int n;
1472
1473 n = php_pollfd_for_ms(s, POLLOUT, 1000);
1474 if (n < 1) {
1475 #ifdef PHP_WIN32
1476 if (n == 0) {
1477 _set_errno(ETIMEDOUT);
1478 }
1479 #elif !(defined(NETWARE) && defined(USE_WINSOCK))
1480 if (n == 0) {
1481 errno = ETIMEDOUT;
1482 }
1483 #endif
1484 return 0;
1485 }
1486
1487 return 1;
1488 }
1489 /* }}} */
1490
1491 /* {{{ my_accept
1492 */
1493 int
my_accept(ftpbuf_t * ftp,php_socket_t s,struct sockaddr * addr,socklen_t * addrlen)1494 my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
1495 {
1496 int n;
1497
1498 n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1499 if (n < 1) {
1500 #ifdef PHP_WIN32
1501 if (n == 0) {
1502 _set_errno(ETIMEDOUT);
1503 }
1504 #elif !(defined(NETWARE) && defined(USE_WINSOCK))
1505 if (n == 0) {
1506 errno = ETIMEDOUT;
1507 }
1508 #endif
1509 return -1;
1510 }
1511
1512 return accept(s, addr, addrlen);
1513 }
1514 /* }}} */
1515
1516 /* {{{ ftp_getdata
1517 */
1518 databuf_t*
ftp_getdata(ftpbuf_t * ftp)1519 ftp_getdata(ftpbuf_t *ftp)
1520 {
1521 int fd = -1;
1522 databuf_t *data;
1523 php_sockaddr_storage addr;
1524 struct sockaddr *sa;
1525 socklen_t size;
1526 union ipbox ipbox;
1527 char arg[sizeof("255, 255, 255, 255, 255, 255")];
1528 struct timeval tv;
1529
1530
1531 /* ask for a passive connection if we need one */
1532 if (ftp->pasv && !ftp_pasv(ftp, 1)) {
1533 return NULL;
1534 }
1535 /* alloc the data structure */
1536 data = ecalloc(1, sizeof(*data));
1537 data->listener = -1;
1538 data->fd = -1;
1539 data->type = ftp->type;
1540
1541 sa = (struct sockaddr *) &ftp->localaddr;
1542 /* bind/listen */
1543 if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
1544 php_error_docref(NULL, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
1545 goto bail;
1546 }
1547
1548 /* passive connection handler */
1549 if (ftp->pasv) {
1550 /* clear the ready status */
1551 ftp->pasv = 1;
1552
1553 /* connect */
1554 /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
1555 size = php_sockaddr_size(&ftp->pasvaddr);
1556 tv.tv_sec = ftp->timeout_sec;
1557 tv.tv_usec = 0;
1558 if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
1559 php_error_docref(NULL, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
1560 goto bail;
1561 }
1562
1563 data->fd = fd;
1564
1565 ftp->data = data;
1566 return data;
1567 }
1568
1569
1570 /* active (normal) connection */
1571
1572 /* bind to a local address */
1573 php_any_addr(sa->sa_family, &addr, 0);
1574 size = php_sockaddr_size(&addr);
1575
1576 if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
1577 php_error_docref(NULL, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
1578 goto bail;
1579 }
1580
1581 if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
1582 php_error_docref(NULL, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
1583 goto bail;
1584 }
1585
1586 if (listen(fd, 5) != 0) {
1587 php_error_docref(NULL, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
1588 goto bail;
1589 }
1590
1591 data->listener = fd;
1592
1593 #if HAVE_IPV6 && HAVE_INET_NTOP
1594 if (sa->sa_family == AF_INET6) {
1595 /* need to use EPRT */
1596 char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
1597 char out[INET6_ADDRSTRLEN];
1598 inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
1599 snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
1600
1601 if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
1602 goto bail;
1603 }
1604
1605 if (!ftp_getresp(ftp) || ftp->resp != 200) {
1606 goto bail;
1607 }
1608
1609 ftp->data = data;
1610 return data;
1611 }
1612 #endif
1613
1614 /* send the PORT */
1615 ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
1616 ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
1617 snprintf(arg, sizeof(arg), "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);
1618
1619 if (!ftp_putcmd(ftp, "PORT", arg)) {
1620 goto bail;
1621 }
1622 if (!ftp_getresp(ftp) || ftp->resp != 200) {
1623 goto bail;
1624 }
1625
1626 ftp->data = data;
1627 return data;
1628
1629 bail:
1630 if (fd != -1) {
1631 closesocket(fd);
1632 }
1633 efree(data);
1634 return NULL;
1635 }
1636 /* }}} */
1637
1638 /* {{{ data_accept
1639 */
1640 databuf_t*
data_accept(databuf_t * data,ftpbuf_t * ftp)1641 data_accept(databuf_t *data, ftpbuf_t *ftp)
1642 {
1643 php_sockaddr_storage addr;
1644 socklen_t size;
1645
1646 #ifdef HAVE_FTP_SSL
1647 SSL_CTX *ctx;
1648 SSL_SESSION *session;
1649 int err, res;
1650 zend_bool retry;
1651 #endif
1652
1653 if (data->fd != -1) {
1654 goto data_accepted;
1655 }
1656 size = sizeof(addr);
1657 data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
1658 closesocket(data->listener);
1659 data->listener = -1;
1660
1661 if (data->fd == -1) {
1662 efree(data);
1663 return NULL;
1664 }
1665
1666 data_accepted:
1667 #ifdef HAVE_FTP_SSL
1668
1669 /* now enable ssl if we need to */
1670 if (ftp->use_ssl && ftp->use_ssl_for_data) {
1671 ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
1672 if (ctx == NULL) {
1673 php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL context");
1674 return 0;
1675 }
1676
1677 data->ssl_handle = SSL_new(ctx);
1678 if (data->ssl_handle == NULL) {
1679 php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle");
1680 return 0;
1681 }
1682
1683 SSL_set_fd(data->ssl_handle, data->fd);
1684
1685 if (ftp->old_ssl) {
1686 SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
1687 }
1688
1689 /* get the session from the control connection so we can re-use it */
1690 session = SSL_get_session(ftp->ssl_handle);
1691 if (session == NULL) {
1692 php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL session");
1693 SSL_free(data->ssl_handle);
1694 return 0;
1695 }
1696
1697 /* and set it on the data connection */
1698 res = SSL_set_session(data->ssl_handle, session);
1699 if (res == 0) {
1700 php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session");
1701 SSL_free(data->ssl_handle);
1702 return 0;
1703 }
1704
1705 do {
1706 res = SSL_connect(data->ssl_handle);
1707 err = SSL_get_error(data->ssl_handle, res);
1708
1709 switch (err) {
1710 case SSL_ERROR_NONE:
1711 retry = 0;
1712 break;
1713
1714 case SSL_ERROR_ZERO_RETURN:
1715 retry = 0;
1716 SSL_shutdown(data->ssl_handle);
1717 break;
1718
1719 case SSL_ERROR_WANT_READ:
1720 case SSL_ERROR_WANT_WRITE: {
1721 php_pollfd p;
1722 int i;
1723
1724 p.fd = ftp->fd;
1725 p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
1726 p.revents = 0;
1727
1728 i = php_poll2(&p, 1, 300);
1729
1730 retry = i > 0;
1731 }
1732 break;
1733
1734 default:
1735 php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed");
1736 SSL_shutdown(data->ssl_handle);
1737 SSL_free(data->ssl_handle);
1738 return 0;
1739 }
1740 } while (retry);
1741
1742 data->ssl_active = 1;
1743 }
1744
1745 #endif
1746
1747 return data;
1748 }
1749 /* }}} */
1750
1751 /* {{{ ftp_ssl_shutdown
1752 */
1753 #ifdef HAVE_FTP_SSL
ftp_ssl_shutdown(ftpbuf_t * ftp,php_socket_t fd,SSL * ssl_handle)1754 static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) {
1755 /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train
1756 the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the
1757 server might get an ECONNRESET which might lead to data truncation on server side.
1758 */
1759 char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
1760 to be at least 256 bytes long.*/
1761 int done = 1, err, nread;
1762 unsigned long sslerror;
1763
1764 err = SSL_shutdown(ssl_handle);
1765 if (err < 0) {
1766 php_error_docref(NULL, E_WARNING, "SSL_shutdown failed");
1767 }
1768 else if (err == 0) {
1769 /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */
1770 done = 0;
1771 }
1772
1773 while (!done && data_available(ftp, fd)) {
1774 ERR_clear_error();
1775 nread = SSL_read(ssl_handle, buf, sizeof(buf));
1776 if (nread <= 0) {
1777 err = SSL_get_error(ssl_handle, nread);
1778 switch (err) {
1779 case SSL_ERROR_NONE: /* this is not an error */
1780 case SSL_ERROR_ZERO_RETURN: /* no more data */
1781 /* This is the expected response. There was no data but only
1782 the close notify alert */
1783 done = 1;
1784 break;
1785 case SSL_ERROR_WANT_READ:
1786 /* there's data pending, re-invoke SSL_read() */
1787 break;
1788 case SSL_ERROR_WANT_WRITE:
1789 /* SSL wants a write. Really odd. Let's bail out. */
1790 done = 1;
1791 break;
1792 default:
1793 if ((sslerror = ERR_get_error())) {
1794 ERR_error_string_n(sslerror, buf, sizeof(buf));
1795 php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s", buf);
1796 } else if (errno) {
1797 php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", strerror(errno), errno);
1798 }
1799 done = 1;
1800 break;
1801 }
1802 }
1803 }
1804 (void)SSL_free(ssl_handle);
1805 }
1806 #endif
1807 /* }}} */
1808
1809 /* {{{ data_close
1810 */
1811 databuf_t*
data_close(ftpbuf_t * ftp,databuf_t * data)1812 data_close(ftpbuf_t *ftp, databuf_t *data)
1813 {
1814 #ifdef HAVE_FTP_SSL
1815 SSL_CTX *ctx;
1816 #endif
1817 if (data == NULL) {
1818 return NULL;
1819 }
1820 if (data->listener != -1) {
1821 #ifdef HAVE_FTP_SSL
1822 if (data->ssl_active) {
1823 /* don't free the data context, it's the same as the control */
1824 ftp_ssl_shutdown(ftp, data->listener, data->ssl_handle);
1825 data->ssl_active = 0;
1826 }
1827 #endif
1828 closesocket(data->listener);
1829 }
1830 if (data->fd != -1) {
1831 #ifdef HAVE_FTP_SSL
1832 if (data->ssl_active) {
1833 /* don't free the data context, it's the same as the control */
1834 ftp_ssl_shutdown(ftp, data->fd, data->ssl_handle);
1835 data->ssl_active = 0;
1836 }
1837 #endif
1838 closesocket(data->fd);
1839 }
1840 if (ftp) {
1841 ftp->data = NULL;
1842 }
1843 efree(data);
1844 return NULL;
1845 }
1846 /* }}} */
1847
1848 /* {{{ ftp_genlist
1849 */
1850 char**
ftp_genlist(ftpbuf_t * ftp,const char * cmd,const char * path)1851 ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path)
1852 {
1853 php_stream *tmpstream = NULL;
1854 databuf_t *data = NULL;
1855 char *ptr;
1856 int ch, lastch;
1857 size_t size, rcvd;
1858 size_t lines;
1859 char **ret = NULL;
1860 char **entry;
1861 char *text;
1862
1863
1864 if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
1865 php_error_docref(NULL, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
1866 return NULL;
1867 }
1868
1869 if (!ftp_type(ftp, FTPTYPE_ASCII)) {
1870 goto bail;
1871 }
1872
1873 if ((data = ftp_getdata(ftp)) == NULL) {
1874 goto bail;
1875 }
1876 ftp->data = data;
1877
1878 if (!ftp_putcmd(ftp, cmd, path)) {
1879 goto bail;
1880 }
1881 if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
1882 goto bail;
1883 }
1884
1885 /* some servers don't open a ftp-data connection if the directory is empty */
1886 if (ftp->resp == 226) {
1887 ftp->data = data_close(ftp, data);
1888 php_stream_close(tmpstream);
1889 return ecalloc(1, sizeof(char*));
1890 }
1891
1892 /* pull data buffer into tmpfile */
1893 if ((data = data_accept(data, ftp)) == NULL) {
1894 goto bail;
1895 }
1896 size = 0;
1897 lines = 0;
1898 lastch = 0;
1899 while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1900 if (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {
1901 goto bail;
1902 }
1903
1904 php_stream_write(tmpstream, data->buf, rcvd);
1905
1906 size += rcvd;
1907 for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1908 if (*ptr == '\n' && lastch == '\r') {
1909 lines++;
1910 }
1911 lastch = *ptr;
1912 }
1913 }
1914
1915 ftp->data = data_close(ftp, data);
1916
1917 php_stream_rewind(tmpstream);
1918
1919 ret = safe_emalloc((lines + 1), sizeof(char*), size);
1920
1921 entry = ret;
1922 text = (char*) (ret + lines + 1);
1923 *entry = text;
1924 lastch = 0;
1925 while ((ch = php_stream_getc(tmpstream)) != EOF) {
1926 if (ch == '\n' && lastch == '\r') {
1927 *(text - 1) = 0;
1928 *++entry = text;
1929 } else {
1930 *text++ = ch;
1931 }
1932 lastch = ch;
1933 }
1934 *entry = NULL;
1935
1936 php_stream_close(tmpstream);
1937
1938 if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1939 efree(ret);
1940 return NULL;
1941 }
1942
1943 return ret;
1944 bail:
1945 ftp->data = data_close(ftp, data);
1946 php_stream_close(tmpstream);
1947 if (ret)
1948 efree(ret);
1949 return NULL;
1950 }
1951 /* }}} */
1952
1953 /* {{{ ftp_nb_get
1954 */
1955 int
ftp_nb_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,zend_long resumepos)1956 ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, zend_long resumepos)
1957 {
1958 databuf_t *data = NULL;
1959 char arg[11];
1960
1961 if (ftp == NULL) {
1962 return PHP_FTP_FAILED;
1963 }
1964
1965 if (!ftp_type(ftp, type)) {
1966 goto bail;
1967 }
1968
1969 if ((data = ftp_getdata(ftp)) == NULL) {
1970 goto bail;
1971 }
1972
1973 if (resumepos>0) {
1974 snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
1975 if (!ftp_putcmd(ftp, "REST", arg)) {
1976 goto bail;
1977 }
1978 if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1979 goto bail;
1980 }
1981 }
1982
1983 if (!ftp_putcmd(ftp, "RETR", path)) {
1984 goto bail;
1985 }
1986 if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1987 goto bail;
1988 }
1989
1990 if ((data = data_accept(data, ftp)) == NULL) {
1991 goto bail;
1992 }
1993
1994 ftp->data = data;
1995 ftp->stream = outstream;
1996 ftp->lastch = 0;
1997 ftp->nb = 1;
1998
1999 return (ftp_nb_continue_read(ftp));
2000
2001 bail:
2002 ftp->data = data_close(ftp, data);
2003 return PHP_FTP_FAILED;
2004 }
2005 /* }}} */
2006
2007 /* {{{ ftp_nb_continue_read
2008 */
2009 int
ftp_nb_continue_read(ftpbuf_t * ftp)2010 ftp_nb_continue_read(ftpbuf_t *ftp)
2011 {
2012 databuf_t *data = NULL;
2013 char *ptr;
2014 int lastch;
2015 size_t rcvd;
2016 ftptype_t type;
2017
2018 data = ftp->data;
2019
2020 /* check if there is already more data */
2021 if (!data_available(ftp, data->fd)) {
2022 return PHP_FTP_MOREDATA;
2023 }
2024
2025 type = ftp->type;
2026
2027 lastch = ftp->lastch;
2028 if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
2029 if (rcvd == (size_t)-1) {
2030 goto bail;
2031 }
2032
2033 if (type == FTPTYPE_ASCII) {
2034 for (ptr = data->buf; rcvd; rcvd--, ptr++) {
2035 if (lastch == '\r' && *ptr != '\n') {
2036 php_stream_putc(ftp->stream, '\r');
2037 }
2038 if (*ptr != '\r') {
2039 php_stream_putc(ftp->stream, *ptr);
2040 }
2041 lastch = *ptr;
2042 }
2043 } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
2044 goto bail;
2045 }
2046
2047 ftp->lastch = lastch;
2048 return PHP_FTP_MOREDATA;
2049 }
2050
2051 if (type == FTPTYPE_ASCII && lastch == '\r') {
2052 php_stream_putc(ftp->stream, '\r');
2053 }
2054
2055 ftp->data = data = data_close(ftp, data);
2056
2057 if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
2058 goto bail;
2059 }
2060
2061 ftp->nb = 0;
2062 return PHP_FTP_FINISHED;
2063 bail:
2064 ftp->nb = 0;
2065 ftp->data = data_close(ftp, data);
2066 return PHP_FTP_FAILED;
2067 }
2068 /* }}} */
2069
2070 /* {{{ ftp_nb_put
2071 */
2072 int
ftp_nb_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,zend_long startpos)2073 ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, zend_long startpos)
2074 {
2075 databuf_t *data = NULL;
2076 char arg[11];
2077
2078 if (ftp == NULL) {
2079 return 0;
2080 }
2081 if (!ftp_type(ftp, type)) {
2082 goto bail;
2083 }
2084 if ((data = ftp_getdata(ftp)) == NULL) {
2085 goto bail;
2086 }
2087 if (startpos > 0) {
2088 snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
2089 if (!ftp_putcmd(ftp, "REST", arg)) {
2090 goto bail;
2091 }
2092 if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
2093 goto bail;
2094 }
2095 }
2096
2097 if (!ftp_putcmd(ftp, "STOR", path)) {
2098 goto bail;
2099 }
2100 if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
2101 goto bail;
2102 }
2103 if ((data = data_accept(data, ftp)) == NULL) {
2104 goto bail;
2105 }
2106 ftp->data = data;
2107 ftp->stream = instream;
2108 ftp->lastch = 0;
2109 ftp->nb = 1;
2110
2111 return (ftp_nb_continue_write(ftp));
2112
2113 bail:
2114 ftp->data = data_close(ftp, data);
2115 return PHP_FTP_FAILED;
2116 }
2117 /* }}} */
2118
2119
2120 /* {{{ ftp_nb_continue_write
2121 */
2122 int
ftp_nb_continue_write(ftpbuf_t * ftp)2123 ftp_nb_continue_write(ftpbuf_t *ftp)
2124 {
2125 long size;
2126 char *ptr;
2127 int ch;
2128
2129 /* check if we can write more data */
2130 if (!data_writeable(ftp, ftp->data->fd)) {
2131 return PHP_FTP_MOREDATA;
2132 }
2133
2134 size = 0;
2135 ptr = ftp->data->buf;
2136 while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
2137
2138 if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
2139 *ptr++ = '\r';
2140 size++;
2141 }
2142
2143 *ptr++ = ch;
2144 size++;
2145
2146 /* flush if necessary */
2147 if (FTP_BUFSIZE - size < 2) {
2148 if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
2149 goto bail;
2150 }
2151 return PHP_FTP_MOREDATA;
2152 }
2153 }
2154
2155 if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
2156 goto bail;
2157 }
2158 ftp->data = data_close(ftp, ftp->data);
2159
2160 if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
2161 goto bail;
2162 }
2163 ftp->nb = 0;
2164 return PHP_FTP_FINISHED;
2165 bail:
2166 ftp->data = data_close(ftp, ftp->data);
2167 ftp->nb = 0;
2168 return PHP_FTP_FAILED;
2169 }
2170 /* }}} */
2171
2172 #endif /* HAVE_FTP */
2173
2174 /*
2175 * Local variables:
2176 * tab-width: 4
2177 * c-basic-offset: 4
2178 * End:
2179 * vim600: sw=4 ts=4 fdm=marker
2180 * vim<600: sw=4 ts=4
2181 */
2182