/*
 * Copyright (c) 1985, 1988 Regents of the University of California.
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by the University of
 *  California, Berkeley and its contributors.
 * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
 *
 *  @(#)
 */
/* GREGORY's comments
 * Use 194.80.17.14 for when the control connection is on any other address
 * passive address 194.80.17.14 0.0.0.0/0
 *
 * My idea for 'passive address' is that the entire ftpaccess be parsed and a
 * table build from them.  The table is sorted (insertion sort is probably
 * good enough) from largest to smallest CIDR mask (/8 then /0).  The first
 * match when top-down searching for the control connection's address is the
 * most-specific and is taken.  If no matches occur at all, use the default
 * address determined from the interface, just as the daemon always has in
 * the past.
 *
 * That's where CIDR comes in.  A /0 means 'match everything'.  If it's
 * there, it'll give the IP address to use everywhere there isn't a more
 * specific remapping given.  If it's not there, we still need something, so
 * we returnthe default from the interface.  In your case, just 0/0 and
 * nobody'll ever see the internal IP numbers.
 */

#ifndef lint
static char sccsid[] = "@(#)$Id: $";
#endif /* not lint */

#include "config.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <pwd.h>
#include <setjmp.h>
#ifdef SYSSYSLOG
#include <sys/syslog.h>
#else
#include <syslog.h>
#endif
#include <time.h>
#include <string.h>
#include <limits.h>
#include "extensions.h"
#include "pathnames.h"
#include <netdb.h>

extern  struct sockaddr_in ctrl_addr;
extern	struct sockaddr_in his_addr;	/* added.  _H*/
extern	struct sockaddr_in vect_addr;	/* added.  _H*/
static struct sockaddr_in *vector_ptr = (struct sockaddr_in *)NULL;
extern  int logging;
extern  int log_commands;
extern  int debug;
extern  char hostname[], remotehost[];
extern  int usedefault;
extern  int transflag;
extern  int data;
extern  int errno;
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif

/*  The following code is used to create and maintain the 
 *  Address lists using the S_addr struct ( from in.h )
 * struct in_addr { 
 *      union { 
 *              struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b; 
 *              struct { u_short s_w1, s_w2; } S_un_w; 
 *              u_long S_addr; 
 *      } S_un; 
 * define s_addr  S_un.S_addr              should be used for all code 
 */
struct v_addr {
                 u_long s_external_identity;
                 u_long s_vector;
                 unsigned short s_sig;
 };
typedef struct v_addr v_addr;
struct v_index {
                v_addr  *memory;
                v_addr **index;
                unsigned short count; 
                unsigned short slots; 
 };
typedef struct v_index v_index;
struct v_paddr {
                 u_long s_address;
                 int minport;
                 int maxport;
                 unsigned short s_sig;
 };
typedef struct v_paddr v_paddr;
struct v_ports {
		  v_paddr  *memory;
		  v_paddr **index;
		  unsigned short count; 
		  unsigned short slots; 
 };
typedef struct v_ports v_ports;

static struct v_index *VECTORS = NULL;
static struct v_ports *PORTS = NULL;

extern int passive_port_max;
extern int passive_port_min;

#ifdef __STDC__
extern void perror_reply(int, char *); 
int routevector(void); 
void checkports(void);
static struct v_index *initvectstruct(int num);
static struct v_ports *initportstruct(int num);
static void makeportentry(v_paddr *p, u_long entry, u_short s_sig, char *minport_in, char *maxport_in);
static void makevectentry(v_addr *p, u_long external_identity, u_long vector, u_short s_sig);
static void addportentry(char *address, char *minport, char *maxport);
static void addvectentry(char *external_identity, char *vector);
static void initportvectors(void);
static int addr_cmp(u_long s1, u_short s1_sig, u_long s2, u_short s2_sig);
static int addr_smatch(u_long s1, u_long s2, u_short shift_in);
static v_paddr *find_passive_port_entry(u_long port);
static v_addr *find_passive_vect_entry(u_long e);
#else
extern void perror_reply(); 
int routevector();
void checkports();
static struct v_index *initvectstruct();
static struct v_ports *initportstruct();
static void makeportentry();
static void makevectentry();
static void addportentry();
static void addvectentry();
static void initportvectors();
static int addr_cmp();
static int addr_smatch();
static v_paddr *find_passive_port_entry();
static v_addr *find_passive_vect_entry();
#endif

