- poll(2) support.

- simplify by strdup.
- set ai_protocol in hints to TCP.
- g/c FAITH_NS (no description, not maintained for years)
- warn if connection from IPv4 mapped is reached.
- IPV6_V6ONLY if possible.
- unifdef -UFAITH4.
- drop rsh/rlogin support.
- deal with negative return value from wait3.

Obtained from:	KAME
This commit is contained in:
ume 2003-11-14 17:34:08 +00:00
parent b0479caf61
commit 1cc5ee037b
10 changed files with 444 additions and 762 deletions

View File

@ -16,8 +16,8 @@
PROG= faithd
MAN= faithd.8
SRCS= faithd.c tcp.c ftp.c rsh.c prefix.c
SRCS= faithd.c tcp.c ftp.c prefix.c
#CFLAGS+= -DFAITH4
CFLAGS= -DHAVE_POLL_H
.include <bsd.prog.mk>

View File

@ -1,9 +1,10 @@
Configuring FAITH IPv6-to-IPv4 TCP relay
Kazu Yamamoto and Jun-ichiro itojun Hagino
$KAME: README,v 1.8 2001/09/05 03:04:20 itojun Exp $
$KAME: README,v 1.10 2003/01/06 21:40:33 sumikawa Exp $
$FreeBSD$
Introduction
============
@ -73,8 +74,6 @@ The following example assumes:
More examples:
# faithd login /usr/libexec/rlogin rlogind
# faithd shell /usr/libexec/rshd rshd
# faithd ftpd /usr/libexec/ftpd ftpd -l
# faithd sshd

View File

@ -1,4 +1,4 @@
.\" $KAME: faithd.8,v 1.33 2001/09/05 03:04:20 itojun Exp $
.\" $KAME: faithd.8,v 1.37 2002/05/09 14:21:23 itojun Exp $
.\"
.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
.\" All rights reserved.
@ -41,10 +41,12 @@
.Op Fl f Ar configfile
.Ar service
.Op Ar serverpath Op Ar serverargs
.Nm ""
.Sh DESCRIPTION
The
.Nm
utility provides IPv6-to-IPv4 TCP relay. It
utility provides IPv6-to-IPv4 TCP relay.
.Nm
must be used on an IPv4/v6 dual stack router.
.Pp
When
@ -65,7 +67,7 @@ destination.
For example, if
.Li 3ffe:0501:4819:ffff::
is reserved for
.Nm ,
.Nm Ns ,
and the
.Tn TCPv6
destination address is
@ -116,7 +118,6 @@ at
.Pa http://www.vermicelli.pasta.cs.uit.no/ipv6/software.html .
Make sure you do not propagate translated DNS records to normal DNS cloud,
it is highly harmful.
.Pp
.Ss Daemon mode
When
.Nm
@ -147,7 +148,7 @@ or other standard mechanisms.
By specifying
.Ar serverpath
to
.Nm ,
.Nm Ns ,
you can run local daemons on the router.
The
.Nm
@ -172,8 +173,6 @@ Use privileged TCP port number as source port,
for IPv4 TCP connection toward final destination.
For relaying
.Xr ftp 1
and
.Xr rlogin 1 ,
this flag is not necessary as special program code is supplied.
.El
.Pp
@ -184,9 +183,7 @@ It is capable of emulating TCP half close as well.
The
.Nm
utility includes special support for protocols used by
.Xr ftp 1
and
.Xr rlogin 1 .
.Xr ftp 1 .
When translating FTP protocol,
.Nm
translates network level addresses in
@ -194,18 +191,11 @@ translates network level addresses in
and
.Li PASV/LPSV/EPSV
commands.
For RLOGIN protocol,
.Nm
will relay back connection from
.Xr rlogind 8
on the server to
.Xr rlogin 1
on client.
.Pp
Inactive sessions will be disconnected in 30 minutes,
to avoid stale sessions from chewing up resources.
This may be inappropriate for some of the services
(should this be configurable?).
.Pq should this be configurable? .
.Ss inetd mode
When
.Nm
@ -243,10 +233,12 @@ To prevent malicious accesses,
implements a simple address-based access control.
With
.Pa /etc/faithd.conf
(or
.Po
or
.Ar configfile
specified by
.Fl f ) ,
.Fl f
.Pc ,
.Nm
will avoid relaying unwanted traffic.
The
@ -254,35 +246,48 @@ The
contains directives with the following format:
.Bl -bullet
.It
.Ar src Ns / Ns Ar slen Cm deny Ar dst Ns / Ns Ar dlen
.Xo
.Ic Ar src/slen Li deny Ar dst/dlen
.Xc
.Pp
If the source address of a query matches
.Ar src Ns / Ns Ar slen ,
.Ar src/slen ,
and the translated destination address matches
.Ar dst Ns / Ns Ar dlen ,
.Ar dst/dlen ,
deny the connection.
.It
.Ar src Ns / Ns Ar slen Cm permit Ar dst Ns / Ns Ar dlen
.Xo
.Ic Ar src/slen Li permit Ar dst/dlen
.Xc
.Pp
If the source address of a query matches
.Ar src Ns / Ns Ar slen ,
.Ar src/slen ,
and the translated destination address matches
.Ar dst Ns / Ns Ar dlen ,
.Ar dst/dlen ,
permit the connection.
.El
.Pp
The directives are evaluated in sequence,
and the first matching entry will be effective.
If there is no match
(if we reach the end of the ruleset)
.Pq if we reach the end of the ruleset
the traffic will be denied.
.Pp
With inetd mode,
traffic may be filtered by using access control functionality in
.Xr inetd 8 .
.Sh RETURN VALUES
.Nm
exits with
.Dv EXIT_SUCCESS
.Pq 0
on success, and
.Dv EXIT_FAILURE
.Pq 1
on error.
.Sh EXAMPLES
Before invoking
.Nm ,
.Nm Ns ,
.Xr faith 4
interface has to be configured properly.
.Bd -literal -offset
@ -320,26 +325,19 @@ If you would like to pass extra arguments to the local daemon:
Here are some other examples.
You may need
.Fl p
to translate rsh/rlogin services.
if the service checks the source port range.
.Bd -literal -offset
# faithd ssh
# faithd login /usr/libexec/rlogin rlogind
# faithd shell /usr/libexec/rshd rshd
# faithd telnet /usr/libexec/telnetd telnetd
.Ed
.Pp
However, you should be careful when translating rlogin or rsh
connections.
See
.Sx SECURITY CONSIDERATIONS
for more details.
.Ss inetd mode samples
Add the following lines into
.Xr inetd.conf 5 .
Syntax may vary depending upon your operating system.
.Bd -literal -offset
telnet stream tcp6/faith nowait root /usr/sbin/faithd telnetd
ftp stream tcp6/faith nowait root /usr/sbin/faithd ftpd -l
ssh stream tcp6/faith nowait root /usr/sbin/faithd /usr/sbin/sshd -i
telnet stream tcp6/faith nowait root faithd telnetd
ftp stream tcp6/faith nowait root faithd ftpd -l
ssh stream tcp6/faith nowait root faithd /usr/sbin/sshd -i
.Ed
.Pp
.Xr inetd 8
@ -370,16 +368,6 @@ setting.
3ffe:501:ffff::/48 deny 127.0.0.0/8
3ffe:501:ffff::/48 permit 0.0.0.0/0
.Ed
.Sh RETURN VALUES
The
.Nm
utility exits with
.Dv EXIT_SUCCESS
.Pq 0
on success, and
.Dv EXIT_FAILURE
.Pq 1
on error.
.Sh SEE ALSO
.Xr faith 4 ,
.Xr route 8 ,
@ -403,11 +391,9 @@ IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
was initially integrated into
.Fx 4.0
.Sh SECURITY CONSIDERATIONS
It is very insecure to use
.Xr rhosts 5
and other IP-address based authentication, for connections relayed by
.Nm
(and any other TCP relaying services).
It is very insecure to use IP-address based authentication, for connections relayed by
.Nm Ns ,
and any other TCP relaying services.
.Pp
Administrators are advised to limit accesses to
.Nm

