#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <ftpio.h>
#include <inttypes.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SUCCESS 0
#define FAILURE -1
#ifndef TRUE
#define TRUE (1)
#define FALSE (0)
#endif
#define FTP_TIMEOUT(code) (FtpTimedOut || code == FTP_TIMED_OUT)
static FTP_t ftp_new(void);
static void check_passive(FILE *fp);
static int ftp_read_method(void *n, char *buf, int nbytes);
static int ftp_write_method(void *n, const char *buf, int nbytes);
static int ftp_close_method(void *n);
static int writes(int fd, const char *s);
static char *get_a_line(FTP_t ftp);
static int get_a_number(FTP_t ftp, char **q);
static int botch(const char *func, const char *botch_state);
static int cmd(FTP_t ftp, const char *fmt, ...) __printflike(2, 3);
static int ftp_login_session(FTP_t ftp, const char *host, int af,
const char *user, const char *passwd,
int port, int verbose);
static int ftp_file_op(FTP_t ftp, const char *operation, const char *file,
FILE **fp, const char *mode, off_t *seekto);
static int ftp_close(FTP_t ftp);
static int get_url_info(const char *url_in, char *host_ret,
size_t host_len, int *port_ret,
char *name_ret, size_t name_len);
static void ftp_timeout(int sig);
static void ftp_set_timeout(void);
static void ftp_clear_timeout(void);
static void ai_unmapped(struct addrinfo *);
int networkInit(void);
int FtpTimedOut;
#define FTP_GENERALLY_HAPPY 200
#define FTP_ASCII_HAPPY FTP_GENERALLY_HAPPY
#define FTP_BINARY_HAPPY FTP_GENERALLY_HAPPY
#define FTP_PORT_HAPPY FTP_GENERALLY_HAPPY
#define FTP_HAPPY_COMMENT 220
#define FTP_QUIT_HAPPY 221
#define FTP_TRANSFER_HAPPY 226
#define FTP_PASSIVE_HAPPY 227
#define FTP_LPASSIVE_HAPPY 228
#define FTP_EPASSIVE_HAPPY 229
#define FTP_CHDIR_HAPPY 250
#define FTP_TIMED_OUT 421
int
networkInit(void)
{
return SUCCESS;
}
static int
check_code(FTP_t ftp, int var, int preferred)
{
ftp->error = 0;
while (1) {
if (var == preferred)
return 0;
else if (var == FTP_TRANSFER_HAPPY)
var = get_a_number(ftp, NULL);
else if (var == FTP_HAPPY_COMMENT)
var = get_a_number(ftp, NULL);
else if (var == FTP_GENERALLY_HAPPY)
var = get_a_number(ftp, NULL);
else {
ftp->error = var;
return 1;
}
}
}
int
ftpAscii(FILE *fp)
{
FTP_t ftp = fcookie(fp);
int i;
if (!ftp->is_binary)
return SUCCESS;
i = cmd(ftp, "TYPE A");
if (i < 0 || check_code(ftp, i, FTP_ASCII_HAPPY))
return i;
ftp->is_binary = FALSE;
return SUCCESS;
}
int
ftpBinary(FILE *fp)
{
FTP_t ftp = fcookie(fp);
int i;
if (ftp->is_binary)
return SUCCESS;
i = cmd(ftp, "TYPE I");
if (i < 0 || check_code(ftp, i, FTP_BINARY_HAPPY))
return i;
ftp->is_binary = TRUE;
return SUCCESS;
}
void
ftpVerbose(FILE *fp, int status)
{
FTP_t ftp = fcookie(fp);
ftp->is_verbose = status;
}
int
ftpChdir(FILE *fp, char *dir)
{
int i;
FTP_t ftp = fcookie(fp);
i = cmd(ftp, "CWD %s", dir);
if (i < 0 || check_code(ftp, i, FTP_CHDIR_HAPPY))
return i;
return SUCCESS;
}
int
ftpErrno(FILE *fp)
{
FTP_t ftp = fcookie(fp);
return ftp->error;
}
const char *
ftpErrString(int error)
{
int k;
if (error == -1)
return("connection in wrong state");
if (error < 100)
return strerror(error);
for (k = 0; k < ftpErrListLength; k++)
if (ftpErrList[k].num == error)
return(ftpErrList[k].string);
return("Unknown error");
}
off_t
ftpGetSize(FILE *fp, const char *name)
{
int i;
char p[BUFSIZ], *cp, *ep;
FTP_t ftp = fcookie(fp);
off_t size;
check_passive(fp);
if ((size_t)snprintf(p, sizeof(p), "SIZE %s\r\n", name) >= sizeof(p))
return (off_t)(-1);
if (ftp->is_verbose)
fprintf(stderr, "Sending %s", p);
if (writes(ftp->fd_ctrl, p))
return (off_t)(-1);
i = get_a_number(ftp, &cp);
if (check_code(ftp, i, 213))
return (off_t)(-1);
errno = 0;
size = (off_t)strtoq(cp, &ep, 10);
if (*ep != '\0' || errno == ERANGE)
return (off_t)(-1);
return size;
}
time_t
ftpGetModtime(FILE *fp, const char *name)
{
char p[BUFSIZ], *cp;
struct tm t;
time_t t0 = time (0);
FTP_t ftp = fcookie(fp);
int i;
check_passive(fp);
if ((size_t)snprintf(p, sizeof(p), "MDTM %s\r\n", name) >= sizeof(p))
return (time_t)0;
if (ftp->is_verbose)
fprintf(stderr, "Sending %s", p);
if (writes(ftp->fd_ctrl, p))
return (time_t)0;
i = get_a_number(ftp, &cp);
if (check_code(ftp, i, 213))
return (time_t)0;
while (*cp && !isdigit(*cp))
cp++;
if (!*cp)
return (time_t)0;
t0 = localtime (&t0)->tm_gmtoff;
sscanf(cp, "%04d%02d%02d%02d%02d%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
t.tm_mon--;
t.tm_year -= 1900;
t.tm_isdst=-1;
t.tm_gmtoff = 0;
t0 += mktime (&t);
return t0;
}
FILE *
ftpGet(FILE *fp, const char *file, off_t *seekto)
{
FILE *fp2;
FTP_t ftp = fcookie(fp);
check_passive(fp);
if (ftpBinary(fp) != SUCCESS)
return NULL;
if (ftp_file_op(ftp, "RETR", file, &fp2, "r", seekto) == SUCCESS)
return fp2;
return NULL;
}
FILE *
ftpLogin(const char *host, const char *user, const char *passwd, int port,
int verbose, int *retcode)
{
#ifdef INET6
return ftpLoginAf(host, AF_UNSPEC, user, passwd, port, verbose, retcode);
#else
return ftpLoginAf(host, AF_INET, user, passwd, port, verbose, retcode);
#endif
}
FILE *
ftpLoginAf(const char *host, int af, const char *user, const char *passwd,
int port, int verbose, int *retcode)
{
FTP_t n;
FILE *fp;
if (retcode)
*retcode = 0;
if (networkInit() != SUCCESS)
return NULL;
n = ftp_new();
fp = NULL;
if (n && ftp_login_session(n, host, af, user, passwd, port, verbose) == SUCCESS) {
fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method);
}
if (retcode) {
if (!n)
*retcode = (FtpTimedOut ? FTP_TIMED_OUT : -1);
else switch(n->error) {
case EADDRNOTAVAIL:
*retcode = FTP_TIMED_OUT;
break;
case ETIMEDOUT:
*retcode = FTP_TIMED_OUT;
break;
default:
*retcode = n->error;
break;
}
}
return fp;
}
FILE *
ftpPut(FILE *fp, const char *file)
{
FILE *fp2;
FTP_t ftp = fcookie(fp);
check_passive(fp);
if (ftp_file_op(ftp, "STOR", file, &fp2, "w", NULL) == SUCCESS)
return fp2;
return NULL;
}
int
ftpPassive(FILE *fp, int st)
{
FTP_t ftp = fcookie(fp);
ftp->is_passive = !!st;
return SUCCESS;
}
FILE *
ftpGetURL(const char *url, const char *user, const char *passwd, int *retcode)
{
#ifdef INET6
return ftpGetURLAf(url, AF_UNSPEC, user, passwd, retcode);
#else
return ftpGetURLAf(url, AF_INET, user, passwd, retcode);
#endif
}
FILE *
ftpGetURLAf(const char *url, int af, const char *user, const char *passwd,
int *retcode)
{
char host[255], name[255];
int port;
FILE *fp2;
static FILE *fp = NULL;
static char *prev_host;
if (retcode)
*retcode = 0;
if (get_url_info(url, host, sizeof(host), &port, name,
sizeof(name)) == SUCCESS) {
if (fp && prev_host) {
if (!strcmp(prev_host, host)) {
fp2 = ftpGet(fp, name, NULL);
if (!fp2) {
fclose(fp);
free(prev_host);
prev_host = NULL;
}
else
return fp2;
}
else {
fclose(fp);
free(prev_host);
prev_host = NULL;
}
}
fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode);
if (fp) {
fp2 = ftpGet(fp, name, NULL);
if (!fp2) {
if (retcode)
*retcode = ftpErrno(fp);
fclose(fp);
fp = NULL;
}
else
prev_host = strdup(host);
return fp2;
}
}
return NULL;
}
FILE *
ftpPutURL(const char *url, const char *user, const char *passwd, int *retcode)
{
#ifdef INET6
return ftpPutURLAf(url, AF_UNSPEC, user, passwd, retcode);
#else
return ftpPutURLAf(url, AF_INET, user, passwd, retcode);
#endif
}
FILE *
ftpPutURLAf(const char *url, int af, const char *user, const char *passwd,
int *retcode)
{
char host[255], name[255];
int port;
static FILE *fp = NULL;
FILE *fp2;
if (retcode)
*retcode = 0;
if (fp) {
fclose(fp);
fp = NULL;
}
if (get_url_info(url, host, sizeof(host), &port,
name, sizeof(name)) == SUCCESS) {
fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode);
if (fp) {
fp2 = ftpPut(fp, name);
if (!fp2) {
if (retcode)
*retcode = ftpErrno(fp);
fclose(fp);
fp = NULL;
}
return fp2;
}
}
return NULL;
}
static int
get_url_info(const char *url_in, char *host_ret, size_t host_len,
int *port_ret, char *name_ret, size_t name_len)
{
char *name, *host, *cp, url[BUFSIZ];
int port;
name = host = NULL;
if (strncmp("ftp://", url_in, 6) != 0)
return FAILURE;
strncpy(url, url_in, BUFSIZ);
host = url + 6;
if ((cp = index(host, ':')) != NULL) {
*(cp++) = '\0';
port = strtol(cp, 0, 0);
}
else
port = 0;
if (port_ret)
*port_ret = port;
if ((name = index(cp ? cp : host, '/')) != NULL)
*(name++) = '\0';
if (host_ret) {
if (strlen(host) >= host_len)
return FAILURE;
strcpy(host_ret, host);
}
if (name && name_ret) {
if (strlen(name) >= name_len)
return FAILURE;
strcpy(name_ret, name);
}
return SUCCESS;
}
static FTP_t
ftp_new(void)
{
FTP_t ftp;
ftp = (FTP_t)malloc(sizeof *ftp);
if (!ftp)
return NULL;
memset(ftp, 0, sizeof *ftp);
ftp->fd_ctrl = -1;
ftp->con_state = init;
ftp->is_binary = FALSE;
ftp->is_passive = FALSE;
ftp->is_verbose = FALSE;
ftp->error = 0;
return ftp;
}
static int
ftp_read_method(void *vp, char *buf, int nbytes)
{
int i, fd;
FTP_t n = (FTP_t)vp;
fd = n->fd_ctrl;
i = (fd >= 0) ? read(fd, buf, nbytes) : EOF;
return i;
}
static int
ftp_write_method(void *vp, const char *buf, int nbytes)
{
int i, fd;
FTP_t n = (FTP_t)vp;
fd = n->fd_ctrl;
i = (fd >= 0) ? write(fd, buf, nbytes) : EOF;
return i;
}
static int
ftp_close_method(void *n)
{
int i;
i = ftp_close((FTP_t)n);
free(n);
return i;
}
static void
check_passive(FILE *fp)
{
const char *cp = getenv("FTP_PASSIVE_MODE");
if (cp != NULL)
ftpPassive(fp, strncasecmp(cp, "no", 2));
}
static void
ftp_timeout(int sig __unused)
{
FtpTimedOut = TRUE;
}
static void
ftp_set_timeout(void)
{
struct sigaction new;
char *cp;
int ival;
FtpTimedOut = FALSE;
sigemptyset(&new.sa_mask);
new.sa_flags = 0;
new.sa_handler = ftp_timeout;
sigaction(SIGALRM, &new, NULL);
cp = getenv("FTP_TIMEOUT");
if (!cp || !(ival = atoi(cp)))
ival = 120;
alarm(ival);
}
static void
ftp_clear_timeout(void)
{
struct sigaction new;
alarm(0);
sigemptyset(&new.sa_mask);
new.sa_flags = 0;
new.sa_handler = SIG_DFL;
sigaction(SIGALRM, &new, NULL);
}
static int
writes(int fd, const char *s)
{
int n, i = strlen(s);
ftp_set_timeout();
n = write(fd, s, i);
ftp_clear_timeout();
if (FtpTimedOut || i != n)
return TRUE;
return FALSE;
}
static char *
get_a_line(FTP_t ftp)
{
static char buf[BUFSIZ];
int i,j;
for(i = 0; i < BUFSIZ;) {
ftp_set_timeout();
j = read(ftp->fd_ctrl, buf + i, 1);
ftp_clear_timeout();
if (FtpTimedOut || j != 1)
return NULL;
if (buf[i] == '\r' || buf[i] == '\n') {
if (!i)
continue;
buf[i] = '\0';
if (ftp->is_verbose == TRUE)
fprintf(stderr, "%s\n",buf+4);
return buf;
}
i++;
}
return buf;
}
static int
get_a_number(FTP_t ftp, char **q)
{
char *p;
int i = -1, j;
while(1) {
p = get_a_line(ftp);
if (!p) {
ftp_close(ftp);
if (FtpTimedOut)
return FTP_TIMED_OUT;
return FAILURE;
}
if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])))
continue;
if (i == -1 && p[3] == '-') {
i = strtol(p, 0, 0);
continue;
}
if (p[3] != ' ' && p[3] != '\t')
continue;
j = strtol(p, 0, 0);
if (i == -1) {
if (q) *q = p+4;
return j;
} else if (j == i) {
if (q) *q = p+4;
return j;
}
}
}
static int
ftp_close(FTP_t ftp)
{
int i, rcode;
rcode = FAILURE;
if (ftp->con_state == isopen) {
ftp->con_state = quit;
if (ftp->error != FTP_TIMED_OUT)
i = cmd(ftp, "QUIT");
else
i = FTP_QUIT_HAPPY;
if (!check_code(ftp, i, FTP_QUIT_HAPPY))
rcode = SUCCESS;
close(ftp->fd_ctrl);
ftp->fd_ctrl = -1;
}
else if (ftp->con_state == quit)
rcode = SUCCESS;
return rcode;
}
static int
botch(const char *func __unused, const char *botch_state __unused)
{
return FAILURE;
}
static int
cmd(FTP_t ftp, const char *fmt, ...)
{
char p[BUFSIZ];
int i;
va_list ap;
va_start(ap, fmt);
if ((size_t)vsnprintf(p, sizeof p - 2, fmt, ap) >= sizeof(p) - 2)
return FAILURE;
va_end(ap);
if (ftp->con_state == init)
return botch("cmd", "open");
strcat(p, "\r\n");
if (ftp->is_verbose)
fprintf(stderr, "Sending: %s", p);
if (writes(ftp->fd_ctrl, p)) {
if (FtpTimedOut)
return FTP_TIMED_OUT;
return FAILURE;
}
while ((i = get_a_number(ftp, NULL)) == FTP_HAPPY_COMMENT);
return i;
}
static int
ftp_login_session(FTP_t ftp, const char *host, int af,
const char *user, const char *passwd, int port, int verbose)
{
char pbuf[10];
struct addrinfo hints, *res, *res0;
int err;
int s;
int i;
if (networkInit() != SUCCESS)
return FAILURE;
if (ftp->con_state != init) {
ftp_close(ftp);
ftp->error = -1;
return FAILURE;
}
if (!user)
user = "ftp";
if (!passwd)
passwd = "setup@";
if (!port)
port = 21;
if ((size_t)snprintf(pbuf, sizeof(pbuf), "%d", port) >= sizeof(pbuf))
return FAILURE;
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
err = getaddrinfo(host, pbuf, &hints, &res0);
if (err) {
ftp->error = 0;
return FAILURE;
}
s = -1;
for (res = res0; res; res = res->ai_next) {
ai_unmapped(res);
ftp->addrtype = res->ai_family;
if ((s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) < 0)
continue;
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
close(s);
s = -1;
continue;
}
break;
}
freeaddrinfo(res0);
if (s < 0) {
ftp->error = errno;
return FAILURE;
}
ftp->fd_ctrl = s;
ftp->con_state = isopen;
ftp->is_verbose = verbose;
i = cmd(ftp, "USER %s", user);
if (i >= 300 && i < 400)
i = cmd(ftp, "PASS %s", passwd);
if (i >= 299 || i < 0) {
ftp_close(ftp);
if (i > 0)
ftp->error = i;
return FAILURE;
}
return SUCCESS;
}
static int
ftp_file_op(FTP_t ftp, const char *operation, const char *file, FILE **fp,
const char *mode, off_t *seekto)
{
int i,l,s;
char *q;
unsigned char addr[64];
union sockaddr_cmn {
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
} sin;
const char *cmdstr;
if (!fp)
return FAILURE;
*fp = NULL;
if (ftp->con_state != isopen)
return botch("ftp_file_op", "open");
if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
ftp->error = errno;
return FAILURE;
}
if (ftp->is_passive) {
if (ftp->addrtype == AF_INET) {
if (ftp->is_verbose)
fprintf(stderr, "Sending PASV\n");
if (writes(ftp->fd_ctrl, "PASV\r\n")) {
ftp_close(ftp);
if (FtpTimedOut)
ftp->error = FTP_TIMED_OUT;
return FTP_TIMED_OUT;
}
i = get_a_number(ftp, &q);
if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) {
ftp_close(ftp);
return i;
}
cmdstr = "PASV";
} else {
if (ftp->is_verbose)
fprintf(stderr, "Sending EPSV\n");
if (writes(ftp->fd_ctrl, "EPSV\r\n")) {
ftp_close(ftp);
if (FtpTimedOut)
ftp->error = FTP_TIMED_OUT;
return FTP_TIMED_OUT;
}
i = get_a_number(ftp, &q);
if (check_code(ftp, i, FTP_EPASSIVE_HAPPY)) {
if (ftp->is_verbose)
fprintf(stderr, "Sending LPSV\n");
if (writes(ftp->fd_ctrl, "LPSV\r\n")) {
ftp_close(ftp);
if (FtpTimedOut)
ftp->error = FTP_TIMED_OUT;
return FTP_TIMED_OUT;
}
i = get_a_number(ftp, &q);
if (check_code(ftp, i, FTP_LPASSIVE_HAPPY)) {
ftp_close(ftp);
return i;
}
cmdstr = "LPSV";
} else
cmdstr = "EPSV";
}
if (strcmp(cmdstr, "PASV") == 0 || strcmp(cmdstr, "LPSV") == 0) {
while (*q && !isdigit(*q))
q++;
if (!*q) {
ftp_close(ftp);
return FAILURE;
}
q--;
l = (ftp->addrtype == AF_INET ? 6 : 21);
for (i = 0; i < l; i++) {
q++;
addr[i] = strtol(q, &q, 10);
}
sin.sin4.sin_family = ftp->addrtype;
if (ftp->addrtype == AF_INET6) {
sin.sin6.sin6_len = sizeof(struct sockaddr_in6);
bcopy(addr + 2, (char *)&sin.sin6.sin6_addr, 16);
bcopy(addr + 19, (char *)&sin.sin6.sin6_port, 2);
} else {
sin.sin4.sin_len = sizeof(struct sockaddr_in);
bcopy(addr, (char *)&sin.sin4.sin_addr, 4);
bcopy(addr + 4, (char *)&sin.sin4.sin_port, 2);
}
} else if (strcmp(cmdstr, "EPSV") == 0) {
int port;
int sinlen;
while (*q && *q != '(')
q++;
if (!*q) {
ftp_close(ftp);
return FAILURE;
}
q++;
if (sscanf(q, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
&port, &addr[3]) != 5
|| addr[0] != addr[1] || addr[0] != addr[2] || addr[0] != addr[3]) {
ftp_close(ftp);
return FAILURE;
}
sinlen = sizeof(sin);
if (getpeername(ftp->fd_ctrl, (struct sockaddr *)&sin, &sinlen) < 0) {
ftp_close(ftp);
return FAILURE;
}
switch (sin.sin4.sin_family) {
case AF_INET:
sin.sin4.sin_port = htons(port);
break;
case AF_INET6:
sin.sin6.sin6_port = htons(port);
break;
default:
ftp_close(ftp);
return FAILURE;
}
}
if (connect(s, (struct sockaddr *)&sin, sin.sin4.sin_len) < 0) {
(void)close(s);
return FAILURE;
}
if (seekto && *seekto) {
i = cmd(ftp, "REST %" PRId64, *seekto);
if (i < 0 || FTP_TIMEOUT(i)) {
close(s);
ftp->error = i;
*seekto = (off_t)0;
return i;
}
}
i = cmd(ftp, "%s %s", operation, file);
if (i < 0 || i > 299) {
close(s);
ftp->error = i;
return i;
}
*fp = fdopen(s, mode);
}
else {
int fd,portrange;
#ifdef IPV6_PORTRANGE
if (ftp->addrtype == AF_INET6) {
portrange = IPV6_PORTRANGE_HIGH;
if (setsockopt(s, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)
&portrange, sizeof(portrange)) < 0) {
close(s);
return FAILURE;
}
}
#endif
#ifdef IP_PORTRANGE
if (ftp->addrtype == AF_INET) {
portrange = IP_PORTRANGE_HIGH;
if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *)
&portrange, sizeof(portrange)) < 0) {
close(s);
return FAILURE;
}
}
#endif
i = sizeof sin;
getsockname(ftp->fd_ctrl, (struct sockaddr *)&sin, &i);
sin.sin4.sin_port = 0;
i = ((struct sockaddr *)&sin)->sa_len;
if (bind(s, (struct sockaddr *)&sin, i) < 0) {
close(s);
return FAILURE;
}
i = sizeof sin;
getsockname(s,(struct sockaddr *)&sin,&i);
if (listen(s, 1) < 0) {
close(s);
return FAILURE;
}
if (sin.sin4.sin_family == AF_INET) {
u_long a;
a = ntohl(sin.sin4.sin_addr.s_addr);
i = cmd(ftp, "PORT %ld,%ld,%ld,%ld,%d,%d",
(a >> 24) & 0xff,
(a >> 16) & 0xff,
(a >> 8) & 0xff,
a & 0xff,
(ntohs(sin.sin4.sin_port) >> 8) & 0xff,
ntohs(sin.sin4.sin_port) & 0xff);
if (check_code(ftp, i, FTP_PORT_HAPPY)) {
close(s);
return i;
}
} else {
#define UC(b) (((int)b)&0xff)
char *a;
char hname[INET6_ADDRSTRLEN];
sin.sin6.sin6_scope_id = 0;
if (getnameinfo((struct sockaddr *)&sin, sin.sin6.sin6_len,
hname, sizeof(hname),
NULL, 0, NI_NUMERICHOST) != 0) {
goto try_lprt;
}
i = cmd(ftp, "EPRT |%d|%s|%d|", 2, hname,
htons(sin.sin6.sin6_port));
if (check_code(ftp, i, FTP_PORT_HAPPY)) {
try_lprt:
a = (char *)&sin.sin6.sin6_addr;
i = cmd(ftp,
"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
6, 16,
UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]),
UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]),
UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]),
UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]),
2,
(ntohs(sin.sin4.sin_port) >> 8) & 0xff,
ntohs(sin.sin4.sin_port) & 0xff);
if (check_code(ftp, i, FTP_PORT_HAPPY)) {
close(s);
return i;
}
}
}
if (seekto && *seekto) {
i = cmd(ftp, "REST %" PRId64, *seekto);
if (i < 0 || FTP_TIMEOUT(i)) {
close(s);
ftp->error = i;
return i;
}
else if (i != 350)
*seekto = (off_t)0;
}
i = cmd(ftp, "%s %s", operation, file);
if (i < 0 || i > 299) {
close(s);
ftp->error = i;
return FAILURE;
}
fd = accept(s, 0, 0);
if (fd < 0) {
close(s);
ftp->error = 401;
return FAILURE;
}
close(s);
*fp = fdopen(fd, mode);
}
if (*fp)
return SUCCESS;
else
return FAILURE;
}
static void
ai_unmapped(struct addrinfo *ai)
{
struct sockaddr_in6 *sin6;
struct sockaddr_in sin;
if (ai->ai_family != AF_INET6)
return;
if (ai->ai_addrlen != sizeof(struct sockaddr_in6) ||
sizeof(sin) > ai->ai_addrlen)
return;
sin6 = (struct sockaddr_in6 *)ai->ai_addr;
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
return;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
sizeof(sin.sin_addr));
sin.sin_port = sin6->sin6_port;
ai->ai_family = AF_INET;
memcpy(ai->ai_addr, &sin, sin.sin_len);
ai->ai_addrlen = sin.sin_len;
}