/* * ------------------------------------------------------------------------------ * Hermes FTP Server * Copyright (c) 2005-2014 Lars Behnke * ------------------------------------------------------------------------------ * * This file is part of Hermes FTP Server. * * Hermes FTP Server 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. * * Hermes FTP Server 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 Hermes FTP Server; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ------------------------------------------------------------------------------ */ package com.apporiented.hermesftp.cmd.impl; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.apporiented.hermesftp.cmd.AbstractFtpCmdPort; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <b>The EPRT Command (EPRT)</b> * <p> * The EPRT command allows for the specification of an extended address for the * data connection. The extended address MUST consist of the network protocol as * well as the network and transport addresses. The format of EPRT is: * </p> * * <pre> * EPRT<space><d><de-prt><d><net-addr><d><tcp-port><d> * </pre> * * <p> * The EPRT command keyword MUST be followed by a single space (ASCII 32). * Following the space, a delimiter character (<d>) MUST be specified. The * delimiter character MUST be one of the ASCII characters in range 33-126 * inclusive. The character "|" (ASCII 124) is recommended unless it coincides * with a character needed to encode the network address. * </p> * <p> * The <net-prt> argument MUST be an address family number defined by IANA * in the latest Assigned Numbers RFC (RFC 1700 [RP94] as of the writing of this * document). This number indicates the protocol to be used (and, implicitly, * the address length). This document will use two of address family numbers * from [RP94] as examples, according to the following table: * </p> * * <pre> * AF Number Protocol * --------- -------- * 1 Internet Protocol, Version 4 [Pos81a] * 2 Internet Protocol, Version 6 [DH96] * </pre> * * <p> * The <net-addr> is a protocol specific string representation of the * network address. For the two address families specified above (AF Number 1 * and 2), addresses MUST be in the following format: * </p> * * <pre> * AF Number Address Format Example * --------- -------------- ------- * 1 dotted decimal 132.235.1.2 * 2 IPv6 string 1080::8:800:200C:417A * representations * defined in [HD96] * </pre> * * <p> * The <tcp-port> argument must be the string representation of the number * of the TCP port on which the host is listening for the data connection. The * following are sample EPRT commands: * * <pre> * EPRT |1|132.235.1.2|6275| * * EPRT |2|1080::8:800:200C:417A|5282| * </pre> * * <p> * The first command specifies that the server should use IPv4 to open a data * connection to the host "132.235.1.2" on TCP port 6275. The second command * specifies that the server should use the IPv6 network protocol and the * network address "1080::8:800:200C:417A" to open a TCP data connection on port * 5282. * </p> * <p> * Upon receipt of a valid EPRT command, the server MUST return a code of 200 * (Command OK). The standard negative error code 500 and 501 [PR85] are * sufficient to handle most errors (e.g., syntax errors) involving the EPRT * command. However, an additional error code is needed. The response code 522 * indicates that the server does not support the requested network protocol. * The interpretation of this new error code is: * </p> * * <pre> * 5yz Negative Completion * x2z Connections * xy2 Extended Port Failure - unknown network protocol * </pre> * * <p> * The text portion of the response MUST indicate which network protocols the * server does support. If the network protocol is unsupported, the format of * the response string MUST be: * </p> * * <pre> * <text stating that the network protocol is unsupported> \ * (prot1,prot2,...,protn) * </pre> * * <p> * Both the numeric code specified above and the protocol information between * the characters '(' and ')' are intended for the software automata receiving * the response; the textual message between the numeric code and the '(' is * intended for the human user and can be any arbitrary text, but MUST NOT * include the characters '(' and ')'. In the above case, the text SHOULD * indicate that the network protocol in the EPRT command is not supported by * the server. The list of protocols inside the parenthesis MUST be a comma * separated list of address family numbers. Two example response strings * follow: Network protocol not supported, use (1) Network protocol not * supported, use (1,2) * </p> * <p> * <i>[Excerpt from RFC-2428, Allman]</i> * </p> * * @author Lars Behnke */ public class FtpCmdEprt extends AbstractFtpCmdPort { private static final String EPRT_ARG_PATTERN = "^(.)([12])\\1([A-Fa-f0-9\\.\\:]+)\\1([0-9]*)\\1$"; private static Log log = LogFactory .getLog(FtpCmdEprt.class); private static Pattern pattern; private int port; private String addr; private int protocolIdx; private String lastArgs; static { pattern = Pattern.compile(EPRT_ARG_PATTERN); } /** * {@inheritDoc} */ public String getHelp() { return "Sets port for active transfer."; } /** * {@inheritDoc} */ protected String doReadIPAddr(String args) { if (!paramsParsed(args)) { parseParams(args); } return addr; } /** * {@inheritDoc} */ protected int doReadPort(String args) { if (!paramsParsed(args)) { parseParams(args); } return port; } /** * {@inheritDoc} */ protected int doReadProtocolIdx(String args) { if (!paramsParsed(args)) { parseParams(args); } return protocolIdx; } @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean paramsParsed(String args) { return lastArgs != null && !lastArgs.equals(args); } private void parseParams(String args) { lastArgs = args; Matcher matcher = pattern.matcher(args); if (matcher.matches()) { /* CHECKSTYLE:OFF */ int protocolIdx = Integer.parseInt(matcher.group(2)); String ipAddr = matcher.group(3); int port = Integer.parseInt(matcher.group(4)); log.debug("Protocol: " + protocolIdx + ", IPAddr: " + ipAddr + ", Port: " + port); this.addr = ipAddr; this.port = port; this.protocolIdx = protocolIdx; /* CHECKSTYLE:ON */ } else { throw new IllegalArgumentException("Invalid arguments: " + args); } } }