Added IPv6 support

@- Added IPv6 support in FTP extension. (Stig Venaas)
This commit is contained in:
Stig Venaas 2002-01-06 23:10:54 +00:00
parent a6ec8d37e6
commit 9a307e4550
2 changed files with 96 additions and 115 deletions

View File

@ -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;
}
ftp->fd = php_hostconnect(host, port ? port : 21, SOCK_STREAM, (int) timeout_sec);
if (ftp->fd == -1) {
goto bail;
}
/* Default Settings */
ftp->timeout_sec = timeout_sec;
/* connect */
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
goto bail;
}
if (my_connect(ftp, fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
perror("connect");
goto bail;
}
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)

View File

@ -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;