/* 
 * Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 */

#include "jftpgw.h"
#include <ctype.h>

extern int timeout;
static struct portrangestruct *pasvrangeclient = (struct portrangestruct*) 0;
static struct portrangestruct *pasvrangeserver = (struct portrangestruct*) 0;
static void saypasv(int, char*, unsigned short int);


/* pasvserver() handles nearly the whole passive connection, i.e., it
 * requests a port on the server, binds to one on the proxy host and sends
 * the address & port to the client
 * 
 * Parameters: clntinfo: connection information
 *
 * Return value: -1 on error,
 *                0 on success
 *
 * Called by: handlecmds (if the client issued a PORT or PASV command)
 *
 * */

int pasvserver(struct clientinfo* clntinfo) {
	int ss, cs, ret, pssock;
	char* brk;
	char* buffer;
	struct sockaddr_in pasvserv_sin;
	struct portrangestruct* prs;

	ss = clntinfo->serversocket;
	cs = clntinfo->clientsocket;

	say(ss, "PASV\r\n");
	buffer = ftp_readline(ss);
	if (!buffer) {
		if (timeout) {
			jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
			err_time_readline(cs);
		} else {
			err_readline(cs);
			jlog(2, "Error reading response to PASV from server: %s",
				strerror(errno));
		}
		return -1;
	}

	/* 
	 * An example of the answer would be:
	 * "227 Entering Passive Mode (127,0,0,1,7,123)"
	 *
	 * Roxen Challenger at ftp.mutt.org sends this reply
	 * "227 Entering Passive Mode. 217,69,76,44,156,68"
	 */
	/* Look for the beginning of the IP */
	brk = (char*) strchr(buffer, '(');

	if (!checkdigits(buffer, 227)) {
		/* invalid return string */
		say(cs, buffer);
		jlog(2, "Strange answer after sending PASV: %s", buffer);
		free(buffer);
		return -1;
	}
	if (!brk) {
		/* don`t give up, maybe it just didn`t send a bracket */
		/* skip over the numerical code */
		brk = strchr(buffer, ' ');
		brk++;
		/* increase until we found a numeric */
		while (*brk && !isdigit((int)*brk)) {
			brk++;
		}
		/* now brk should point to the first numeric */
	} else {
		/* skip over the bracket sign */
		brk++;
	}

	/* call parsesock to parse the answer string and get the values into
	 * PASVSERVERSOCK */
	ret = parsesock(brk, &pasvserv_sin, PASSIVE);
	if (ret) {
		say(cs, "500 Could not parse a valid address from the PASV response\r\n");
		jlog(2, "Could not parse a valid socket from the PASV "
		    " response: %s", buffer);
		free(buffer);
		return -1;
	}

	if ( ! pasvrangeserver ) {
		const char* range =config_get_option("passiveportrangeserver");
		if ( ! range ) {
			range = config_get_option("passiveportrange");
		}
		pasvrangeserver = config_parse_portranges(range);
	}
	if ( ! pasvrangeserver ) {
		prs = config_port2portrange(clntinfo->dataport);
	} else {
		prs = pasvrangeserver;
	}

	jlog(7, "Opening passive port %s:%d",
					inet_ntoa(pasvserv_sin.sin_addr),
					ntohs(pasvserv_sin.sin_port));
	/* open the port on the foreign machine specified by PASVSERVERSOCK */
	pssock = openportiaddr(pasvserv_sin.sin_addr.s_addr, /* dest ip   */
			ntohs(pasvserv_sin.sin_port),        /* dest port */
			clntinfo->data_addr_to_server,       /* source ip */
			prs);                             /* source ports */
	if (pssock < 0) {
		say(cs, "500 Could not connect to the specified PASV host\r\n");
		jlog(3, "Could not connect to the specified PASV host: (%s)", buffer);
		free(buffer);
		return -1;
	}
	clntinfo->dataserversock = pssock;

	free(buffer);
	return 0;
}


/* pasvclient() opens a port on the proxy machine and tells the addr and
 * port to the client
 *
 * Parameters: clntinfo: connection information
 *
 * Return values: -1 on error
 *                 0 on success
 *
 * Called by: handlecmds (if the client issued a PORT or PASV command)
 */


int pasvclient(struct clientinfo* clntinfo) {
	int pcsock;
	int cs = clntinfo->clientsocket;
	struct sockaddr_in pasvclientsin;
	struct in_addr in;

	clntinfo->clientmode = PASSIVE;

	if ( ! pasvrangeclient ) {
		const char* range =config_get_option("passiveportrangeclient");
		if (! range) {
			range = config_get_option("passiveportrange");
		}
		pasvrangeclient = config_parse_portranges(range);
	}
	if (clntinfo->dataclientsock >= 0) {
		/* we are still listening on another socket. Close it */
		close(clntinfo->dataclientsock);
		clntinfo->dataclientsock = -1;
	}
	errno = 0;
	/* open a port on the interface the client is connected to and store
	 * the values in PASVCLIENTSIN */
	pcsock = openlocalport(&pasvclientsin,
			       clntinfo->data_addr_to_client,
			       pasvrangeclient);
	if (pcsock < 0) {
		/*say(cs, "500 Error binding to a port\r\n");*/
		jlog(2, "Could not bind (%s)", strerror(errno));
		return -1;
	}
	clntinfo->dataclientsock = pcsock;
	clntinfo->waitforconnect = &clntinfo->dataclientsock;

	/* write the values to the client socket CS */
	in.s_addr = pasvclientsin.sin_addr.s_addr;
	saypasv(cs, inet_ntoa(in), htons(pasvclientsin.sin_port));

	return 0;
}


/* saypasv() writes the "227 Entering Passive Mode ..." to the client
 * descriptor CS
 * 
 * Parameters: cs: client descriptor
 *             addr: Our address of the interface the client connected
 *                   through
 *             port: The corresponding port on our side
 *
 * Return value: void
 *
 * Called by: pasvclient()
 *
 * */ 

static void saypasv(int cs, char* addr, unsigned short int port) {
	unsigned short int lo, hi;
	char *dot;

	hi = port % 256;
	lo = (port - hi) / 256;

	while ((dot = strchr(addr, '.'))) {
		*dot = ',';
	}

	sayf(cs, "227 Entering Passive Mode (%s,%d,%d)\r\n", addr, lo, hi);
}

void destroy_passive_portrange() {
	config_destroy_portrange(pasvrangeserver);
	config_destroy_portrange(pasvrangeclient);
	pasvrangeclient = (struct portrangestruct*) 0;
	pasvrangeserver = (struct portrangestruct*) 0;
}



syntax highlighted by Code2HTML, v. 0.9.1