1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-12 01:11:27 +02:00

zsh-workers/8764

This commit is contained in:
Tanaka Akira 1999-11-24 04:41:47 +00:00
parent 7cccf04d56
commit a4a939cfd8
3 changed files with 323 additions and 105 deletions

View file

@ -42,7 +42,10 @@
*/ */
/* needed in prototypes for statics */ /* needed in prototypes for statics */
struct hostent;
struct sockaddr_in; struct sockaddr_in;
struct sockaddr_in6;
union zftp_sockaddr;
struct zftp_session; struct zftp_session;
typedef struct zftp_session *Zftp_session; typedef struct zftp_session *Zftp_session;
@ -71,11 +74,132 @@ typedef struct zftp_session *Zftp_session;
# undef HAVE_POLL # undef HAVE_POLL
#endif #endif
/* pinch the definition from <netinet/in.h> for deficient headers */ /* Is IPv6 supported by the library? */
#ifndef INADDR_NONE
# define INADDR_NONE 0xffffffff #if defined(AF_INET6) && defined(IN6ADDR_LOOPBACK_INIT) \
&& defined(HAVE_INET_NTOP) && defined(HAVE_INET_PTON)
# define SUPPORT_IPV6 1
#endif #endif
union zftp_sockaddr {
struct sockaddr a;
struct sockaddr_in in;
#ifdef SUPPORT_IPV6
struct sockaddr_in6 in6;
#endif
};
/* We use the RFC 2553 interfaces. If the functions don't exist in the library,
simulate them. */
#ifndef INET_ADDRSTRLEN
# define INET_ADDRSTRLEN 16
#endif
#ifndef INET6_ADDRSTRLEN
# define INET6_ADDRSTRLEN 46
#endif
/**/
#ifndef HAVE_INET_NTOP
/**/
static char const *
inet_ntop(int af, void const *cp, char *buf, size_t len)
{
if(af != AF_INET) {
errno = EAFNOSUPPORT;
return NULL;
}
if(len < INET_ADDRSTRLEN) {
errno = ENOSPC;
return NULL;
}
strcpy(buf, inet_ntoa(*(struct in_addr *)cp));
return buf;
}
/**/
#endif /* !HAVE_INET_NTOP */
/**/
#ifndef HAVE_INET_PTON
/**/
static int
inet_pton(int af, char const *src, void *dst)
{
if(af != AF_INET) {
errno = EAFNOSUPPORT;
return -1;
}
return !!inet_aton(src, dst);
}
/**/
#endif /* !HAVE_INET_PTON */
/**/
#ifndef HAVE_GETIPNODEBYNAME
/* note: this is not a complete implementation. If ignores the flags,
and does not provide the memory allocation of the standard interface.
Each returned structure will overwrite the previous one. */
/**/
static struct hostent *
getipnodebyname(char const *name, int af, int flags, int *errorp)
{
static struct hostent ahe;
static char nbuf[16];
static char *addrlist[] = { nbuf, NULL };
# ifdef SUPPORT_IPV6
static char pbuf[INET6_ADDRSTRLEN];
# else
static char pbuf[INET_ADDRSTRLEN];
# endif
struct hostent *he;
if(inet_pton(af, name, nbuf) == 1) {
inet_ntop(af, nbuf, pbuf, sizeof(pbuf));
ahe.h_name = pbuf;
ahe.h_aliases = addrlist+1;
ahe.h_addrtype = af;
ahe.h_length = (af == AF_INET) ? 4 : 16;
ahe.h_addr_list = addrlist;
return &ahe;
}
he = gethostbyname2(name, af);
if(!he)
*errorp = h_errno;
return he;
}
/**/
# ifndef HAVE_GETHOSTBYNAME2
/**/
static struct hostent *
gethostbyname2(char const *name, int af)
{
if(af != AF_INET) {
h_errno = NO_RECOVERY;
return NULL;
}
return gethostbyname(name);
}
/**/
# endif /* !HAVE_GETHOSTBYNAME2 */
/**/
static void
freehostent(struct hostent *ptr)
{
}
/**/
#endif /* !HAVE_GETIPNODEBYNAME */
/* /*
* For FTP block mode * For FTP block mode
* *
@ -292,7 +416,8 @@ struct zftp_session {
char **userparams; /* user parameters set by zftp_params */ char **userparams; /* user parameters set by zftp_params */
int cfd; /* control file descriptor */ int cfd; /* control file descriptor */
FILE *cin; /* control input file */ FILE *cin; /* control input file */
struct sockaddr_in sock; /* the socket for the control connection */ union zftp_sockaddr sock; /* this end of the control connection */
union zftp_sockaddr peer; /* far end of the control connection */
int dfd; /* data connection */ int dfd; /* data connection */
int has_size; /* understands SIZE? */ int has_size; /* understands SIZE? */
int has_mdtm; /* understands MDTM? */ int has_mdtm; /* understands MDTM? */
@ -846,7 +971,7 @@ zfsendcmd(char *cmd)
/**/ /**/
static int static int
zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep) zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
{ {
if (!(zfprefs & (ZFPF_SNDP|ZFPF_PASV))) { if (!(zfprefs & (ZFPF_SNDP|ZFPF_PASV))) {
zwarnnam(name, "Must set preference S or P to transfer data", NULL, 0); zwarnnam(name, "Must set preference S or P to transfer data", NULL, 0);
@ -858,15 +983,17 @@ zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep)
return 1; return 1;
} }
*zdsockp = zfsess->sock;
zdsockp->sin_family = AF_INET;
if (!(zfstatusp[zfsessno] & ZFST_NOPS) && (zfprefs & ZFPF_PASV)) { if (!(zfstatusp[zfsessno] & ZFST_NOPS) && (zfprefs & ZFPF_PASV)) {
char *ptr; char *psv_cmd;
int i, nums[6], err; int err, salen;
unsigned char iaddr[4], iport[2];
if (zfsendcmd("PASV\r\n") == 6) #ifdef SUPPORT_IPV6
if(zfsess->peer.a.sa_family == AF_INET6)
psv_cmd = "EPSV\r\n";
else
#endif /* SUPPORT_IPV6 */
psv_cmd = "PASV\r\n";
if (zfsendcmd(psv_cmd) == 6)
return 1; return 1;
else if (lastcode >= 500 && lastcode <= 504) { else if (lastcode >= 500 && lastcode <= 504) {
/* /*
@ -877,32 +1004,73 @@ zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep)
zfclosedata(); zfclosedata();
return zfopendata(name, zdsockp, is_passivep); return zfopendata(name, zdsockp, is_passivep);
} }
/* zdsockp->a.sa_family = zfsess->peer.a.sa_family;
* OK, now we need to know what port we're looking at, #ifdef SUPPORT_IPV6
* which is cunningly concealed in the reply. if(zfsess->peer.a.sa_family == AF_INET6) {
* lastmsg already has the reply code expunged. /* see RFC 2428 for explanation */
*/ char const *ptr, *end;
for (ptr = lastmsg; *ptr; ptr++) char delim, portbuf[6], *pbp;
if (isdigit(STOUC(*ptr))) unsigned long portnum;
break; ptr = strchr(lastmsg, '(');
if (sscanf(ptr, "%d,%d,%d,%d,%d,%d", if(!ptr) {
nums, nums+1, nums+2, nums+3, nums+4, nums+5) != 6) { bad_epsv:
zwarnnam(name, "bad response to PASV: %s", lastmsg, 0); zwarnnam(name, "bad response to EPSV: %s", lastmsg, 0);
zfclosedata(); zfclosedata();
return 1; return 1;
} }
for (i = 0; i < 4; i++) delim = ptr[1];
iaddr[i] = STOUC(nums[i]); if(delim < 33 || delim > 126 || ptr[2] != delim || ptr[3] != delim)
iport[0] = STOUC(nums[4]); goto bad_epsv;
iport[1] = STOUC(nums[5]); ptr += 3;
end = strchr(ptr, delim);
if(!end || end[1] != ')')
goto bad_epsv;
while(ptr != end && *ptr == '0')
ptr++;
if(ptr == end || (end-ptr) > 5 || !isdigit(STOUC(*ptr)))
goto bad_epsv;
memcpy(portbuf, ptr, (end-ptr));
portbuf[end-ptr] = 0;
portnum = strtoul(portbuf, &pbp, 10);
if(*pbp || portnum > 65535UL)
goto bad_epsv;
*zdsockp = zfsess->peer;
zdsockp->in6.sin6_port = htons((unsigned)portnum);
salen = sizeof(struct sockaddr_in6);
} else
#endif /* SUPPORT_IPV6 */
{
char *ptr;
int i, nums[6];
unsigned char iaddr[4], iport[2];
memcpy(&zdsockp->sin_addr, iaddr, sizeof(iaddr)); /*
memcpy(&zdsockp->sin_port, iport, sizeof(iport)); * OK, now we need to know what port we're looking at,
* which is cunningly concealed in the reply.
* lastmsg already has the reply code expunged.
*/
for (ptr = lastmsg; *ptr; ptr++)
if (isdigit(STOUC(*ptr)))
break;
if (sscanf(ptr, "%d,%d,%d,%d,%d,%d",
nums, nums+1, nums+2, nums+3, nums+4, nums+5) != 6) {
zwarnnam(name, "bad response to PASV: %s", lastmsg, 0);
zfclosedata();
return 1;
}
for (i = 0; i < 4; i++)
iaddr[i] = STOUC(nums[i]);
iport[0] = STOUC(nums[4]);
iport[1] = STOUC(nums[5]);
memcpy(&zdsockp->in.sin_addr, iaddr, sizeof(iaddr));
memcpy(&zdsockp->in.sin_port, iport, sizeof(iport));
salen = sizeof(struct sockaddr_in);
}
/* we should timeout this connect */ /* we should timeout this connect */
do { do {
err = connect(zfsess->dfd, err = connect(zfsess->dfd, (struct sockaddr *)zdsockp, salen);
(struct sockaddr *)zdsockp, sizeof(*zdsockp));
} while (err && errno == EINTR && !errflag); } while (err && errno == EINTR && !errflag);
if (err) { if (err) {
@ -913,8 +1081,11 @@ zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep)
*is_passivep = 1; *is_passivep = 1;
} else { } else {
#ifdef SUPPORT_IPV6
char portcmd[8+INET6_ADDRSTRLEN+9];
#else
char portcmd[40]; char portcmd[40];
unsigned char *addr, *port; #endif
int ret, len; int ret, len;
if (!(zfprefs & ZFPF_SNDP)) { if (!(zfprefs & ZFPF_SNDP)) {
@ -922,11 +1093,19 @@ zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep)
return 1; return 1;
} }
zdsockp->sin_port = 0; /* to be set by bind() */ *zdsockp = zfsess->sock;
len = sizeof(*zdsockp); #ifdef SUPPORT_IPV6
if(zdsockp->a.sa_family == AF_INET6) {
zdsockp->in6.sin6_port = 0; /* to be set by bind() */
len = sizeof(struct sockaddr_in6);
} else
#endif /* SUPPORT_IPV6 */
{
zdsockp->in.sin_port = 0; /* to be set by bind() */
len = sizeof(struct sockaddr_in);
}
/* need to do timeout stuff and probably handle EINTR here */ /* need to do timeout stuff and probably handle EINTR here */
if (bind(zfsess->dfd, (struct sockaddr *)zdsockp, if (bind(zfsess->dfd, (struct sockaddr *)zdsockp, len) < 0)
sizeof(*zdsockp)) < 0)
ret = 1; ret = 1;
else if (getsockname(zfsess->dfd, (struct sockaddr *)zdsockp, else if (getsockname(zfsess->dfd, (struct sockaddr *)zdsockp,
&len) < 0) &len) < 0)
@ -944,10 +1123,22 @@ zfopendata(char *name, struct sockaddr_in *zdsockp, int *is_passivep)
return 1; return 1;
} }
addr = (unsigned char *) &zdsockp->sin_addr; #ifdef SUPPORT_IPV6
port = (unsigned char *) &zdsockp->sin_port; if(zdsockp->a.sa_family == AF_INET6) {
sprintf(portcmd, "PORT %d,%d,%d,%d,%d,%d\r\n", /* see RFC 2428 for explanation */
addr[0],addr[1],addr[2],addr[3],port[0],port[1]); strcpy(portcmd, "EPRT |2|");
inet_ntop(AF_INET6, &zdsockp->in6.sin6_addr,
portcmd+8, INET6_ADDRSTRLEN);
sprintf(strchr(portcmd, 0), "|%u|\r\n",
(unsigned)ntohs(zdsockp->in6.sin6_port));
} else
#endif /* SUPPORT_IPV6 */
{
unsigned char *addr = (unsigned char *) &zdsockp->in.sin_addr;
unsigned char *port = (unsigned char *) &zdsockp->in.sin_port;
sprintf(portcmd, "PORT %d,%d,%d,%d,%d,%d\r\n",
addr[0],addr[1],addr[2],addr[3],port[0],port[1]);
}
if (zfsendcmd(portcmd) >= 5) { if (zfsendcmd(portcmd) >= 5) {
zwarnnam(name, "port command failed", NULL, 0); zwarnnam(name, "port command failed", NULL, 0);
zfclosedata(); zfclosedata();
@ -988,7 +1179,7 @@ static int
zfgetdata(char *name, char *rest, char *cmd, int getsize) zfgetdata(char *name, char *rest, char *cmd, int getsize)
{ {
int len, newfd, is_passive; int len, newfd, is_passive;
struct sockaddr_in zdsock; union zftp_sockaddr zdsock;
if (zfopendata(name, &zdsock, &is_passive)) if (zfopendata(name, &zdsock, &is_passive))
return 1; return 1;
@ -1618,12 +1809,12 @@ zfsenddata(char *name, int recv, int progress, off_t startat)
static int static int
zftp_open(char *name, char **args, int flags) zftp_open(char *name, char **args, int flags)
{ {
struct in_addr ipaddr;
struct protoent *zprotop; struct protoent *zprotop;
struct servent *zservp; struct servent *zservp;
struct hostent *zhostp = NULL; struct hostent *zhostp = NULL;
char **addrp, *fname; char **addrp, *fname;
int err, len, tmout; int err, len, tmout;
int herrno, af, salen;
if (!*args) { if (!*args) {
if (zfsess->userparams) if (zfsess->userparams)
@ -1671,90 +1862,105 @@ zftp_open(char *name, char **args, int flags)
} }
zfalarm(tmout); zfalarm(tmout);
/* #ifdef SUPPORT_IPV6
* Now this is what I like. A library which provides decent higher for(af=AF_INET6; 1; af = AF_INET)
* level functions to do things like converting address types. It saves # define SUCCEEDED() break
* so much trouble. Pity about the rest of the network interface, though. # define FAILED() if(af == AF_INET) { } else continue
*/ #else
ipaddr.s_addr = inet_addr(args[0]); af = AF_INET;
if (ipaddr.s_addr != INADDR_NONE) { # define SUCCEEDED() do { } while(0)
/* # define FAILED() do { } while(0)
* hmmm, we don't necessarily want to do this... maybe the #endif
* user is actively trying to avoid a bad nameserver. {
* perhaps better just to set ZFTP_HOST to the dot address, too. zhostp = getipnodebyname(args[0], af, 0, &herrno);
* that way shell functions know how it was opened.
*
* zhostp = gethostbyaddr(&ipaddr, sizeof(ipaddr), AF_INET);
*
* or, we could have a `no_lookup' flag.
*/
zfsetparam("ZFTP_HOST", ztrdup(args[0]), ZFPM_READONLY);
zfsess->sock.sin_family = AF_INET;
} else {
zhostp = gethostbyname(args[0]);
if (!zhostp || errflag) { if (!zhostp || errflag) {
/* should use herror() here if available, but maybe /* should use herror() here if available, but maybe
* needs configure test. on AIX it's present but not * needs configure test. on AIX it's present but not
* in headers. * in headers.
*/ */
FAILED();
zwarnnam(name, "host not found: %s", args[0], 0); zwarnnam(name, "host not found: %s", args[0], 0);
alarm(0); alarm(0);
return 1; return 1;
} }
zfsess->sock.sin_family = zhostp->h_addrtype;
zfsetparam("ZFTP_HOST", ztrdup(zhostp->h_name), ZFPM_READONLY); zfsetparam("ZFTP_HOST", ztrdup(zhostp->h_name), ZFPM_READONLY);
}
zfsess->sock.sin_port = zservp->s_port; zfsess->peer.a.sa_family = af;
zfsess->cfd = socket(zfsess->sock.sin_family, SOCK_STREAM, 0); #ifdef SUPPORT_IPV6
if (zfsess->cfd < 0) { if(af == AF_INET6) {
zwarnnam(name, "socket failed: %e", NULL, errno); zfsess->peer.in6.sin6_port = zservp->s_port;
zfunsetparam("ZFTP_HOST"); zfsess->peer.in6.sin6_flowinfo = 0;
alarm(0); # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
return 1; zfsess->peer.in6.sin6_scope_id = 0;
} # endif
/* counts as `open' so long as it's not negative */ salen = sizeof(struct sockaddr_in6);
zfnopen++; } else
#endif /* SUPPORT_IPV6 */
{
zfsess->peer.in.sin_port = zservp->s_port;
salen = sizeof(struct sockaddr_in);
}
/* zfsess->cfd = socket(af, SOCK_STREAM, 0);
* now connect the socket. manual pages all say things like `this is all if (zfsess->cfd < 0) {
* explained oh-so-wonderfully in some other manual page'. not. freehostent(zhostp);
*/ zfunsetparam("ZFTP_HOST");
FAILED();
zwarnnam(name, "socket failed: %e", NULL, errno);
alarm(0);
return 1;
}
/* counts as `open' so long as it's not negative */
zfnopen++;
err = 1; /*
* now connect the socket. manual pages all say things like `this is
* all explained oh-so-wonderfully in some other manual page'. not.
*/
if (ipaddr.s_addr != INADDR_NONE) { err = 1;
/* dot address */
memcpy(&zfsess->sock.sin_addr, &ipaddr, sizeof(ipaddr)); /* try all possible IP's */
do { for (addrp = zhostp->h_addr_list; err && *addrp; addrp++) {
err = connect(zfsess->cfd, (struct sockaddr *)&zfsess->sock, #ifdef SUPPORT_IPV6
sizeof(zfsess->sock)); if(af == AF_INET6)
} while (err && errno == EINTR && !errflag); memcpy(&zfsess->peer.in6.sin6_addr, *addrp, zhostp->h_length);
} else { else
/* host name: try all possible IP's */ #endif /* SUPPORT_IPV6 */
for (addrp = zhostp->h_addr_list; *addrp; addrp++) { memcpy(&zfsess->peer.in.sin_addr, *addrp, zhostp->h_length);
memcpy(&zfsess->sock.sin_addr, *addrp, zhostp->h_length);
do { do {
err = connect(zfsess->cfd, (struct sockaddr *)&zfsess->sock, err = connect(zfsess->cfd, (struct sockaddr *)&zfsess->peer,
sizeof(zfsess->sock)); salen);
} while (err && errno == EINTR && !errflag); } while (err && errno == EINTR && !errflag);
/* you can check whether it's worth retrying here */ /* you can check whether it's worth retrying here */
} }
}
if (err) {
freehostent(zhostp);
zfclose(0);
FAILED();
zwarnnam(name, "connect failed: %e", NULL, errno);
alarm(0);
return 1;
}
SUCCEEDED();
}
alarm(0); alarm(0);
{
if (err < 0) { #ifdef SUPPORT_IPV6
zwarnnam(name, "connect failed: %e", NULL, errno); char pbuf[INET6_ADDRSTRLEN];
zfclose(0); #else
return 1; char pbuf[INET_ADDRSTRLEN];
#endif
addrp--;
inet_ntop(af, *addrp, pbuf, sizeof(pbuf));
zfsetparam("ZFTP_IP", ztrdup(pbuf), ZFPM_READONLY);
} }
zfsetparam("ZFTP_IP", ztrdup(inet_ntoa(zfsess->sock.sin_addr)), freehostent(zhostp);
ZFPM_READONLY);
/* now we can talk to the control connection */ /* now we can talk to the control connection */
zcfinish = 0; zcfinish = 0;
/* /*
* Move the fd out of the user-visible range. We need to do * Move the fd out of the user-visible range. We need to do
* this after the connect() on some systems. * this after the connect() on some systems.

View file

@ -133,6 +133,9 @@
/* Define if your system's struct direct has a member named d_stat. */ /* Define if your system's struct direct has a member named d_stat. */
#undef HAVE_STRUCT_DIRECT_D_STAT #undef HAVE_STRUCT_DIRECT_D_STAT
/* Define if your system's struct sockaddr_in6 has a member named sin6_scope_id. */
#undef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
/* Define to be a string corresponding the vendor of the machine */ /* Define to be a string corresponding the vendor of the machine */
#undef VENDOR #undef VENDOR

View file

@ -773,6 +773,14 @@ zsh_STRUCT_MEMBER([
#endif #endif
], struct direct, d_stat) ], struct direct, d_stat)
dnl Check IPv6 socket address structure type
zsh_STRUCT_MEMBER([
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <netinet/in.h>
], struct sockaddr_in6, sin6_scope_id)
dnl need to integrate this function dnl need to integrate this function
dnl AC_FUNC_STRFTIME dnl AC_FUNC_STRFTIME
@ -783,6 +791,7 @@ AC_CHECK_FUNCS(memcpy memmove \
sigprocmask setuid seteuid setreuid setresuid setsid strerror \ sigprocmask setuid seteuid setreuid setresuid setsid strerror \
nis_list initgroups fchdir cap_get_proc readlink nice \ nis_list initgroups fchdir cap_get_proc readlink nice \
getgrgid getgrnam getpwent getpwnam getpwuid setpgrp \ getgrgid getgrnam getpwent getpwnam getpwuid setpgrp \
inet_pton inet_ntop getipnodebyname gethostbyname2 \
fseeko ftello mmap munmap msync ftruncate setlocale) fseeko ftello mmap munmap msync ftruncate setlocale)
dnl --------------- dnl ---------------