12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Andrew Skalski <askalski@chek.com> |
- | Stefan Esser <sesser@php.net> (resume functions) |
- +----------------------------------------------------------------------+
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "php.h"
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include <fcntl.h>
- #include <string.h>
- #include <time.h>
- #ifdef PHP_WIN32
- #include <winsock2.h>
- #else
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #endif
- #include <errno.h>
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #ifdef HAVE_FTP_SSL
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #endif
- #include "ftp.h"
- #include "ext/standard/fsock.h"
- #ifdef PHP_WIN32
- # undef ETIMEDOUT
- # define ETIMEDOUT WSAETIMEDOUT
- #endif
- /* sends an ftp command, returns true on success, false on error.
- * it sends the string "cmd args\r\n" if args is non-null, or
- * "cmd\r\n" if args is null
- */
- static int ftp_putcmd( ftpbuf_t *ftp,
- const char *cmd,
- const size_t cmd_len,
- const char *args,
- const size_t args_len);
- /* wrapper around send/recv to handle timeouts */
- static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
- static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
- static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
- /* reads a line the socket , returns true on success, false on error */
- static int ftp_readline(ftpbuf_t *ftp);
- /* reads an ftp response, returns true on success, false on error */
- static int ftp_getresp(ftpbuf_t *ftp);
- /* sets the ftp transfer type */
- static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
- /* opens up a data stream */
- static databuf_t* ftp_getdata(ftpbuf_t *ftp);
- /* accepts the data connection, returns updated data buffer */
- static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp);
- /* closes the data connection, returns NULL */
- static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
- /* generic file lister */
- static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len);
- #ifdef HAVE_FTP_SSL
- /* shuts down a TLS/SSL connection */
- static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle);
- #endif
- /* IP and port conversion box */
- union ipbox {
- struct in_addr ia[2];
- unsigned short s[4];
- unsigned char c[8];
- };
- /* {{{ ftp_open */
- ftpbuf_t*
- ftp_open(const char *host, short port, zend_long timeout_sec)
- {
- ftpbuf_t *ftp;
- socklen_t size;
- struct timeval tv;
- /* alloc the ftp structure */
- ftp = ecalloc(1, sizeof(*ftp));
- tv.tv_sec = timeout_sec;
- tv.tv_usec = 0;
- ftp->fd = php_network_connect_socket_to_host(host,
- (unsigned short) (port ? port : 21), SOCK_STREAM,
- 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);
- if (ftp->fd == -1) {
- goto bail;
- }
- /* Default Settings */
- ftp->timeout_sec = timeout_sec;
- ftp->nb = 0;
- size = sizeof(ftp->localaddr);
- memset(&ftp->localaddr, 0, size);
- if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
- php_error_docref(NULL, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 220) {
- goto bail;
- }
- return ftp;
- bail:
- if (ftp->fd != -1) {
- closesocket(ftp->fd);
- }
- efree(ftp);
- return NULL;
- }
- /* }}} */
- /* {{{ ftp_close */
- ftpbuf_t*
- ftp_close(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return NULL;
- }
- if (ftp->data) {
- data_close(ftp, ftp->data);
- }
- if (ftp->stream && ftp->closestream) {
- php_stream_close(ftp->stream);
- }
- if (ftp->fd != -1) {
- #ifdef HAVE_FTP_SSL
- if (ftp->ssl_active) {
- ftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle);
- }
- #endif
- closesocket(ftp->fd);
- }
- ftp_gc(ftp);
- efree(ftp);
- return NULL;
- }
- /* }}} */
- /* {{{ ftp_gc */
- void
- ftp_gc(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return;
- }
- if (ftp->pwd) {
- efree(ftp->pwd);
- ftp->pwd = NULL;
- }
- if (ftp->syst) {
- efree(ftp->syst);
- ftp->syst = NULL;
- }
- }
- /* }}} */
- /* {{{ ftp_quit */
- int
- ftp_quit(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "QUIT", sizeof("QUIT")-1, NULL, (size_t) 0)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 221) {
- return 0;
- }
- if (ftp->pwd) {
- efree(ftp->pwd);
- ftp->pwd = NULL;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_login */
- int
- ftp_login(ftpbuf_t *ftp, const char *user, const size_t user_len, const char *pass, const size_t pass_len)
- {
- #ifdef HAVE_FTP_SSL
- SSL_CTX *ctx = NULL;
- long ssl_ctx_options = SSL_OP_ALL;
- int err, res;
- bool retry;
- #endif
- if (ftp == NULL) {
- return 0;
- }
- #ifdef HAVE_FTP_SSL
- if (ftp->use_ssl && !ftp->ssl_active) {
- if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "TLS", sizeof("TLS")-1)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- if (ftp->resp != 234) {
- if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "SSL", sizeof("SSL")-1)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- if (ftp->resp != 334) {
- return 0;
- } else {
- ftp->old_ssl = 1;
- ftp->use_ssl_for_data = 1;
- }
- }
- ctx = SSL_CTX_new(SSLv23_client_method());
- if (ctx == NULL) {
- php_error_docref(NULL, E_WARNING, "Failed to create the SSL context");
- return 0;
- }
- #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
- ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
- #endif
- SSL_CTX_set_options(ctx, ssl_ctx_options);
- /* allow SSL to re-use sessions */
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
- ftp->ssl_handle = SSL_new(ctx);
- SSL_CTX_free(ctx);
- if (ftp->ssl_handle == NULL) {
- php_error_docref(NULL, E_WARNING, "Failed to create the SSL handle");
- return 0;
- }
- SSL_set_fd(ftp->ssl_handle, ftp->fd);
- do {
- res = SSL_connect(ftp->ssl_handle);
- err = SSL_get_error(ftp->ssl_handle, res);
- /* TODO check if handling other error codes would make sense */
- switch (err) {
- case SSL_ERROR_NONE:
- retry = 0;
- break;
- case SSL_ERROR_ZERO_RETURN:
- retry = 0;
- SSL_shutdown(ftp->ssl_handle);
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE: {
- php_pollfd p;
- int i;
- p.fd = ftp->fd;
- p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
- p.revents = 0;
- i = php_poll2(&p, 1, 300);
- retry = i > 0;
- }
- break;
- default:
- php_error_docref(NULL, E_WARNING, "SSL/TLS handshake failed");
- SSL_shutdown(ftp->ssl_handle);
- SSL_free(ftp->ssl_handle);
- return 0;
- }
- } while (retry);
- ftp->ssl_active = 1;
- if (!ftp->old_ssl) {
- /* set protection buffersize to zero */
- if (!ftp_putcmd(ftp, "PBSZ", sizeof("PBSZ")-1, "0", sizeof("0")-1)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- /* enable data conn encryption */
- if (!ftp_putcmd(ftp, "PROT", sizeof("PROT")-1, "P", sizeof("P")-1)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
- }
- }
- #endif
- if (!ftp_putcmd(ftp, "USER", sizeof("USER")-1, user, user_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- if (ftp->resp == 230) {
- return 1;
- }
- if (ftp->resp != 331) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "PASS", sizeof("PASS")-1, pass, pass_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- return (ftp->resp == 230);
- }
- /* }}} */
- /* {{{ ftp_reinit */
- int
- ftp_reinit(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return 0;
- }
- ftp_gc(ftp);
- ftp->nb = 0;
- if (!ftp_putcmd(ftp, "REIN", sizeof("REIN")-1, NULL, (size_t) 0)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 220) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_syst */
- const char*
- ftp_syst(ftpbuf_t *ftp)
- {
- char *syst, *end;
- if (ftp == NULL) {
- return NULL;
- }
- /* default to cached value */
- if (ftp->syst) {
- return ftp->syst;
- }
- if (!ftp_putcmd(ftp, "SYST", sizeof("SYST")-1, NULL, (size_t) 0)) {
- return NULL;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 215) {
- return NULL;
- }
- syst = ftp->inbuf;
- while (*syst == ' ') {
- syst++;
- }
- if ((end = strchr(syst, ' '))) {
- *end = 0;
- }
- ftp->syst = estrdup(syst);
- if (end) {
- *end = ' ';
- }
- return ftp->syst;
- }
- /* }}} */
- /* {{{ ftp_pwd */
- const char*
- ftp_pwd(ftpbuf_t *ftp)
- {
- char *pwd, *end;
- if (ftp == NULL) {
- return NULL;
- }
- /* default to cached value */
- if (ftp->pwd) {
- return ftp->pwd;
- }
- if (!ftp_putcmd(ftp, "PWD", sizeof("PWD")-1, NULL, (size_t) 0)) {
- return NULL;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 257) {
- return NULL;
- }
- /* copy out the pwd from response */
- if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
- return NULL;
- }
- if ((end = strrchr(++pwd, '"')) == NULL) {
- return NULL;
- }
- ftp->pwd = estrndup(pwd, end - pwd);
- return ftp->pwd;
- }
- /* }}} */
- /* {{{ ftp_exec */
- int
- ftp_exec(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "SITE EXEC", sizeof("SITE EXEC")-1, cmd, cmd_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 200) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_raw */
- void
- ftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_value)
- {
- if (ftp == NULL || cmd == NULL) {
- RETURN_NULL();
- }
- if (!ftp_putcmd(ftp, cmd, cmd_len, NULL, (size_t) 0)) {
- RETURN_NULL();
- }
- array_init(return_value);
- while (ftp_readline(ftp)) {
- add_next_index_string(return_value, ftp->inbuf);
- if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
- return;
- }
- }
- }
- /* }}} */
- /* {{{ ftp_chdir */
- int
- ftp_chdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (ftp->pwd) {
- efree(ftp->pwd);
- ftp->pwd = NULL;
- }
- if (!ftp_putcmd(ftp, "CWD", sizeof("CWD")-1, dir, dir_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 250) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_cdup */
- int
- ftp_cdup(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (ftp->pwd) {
- efree(ftp->pwd);
- ftp->pwd = NULL;
- }
- if (!ftp_putcmd(ftp, "CDUP", sizeof("CDUP")-1, NULL, (size_t) 0)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 250) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_mkdir */
- zend_string*
- ftp_mkdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
- {
- char *mkd, *end;
- zend_string *ret;
- if (ftp == NULL) {
- return NULL;
- }
- if (!ftp_putcmd(ftp, "MKD", sizeof("MKD")-1, dir, dir_len)) {
- return NULL;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 257) {
- return NULL;
- }
- /* copy out the dir from response */
- if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
- return zend_string_init(dir, dir_len, 0);
- }
- if ((end = strrchr(++mkd, '"')) == NULL) {
- return NULL;
- }
- *end = 0;
- ret = zend_string_init(mkd, end - mkd, 0);
- *end = '"';
- return ret;
- }
- /* }}} */
- /* {{{ ftp_rmdir */
- int
- ftp_rmdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "RMD", sizeof("RMD")-1, dir, dir_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 250) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_chmod */
- int
- ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
- {
- char *buffer;
- size_t buffer_len;
- if (ftp == NULL || filename_len <= 0) {
- return 0;
- }
- buffer_len = spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
- if (!buffer) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, buffer, buffer_len)) {
- efree(buffer);
- return 0;
- }
- efree(buffer);
- if (!ftp_getresp(ftp) || ftp->resp != 200) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_alloc */
- int
- ftp_alloc(ftpbuf_t *ftp, const zend_long size, zend_string **response)
- {
- char buffer[64];
- int buffer_len;
- if (ftp == NULL || size <= 0) {
- return 0;
- }
- buffer_len = snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size);
- if (buffer_len < 0) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "ALLO", sizeof("ALLO")-1, buffer, buffer_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- if (response) {
- *response = zend_string_init(ftp->inbuf, strlen(ftp->inbuf), 0);
- }
- if (ftp->resp < 200 || ftp->resp >= 300) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_nlist */
- char**
- ftp_nlist(ftpbuf_t *ftp, const char *path, const size_t path_len)
- {
- return ftp_genlist(ftp, "NLST", sizeof("NLST")-1, path, path_len);
- }
- /* }}} */
- /* {{{ ftp_list */
- char**
- ftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive)
- {
- return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), ((recursive) ? sizeof("LIST -R")-1 : sizeof("LIST")-1), path, path_len);
- }
- /* }}} */
- /* {{{ ftp_mlsd */
- char**
- ftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len)
- {
- return ftp_genlist(ftp, "MLSD", sizeof("MLSD")-1, path, path_len);
- }
- /* }}} */
- /* {{{ ftp_mlsd_parse_line */
- int
- ftp_mlsd_parse_line(HashTable *ht, const char *input) {
- zval zstr;
- const char *end = input + strlen(input);
- const char *sp = memchr(input, ' ', end - input);
- if (!sp) {
- php_error_docref(NULL, E_WARNING, "Missing pathname in MLSD response");
- return FAILURE;
- }
- /* Extract pathname */
- ZVAL_STRINGL(&zstr, sp + 1, end - sp - 1);
- zend_hash_str_update(ht, "name", sizeof("name")-1, &zstr);
- end = sp;
- while (input < end) {
- const char *semi, *eq;
- /* Find end of fact */
- semi = memchr(input, ';', end - input);
- if (!semi) {
- php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response");
- return FAILURE;
- }
- /* Separate fact key and value */
- eq = memchr(input, '=', semi - input);
- if (!eq) {
- php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response");
- return FAILURE;
- }
- ZVAL_STRINGL(&zstr, eq + 1, semi - eq - 1);
- zend_hash_str_update(ht, input, eq - input, &zstr);
- input = semi + 1;
- }
- return SUCCESS;
- }
- /* }}} */
- /* {{{ ftp_type */
- int
- ftp_type(ftpbuf_t *ftp, ftptype_t type)
- {
- const char *typechar;
- if (ftp == NULL) {
- return 0;
- }
- if (type == ftp->type) {
- return 1;
- }
- if (type == FTPTYPE_ASCII) {
- typechar = "A";
- } else if (type == FTPTYPE_IMAGE) {
- typechar = "I";
- } else {
- return 0;
- }
- if (!ftp_putcmd(ftp, "TYPE", sizeof("TYPE")-1, typechar, 1)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 200) {
- return 0;
- }
- ftp->type = type;
- return 1;
- }
- /* }}} */
- /* {{{ ftp_pasv */
- int
- ftp_pasv(ftpbuf_t *ftp, int pasv)
- {
- char *ptr;
- union ipbox ipbox;
- unsigned long b[6];
- socklen_t n;
- struct sockaddr *sa;
- struct sockaddr_in *sin;
- if (ftp == NULL) {
- return 0;
- }
- if (pasv && ftp->pasv == 2) {
- return 1;
- }
- ftp->pasv = 0;
- if (!pasv) {
- return 1;
- }
- n = sizeof(ftp->pasvaddr);
- memset(&ftp->pasvaddr, 0, n);
- sa = (struct sockaddr *) &ftp->pasvaddr;
- if (getpeername(ftp->fd, sa, &n) < 0) {
- return 0;
- }
- #ifdef HAVE_IPV6
- if (sa->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
- char *endptr, delimiter;
- /* try EPSV first */
- if (!ftp_putcmd(ftp, "EPSV", sizeof("EPSV")-1, NULL, (size_t) 0)) {
- return 0;
- }
- if (!ftp_getresp(ftp)) {
- return 0;
- }
- if (ftp->resp == 229) {
- /* parse out the port */
- for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
- if (!*ptr) {
- return 0;
- }
- delimiter = *++ptr;
- for (n = 0; *ptr && n < 3; ptr++) {
- if (*ptr == delimiter) {
- n++;
- }
- }
- sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
- if (ptr == endptr || *endptr != delimiter) {
- return 0;
- }
- ftp->pasv = 2;
- return 1;
- }
- }
- /* fall back to PASV */
- #endif
- if (!ftp_putcmd(ftp, "PASV", sizeof("PASV")-1, NULL, (size_t) 0)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 227) {
- return 0;
- }
- /* parse out the IP and port */
- for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
- n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
- if (n != 6) {
- return 0;
- }
- for (n = 0; n < 6; n++) {
- ipbox.c[n] = (unsigned char) b[n];
- }
- sin = (struct sockaddr_in *) sa;
- if (ftp->usepasvaddress) {
- sin->sin_addr = ipbox.ia[0];
- }
- sin->sin_port = ipbox.s[2];
- ftp->pasv = 2;
- return 1;
- }
- /* }}} */
- /* {{{ ftp_get */
- int
- ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)
- {
- databuf_t *data = NULL;
- size_t rcvd;
- char arg[11];
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_type(ftp, type)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- if (resumepos > 0) {
- int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
- if (arg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
- goto bail;
- }
- }
- if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
- goto bail;
- }
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == (size_t)-1) {
- goto bail;
- }
- if (type == FTPTYPE_ASCII) {
- #ifndef PHP_WIN32
- char *s;
- #endif
- char *ptr = data->buf;
- char *e = ptr + rcvd;
- /* logic depends on the OS EOL
- * Win32 -> \r\n
- * Everything Else \n
- */
- #ifdef PHP_WIN32
- php_stream_write(outstream, ptr, (e - ptr));
- ptr = e;
- #else
- while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
- php_stream_write(outstream, ptr, (s - ptr));
- if (*(s + 1) == '\n') {
- s++;
- php_stream_putc(outstream, '\n');
- }
- ptr = s + 1;
- }
- #endif
- if (ptr < e) {
- php_stream_write(outstream, ptr, (e - ptr));
- }
- } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
- goto bail;
- }
- }
- ftp->data = data = data_close(ftp, data);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
- goto bail;
- }
- return 1;
- bail:
- ftp->data = data_close(ftp, data);
- return 0;
- }
- /* }}} */
- /* {{{ ftp_put */
- int
- ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)
- {
- databuf_t *data = NULL;
- zend_long size;
- char *ptr;
- int ch;
- char arg[11];
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_type(ftp, type)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- if (startpos > 0) {
- int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
- if (arg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
- goto bail;
- }
- }
- if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
- goto bail;
- }
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- size = 0;
- ptr = data->buf;
- while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
- /* flush if necessary */
- if (FTP_BUFSIZE - size < 2) {
- if (my_send(ftp, data->fd, data->buf, size) != size) {
- goto bail;
- }
- ptr = data->buf;
- size = 0;
- }
- if (ch == '\n' && type == FTPTYPE_ASCII) {
- *ptr++ = '\r';
- size++;
- }
- *ptr++ = ch;
- size++;
- }
- if (size && my_send(ftp, data->fd, data->buf, size) != size) {
- goto bail;
- }
- ftp->data = data = data_close(ftp, data);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
- goto bail;
- }
- return 1;
- bail:
- ftp->data = data_close(ftp, data);
- return 0;
- }
- /* }}} */
- /* {{{ ftp_append */
- int
- ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type)
- {
- databuf_t *data = NULL;
- zend_long size;
- char *ptr;
- int ch;
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_type(ftp, type)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- if (!ftp_putcmd(ftp, "APPE", sizeof("APPE")-1, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
- goto bail;
- }
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- size = 0;
- ptr = data->buf;
- while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
- /* flush if necessary */
- if (FTP_BUFSIZE - size < 2) {
- if (my_send(ftp, data->fd, data->buf, size) != size) {
- goto bail;
- }
- ptr = data->buf;
- size = 0;
- }
- if (ch == '\n' && type == FTPTYPE_ASCII) {
- *ptr++ = '\r';
- size++;
- }
- *ptr++ = ch;
- size++;
- }
- if (size && my_send(ftp, data->fd, data->buf, size) != size) {
- goto bail;
- }
- ftp->data = data = data_close(ftp, data);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
- goto bail;
- }
- return 1;
- bail:
- ftp->data = data_close(ftp, data);
- return 0;
- }
- /* }}} */
- /* {{{ ftp_size */
- zend_long
- ftp_size(ftpbuf_t *ftp, const char *path, const size_t path_len)
- {
- if (ftp == NULL) {
- return -1;
- }
- if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
- return -1;
- }
- if (!ftp_putcmd(ftp, "SIZE", sizeof("SIZE")-1, path, path_len)) {
- return -1;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 213) {
- return -1;
- }
- return ZEND_ATOL(ftp->inbuf);
- }
- /* }}} */
- /* {{{ ftp_mdtm */
- time_t
- ftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len)
- {
- time_t stamp;
- struct tm *gmt, tmbuf;
- struct tm tm;
- char *ptr;
- int n;
- if (ftp == NULL) {
- return -1;
- }
- if (!ftp_putcmd(ftp, "MDTM", sizeof("MDTM")-1, path, path_len)) {
- return -1;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 213) {
- return -1;
- }
- /* parse out the timestamp */
- for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
- 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);
- if (n != 6) {
- return -1;
- }
- tm.tm_year -= 1900;
- tm.tm_mon--;
- tm.tm_isdst = -1;
- /* figure out the GMT offset */
- stamp = time(NULL);
- gmt = php_gmtime_r(&stamp, &tmbuf);
- if (!gmt) {
- return -1;
- }
- gmt->tm_isdst = -1;
- /* apply the GMT offset */
- tm.tm_sec += stamp - mktime(gmt);
- tm.tm_isdst = gmt->tm_isdst;
- stamp = mktime(&tm);
- return stamp;
- }
- /* }}} */
- /* {{{ ftp_delete */
- int
- ftp_delete(ftpbuf_t *ftp, const char *path, const size_t path_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "DELE", sizeof("DELE")-1, path, path_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 250) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_rename */
- int
- ftp_rename(ftpbuf_t *ftp, const char *src, const size_t src_len, const char *dest, const size_t dest_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "RNFR", sizeof("RNFR")-1, src, src_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 350) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "RNTO", sizeof("RNTO")-1, dest, dest_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 250) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_site */
- int
- ftp_site(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)
- {
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, cmd, cmd_len)) {
- return 0;
- }
- if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* static functions */
- /* {{{ ftp_putcmd */
- int
- ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *args, const size_t args_len)
- {
- int size;
- char *data;
- if (strpbrk(cmd, "\r\n")) {
- return 0;
- }
- /* build the output buffer */
- if (args && args[0]) {
- /* "cmd args\r\n\0" */
- if (cmd_len + args_len + 4 > FTP_BUFSIZE) {
- return 0;
- }
- if (strpbrk(args, "\r\n")) {
- return 0;
- }
- size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
- } else {
- /* "cmd\r\n\0" */
- if (cmd_len + 3 > FTP_BUFSIZE) {
- return 0;
- }
- size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
- }
- data = ftp->outbuf;
- /* Clear the inbuf and extra-lines buffer */
- ftp->inbuf[0] = '\0';
- ftp->extra = NULL;
- if (my_send(ftp, ftp->fd, data, size) != size) {
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ ftp_readline */
- int
- ftp_readline(ftpbuf_t *ftp)
- {
- long size, rcvd;
- char *data, *eol;
- /* shift the extra to the front */
- size = FTP_BUFSIZE;
- rcvd = 0;
- if (ftp->extra) {
- memmove(ftp->inbuf, ftp->extra, ftp->extralen);
- rcvd = ftp->extralen;
- }
- data = ftp->inbuf;
- do {
- size -= rcvd;
- for (eol = data; rcvd; rcvd--, eol++) {
- if (*eol == '\r') {
- *eol = 0;
- ftp->extra = eol + 1;
- if (rcvd > 1 && *(eol + 1) == '\n') {
- ftp->extra++;
- rcvd--;
- }
- if ((ftp->extralen = --rcvd) == 0) {
- ftp->extra = NULL;
- }
- return 1;
- } else if (*eol == '\n') {
- *eol = 0;
- ftp->extra = eol + 1;
- if ((ftp->extralen = --rcvd) == 0) {
- ftp->extra = NULL;
- }
- return 1;
- }
- }
- data = eol;
- if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
- *data = 0;
- return 0;
- }
- } while (size);
- *data = 0;
- return 0;
- }
- /* }}} */
- /* {{{ ftp_getresp */
- int
- ftp_getresp(ftpbuf_t *ftp)
- {
- if (ftp == NULL) {
- return 0;
- }
- ftp->resp = 0;
- while (1) {
- if (!ftp_readline(ftp)) {
- return 0;
- }
- /* Break out when the end-tag is found */
- if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
- break;
- }
- }
- /* translate the tag */
- if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
- return 0;
- }
- ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
- memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
- if (ftp->extra) {
- ftp->extra -= 4;
- }
- return 1;
- }
- /* }}} */
- int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
- #ifdef HAVE_FTP_SSL
- int err;
- bool retry = 0;
- SSL *handle = NULL;
- php_socket_t fd;
- size_t sent;
- if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
- handle = ftp->ssl_handle;
- fd = ftp->fd;
- } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
- handle = ftp->data->ssl_handle;
- fd = ftp->data->fd;
- } else {
- return send(s, buf, size, 0);
- }
- do {
- sent = SSL_write(handle, buf, size);
- err = SSL_get_error(handle, sent);
- switch (err) {
- case SSL_ERROR_NONE:
- retry = 0;
- break;
- case SSL_ERROR_ZERO_RETURN:
- retry = 0;
- SSL_shutdown(handle);
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_CONNECT: {
- php_pollfd p;
- int i;
- p.fd = fd;
- p.events = POLLOUT;
- p.revents = 0;
- i = php_poll2(&p, 1, 300);
- retry = i > 0;
- }
- break;
- default:
- php_error_docref(NULL, E_WARNING, "SSL write failed");
- return -1;
- }
- } while (retry);
- return sent;
- #else
- return send(s, buf, size, 0);
- #endif
- }
- /* {{{ my_send */
- int
- my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
- {
- zend_long size, sent;
- int n;
- size = len;
- while (size) {
- n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
- if (n < 1) {
- char buf[256];
- if (n == 0) {
- #ifdef PHP_WIN32
- _set_errno(ETIMEDOUT);
- #else
- errno = ETIMEDOUT;
- #endif
- }
- php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf));
- return -1;
- }
- sent = single_send(ftp, s, buf, size);
- if (sent == -1) {
- return -1;
- }
- buf = (char*) buf + sent;
- size -= sent;
- }
- return len;
- }
- /* }}} */
- /* {{{ my_recv */
- int
- my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
- {
- int n, nr_bytes;
- #ifdef HAVE_FTP_SSL
- int err;
- bool retry = 0;
- SSL *handle = NULL;
- php_socket_t fd;
- #endif
- n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
- if (n < 1) {
- char buf[256];
- if (n == 0) {
- #ifdef PHP_WIN32
- _set_errno(ETIMEDOUT);
- #else
- errno = ETIMEDOUT;
- #endif
- }
- php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf));
- return -1;
- }
- #ifdef HAVE_FTP_SSL
- if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
- handle = ftp->ssl_handle;
- fd = ftp->fd;
- } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
- handle = ftp->data->ssl_handle;
- fd = ftp->data->fd;
- }
- if (handle) {
- do {
- nr_bytes = SSL_read(handle, buf, len);
- err = SSL_get_error(handle, nr_bytes);
- switch (err) {
- case SSL_ERROR_NONE:
- retry = 0;
- break;
- case SSL_ERROR_ZERO_RETURN:
- retry = 0;
- SSL_shutdown(handle);
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_CONNECT: {
- php_pollfd p;
- int i;
- p.fd = fd;
- p.events = POLLIN|POLLPRI;
- p.revents = 0;
- i = php_poll2(&p, 1, 300);
- retry = i > 0;
- }
- break;
- default:
- php_error_docref(NULL, E_WARNING, "SSL read failed");
- return -1;
- }
- } while (retry);
- } else {
- #endif
- nr_bytes = recv(s, buf, len, 0);
- #ifdef HAVE_FTP_SSL
- }
- #endif
- return (nr_bytes);
- }
- /* }}} */
- /* {{{ data_available */
- int
- data_available(ftpbuf_t *ftp, php_socket_t s)
- {
- int n;
- n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
- if (n < 1) {
- char buf[256];
- if (n == 0) {
- #ifdef PHP_WIN32
- _set_errno(ETIMEDOUT);
- #else
- errno = ETIMEDOUT;
- #endif
- }
- php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf));
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ data_writeable */
- int
- data_writeable(ftpbuf_t *ftp, php_socket_t s)
- {
- int n;
- n = php_pollfd_for_ms(s, POLLOUT, 1000);
- if (n < 1) {
- char buf[256];
- if (n == 0) {
- #ifdef PHP_WIN32
- _set_errno(ETIMEDOUT);
- #else
- errno = ETIMEDOUT;
- #endif
- }
- php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf));
- return 0;
- }
- return 1;
- }
- /* }}} */
- /* {{{ my_accept */
- int
- my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
- {
- int n;
- n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
- if (n < 1) {
- char buf[256];
- if (n == 0) {
- #ifdef PHP_WIN32
- _set_errno(ETIMEDOUT);
- #else
- errno = ETIMEDOUT;
- #endif
- }
- php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf));
- return -1;
- }
- return accept(s, addr, addrlen);
- }
- /* }}} */
- /* {{{ ftp_getdata */
- databuf_t*
- ftp_getdata(ftpbuf_t *ftp)
- {
- int fd = -1;
- databuf_t *data;
- php_sockaddr_storage addr;
- struct sockaddr *sa;
- socklen_t size;
- union ipbox ipbox;
- char arg[sizeof("255, 255, 255, 255, 255, 255")];
- struct timeval tv;
- int arg_len;
- /* ask for a passive connection if we need one */
- if (ftp->pasv && !ftp_pasv(ftp, 1)) {
- return NULL;
- }
- /* alloc the data structure */
- data = ecalloc(1, sizeof(*data));
- data->listener = -1;
- data->fd = -1;
- data->type = ftp->type;
- sa = (struct sockaddr *) &ftp->localaddr;
- /* bind/listen */
- if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
- php_error_docref(NULL, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- /* passive connection handler */
- if (ftp->pasv) {
- /* clear the ready status */
- ftp->pasv = 1;
- /* connect */
- /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
- size = php_sockaddr_size(&ftp->pasvaddr);
- tv.tv_sec = ftp->timeout_sec;
- tv.tv_usec = 0;
- if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
- php_error_docref(NULL, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- data->fd = fd;
- ftp->data = data;
- return data;
- }
- /* active (normal) connection */
- /* bind to a local address */
- php_any_addr(sa->sa_family, &addr, 0);
- size = php_sockaddr_size(&addr);
- if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
- php_error_docref(NULL, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
- php_error_docref(NULL, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- if (listen(fd, 5) != 0) {
- php_error_docref(NULL, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
- goto bail;
- }
- data->listener = fd;
- #if defined(HAVE_IPV6) && defined(HAVE_INET_NTOP)
- if (sa->sa_family == AF_INET6) {
- /* need to use EPRT */
- char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
- char out[INET6_ADDRSTRLEN];
- int eprtarg_len;
- inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
- eprtarg_len = snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
- if (eprtarg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "EPRT", sizeof("EPRT")-1, eprtarg, eprtarg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 200) {
- goto bail;
- }
- ftp->data = data;
- return data;
- }
- #endif
- /* send the PORT */
- ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
- ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
- arg_len = 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]);
- if (arg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "PORT", sizeof("PORT")-1, arg, arg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || ftp->resp != 200) {
- goto bail;
- }
- ftp->data = data;
- return data;
- bail:
- if (fd != -1) {
- closesocket(fd);
- }
- efree(data);
- return NULL;
- }
- /* }}} */
- /* {{{ data_accept */
- databuf_t*
- data_accept(databuf_t *data, ftpbuf_t *ftp)
- {
- php_sockaddr_storage addr;
- socklen_t size;
- #ifdef HAVE_FTP_SSL
- SSL_CTX *ctx;
- SSL_SESSION *session;
- int err, res;
- bool retry;
- #endif
- if (data->fd != -1) {
- goto data_accepted;
- }
- size = sizeof(addr);
- data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
- closesocket(data->listener);
- data->listener = -1;
- if (data->fd == -1) {
- efree(data);
- return NULL;
- }
- data_accepted:
- #ifdef HAVE_FTP_SSL
- /* now enable ssl if we need to */
- if (ftp->use_ssl && ftp->use_ssl_for_data) {
- ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
- if (ctx == NULL) {
- php_error_docref(NULL, E_WARNING, "data_accept: failed to retrieve the existing SSL context");
- return 0;
- }
- data->ssl_handle = SSL_new(ctx);
- if (data->ssl_handle == NULL) {
- php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle");
- return 0;
- }
- SSL_set_fd(data->ssl_handle, data->fd);
- if (ftp->old_ssl) {
- SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
- }
- /* get the session from the control connection so we can re-use it */
- session = SSL_get_session(ftp->ssl_handle);
- if (session == NULL) {
- php_error_docref(NULL, E_WARNING, "data_accept: failed to retrieve the existing SSL session");
- SSL_free(data->ssl_handle);
- return 0;
- }
- /* and set it on the data connection */
- res = SSL_set_session(data->ssl_handle, session);
- if (res == 0) {
- php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session");
- SSL_free(data->ssl_handle);
- return 0;
- }
- do {
- res = SSL_connect(data->ssl_handle);
- err = SSL_get_error(data->ssl_handle, res);
- switch (err) {
- case SSL_ERROR_NONE:
- retry = 0;
- break;
- case SSL_ERROR_ZERO_RETURN:
- retry = 0;
- SSL_shutdown(data->ssl_handle);
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE: {
- php_pollfd p;
- int i;
- p.fd = data->fd;
- p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
- p.revents = 0;
- i = php_poll2(&p, 1, 300);
- retry = i > 0;
- }
- break;
- default:
- php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed");
- SSL_shutdown(data->ssl_handle);
- SSL_free(data->ssl_handle);
- return 0;
- }
- } while (retry);
- data->ssl_active = 1;
- }
- #endif
- return data;
- }
- /* }}} */
- /* {{{ ftp_ssl_shutdown */
- #ifdef HAVE_FTP_SSL
- static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) {
- /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train
- the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the
- server might get an ECONNRESET which might lead to data truncation on server side.
- */
- char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
- to be at least 256 bytes long.*/
- int done = 1, err, nread;
- unsigned long sslerror;
- err = SSL_shutdown(ssl_handle);
- if (err < 0) {
- php_error_docref(NULL, E_WARNING, "SSL_shutdown failed");
- }
- else if (err == 0) {
- /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */
- done = 0;
- }
- while (!done && data_available(ftp, fd)) {
- ERR_clear_error();
- nread = SSL_read(ssl_handle, buf, sizeof(buf));
- if (nread <= 0) {
- err = SSL_get_error(ssl_handle, nread);
- switch (err) {
- case SSL_ERROR_NONE: /* this is not an error */
- case SSL_ERROR_ZERO_RETURN: /* no more data */
- /* This is the expected response. There was no data but only
- the close notify alert */
- done = 1;
- break;
- case SSL_ERROR_WANT_READ:
- /* there's data pending, re-invoke SSL_read() */
- break;
- case SSL_ERROR_WANT_WRITE:
- /* SSL wants a write. Really odd. Let's bail out. */
- done = 1;
- break;
- case SSL_ERROR_SYSCALL:
- /* most likely the peer closed the connection without
- sending a close_notify shutdown alert;
- bail out to avoid raising a spurious warning */
- done = 1;
- break;
- default:
- if ((sslerror = ERR_get_error())) {
- ERR_error_string_n(sslerror, buf, sizeof(buf));
- php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s", buf);
- } else if (errno) {
- php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", strerror(errno), errno);
- }
- done = 1;
- break;
- }
- }
- }
- (void)SSL_free(ssl_handle);
- }
- #endif
- /* }}} */
- /* {{{ data_close */
- databuf_t*
- data_close(ftpbuf_t *ftp, databuf_t *data)
- {
- if (data == NULL) {
- return NULL;
- }
- if (data->listener != -1) {
- #ifdef HAVE_FTP_SSL
- if (data->ssl_active) {
- /* don't free the data context, it's the same as the control */
- ftp_ssl_shutdown(ftp, data->listener, data->ssl_handle);
- data->ssl_active = 0;
- }
- #endif
- closesocket(data->listener);
- }
- if (data->fd != -1) {
- #ifdef HAVE_FTP_SSL
- if (data->ssl_active) {
- /* don't free the data context, it's the same as the control */
- ftp_ssl_shutdown(ftp, data->fd, data->ssl_handle);
- data->ssl_active = 0;
- }
- #endif
- closesocket(data->fd);
- }
- if (ftp) {
- ftp->data = NULL;
- }
- efree(data);
- return NULL;
- }
- /* }}} */
- /* {{{ ftp_genlist */
- char**
- ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len)
- {
- php_stream *tmpstream = NULL;
- databuf_t *data = NULL;
- char *ptr;
- int ch, lastch;
- size_t size, rcvd;
- size_t lines;
- char **ret = NULL;
- char **entry;
- char *text;
- if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
- php_error_docref(NULL, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
- return NULL;
- }
- if (!ftp_type(ftp, FTPTYPE_ASCII)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- if (!ftp_putcmd(ftp, cmd, cmd_len, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
- goto bail;
- }
- /* some servers don't open a ftp-data connection if the directory is empty */
- if (ftp->resp == 226) {
- ftp->data = data_close(ftp, data);
- php_stream_close(tmpstream);
- return ecalloc(1, sizeof(char*));
- }
- /* pull data buffer into tmpfile */
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- size = 0;
- lines = 0;
- lastch = 0;
- while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {
- goto bail;
- }
- php_stream_write(tmpstream, data->buf, rcvd);
- size += rcvd;
- for (ptr = data->buf; rcvd; rcvd--, ptr++) {
- if (*ptr == '\n' && lastch == '\r') {
- lines++;
- }
- lastch = *ptr;
- }
- }
- ftp->data = data_close(ftp, data);
- php_stream_rewind(tmpstream);
- ret = safe_emalloc((lines + 1), sizeof(char*), size);
- entry = ret;
- text = (char*) (ret + lines + 1);
- *entry = text;
- lastch = 0;
- while ((ch = php_stream_getc(tmpstream)) != EOF) {
- if (ch == '\n' && lastch == '\r') {
- *(text - 1) = 0;
- *++entry = text;
- } else {
- *text++ = ch;
- }
- lastch = ch;
- }
- *entry = NULL;
- php_stream_close(tmpstream);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
- efree(ret);
- return NULL;
- }
- return ret;
- bail:
- ftp->data = data_close(ftp, data);
- php_stream_close(tmpstream);
- if (ret)
- efree(ret);
- return NULL;
- }
- /* }}} */
- /* {{{ ftp_nb_get */
- int
- ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)
- {
- databuf_t *data = NULL;
- char arg[11];
- if (ftp == NULL) {
- return PHP_FTP_FAILED;
- }
- if (!ftp_type(ftp, type)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- if (resumepos>0) {
- int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
- if (arg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
- goto bail;
- }
- }
- if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
- goto bail;
- }
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- ftp->stream = outstream;
- ftp->lastch = 0;
- ftp->nb = 1;
- return (ftp_nb_continue_read(ftp));
- bail:
- ftp->data = data_close(ftp, data);
- return PHP_FTP_FAILED;
- }
- /* }}} */
- /* {{{ ftp_nb_continue_read */
- int
- ftp_nb_continue_read(ftpbuf_t *ftp)
- {
- databuf_t *data = NULL;
- char *ptr;
- int lastch;
- size_t rcvd;
- ftptype_t type;
- data = ftp->data;
- /* check if there is already more data */
- if (!data_available(ftp, data->fd)) {
- return PHP_FTP_MOREDATA;
- }
- type = ftp->type;
- lastch = ftp->lastch;
- if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == (size_t)-1) {
- goto bail;
- }
- if (type == FTPTYPE_ASCII) {
- for (ptr = data->buf; rcvd; rcvd--, ptr++) {
- if (lastch == '\r' && *ptr != '\n') {
- php_stream_putc(ftp->stream, '\r');
- }
- if (*ptr != '\r') {
- php_stream_putc(ftp->stream, *ptr);
- }
- lastch = *ptr;
- }
- } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
- goto bail;
- }
- ftp->lastch = lastch;
- return PHP_FTP_MOREDATA;
- }
- if (type == FTPTYPE_ASCII && lastch == '\r') {
- php_stream_putc(ftp->stream, '\r');
- }
- ftp->data = data = data_close(ftp, data);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
- goto bail;
- }
- ftp->nb = 0;
- return PHP_FTP_FINISHED;
- bail:
- ftp->nb = 0;
- ftp->data = data_close(ftp, data);
- return PHP_FTP_FAILED;
- }
- /* }}} */
- /* {{{ ftp_nb_put */
- int
- ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)
- {
- databuf_t *data = NULL;
- char arg[11];
- if (ftp == NULL) {
- return 0;
- }
- if (!ftp_type(ftp, type)) {
- goto bail;
- }
- if ((data = ftp_getdata(ftp)) == NULL) {
- goto bail;
- }
- if (startpos > 0) {
- int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
- if (arg_len < 0) {
- goto bail;
- }
- if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
- goto bail;
- }
- }
- if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) {
- goto bail;
- }
- if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
- goto bail;
- }
- if ((data = data_accept(data, ftp)) == NULL) {
- goto bail;
- }
- ftp->data = data;
- ftp->stream = instream;
- ftp->lastch = 0;
- ftp->nb = 1;
- return (ftp_nb_continue_write(ftp));
- bail:
- ftp->data = data_close(ftp, data);
- return PHP_FTP_FAILED;
- }
- /* }}} */
- /* {{{ ftp_nb_continue_write */
- int
- ftp_nb_continue_write(ftpbuf_t *ftp)
- {
- long size;
- char *ptr;
- int ch;
- /* check if we can write more data */
- if (!data_writeable(ftp, ftp->data->fd)) {
- return PHP_FTP_MOREDATA;
- }
- size = 0;
- ptr = ftp->data->buf;
- while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
- if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
- *ptr++ = '\r';
- size++;
- }
- *ptr++ = ch;
- size++;
- /* flush if necessary */
- if (FTP_BUFSIZE - size < 2) {
- if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
- goto bail;
- }
- return PHP_FTP_MOREDATA;
- }
- }
- if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
- goto bail;
- }
- ftp->data = data_close(ftp, ftp->data);
- if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
- goto bail;
- }
- ftp->nb = 0;
- return PHP_FTP_FINISHED;
- bail:
- ftp->data = data_close(ftp, ftp->data);
- ftp->nb = 0;
- return PHP_FTP_FAILED;
- }
- /* }}} */
|