int 
#ifdef __STDC__
routevector(void)
#else
routevector()
#endif
{
    int ret;
    int vect_addr_set;
    u_long retval;
    u_long entry;
    v_addr *vaddr;
    extern struct sockaddr_in vect_addr;
    vect_addr_set = 0;

	initportvectors(); 
	if ( VECTORS != NULL )
	{
	    entry =  ntohl( (his_addr.sin_addr.s_addr) );
	    if ( (int)entry != -1 )
            {
                vaddr=find_passive_vect_entry( entry );
                if (  vaddr != NULL )
	        {
		   vect_addr.sin_addr.s_addr = htonl( vaddr->s_external_identity )  ;
                   vect_addr.sin_family = AF_INET;
                   vect_addr_set = 1;
                   vector_ptr=&vect_addr;

                }
            }
      }
#if 0
{
extern void debug_listing();
debug_listing();
}
#endif
    return (vect_addr_set);
}



void 
#ifdef __STDC__
checkports(void)
#else
checkports()
#endif
{
    u_long entry;
    v_paddr *addr;

    passive_port_min= -1;
    passive_port_max= -1;
    if ( PORTS != NULL ) {
	entry = his_addr.sin_addr.s_addr;
	if ( (int)entry != -1 ) {
	     addr=find_passive_port_entry( entry );
	     if ( addr != NULL ) {
                 passive_port_min=addr->minport;
                 passive_port_max=addr->maxport;
	     }
	}
	
    }

}



static struct v_index *
#ifdef __STDC__
initvectstruct(int num)
#else
initvectstruct(num)
int num;
#endif
{
    int i;
    v_addr *ptr, **index;
    v_index *v;
    
    if ((v = (v_index *)malloc(sizeof(v_index))) == (v_index *)NULL) {
        syslog (LOG_INFO, "ERROR allocating memory for index record");
        perror_reply(421, "Local resource failure: malloc"); 
        dologout(1); 
    } else {
        v->count=0;
        v->slots=0;
        v->index = NULL;
        if ((v->memory = (v_addr *)malloc(sizeof(v_addr) * num)) == (v_addr *)NULL) {
            syslog (LOG_INFO, "ERROR allocating memory for port addresses");
            perror_reply(421, "Local resource failure: malloc"); 
            dologout(1); 
        } else {
            if ((v->index = (v_addr **)malloc(sizeof(v_addr *) * num)) == (v_addr **)NULL) {
                syslog (LOG_INFO, "ERROR allocating memory for vector index");
                perror_reply(421, "Local resource failure: malloc"); 
                dologout(1); 
            } else {
                v->slots=num;
                for (i = 0, ptr = v->memory, index=v->index; i < num; i++ ) {
                    ptr->s_external_identity  = ntohl(inet_addr("0.0.0.0"));
                    ptr->s_vector             = ntohl(inet_addr("0.0.0.0"));
                    ptr->s_sig     = (u_short)0;
                    *index++ = ptr++;
                }
            }
        }
    }
    return ( v );
}



