/* * Copyright (C) 1999-2004 Joachim Wieland * * 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; }