diff options
-rw-r--r-- | ext/ftp/ftp.c | 203 | ||||
-rw-r--r-- | ext/ftp/ftp.h | 6 |
2 files changed, 95 insertions, 114 deletions
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index c9a23bedaf..e44186ec39 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -35,6 +35,7 @@ #include <winsock.h> #else #include <sys/socket.h> +#include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #endif @@ -68,8 +69,6 @@ static int ftp_putcmd( ftpbuf_t *ftp, /* wrapper around send/recv to handle timeouts */ static int my_send(ftpbuf_t *ftp, int s, void *buf, size_t len); static int my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len); -static int my_connect(ftpbuf_t *ftp, int s, const struct sockaddr *addr, - int addrlen); static int my_accept(ftpbuf_t *ftp, int s, struct sockaddr *addr, int *addrlen); @@ -107,27 +106,10 @@ union ipbox { ftpbuf_t* ftp_open(const char *host, short port, long timeout_sec) { - int fd = -1; ftpbuf_t *ftp; - struct sockaddr_in addr; - struct hostent *he; int size; - /* set up the address */ - if ((he = gethostbyname(host)) == NULL) { -#if 0 - herror("gethostbyname"); -#endif - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - memcpy(&addr.sin_addr, he->h_addr, he->h_length); - addr.sin_family = AF_INET; - addr.sin_port = port ? port : htons(21); - - /* alloc the ftp structure */ ftp = calloc(1, sizeof(*ftp)); if (ftp == NULL) { @@ -135,29 +117,21 @@ ftp_open(const char *host, short port, long timeout_sec) return NULL; } - /* Default Settings */ - ftp->timeout_sec = timeout_sec; - - /* connect */ - if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { - perror("socket"); + ftp->fd = php_hostconnect(host, port ? port : 21, SOCK_STREAM, (int) timeout_sec); + if (ftp->fd == -1) { goto bail; } - if (my_connect(ftp, fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) { - perror("connect"); - goto bail; - } + /* Default Settings */ + ftp->timeout_sec = timeout_sec; - size = sizeof(addr); - if (getsockname(fd, (struct sockaddr*) &addr, &size) == -1) { + size = sizeof(ftp->localaddr); + memset(&ftp->localaddr, 0, size); + if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) == -1) { perror("getsockname"); goto bail; } - ftp->localaddr = addr.sin_addr; - ftp->fd = fd; - if (!ftp_getresp(ftp) || ftp->resp != 220) { goto bail; } @@ -165,8 +139,8 @@ ftp_open(const char *host, short port, long timeout_sec) return ftp; bail: - if (fd != -1) - closesocket(fd); + if (ftp->fd != -1) + closesocket(ftp->fd); free(ftp); return NULL; } @@ -487,6 +461,8 @@ ftp_pasv(ftpbuf_t *ftp, int pasv) union ipbox ipbox; unsigned long b[6]; int n; + struct sockaddr *sa; + struct sockaddr_in *sin; if (ftp == NULL) return 0; @@ -498,6 +474,47 @@ ftp_pasv(ftpbuf_t *ftp, int pasv) if (!pasv) return 1; + n = sizeof(ftp->pasvaddr); + memset(&ftp->pasvaddr, 0, n); + sa = (struct sockaddr *) &ftp->pasvaddr; + +#ifdef HAVE_IPV6 + if (getpeername(ftp->fd, sa, &n) < 0) + return 0; + + 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", NULL)) + 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) strtol(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", NULL)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 227) @@ -513,10 +530,10 @@ ftp_pasv(ftpbuf_t *ftp, int pasv) for (n=0; n<6; n++) ipbox.c[n] = (unsigned char) b[n]; - memset(&ftp->pasvaddr, 0, sizeof(ftp->pasvaddr)); - ftp->pasvaddr.sin_family = AF_INET; - ftp->pasvaddr.sin_addr.s_addr = ipbox.l[0]; - ftp->pasvaddr.sin_port = ipbox.s[2]; + sin = (struct sockaddr_in *) sa; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ipbox.l[0]; + sin->sin_port = ipbox.s[2]; ftp->pasv = 2; @@ -970,61 +987,6 @@ my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len) } /* }}} */ -/* {{{ my_connect - */ -int -my_connect(ftpbuf_t *ftp, int s, const struct sockaddr *addr, int addrlen) -#ifndef PHP_WIN32 -{ - fd_set conn_set; - int flags; - int n; - int error = 0; - struct timeval tv; - - flags = fcntl(s, F_GETFL, 0); - fcntl(s, F_SETFL, flags | O_NONBLOCK); - - n = connect(s, addr, addrlen); - if (n == -1 && errno != EINPROGRESS) - return -1; - - if (n) { - FD_ZERO(&conn_set); - FD_SET(s, &conn_set); - - tv.tv_sec = ftp->timeout_sec; - tv.tv_usec = 0; - - n = select(s + 1, &conn_set, &conn_set, NULL, &tv); - if (n < 1) { - if (n == 0) - errno = ETIMEDOUT; - return -1; - } - - if (FD_ISSET(s, &conn_set)) { - n = sizeof(error); - n = getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &n); - if (n == -1 || error) { - if (error) - errno = error; - return -1; - } - } - } - - fcntl(s, F_SETFL, flags); - - return 0; -} -#else -{ - return connect(s, addr, addrlen); -} -#endif -/* }}} */ - /* {{{ my_accept */ int @@ -1059,10 +1021,12 @@ ftp_getdata(ftpbuf_t *ftp) { int fd = -1; databuf_t *data; - struct sockaddr_in addr; + php_sockaddr_storage addr; + struct sockaddr *sa; int size; union ipbox ipbox; char arg[sizeof("255, 255, 255, 255, 255, 255")]; + struct timeval tv; /* ask for a passive connection if we need one */ @@ -1079,25 +1043,26 @@ ftp_getdata(ftpbuf_t *ftp) data->fd = -1; Z_TYPE_P(data) = Z_TYPE_P(ftp); + sa = (struct sockaddr *) &ftp->localaddr; /* bind/listen */ - if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { perror("socket"); goto bail; } + size = sizeof(php_sockaddr_storage); + /* passive connection handler */ if (ftp->pasv) { /* clear the ready status */ ftp->pasv = 1; /* connect */ - if (my_connect(ftp, fd, (struct sockaddr*) &ftp->pasvaddr, - sizeof(ftp->pasvaddr)) == -1) - { + tv.tv_sec = ftp->timeout_sec; + tv.tv_usec = 0; + if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) { perror("connect"); - closesocket(fd); - free(data); - return NULL; + goto bail; } data->fd = fd; @@ -1109,17 +1074,13 @@ ftp_getdata(ftpbuf_t *ftp) /* active (normal) connection */ /* bind to a local address */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = 0; + php_any_addr(sa->sa_family, &addr, 0); - if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) { + if (bind(fd, (struct sockaddr*) &addr, size) == -1) { perror("bind"); goto bail; } - size = sizeof(addr); if (getsockname(fd, (struct sockaddr*) &addr, &size) == -1) { perror("getsockname"); goto bail; @@ -1132,9 +1093,27 @@ ftp_getdata(ftpbuf_t *ftp) data->listener = fd; +#ifdef HAVE_IPV6 + if (sa->sa_family == AF_INET6) { + /* need to use EPRT */ + char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")]; + char out[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out)); + sprintf(eprtarg, "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port)); + + if (!ftp_putcmd(ftp, "EPRT", eprtarg)) + goto bail; + + if (!ftp_getresp(ftp) || ftp->resp != 200) + goto bail; + + return data; + } +#endif + /* send the PORT */ - ipbox.l[0] = ftp->localaddr.s_addr; - ipbox.s[2] = addr.sin_port; + ipbox.l[0] = ((struct sockaddr_in*) sa)->sin_addr.s_addr; + ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port; sprintf(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]); @@ -1159,7 +1138,7 @@ bail: databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp) { - struct sockaddr_in addr; + php_sockaddr_storage addr; int size; if (data->fd != -1) diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h index 9546e53c44..416c36df83 100644 --- a/ext/ftp/ftp.h +++ b/ext/ftp/ftp.h @@ -22,6 +22,8 @@ #ifndef FTP_H #define FTP_H +#include "php_network.h" + #include <stdio.h> #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> @@ -40,7 +42,7 @@ typedef enum ftptype { typedef struct ftpbuf { int fd; /* control connection */ - struct in_addr localaddr; /* local inet address */ + php_sockaddr_storage localaddr; /* local address */ int resp; /* last response code */ char inbuf[FTP_BUFSIZE]; /* last response text */ char *extra; /* extra characters */ @@ -50,7 +52,7 @@ typedef struct ftpbuf char *syst; /* cached system type */ ftptype_t type; /* current transfer type */ int pasv; /* 0=off; 1=pasv; 2=ready */ - struct sockaddr_in pasvaddr; /* passive mode address */ + php_sockaddr_storage pasvaddr; /* passive mode address */ long timeout_sec; /* User configureable timeout (seconds) */ } ftpbuf_t; |