struct v_ports *
#ifdef __STDC__
initportstruct(int num)
#else
initportstruct(num)
int num;
#endif
{
    int i;
    v_paddr *ptr;
    v_ports *v;

    if ((v = (v_ports *)malloc(sizeof(v_ports))) == (v_ports *)NULL) {
        syslog (LOG_INFO, "ERROR allocating memory for index record");
        perror_reply(421, "Local resource failure: malloc"); 
        dologout(1); 
    } else {
        v->slots=0;
        v->count=0;
        v->index=NULL;
        if ((v->memory = (v_paddr *)malloc(sizeof(v_paddr) * (num +1))) == (v_paddr *)NULL) {
            syslog (LOG_INFO, "ERROR allocating memory for port addresses");
            perror_reply(421, "Local resource failure: malloc"); 
            dologout(1); 
        } else {
            if ((v->index =( v_paddr **)malloc(sizeof(v_paddr *) * num)) == (v_paddr **)NULL) {
                syslog (LOG_INFO, "ERROR allocating memory for port index");
                perror_reply(421, "Local resource failure: malloc"); 
                dologout(1); 
            } else {
                v->slots=(u_short)num;
                for (i = 0, ptr = v->memory; i < num; i++ , ptr++) {
                    *(v->index + i) = ptr;
                    ptr->s_address = ntohl(inet_addr("0.0.0.0"));
                    ptr->minport = -1;
                    ptr->maxport = -1;
                    ptr->s_sig   = (u_short)0;
                }
            }
        }
    }
    return ( v );
}



static void 
#ifdef __STDC__
makeportentry(v_paddr *p, u_long entry, u_short s_sig, char *minport_in, char *maxport_in)
#else
makeportentry(p, entry, s_sig, minport_in, maxport_in)
v_paddr *p;
u_long entry;
u_short s_sig;
char *minport_in;
char *maxport_in;
#endif
{
    p->s_address = entry;
    p->s_sig = s_sig;
    p->minport=atoi( minport_in );
    p->maxport=atoi( maxport_in );

    if (p->maxport < p->minport) {
        p->minport = -1;
        p->maxport = -1;
    }
}



static void 
#ifdef __STDC__
makevectentry(v_addr *p, u_long external_identity, u_long vector, u_short s_sig)
#else
makevectentry(p, external_identity, vector, s_sig)
v_addr *p;
u_long external_identity;
u_long vector;
u_short s_sig;
#endif
{
    p->s_external_identity = external_identity;
    p->s_vector = vector;
    p->s_sig = s_sig;
}



static void
#ifdef __STDC__
addportentry(char *address_in, char *minport_in, char *maxport_in)
#else
addportentry(address_in, minport_in, maxport_in)
char *address_in;
char *minport_in;
char *maxport_in;
#endif
{
    v_paddr  *v;
    u_long   entry;
    int      pval;
    int cmp;
    u_short  s_sig;
    char     *s1, *s2;
    char addrstr[ MAXHOSTNAMELEN + 1 ];
    register unsigned short i, fnd;
    s_sig=(u_short)0;

    if (PORTS == NULL) {
        syslog (LOG_INFO, "ERROR port index addition, invalid ptr");
        perror_reply(421, "Local resource failure: malloc"); 
        dologout(1); 
    } else {
        if (strlen(address_in) > MAXHOSTNAMELEN)
            syslog (LOG_INFO, "ERROR ftpaccess port entry<%s> too large", address_in);
        else {
            /* Find the significance of the entry ( if it exists )
            *  eg XXX.XXX.XXX.XXX/sig
            */
            for (i = 0; i < MAXHOSTNAMELEN; i++)
                addrstr[i]='\0';
            for (s1 = address_in, s2=&(addrstr[0]); *s1 && *s1 != '/'; s1++, s2++)
                *s2 = *s1;
            *s2='\0';
            if (*s1 == '/') {
                s1++;
                if (*s1)
                    s_sig = (u_short)atoi(s1);
            } 
            if ((int)(entry = ntohl(inet_addr(addrstr))) == -1)
                syslog (LOG_INFO, "ERROR ftpaccess port entry<%s> invalid", address_in);
            else {
                for (i=0, fnd=0; i < PORTS->count && fnd == 0 && entry != (u_long)0; i++) {
                    v_paddr *vp = *(PORTS->index + i);
                    cmp=addr_cmp(vp->s_address, vp->s_sig, entry, s_sig);
                    if (cmp == 0) {
                        fnd = 1;
                        makeportentry(vp, entry, s_sig, minport_in, maxport_in);
                    } else if ( cmp  > 0 ) {
                        v_paddr *p1;
                        for (p1 = *(PORTS->index + PORTS->count); p1 >= vp; p1--)
                            *p1 = *(p1 - 1);
                        fnd=2;
                        makeportentry(vp, entry, s_sig, minport_in, maxport_in);
                        PORTS->count +=  1;
                    }
                }
                if (fnd == 0) {
                    makeportentry(*(PORTS->index + PORTS->count), entry, s_sig, minport_in, maxport_in);
                    PORTS->count += 1;
                }
                if (i == PORTS->slots) {
                    syslog (LOG_INFO, "ERROR bad slot count  for port index record");
                    perror_reply(421, "Local resource failure: malloc"); 
                    dologout(1); 
                }
            }
        }
    }
}