View File

@ -1,4 +1,4 @@
/* $KAME: faithd.c,v 1.46 2002/01/24 16:40:42 sumikawa Exp $ */
/* $KAME: faithd.c,v 1.67 2003/10/16 05:26:21 itojun Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
@ -46,10 +46,11 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#ifdef __FreeBSD__
#include <libutil.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@ -74,14 +75,6 @@
#include <netdb.h>
#include <ifaddrs.h>
#ifdef FAITH4
#include <resolv.h>
#include <arpa/nameser.h>
#ifndef FAITH_NS
#define FAITH_NS "FAITH_NS"
#endif
#endif
#include "faithd.h"
#include "prefix.h"
@ -90,11 +83,13 @@ char *serverarg[MAXARGV + 1];
static char *faithdname = NULL;
char logname[BUFSIZ];
char procname[BUFSIZ];
struct myaddrs {
struct myaddrs *next;
struct sockaddr *addr;
};
struct myaddrs *myaddrs = NULL;
static const char *service;
#ifdef USE_ROUTE
static int sockfd = 0;
@ -111,9 +106,6 @@ static void play_service __P((int));
static void play_child __P((int, struct sockaddr *));
static int faith_prefix __P((struct sockaddr *));
static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *));
#ifdef FAITH4
static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *));
#endif
static void sig_child __P((int));
static void sig_terminate __P((int));
static void start_daemon __P((void));
@ -151,7 +143,7 @@ inetd_main(int argc, char **argv)
char path[MAXPATHLEN];
struct sockaddr_storage me;
struct sockaddr_storage from;
int melen, fromlen;
socklen_t melen, fromlen;
int i;
int error;
const int on = 1;
@ -228,11 +220,8 @@ daemon_main(int argc, char **argv)
int s_wld, error, i, serverargc, on = 1;
int family = AF_INET6;
int c;
#ifdef FAITH_NS
char *ns;
#endif /* FAITH_NS */
while ((c = getopt(argc, argv, "df:p46")) != -1) {
while ((c = getopt(argc, argv, "df:p")) != -1) {
switch (c) {
case 'd':
dflag++;
@ -243,14 +232,6 @@ daemon_main(int argc, char **argv)
case 'p':
pflag++;
break;
#ifdef FAITH4
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
#endif
default:
usage();
/*NOTREACHED*/
@ -264,23 +245,6 @@ daemon_main(int argc, char **argv)
/*NOTREACHED*/
}
#ifdef FAITH_NS
if ((ns = getenv(FAITH_NS)) != NULL) {
struct sockaddr_storage ss;
struct addrinfo hints, *res;
char serv[NI_MAXSERV];
memset(&ss, 0, sizeof(ss));
memset(&hints, 0, sizeof(hints));
snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT);
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(ns, serv, &hints, &res) == 0) {
res_init();
memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen);
_res.nscount = 1;
}
}
#endif /* FAITH_NS */
#ifdef USE_ROUTE
grab_myaddrs();
@ -295,11 +259,13 @@ daemon_main(int argc, char **argv)
if (serverargc >= MAXARGV)
exit_stderr("too many arguments");
serverpath = malloc(strlen(argv[NUMPRG]) + 1);
strcpy(serverpath, argv[NUMPRG]);
serverpath = strdup(argv[NUMPRG]);
if (!serverpath)
exit_stderr("not enough core");
for (i = 0; i < serverargc; i++) {
serverarg[i] = malloc(strlen(argv[i + NUMARG]) + 1);
strcpy(serverarg[i], argv[i + NUMARG]);
serverarg[i] = strdup(argv[i + NUMARG]);
if (!serverarg[i])
exit_stderr("not enough core");
}
serverarg[i] = NULL;
/* fall throuth */
@ -308,6 +274,8 @@ daemon_main(int argc, char **argv)
break;
}
start_daemon();
/*
* Opening wild card socket for this service.
*/
@ -316,7 +284,7 @@ daemon_main(int argc, char **argv)
hints.ai_flags = AI_PASSIVE;
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_protocol = IPPROTO_TCP; /* SCTP? */
error = getaddrinfo(NULL, service, &hints, &res);
if (error)
exit_failure("getaddrinfo: %s", gai_strerror(error));
@ -333,16 +301,6 @@ daemon_main(int argc, char **argv)
strerror(errno));
}
#endif
#ifdef FAITH4
#ifdef IP_FAITH
if (res->ai_family == AF_INET) {
error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on));
if (error == -1)
exit_failure("setsockopt(IP_FAITH): %s",
strerror(errno));
}
#endif
#endif /* FAITH4 */
error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (error == -1)
@ -352,6 +310,12 @@ daemon_main(int argc, char **argv)
if (error == -1)
exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
#ifdef IPV6_V6ONLY
error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (error == -1)
exit_failure("setsockopt(IPV6_V6ONLY): %s", strerror(errno));
#endif
error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
if (error == -1)
exit_failure("bind: %s", strerror(errno));
@ -372,8 +336,6 @@ daemon_main(int argc, char **argv)
* Everything is OK.
*/
start_daemon();
snprintf(logname, sizeof(logname), "faithd %s", service);
snprintf(procname, sizeof(procname), "accepting port %s", service);
openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
@ -388,12 +350,16 @@ static void
play_service(int s_wld)
{
struct sockaddr_storage srcaddr;
int len;
socklen_t len;
int s_src;
pid_t child_pid;
#ifdef HAVE_POLL_H
struct pollfd pfd[2];
#else
fd_set rfds;
int error;
int maxfd;
#endif
int error;
/*
* Wait, accept, fork, faith....
@ -401,17 +367,37 @@ play_service(int s_wld)
again:
setproctitle("%s", procname);
#ifdef HAVE_POLL_H
pfd[0].fd = s_wld;
pfd[0].events = POLLIN;
pfd[1].fd = -1;
pfd[1].revents = 0;
#else
FD_ZERO(&rfds);
if (s_wld >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_wld, &rfds);
maxfd = s_wld;
#endif
#ifdef USE_ROUTE
if (sockfd) {
#ifdef HAVE_POLL_H
pfd[1].fd = sockfd;
pfd[1].events = POLLIN;
#else
if (sockfd >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(sockfd, &rfds);
maxfd = (maxfd < sockfd) ? sockfd : maxfd;
#endif
}
#endif
#ifdef HAVE_POLL_H
error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), INFTIM);
#else
error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
#endif
if (error < 0) {
if (errno == EINTR)
goto again;
@ -420,23 +406,38 @@ play_service(int s_wld)
}
#ifdef USE_ROUTE
if (FD_ISSET(sockfd, &rfds)) {
#ifdef HAVE_POLL_H
if (pfd[1].revents & POLLIN)
#else
if (FD_ISSET(sockfd, &rfds))
#endif
{
update_myaddrs();
}
#endif
if (FD_ISSET(s_wld, &rfds)) {
#ifdef HAVE_POLL_H
if (pfd[0].revents & POLLIN)
#else
if (FD_ISSET(s_wld, &rfds))
#endif
{
len = sizeof(srcaddr);
s_src = accept(s_wld, (struct sockaddr *)&srcaddr,
&len);
s_src = accept(s_wld, (struct sockaddr *)&srcaddr, &len);
if (s_src < 0) {
if (errno == ECONNABORTED)
goto again;
exit_failure("socket: %s", strerror(errno));
/*NOTREACHED*/
}
if (srcaddr.ss_family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&srcaddr)->sin6_addr)) {
close(s_src);
syslog(LOG_ERR, "connection from IPv4 mapped address?");
goto again;
}
child_pid = fork();
if (child_pid == 0) {
/* child process */
close(s_wld);
@ -463,7 +464,7 @@ play_child(int s_src, struct sockaddr *srcaddr)
char src[NI_MAXHOST];
char dst6[NI_MAXHOST];
char dst4[NI_MAXHOST];
int len = sizeof(dstaddr6);
socklen_t len = sizeof(dstaddr6);
int s_dst, error, hport, nresvport, on = 1;
struct timeval tv;
struct sockaddr *sa4;
@ -473,7 +474,7 @@ play_child(int s_src, struct sockaddr *srcaddr)
tv.tv_usec = 0;
getnameinfo(srcaddr, srcaddr->sa_len,
src, sizeof(src), NULL, 0, NI_NUMERICHOST);
src, sizeof(src), NULL, 0, NI_NUMERICHOST);
syslog(LOG_INFO, "accepted a client from %s", src);
error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
@ -483,7 +484,7 @@ play_child(int s_src, struct sockaddr *srcaddr)
}
getnameinfo((struct sockaddr *)&dstaddr6, len,
dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
syslog(LOG_INFO, "the client is connecting to %s", dst6);
if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
@ -522,17 +523,6 @@ play_child(int s_src, struct sockaddr *srcaddr)
}
syslog(LOG_INFO, "translating from v6 to v4");
break;
#ifdef FAITH4
case AF_INET:
if (!map4to6((struct sockaddr_in *)&dstaddr6,
(struct sockaddr_in6 *)&dstaddr4)) {
close(s_src);
exit_failure("map4to6 failed");
/*NOTREACHED*/
}
syslog(LOG_INFO, "translating from v4 to v6");
break;
#endif
default:
close(s_src);
exit_failure("family not supported");
@ -541,7 +531,7 @@ play_child(int s_src, struct sockaddr *srcaddr)
sa4 = (struct sockaddr *)&dstaddr4;
getnameinfo(sa4, sa4->sa_len,
dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
conf = config_match(srcaddr, sa4);
if (!conf || !conf->permit) {
@ -565,18 +555,10 @@ play_child(int s_src, struct sockaddr *srcaddr)
else /* AF_INET */
hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
switch (hport) {
case RLOGIN_PORT:
case RSH_PORT:
if (pflag)
s_dst = rresvport_af(&nresvport, sa4->sa_family);
break;
default:
if (pflag)
s_dst = rresvport_af(&nresvport, sa4->sa_family);
else
s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
break;
}
else
s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
if (s_dst < 0) {
exit_failure("socket: %s", strerror(errno));
/*NOTREACHED*/
@ -617,15 +599,6 @@ play_child(int s_src, struct sockaddr *srcaddr)
case FTP_PORT:
ftp_relay(s_src, s_dst);
break;
case RSH_PORT:
syslog(LOG_WARNING,
"WARINNG: it is insecure to relay rsh port");
rsh_relay(s_src, s_dst);
break;
case RLOGIN_PORT:
syslog(LOG_WARNING,
"WARINNG: it is insecure to relay rlogin port");
/*FALLTHROUGH*/
default:
tcp_relay(s_src, s_dst, service);
break;
@ -657,7 +630,7 @@ faith_prefix(struct sockaddr *dst)
}
if (memcmp(dst, &faith_prefix,
sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
return 1;
}
return 0;
@ -727,39 +700,6 @@ map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
return 1;
}
#ifdef FAITH4
/* 0: non faith, 1: faith */
static int
map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6)
{
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
struct addrinfo hints, *res;
int ai_errno;
if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host),
serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0)
return 0;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = 0;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) {
syslog(LOG_INFO, "%s %s: %s", host, serv,
gai_strerror(ai_errno));
return 0;
}
memcpy(dst6, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
return 1;
}
#endif /* FAITH4 */
static void
sig_child(int sig)
@ -767,9 +707,10 @@ sig_child(int sig)
int status;
pid_t pid;
pid = wait3(&status, WNOHANG, (struct rusage *)0);
if (pid && WEXITSTATUS(status))
syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0)
if (WEXITSTATUS(status))
syslog(LOG_WARNING, "child %ld exit status 0x%x",
(long)pid, status);
}
void
@ -894,8 +835,8 @@ grab_myaddrs()
if (dflag) {
char hbuf[NI_MAXHOST];
getnameinfo(p->addr, p->addr->sa_len,
hbuf, sizeof(hbuf), NULL, 0,
NI_NUMERICHOST);
hbuf, sizeof(hbuf), NULL, 0,
NI_NUMERICHOST);
syslog(LOG_INFO, "my interface: %s %s", hbuf,
ifa->ifa_name);
}

