Logo Search packages:      
Sourcecode: jftpgw version File versions  Download package

passive.c

/* 
 * 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;
}


Generated by  Doxygen 1.6.0   Back to index