static void
#ifdef __STDC__
addvectentry(char *external_identity_in, char *vector_in)
#else
addvectentry(external_identity_in, vector_in)
char *external_identity_in;
char *vector_in;
#endif
{
    v_addr  *v;
    u_long   external_identity, vector;
    int      pval;
    int cmp;
    u_short  s_sig;
    char     *s1, *s2;
    char addrstr[ MAXHOSTNAMELEN + 1 ];
    register unsigned short i, fnd;
    s_sig=(u_short)0;

    if (VECTORS == NULL) {
        syslog (LOG_INFO, "ERROR vector index addition, invalid ptr");
        perror_reply(421, "Local resource failure: malloc"); 
        dologout(1); 
    } else {
        if (strlen(external_identity_in) > MAXHOSTNAMELEN)
            syslog (LOG_INFO, "ERROR ftpaccess passive entry <%s> to large", external_identity_in);
        else {
            if (strlen(vector_in) > MAXHOSTNAMELEN)
                syslog (LOG_INFO, "ERROR ftpaccess vector entry<%s> to large", vector_in);
            else {
                /* Find the significance of the entry ( if it exists )
                *  eg XXX.XXX.XXX.XXX/sig
                */
                for (i = 0; i < MAXHOSTNAMELEN; i++)
                    addrstr[i]='\0';
                for (s1 = vector_in, s2=&(addrstr[0]); *s1 && (*s1 != '/'); s1++, s2++)
                    *s2 = *s1;
                *s2='\0';
                if (*s1 == '/') {
                    s1++;
                    if (*s1)
                        s_sig = (u_short)atoi(s1);
                } 
                if ((int)(vector = ntohl(inet_addr(addrstr))) == -1)
                    syslog (LOG_INFO, "ERROR ftpaccess vector entry<%s> invalid", vector_in);
                else {
                    if ((int)(external_identity = ntohl(inet_addr(external_identity_in))) == -1)
                        syslog (LOG_INFO, "ERROR ftpaccess vector entry<%s> invalid", external_identity_in);
                    else {
                        for (i=0, fnd=0; i < VECTORS->count && fnd == 0 && vector != (u_long)0; i++) {
                            v_addr *vp = *(VECTORS->index + i);
                            cmp=addr_cmp(vp->s_vector, vp->s_sig, vector, s_sig);
                            if (cmp == 0) {
                                fnd = 1;
                                makevectentry(vp, external_identity, vector, s_sig);
                            } else if (cmp > 0) {
                                v_addr *p1;
                                for (p1 = *(VECTORS->index + VECTORS->count); p1 >= vp ; p1--)
                                    *p1 = *(p1 - 1);
                                fnd=2;
                                makevectentry(vp, external_identity, vector, s_sig);
                                VECTORS->count += 1;
                            }
                        }
                        if (fnd == 0) {
                            makevectentry(*(VECTORS->index + VECTORS->count), external_identity, vector, s_sig);
                            VECTORS->count += 1;
                        }
                        if (i >= VECTORS->slots) {
                            syslog (LOG_INFO, "ERROR bad slot count  for passive vectors index record");
                            perror_reply(421, "Local resource failure: malloc"); 
                            dologout(1); 
                        }
                    }
                }
            }
        }
    }
}



