/*
* 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"
extern int timeout;
static struct portrangestruct *actvrangeserver = (struct portrangestruct*) 0;
static struct portrangestruct *actvrangeclient = (struct portrangestruct*) 0;
static void sayact(int, char*, int);
int portcommandcheck(const char* buffer, struct sockaddr_in* sin,
struct clientinfo* clntinfo) {
int cs = clntinfo->clientsocket;
char* beginning = 0;
int ret;
clntinfo->clientmode = ACTIVE;
beginning = strchr(buffer, ' ');
if (!beginning) {
/* It should not fail, since we are testing for "PORT " in
* cmds.c */
jlog(3, "Invalid PORT command (no space): %s", buffer);
say(cs, "500 Invalid PORT command (no space char)\r\n");
return -1;
}
beginning++;
ret = parsesock(beginning, sin, ACTIVE);
if (ret) {
jlog(3, "Error parsing the PORT command: %s (%s)",
buffer, beginning);
say(cs, "501 Error parsing the PORT command\r\n");
return -1;
}
if (config_get_bool("allowforeignaddress") == 0) {
char* sin_addr = strdup(inet_ntoa(sin->sin_addr));
enough_mem (sin_addr);
if (0 != strcmp(sin_addr,
get_char_ip(GET_IP_CLIENT, clntinfo))) {
jlog(3, "Illegal foreign address %s in PORT command.",
inet_ntoa(sin->sin_addr));
jlog(5, "Expected address %s instead",
get_char_ip(GET_IP_CLIENT, clntinfo));
say(cs, "500 Illegal address in PORT command\r\n");
free(sin_addr);
return -1;
}
free(sin_addr);
}
if (config_get_bool("allowreservedports") == 0) {
if (ntohs(sin->sin_port) < IPPORT_RESERVED) {
jlog(3, "Illegal port %d in PORT command.",
ntohs(sin->sin_port));
say(cs, "500 Illegal port in PORT command\r\n");
return -1;
}
}
/* seems to be okay */
return 0;
}
/*
* activeclient() reads the "PORT x,x,x,x,x,x" command from the client,
* opens that port on the client's side and returns the socket descriptor
*
* Parameters: buffer: contains the "PORT x,x,x,x,x,x" command
* clntinfo: the connection variables
*
* Returns: -1 on error, 0 on success
*
* Called by: passcmd (if cmd starts with PORT)
*
* */
int activeclient(char* buffer, struct clientinfo* clntinfo) {
struct sockaddr_in sin;
struct portrangestruct* prs;
int ret;
ret = portcommandcheck(buffer, &sin, clntinfo);
if (ret < 0) {
return ret;
}
jlog(8, "Opening the active FTP port %d on %s",
ntohs(sin.sin_port), inet_ntoa(sin.sin_addr));
if (clntinfo->waitforconnect == &clntinfo->dataclientsock) {
clntinfo->waitforconnect = (int*) 0;
}
if ( ! actvrangeclient ) {
const char* range = config_get_option("activeportrangeclient");
if ( ! range ) {
range = config_get_option("activeportrange");
}
actvrangeclient = config_parse_portranges(range);
}
if ( ! actvrangeclient ) {
prs = config_port2portrange(clntinfo->dataport);
} else {
prs = actvrangeclient;
}
ret = openportiaddr(sin.sin_addr.s_addr, /* dest ip */
ntohs(sin.sin_port), /* dest port */
clntinfo->data_addr_to_client,/* source ip, port */
prs);
if (ret < 0) {
jlog(8, "setting dataclientsock to -1 (openport error)");
clntinfo->dataclientsock = -1;
return -1;
}
clntinfo->dataclientsock = ret;
return 0;
}
/*
* activeserver() opens a port on the proxy machine, determins the port
* number and tells it to the server (along with the address of the interface
* over which we connect to it).
*
* Parameters: answer: A pointer to a pointer used to store the answer of the
* server
*
* Returns: -1 on error, 0 on success
*
* Called by: handlecmds (when client issued PORT or PASV)
*
* */
int activeserver(char** answer, struct clientinfo *clntinfo) {
int ret;
struct sockaddr_in sin;
int ss = clntinfo->serversocket;
char* buffer;
if ( ! actvrangeserver ) {
const char* range = config_get_option("activeportrangeserver");
if ( ! range ) {
range = config_get_option("activeportrange");
}
actvrangeserver = config_parse_portranges(range);
}
/* open a port on our side use the interface address through which
* we are connected to the server */
ret = openlocalport(&sin, /* for the return value */
clntinfo->data_addr_to_server, /* source ip */
actvrangeserver); /* source ports */
if (clntinfo->waitforconnect == &clntinfo->dataserversock) {
clntinfo->waitforconnect = (int*) 0;
}
if (ret < 0) {
say(clntinfo->clientsocket,
"425 Could not establish a connection endpoint\r\n");
*answer = 0;
return -1;
}
clntinfo->dataserversock = ret;
clntinfo->waitforconnect = &clntinfo->dataserversock;
/* tell the server about the addr + port it can connect to */
sin.sin_addr.s_addr = clntinfo->data_addr_to_server;
sayact(ss, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
buffer = ftp_readline(ss);
*answer = buffer;
if (!buffer) {
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
jlog(2, "Timed out reading answer for the PORT command");
say(clntinfo->clientsocket,
"500 Timeout waiting for answer for the PORT command\r\n");
}
else {
jlog(1, "Error in readline reading answer for the PORT command");
say(clntinfo->clientsocket,
"Error reading answer for PORT command\r\n");
}
return -1;
}
/* code 200 would be "PORT command successful" */
if (!checkdigits(buffer, 200)) {
jlog(1, "Failure sending PORT command (%s) to the server: %s",
inet_ntoa(sin.sin_addr), buffer);
say(clntinfo->clientsocket,
"425 Can't establish connection\r\n");
return -1;
}
return 0;
}
/* sayact() transforms the parameters ADDR and PORT to the
* "PORT x,x,x,x,x,x" notation and says it to the descriptor fd
*
* Paramters: fd: the file descriptor the command is written to
* addr: the address that should be included in dot notation
* format
* port: the port that was opened on addr's side
*/
static void sayact(int fd, char* addr, int port) {
unsigned short int lo, hi;
char buffer[MAX_LINE_SIZE], *dot;
hi = port % 256;
lo = (port - hi) / 256;
while ((dot = strchr(addr, '.'))) {
*dot = ',';
}
snprintf(buffer, sizeof(buffer), "PORT %s,%d,%d\r\n",
addr, lo, hi);
say(fd, buffer);
}
void destroy_active_portrange() {
config_destroy_portrange(actvrangeserver);
config_destroy_portrange(actvrangeclient);
actvrangeserver = (struct portrangestruct*) 0;
actvrangeclient = (struct portrangestruct*) 0;
}
syntax highlighted by Code2HTML, v. 0.9.1