View File

@ -1,4 +1,4 @@
/* $KAME: faithd.h,v 1.8 2001/09/05 03:04:21 itojun Exp $ */
/* $KAME: faithd.h,v 1.9 2002/05/09 09:41:24 itojun Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
@ -38,8 +38,6 @@ extern void tcp_relay __P((int, int, const char *));
extern void ftp_relay __P((int, int));
extern int ftp_active __P((int, int, int *, int *));
extern int ftp_passive __P((int, int, int *, int *));
extern void rsh_relay __P((int, int));
extern void rsh_dual_relay __P((int, int));
extern void exit_success __P((const char *, ...))
__attribute__((__format__(__printf__, 1, 2)));
extern void exit_failure __P((const char *, ...))

View File

@ -1,4 +1,4 @@
/* $KAME: ftp.c,v 1.11 2001/07/02 14:36:49 itojun Exp $ */
/* $KAME: ftp.c,v 1.23 2003/08/19 21:20:33 itojun Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
@ -42,6 +42,9 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <errno.h>
#include <ctype.h>
@ -62,11 +65,7 @@ static struct sockaddr_storage data4; /* server data address */
static struct sockaddr_storage data6; /* client data address */
static int epsvall = 0;
#ifdef FAITH4
enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
#else
enum state { NONE, LPRT, EPRT, LPSV, EPSV };
#endif
static int ftp_activeconn __P((void));
static int ftp_passiveconn __P((void));
@ -77,7 +76,11 @@ static int ftp_copycommand __P((int, int, enum state *));
void
ftp_relay(int ctl6, int ctl4)
{
#ifdef HAVE_POLL_H
struct pollfd pfd[6];
#else
fd_set readfds;
#endif
int error;
enum state state = NONE;
struct timeval tv;
@ -85,25 +88,90 @@ ftp_relay(int ctl6, int ctl4)
syslog(LOG_INFO, "starting ftp control connection");
for (;;) {
FD_ZERO(&readfds);
FD_SET(ctl4, &readfds);
FD_SET(ctl6, &readfds);
if (0 <= port4)
FD_SET(port4, &readfds);
if (0 <= port6)
FD_SET(port6, &readfds);
#ifdef HAVE_POLL_H
pfd[0].fd = ctl4;
pfd[0].events = POLLIN;
pfd[1].fd = ctl6;
pfd[1].events = POLLIN;
if (0 <= port4) {
pfd[2].fd = port4;
pfd[2].events = POLLIN;
} else
pfd[2].fd = -1;
if (0 <= port6) {
pfd[3].fd = port6;
pfd[3].events = POLLIN;
} else
pfd[3].fd = -1;
#if 0
if (0 <= wport4)
if (0 <= wport4) {
pfd[4].fd = wport4;
pfd[4].events = POLLIN;
} else
pfd[4].fd = -1;
if (0 <= wport6) {
pfd[5].fd = wport4;
pfd[5].events = POLLIN;
} else
pfd[5].fd = -1;
#else
pfd[4].fd = pfd[5].fd = -1;
pfd[4].events = pfd[5].events = 0;
#endif
#else
int maxfd = 0;
FD_ZERO(&readfds);
if (ctl4 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(ctl4, &readfds);
maxfd = ctl4;
if (ctl6 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(ctl6, &readfds);
maxfd = (ctl6 > maxfd) ? ctl6 : maxfd;
if (0 <= port4) {
if (port4 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(port4, &readfds);
maxfd = (port4 > maxfd) ? port4 : maxfd;
}
if (0 <= port6) {
if (port6 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(port6, &readfds);
maxfd = (port6 > maxfd) ? port6 : maxfd;
}
#if 0
if (0 <= wport4) {
if (wport4 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(wport4, &readfds);
if (0 <= wport6)
maxfd = (wport4 > maxfd) ? wport4 : maxfd;
}
if (0 <= wport6) {
if (wport6 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(wport6, &readfds);
maxfd = (wport6 > maxfd) ? wport6 : maxfd;
}
#endif
#endif
tv.tv_sec = FAITH_TIMEOUT;
tv.tv_usec = 0;
error = select(256, &readfds, NULL, NULL, &tv);
if (error == -1)
#ifdef HAVE_POLL_H
error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
#else
error = select(maxfd + 1, &readfds, NULL, NULL, &tv);
#endif
if (error == -1) {
#ifdef HAVE_POLL_H
exit_failure("poll: %s", strerror(errno));
#else
exit_failure("select: %s", strerror(errno));
#endif
}
else if (error == 0)
exit_failure("connection timeout");
@ -113,7 +181,12 @@ ftp_relay(int ctl6, int ctl4)
* otherwise some of the pipe may become full and we cannot
* relay correctly.
*/
if (FD_ISSET(ctl6, &readfds)) {
#ifdef HAVE_POLL_H
if (pfd[1].revents & POLLIN)
#else
if (FD_ISSET(ctl6, &readfds))
#endif
{
/*
* copy control connection from the client.
* command translation is necessary.
@ -126,9 +199,15 @@ ftp_relay(int ctl6, int ctl4)
close(ctl4);
close(ctl6);
exit_success("terminating ftp control connection");
/*NOTREACHED*/
}
}
if (FD_ISSET(ctl4, &readfds)) {
#ifdef HAVE_POLL_H
if (pfd[0].revents & POLLIN)
#else
if (FD_ISSET(ctl4, &readfds))
#endif
{
/*
* copy control connection from the server
* translation of result code is necessary.
@ -141,14 +220,24 @@ ftp_relay(int ctl6, int ctl4)
close(ctl4);
close(ctl6);
exit_success("terminating ftp control connection");
/*NOTREACHED*/
}
}
if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
#ifdef HAVE_POLL_H
if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
#else
if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds))
#endif
{
/*
* copy data connection.
* no special treatment necessary.
*/
#ifdef HAVE_POLL_H
if (pfd[2].revents & POLLIN)
#else
if (FD_ISSET(port4, &readfds))
#endif
error = ftp_copy(port4, port6);
switch (error) {
case -1:
@ -163,12 +252,21 @@ ftp_relay(int ctl6, int ctl4)
break;
}
}
if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
#ifdef HAVE_POLL_H
if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
#else
if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds))
#endif
{
/*
* copy data connection.
* no special treatment necessary.
*/
#ifdef HAVE_POLL_H
if (pfd[3].revents & POLLIN)
#else
if (FD_ISSET(port6, &readfds))
#endif
error = ftp_copy(port6, port4);
switch (error) {
case -1:
@ -184,13 +282,23 @@ ftp_relay(int ctl6, int ctl4)
}
}
#if 0
if (wport4 && FD_ISSET(wport4, &readfds)) {
#ifdef HAVE_POLL_H
if (wport4 && (pfd[4].revents & POLLIN))
#else
if (wport4 && FD_ISSET(wport4, &readfds))
#endif
{
/*
* establish active data connection from the server.
*/
ftp_activeconn();
}
if (wport6 && FD_ISSET(wport6, &readfds)) {
#ifdef HAVE_POLL_H
if (wport4 && (pfd[5].revents & POLLIN))
#else
if (wport6 && FD_ISSET(wport6, &readfds))
#endif
{
/*
* establish passive data connection from the client.
*/
@ -206,20 +314,37 @@ ftp_relay(int ctl6, int ctl4)
static int
ftp_activeconn()
{
int n;
socklen_t n;
int error;
#ifdef HAVE_POLL_H
struct pollfd pfd[1];
#else
fd_set set;
#endif
struct timeval timeout;
struct sockaddr *sa;
/* get active connection from server */
#ifdef HAVE_POLL_H
pfd[0].fd = wport4;
pfd[0].events = POLLIN;
#else
FD_ZERO(&set);
if (wport4 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(wport4, &set);
#endif
timeout.tv_sec = 120;
timeout.tv_usec = -1;
timeout.tv_usec = 0;
n = sizeof(data4);
if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
|| (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
#ifdef HAVE_POLL_H
if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
(port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
#else
if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 ||
(port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
#endif
{
close(wport4);
wport4 = -1;
syslog(LOG_INFO, "active mode data connection failed");
@ -253,20 +378,37 @@ ftp_activeconn()
static int
ftp_passiveconn()
{
int n;
socklen_t len;
int error;
#ifdef HAVE_POLL_H
struct pollfd pfd[1];
#else
fd_set set;
#endif
struct timeval timeout;
struct sockaddr *sa;
/* get passive connection from client */
#ifdef HAVE_POLL_H
pfd[0].fd = wport6;
pfd[0].events = POLLIN;
#else
FD_ZERO(&set);
if (wport6 >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(wport6, &set);
#endif
timeout.tv_sec = 120;
timeout.tv_usec = 0;
n = sizeof(data6);
if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
|| (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
len = sizeof(data6);
#ifdef HAVE_POLL_H
if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
(port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
#else
if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 ||
(port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
#endif
{
close(wport6);
wport6 = -1;
syslog(LOG_INFO, "passive mode data connection failed");
@ -300,8 +442,7 @@ ftp_passiveconn()
static int
ftp_copy(int src, int dst)
{
int error, atmark;
int n;
int error, atmark, n;
/* OOB data handling */
error = ioctl(src, SIOCATMARK, &atmark);
@ -338,10 +479,12 @@ ftp_copy(int src, int dst)
static int
ftp_copyresult(int src, int dst, enum state state)
{
int error, atmark;
int n;
int error, atmark, n;
socklen_t len;
char *param;
int code;
char *a, *p;
int i;
/* OOB data handling */
error = ioctl(src, SIOCATMARK, &atmark);
@ -367,10 +510,6 @@ ftp_copyresult(int src, int dst, enum state state)
/*
* parse argument
*/
{
char *p;
int i;
p = rbuf;
for (i = 0; i < 3; i++) {
if (!isdigit(*p)) {
@ -392,7 +531,6 @@ ftp_copyresult(int src, int dst, enum state state)
param++;
if (!*param)
param = NULL;
}
switch (state) {
case NONE:
@ -400,15 +538,17 @@ ftp_copyresult(int src, int dst, enum state state)
if (ftp_activeconn() < 0) {
n = snprintf(rbuf, sizeof(rbuf),
"425 Cannot open data connetion\r\n");
if (n < 0 || n >= sizeof(rbuf))
n = 0;
}
}
return n > 0 ? write(dst, rbuf, n) : n;
if (n)
write(dst, rbuf, n);
return n;
case LPRT:
case EPRT:
/* expecting "200 PORT command successful." */
if (code == 200) {
char *p;
p = strstr(rbuf, "PORT");
if (p) {
p[0] = (state == LPRT) ? 'L' : 'E';
@ -420,24 +560,6 @@ ftp_copyresult(int src, int dst, enum state state)
}
write(dst, rbuf, n);
return n;
#ifdef FAITH4
case PORT:
/* expecting "200 EPRT command successful." */
if (code == 200) {
char *p;
p = strstr(rbuf, "EPRT");
if (p) {
p[0] = 'P';
p[1] = 'O';
}
} else {
close(wport4);
wport4 = -1;
}
write(dst, rbuf, n);
return n;
#endif
case LPSV:
case EPSV:
/*
@ -457,7 +579,6 @@ ftp_copyresult(int src, int dst, enum state state)
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
u_short port;
char *p;
/*
* PASV result -> LPSV/EPSV result
@ -496,7 +617,11 @@ ftp_copyresult(int src, int dst, enum state state)
passivefail:
n = snprintf(sbuf, sizeof(sbuf),
"500 could not translate from PASV\r\n");
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
#ifdef IPV6_FAITH
{
@ -524,8 +649,8 @@ ftp_copyresult(int src, int dst, enum state state)
/*
* addr from dst, port from wport6
*/
n = sizeof(data6);
error = getsockname(wport6, (struct sockaddr *)&data6, &n);
len = sizeof(data6);
error = getsockname(wport6, (struct sockaddr *)&data6, &len);
if (error == -1) {
close(wport6);
wport6 = -1;
@ -534,8 +659,8 @@ ftp_copyresult(int src, int dst, enum state state)
sin6 = (struct sockaddr_in6 *)&data6;
port = sin6->sin6_port;
n = sizeof(data6);
error = getsockname(dst, (struct sockaddr *)&data6, &n);
len = sizeof(data6);
error = getsockname(dst, (struct sockaddr *)&data6, &len);
if (error == -1) {
close(wport6);
wport6 = -1;
@ -545,8 +670,6 @@ ftp_copyresult(int src, int dst, enum state state)
sin6->sin6_port = port;
if (state == LPSV) {
char *a, *p;
a = (char *)&sin6->sin6_addr;
p = (char *)&sin6->sin6_port;
n = snprintf(sbuf, sizeof(sbuf),
@ -556,132 +679,24 @@ ftp_copyresult(int src, int dst, enum state state)
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, UC(p[0]), UC(p[1]));
if (n > 0)
n = write(dst, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(dst, sbuf, n);
passivemode = 1;
return n;
} else {
n = snprintf(sbuf, sizeof(sbuf),
"229 Entering Extended Passive Mode (|||%d|)\r\n",
ntohs(sin6->sin6_port));
if (n > 0)
n = write(dst, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(dst, sbuf, n);
passivemode = 1;
return n;
}
}
#ifdef FAITH4
case PASV:
/* expecting "229 Entering Extended Passive Mode (|||x|)" */
if (code != 229) {
passivefail1:
close(wport6);
wport6 = -1;
write(dst, rbuf, n);
return n;
}
{
u_short port;
char *p;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
/*
* EPSV result -> PORT result
*/
p = param;
while (*p && *p != '(') /*)*/
p++;
if (!*p)
goto passivefail1; /*XXX*/
p++;
n = sscanf(p, "|||%hu|", &port);
if (n != 1)
goto passivefail1; /*XXX*/
/* keep EPRT parameter */
n = sizeof(data4);
error = getpeername(src, (struct sockaddr *)&data4, &n);
if (error == -1)
goto passivefail1; /*XXX*/
sin6 = (struct sockaddr_in6 *)&data4;
sin6->sin6_port = htons(port);
/* get ready for passive data connection */
memset(&data6, 0, sizeof(data6));
sin = (struct sockaddr_in *)&data6;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
if (wport6 == -1) {
passivefail2:
n = snprintf(sbuf, sizeof(sbuf),
"500 could not translate from EPSV\r\n");
return n > 0 ? write(src, sbuf, n) : n;
}
#ifdef IP_FAITH
{
int on = 1;
error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
&on, sizeof(on));
if (error == -1)
exit_error("setsockopt(IP_FAITH): %s", strerror(errno));
}
#endif
error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
if (error == -1) {
close(wport6);
wport6 = -1;
goto passivefail2;
}
error = listen(wport6, 1);
if (error == -1) {
close(wport6);
wport6 = -1;
goto passivefail2;
}
/* transmit PORT */
/*
* addr from dst, port from wport6
*/
n = sizeof(data6);
error = getsockname(wport6, (struct sockaddr *)&data6, &n);
if (error == -1) {
close(wport6);
wport6 = -1;
goto passivefail2;
}
sin = (struct sockaddr_in *)&data6;
port = sin->sin_port;
n = sizeof(data6);
error = getsockname(dst, (struct sockaddr *)&data6, &n);
if (error == -1) {
close(wport6);
wport6 = -1;
goto passivefail2;
}
sin = (struct sockaddr_in *)&data6;
sin->sin_port = port;
{
char *a, *p;
a = (char *)&sin->sin_addr;
p = (char *)&sin->sin_port;
n = snprintf(sbuf, sizeof(sbuf),
"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
UC(p[0]), UC(p[1]));
if (n > 0)
n = write(dst, sbuf, n);
passivemode = 1;
return n;
}
}
#endif /* FAITH4 */
}
bad:
@ -693,15 +708,16 @@ ftp_copyresult(int src, int dst, enum state state)
static int
ftp_copycommand(int src, int dst, enum state *state)
{
int error, atmark;
int n;
int error, atmark, n;
socklen_t len;
unsigned int af, hal, ho[16], pal, po[2];
char *a, *p;
char *a, *p, *q;
char cmd[5], *param;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
enum state nstate;
char ch;
int i;
/* OOB data handling */
error = ioctl(src, SIOCATMARK, &atmark);
@ -732,10 +748,6 @@ ftp_copycommand(int src, int dst, enum state *state)
/*
* parse argument
*/
{
char *p, *q;
int i;
p = rbuf;
q = cmd;
for (i = 0; i < 4; i++) {
@ -759,7 +771,6 @@ ftp_copycommand(int src, int dst, enum state *state)
param++;
if (!*param)
param = NULL;
}
*state = NONE;
@ -778,7 +789,11 @@ ftp_copycommand(int src, int dst, enum state *state)
if (epsvall) {
n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
cmd);
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
n = sscanf(param,
@ -791,7 +806,11 @@ ftp_copycommand(int src, int dst, enum state *state)
if (n != 21 || af != 6 || hal != 16|| pal != 2) {
n = snprintf(sbuf, sizeof(sbuf),
"501 illegal parameter to LPRT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
/* keep LPRT parameter */
@ -805,13 +824,17 @@ ftp_copycommand(int src, int dst, enum state *state)
sendport:
/* get ready for active data connection */
n = sizeof(data4);
error = getsockname(dst, (struct sockaddr *)&data4, &n);
len = sizeof(data4);
error = getsockname(dst, (struct sockaddr *)&data4, &len);
if (error == -1) {
lprtfail:
n = snprintf(sbuf, sizeof(sbuf),
"500 could not translate to PORT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
if (((struct sockaddr *)&data4)->sa_family != AF_INET)
goto lprtfail;
@ -834,8 +857,8 @@ ftp_copycommand(int src, int dst, enum state *state)
}
/* transmit PORT */
n = sizeof(data4);
error = getsockname(wport4, (struct sockaddr *)&data4, &n);
len = sizeof(data4);
error = getsockname(wport4, (struct sockaddr *)&data4, &len);
if (error == -1) {
close(wport4);
wport4 = -1;
@ -852,8 +875,10 @@ ftp_copycommand(int src, int dst, enum state *state)
n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
UC(p[0]), UC(p[1]));
if (n > 0)
n = write(dst, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(dst, sbuf, n);
*state = nstate;
passivemode = 0;
return n;
@ -875,7 +900,11 @@ ftp_copycommand(int src, int dst, enum state *state)
if (epsvall) {
n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
cmd);
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
p = param;
@ -887,7 +916,11 @@ ftp_copycommand(int src, int dst, enum state *state)
eprtparamfail:
n = snprintf(sbuf, sizeof(sbuf),
"501 illegal parameter to EPRT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
*p++ = '\0';
hostp = p;
@ -907,21 +940,34 @@ ftp_copycommand(int src, int dst, enum state *state)
if (n != 1 || af != 2) {
n = snprintf(sbuf, sizeof(sbuf),
"501 unsupported address family to EPRT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo(hostp, portp, &hints, &res);
if (error) {
n = snprintf(sbuf, sizeof(sbuf),
"501 EPRT: %s\r\n", gai_strerror(error));
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
if (res->ai_next) {
n = snprintf(sbuf, sizeof(sbuf),
"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
memcpy(&data6, res->ai_addr, res->ai_addrlen);
@ -942,13 +988,19 @@ ftp_copycommand(int src, int dst, enum state *state)
if (epsvall) {
n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
cmd);
return n > 0 ? write(src, sbuf, n) : n;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
}
/* transmit PASV */
n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
if (n > 0)
n = write(dst, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(dst, sbuf, n);
*state = LPSV;
passivemode = 0; /* to be set to 1 later */
return n;
@ -963,8 +1015,10 @@ ftp_copycommand(int src, int dst, enum state *state)
wport4 = wport6 = port4 = port6 = -1;
n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
if (n > 0)
n = write(dst, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(dst, sbuf, n);
*state = EPSV;
passivemode = 0; /* to be set to 1 later */
return n;
@ -975,120 +1029,21 @@ ftp_copycommand(int src, int dst, enum state *state)
*/
epsvall = 1;
n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
return n > 0 ? write(src, sbuf, n) : n;
#ifdef FAITH4
} else if (strcmp(cmd, "PORT") == 0 && param) {
/*
* PORT -> EPRT
*/
char host[NI_MAXHOST], serv[NI_MAXSERV];
nstate = PORT;
close(wport4);
close(wport6);
close(port4);
close(port6);
wport4 = wport6 = port4 = port6 = -1;
p = param;
n = sscanf(p, "%u,%u,%u,%u,%u,%u",
&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
if (n != 6) {
n = snprintf(sbuf, sizeof(sbuf),
"501 illegal parameter to PORT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
}
memset(&data6, 0, sizeof(data6));
sin = (struct sockaddr_in *)&data6;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(
((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
/* get ready for active data connection */
n = sizeof(data4);
error = getsockname(dst, (struct sockaddr *)&data4, &n);
if (error == -1) {
portfail:
n = snprintf(sbuf, sizeof(sbuf),
"500 could not translate to EPRT\r\n");
return n > 0 ? write(src, sbuf, n) : n;
}
if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
goto portfail;
((struct sockaddr_in6 *)&data4)->sin6_port = 0;
sa = (struct sockaddr *)&data4;
wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
if (wport4 == -1)
goto portfail;
error = bind(wport4, sa, sa->sa_len);
if (error == -1) {
close(wport4);
wport4 = -1;
goto portfail;
}
error = listen(wport4, 1);
if (error == -1) {
close(wport4);
wport4 = -1;
goto portfail;
}
/* transmit EPRT */
n = sizeof(data4);
error = getsockname(wport4, (struct sockaddr *)&data4, &n);
if (error == -1) {
close(wport4);
wport4 = -1;
goto portfail;
}
af = 2;
sa = (struct sockaddr *)&data4;
if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
close(wport4);
wport4 = -1;
goto portfail;
}
n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
if (n > 0)
n = write(dst, sbuf, n);
*state = nstate;
passivemode = 0;
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
} else if (strcmp(cmd, "PASV") == 0 && !param) {
/*
* PASV -> EPSV
*/
nstate = PASV;
close(wport4);
close(wport6);
close(port4);
close(port6);
wport4 = wport6 = port4 = port6 = -1;
/* transmit EPSV */
n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
if (n > 0)
n = write(dst, sbuf, n);
*state = PASV;
passivemode = 0; /* to be set to 1 later */
return n;
#else /* FAITH4 */
} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
/*
* reject PORT/PASV
*/
n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
return n > 0 ? write(src, sbuf, n) : n;
#endif /* FAITH4 */
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
return n;
} else if (passivemode
&& (strcmp(cmd, "STOR") == 0
|| strcmp(cmd, "STOU") == 0
@ -1103,8 +1058,10 @@ ftp_copycommand(int src, int dst, enum state *state)
if (ftp_passiveconn() < 0) {
n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
if (n > 0)
n = write(src, sbuf, n);
if (n < 0 || n >= sizeof(sbuf))
n = 0;
if (n)
write(src, sbuf, n);
} else {
/* simply relay the command */
write(dst, rbuf, n);
@ -1115,7 +1072,8 @@ ftp_copycommand(int src, int dst, enum state *state)
} else {
/* simply relay it */
*state = NONE;
return write(dst, rbuf, n);
write(dst, rbuf, n);
return n;
}
bad:

View File

@ -1,4 +1,4 @@
/* $KAME: prefix.c,v 1.9 2001/07/02 14:36:49 itojun Exp $ */
/* $KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $ */
/* $FreeBSD$ */
/*
@ -58,17 +58,16 @@ struct config *config_list = NULL;
const int niflags = NI_NUMERICHOST;
static int
prefix_set(s, prefix, slash)
const char *s;
struct prefix *prefix;
int slash;
prefix_set(const char *s, struct prefix *prefix, int slash)
{
char *p, *q, *r;
char *p = NULL, *q, *r;
struct addrinfo hints, *res = NULL;
int max;
char *a;
p = strdup(s);
if (!p)
goto fail;
q = strchr(p, '/');
if (q) {
if (!slash)
@ -126,8 +125,7 @@ prefix_set(s, prefix, slash)
}
const char *
prefix_string(prefix)
const struct prefix *prefix;
prefix_string(const struct prefix *prefix)
{
static char buf[NI_MAXHOST + 20];
char hbuf[NI_MAXHOST];
@ -140,9 +138,7 @@ prefix_string(prefix)
}
int
prefix_match(prefix, sa)
const struct prefix *prefix;
const struct sockaddr *sa;
prefix_match(const struct prefix *prefix, const struct sockaddr *sa)
{
struct sockaddr_storage a, b;
char *pa, *pb;
@ -194,8 +190,7 @@ prefix_match(prefix, sa)
* 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
*/
static struct config *
config_load1(line)
const char *line;
config_load1(const char *line)
{
struct config *conf;
char buf[BUFSIZ];
@ -268,8 +263,7 @@ config_load1(line)
}
int
config_load(configfile)
const char *configfile;
config_load(const char *configfile)
{
FILE *fp;
char buf[BUFSIZ];
@ -285,6 +279,7 @@ config_load(configfile)
return -1;
p = &sentinel;
sentinel.next = NULL;
while (fgets(buf, sizeof(buf), fp) != NULL) {
conf = config_load1(buf);
if (conf) {
@ -300,8 +295,7 @@ config_load(configfile)
#if 0
static void
config_show1(conf)
const struct config *conf;
config_show1(const struct config *conf)
{
const char *p;
@ -330,8 +324,7 @@ config_show()
#endif
const struct config *
config_match(sa1, sa2)
struct sockaddr *sa1, *sa2;
config_match(struct sockaddr *sa1, struct sockaddr *sa2)
{
static struct config conf;
const struct config *p;

View File

@ -1,212 +0,0 @@
/* $KAME: rsh.c,v 1.7 2001/09/05 01:10:30 itojun Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "faithd.h"
char rshbuf[MSS];
int s_ctl, s_ctl6, s_rcv, s_snd;
int half;
void
rsh_relay(int s_src, int s_dst)
{
ssize_t n;
fd_set readfds;
int error;
struct timeval tv;
FD_ZERO(&readfds);
FD_SET(s_src, &readfds);
tv.tv_sec = FAITH_TIMEOUT;
tv.tv_usec = 0;
error = select(256, &readfds, NULL, NULL, &tv);
if (error == -1)
exit_failure("select %d: %s", s_src, strerror(errno));
else if (error == 0)
exit_failure("connection timeout");
n = read(s_src, rshbuf, sizeof(rshbuf));
if (rshbuf[0] != 0) {
rsh_dual_relay(s_src, s_dst);
/* NOTREACHED */
}
write(s_dst, rshbuf, n);
tcp_relay(s_src, s_dst, "rsh");
/* NOTREACHED */
}
static void
relay(int src, int dst)
{
int error;
ssize_t n;
int atmark;
error = ioctl(s_rcv, SIOCATMARK, &atmark);
if (error != -1 && atmark == 1) {
n = read(s_rcv, rshbuf, 1);
if (n == 1)
send(s_snd, rshbuf, 1, MSG_OOB);
return;
}
n = read(s_rcv, rshbuf, sizeof(rshbuf));
switch (n) {
case -1:
exit_failure("%s", strerror(errno));
case 0:
if (s_rcv == src) {
/* half close */
shutdown(dst, 1);
half = YES;
break;
}
close(src);
close(dst);
close(s_ctl);
close(s_ctl6);
exit_success("terminating rsh/contorol connections");
break;
default:
write(s_snd, rshbuf, n);
}
}
void
rsh_dual_relay(int s_src, int s_dst)
{
fd_set readfds;
int len, s_wld, error;
struct sockaddr_storage ctladdr6;
struct sockaddr_storage ctladdr;
int port6 = 0, lport, lport6;
char *p;
struct timeval tv;
struct sockaddr *sa;
half = NO;
s_rcv = s_src;
s_snd = s_dst;
syslog(LOG_INFO, "starting rsh connection");
for (p = rshbuf; *p; p++)
port6 = port6 * 10 + *p - '0';
len = sizeof(ctladdr6);
getpeername(s_src, (struct sockaddr *)&ctladdr6, &len);
if (((struct sockaddr *)&ctladdr6)->sa_family == AF_INET6)
((struct sockaddr_in6 *)&ctladdr6)->sin6_port = htons(port6);
else
((struct sockaddr_in *)&ctladdr6)->sin_port = htons(port6);
s_wld = rresvport(&lport);
if (s_wld == -1) goto bad;
error = listen(s_wld, 1);
if (error == -1) goto bad;
snprintf(rshbuf, sizeof(rshbuf), "%d", lport);
write(s_dst, rshbuf, strlen(rshbuf)+1);
len = sizeof(ctladdr);
s_ctl = accept(s_wld, (struct sockaddr *)&ctladdr, &len);
if (s_ctl == -1) goto bad;
close(s_wld);
sa = (struct sockaddr *)&ctladdr6;
s_ctl6 = rresvport_af(&lport6, sa->sa_family);
if (s_ctl6 == -1) goto bad;
error = connect(s_ctl6, sa, sa->sa_len);
if (error == -1) goto bad;
syslog(LOG_INFO, "starting rsh control connection");
for (;;) {
FD_ZERO(&readfds);
if (half == NO)
FD_SET(s_src, &readfds);
FD_SET(s_dst, &readfds);
FD_SET(s_ctl, &readfds);
FD_SET(s_ctl6, &readfds);
tv.tv_sec = FAITH_TIMEOUT;
tv.tv_usec = 0;
error = select(256, &readfds, NULL, NULL, &tv);
if (error == -1)
exit_failure("select 4 sockets: %s", strerror(errno));
else if (error == 0)
exit_failure("connection timeout");
if (half == NO && FD_ISSET(s_src, &readfds)) {
s_rcv = s_src;
s_snd = s_dst;
relay(s_src, s_dst);
}
if (FD_ISSET(s_dst, &readfds)) {
s_rcv = s_dst;
s_snd = s_src;
relay(s_src, s_dst);
}
if (FD_ISSET(s_ctl, &readfds)) {
s_rcv = s_ctl;
s_snd = s_ctl6;
relay(s_src, s_dst);
}
if (FD_ISSET(s_ctl6, &readfds)) {
s_rcv = s_ctl6;
s_snd = s_ctl;
relay(s_src, s_dst);
}
}
/* NOTREACHED */
bad:
exit_failure("%s", strerror(errno));
}

View File

@ -1,4 +1,4 @@
/* $KAME: tcp.c,v 1.8 2001/11/21 07:40:22 itojun Exp $ */
/* $KAME: tcp.c,v 1.13 2003/09/02 22:49:21 itojun Exp $ */
/*
* Copyright (C) 1997 and 1998 WIDE Project.
@ -93,8 +93,9 @@ sig_child(int sig)
pid_t pid;
pid = wait3(&status, WNOHANG, (struct rusage *)0);
if (pid && WEXITSTATUS(status))
syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
if (pid > 0 && WEXITSTATUS(status))
syslog(LOG_WARNING, "child %ld exit status 0x%x",
(long)pid, status);
exit_success("terminate connection due to child termination");
}
@ -156,6 +157,8 @@ send_data(int s_rcv, int s_snd, const char *service, int direction)
if (cc == -1)
goto retry_or_err;
oob_exists = 0;
if (s_rcv >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_rcv, &exceptfds);
}
@ -174,12 +177,18 @@ send_data(int s_rcv, int s_snd, const char *service, int direction)
}
#endif /* DEBUG */
tblen = 0; tboff = 0;
if (s_snd >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_CLR(s_snd, &writefds);
if (s_rcv >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_rcv, &readfds);
return;
retry_or_err:
if (errno != EAGAIN)
exit_failure("writing relay data failed: %s", strerror(errno));
if (s_snd >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_snd, &writefds);
}
@ -195,6 +204,8 @@ relay(int s_rcv, int s_snd, const char *service, int direction)
FD_ZERO(&exceptfds);
fcntl(s_snd, F_SETFD, O_NONBLOCK);
oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
if (s_rcv >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_rcv, &readfds);
FD_SET(s_rcv, &exceptfds);
oob_exists = 0;
@ -229,7 +240,11 @@ relay(int s_rcv, int s_snd, const char *service, int direction)
oob_read_retry:
cc = read(s_rcv, atmark_buf, 1);
if (cc == 1) {
if (s_rcv >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_CLR(s_rcv, &exceptfds);
if (s_snd >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_snd, &writefds);
oob_exists = 1;
} else if (cc == -1) {
@ -262,7 +277,11 @@ relay(int s_rcv, int s_snd, const char *service, int direction)
exit_success("terminating %s relay", service);
/* NOTREACHED */
default:
if (s_rcv >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_CLR(s_rcv, &readfds);
if (s_snd >= FD_SETSIZE)
exit_failure("descriptor too big");
FD_SET(s_snd, &writefds);
break;
}

View File

@ -3,7 +3,7 @@
# highly experimental (not working right at all) and very limited
# functionality.
#
# $Id: faithd.rb,v 1.1.1.1 1999/08/08 23:29:31 itojun Exp $
# $Id: faithd.rb,v 1.1.2.4 1999/05/10 17:06:30 itojun Exp $
# $FreeBSD$
require "socket"