static void 
#ifdef __STDC__
initportvectors(void)
#else
initportvectors()
#endif
{
    struct aclmember *entry = NULL;
    int num;
    int pcnt;
    int acnt;
    int i;
    entry = (struct aclmember *) NULL; 
    pcnt=0;
    acnt=0;

    while (getaclentry("passive", &entry)) {
        if (!strcasecmp (ARG0, "ports")) {
            if (!ARG0 || !ARG1 || !ARG2)
                continue;
            pcnt++; 
        }
        if (!strcasecmp (ARG0, "address")) {
            if (!ARG0 || !ARG1 || !ARG2)
                continue;
            acnt++;
        }
    }
    PORTS   = (pcnt > 0) ? initportstruct(pcnt) : NULL;
    VECTORS = (acnt > 0) ? initvectstruct(acnt) : NULL;
    while (getaclentry("passive", &entry)) {
        if (!strcasecmp (ARG0, "ports"))
            addportentry(ARG1, ARG2, ARG3);
        else if (!strcasecmp (ARG0, "address"))
            addvectentry( ARG1, ARG2 ); 
    }
}



/* compare two internet masks so that the significance order
 * is from the widest mask down (wide masks match before narrow ones)
 */
static int 
#ifdef __STDC__
addr_cmp(u_long s1, u_short s1_sig, u_long s2, u_short s2_sig)
#else
addr_cmp(s1, s1_sig, s2, s2_sig)
u_long s1;
u_short s1_sig; 
u_long s2;
u_short s2_sig;
#endif
{
    if (s1_sig > s2_sig) return (-1);
    if (s1_sig < s2_sig) return (1);
    if (s1 < s2) return (-1);
    if (s1 > s2) return (1);
    return (0);
}



/* Compare the first x bits of two internet addresses 
 * 32  means match all ( return is zero on exact match only )
 * 24  means match on first 3 bytes 
 * 16  means match on first 2 bytes 
 *  8  means match on first 1 bytes 
 *  0  means assume match ( match 0 bytes )
 */

#define BITS_PER_BYTE 8

static int  
#ifdef __STDC__
addr_smatch(u_long s1, u_long s2, u_short shift_in)
#else
addr_smatch(s1, s2, shift_in)
u_long s1;
u_long s2;
u_short shift_in;
#endif
{
    u_long sh1, sh2;
    int shift;

    if (shift_in > 0) {
        shift = sizeof (u_long) * BITS_PER_BYTE;
        if (shift >= (int)shift_in)
            shift -= (int)shift_in;
        if (shift > 0) {
            sh2 = (s2 >> shift) << shift;
            sh1 = (s1 >> shift) << shift; 
        } else {
            sh2 = s2;
            sh1 = s1;
        }
        if (sh1 < sh2)
            return (-1);
        if (sh1 > sh2)
	    return (1);
    }
    return (0);
}



static v_paddr *
#ifdef __STDC__
find_passive_port_entry(u_long addr)
#else
find_passive_port_entry(addr)
u_long addr;
#endif
{
    register int i;
    v_paddr *p;

    if (PORTS != NULL)
        for (i = 0; i < PORTS->count; i++) {
            p = *(PORTS->index + i);
            if (addr_smatch (addr, p->s_address, p->s_sig) == 0)
                return (p);
        }
    return (NULL);
}



static v_addr *
#ifdef __STDC__
find_passive_vect_entry(u_long addr)
#else
find_passive_vect_entry(addr)
u_long addr;
#endif
{
    register int i;
    v_addr *p;

    if (VECTORS != NULL)
        for (i = 0; i < VECTORS->count; i++) {
            p = *(VECTORS->index + i);
            if (addr_smatch (addr, p->s_vector, p->s_sig) == 0)
                return (p);
        }
    return (NULL);
}
