1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package net.sf.hermesftp.cmd;
27
28 import java.io.IOException;
29 import java.net.Inet4Address;
30 import java.net.Inet6Address;
31 import java.net.InetAddress;
32 import java.net.ServerSocket;
33 import java.net.Socket;
34
35 import javax.net.ServerSocketFactory;
36 import javax.net.ssl.SSLServerSocket;
37 import javax.net.ssl.SSLServerSocketFactory;
38
39 import net.sf.hermesftp.common.FtpConstants;
40 import net.sf.hermesftp.common.FtpSessionContext;
41 import net.sf.hermesftp.utils.IOUtils;
42
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46 /***
47 * Provides the data transfer socket for transfer passive mode.
48 *
49 * @author Behnke
50 */
51 public class PassiveModeSocketProvider implements SocketProvider {
52
53 private static final int MAX_BIND_RETRIES = 3;
54
55 private static final int DATA_CHANNEL_TIMEOUT = 10000;
56
57 private static Log log = LogFactory.getLog(PassiveModeSocketProvider.class);
58
59 private FtpSessionContext ctx;
60
61 private ServerSocket serverSocket;
62
63 private Socket dataSocket;
64
65 private int preferredProtocol;
66
67 /***
68 * Constructor.
69 *
70 * @param ctx Session context.
71 * @param preferredProtocol Preferred protocol (IPv4 or IPv6)
72 */
73 public PassiveModeSocketProvider(FtpSessionContext ctx, int preferredProtocol) {
74 this.ctx = ctx;
75 this.preferredProtocol = preferredProtocol;
76 }
77
78 /***
79 * {@inheritDoc}
80 */
81 public DataChannelInfo init() throws IOException {
82
83
84 InetAddress localIp = ctx.getClientSocket().getLocalAddress();
85 int currentProtocol = getProtocolIdxByAddr(localIp);
86 boolean ok = (preferredProtocol == currentProtocol) || (preferredProtocol == 0);
87 if (!ok) {
88 throw new IOException("Invalid IP version");
89 }
90
91
92 int retries = MAX_BIND_RETRIES;
93 while (retries > 0) {
94 Integer port = ctx.getNextPassivePort();
95 port = port == null ? new Integer(0) : port;
96 try {
97 log.debug("Trying to bind server socket to port " + port);
98 serverSocket = createServerSocket(localIp, port.intValue());
99 break;
100 } catch (Exception e) {
101 retries--;
102 log.debug("Binding server socket to port " + port + " failed.");
103 }
104 }
105 if (serverSocket == null) {
106 throw new IOException("Initializing server socket failed.");
107 }
108
109
110 log.debug("Server socket successfully bound to port " + serverSocket.getLocalPort() + ".");
111 return new DataChannelInfo(localIp.getHostAddress(), serverSocket.getLocalPort());
112
113 }
114
115 /***
116 * {@inheritDoc}
117 */
118 public void closeSocket() {
119 IOUtils.closeGracefully(serverSocket);
120 IOUtils.closeGracefully(dataSocket);
121 serverSocket = null;
122 dataSocket = null;
123
124 }
125
126 /***
127 * {@inheritDoc}
128 */
129 public Socket provideSocket() throws IOException {
130 if (dataSocket == null) {
131 if (serverSocket == null) {
132 throw new IOException("Server socket not initialized.");
133 }
134 dataSocket = serverSocket.accept();
135 }
136 return dataSocket;
137 }
138
139 private int getProtocolIdxByAddr(InetAddress addr) {
140 if (addr instanceof Inet4Address) {
141 return 1;
142 } else if (addr instanceof Inet6Address) {
143 return 2;
144 } else {
145 return 0;
146 }
147 }
148
149 /***
150 * Creates the server socket that accepts the data connection.
151 *
152 * @param localIp The local IP address.
153 * @param port The port.
154 * @return The server socket.
155 * @throws IOException Error on creating server socket.
156 */
157 private ServerSocket createServerSocket(InetAddress localIp, int port) throws IOException {
158 ServerSocket sock;
159 Boolean dataProtection = (Boolean) ctx.getAttribute(FtpConstants.ATTR_DATA_PROT);
160 boolean ssl = dataProtection != null && dataProtection.booleanValue();
161 if (ssl) {
162 SSLServerSocketFactory factory = ctx.getOptions().getSslContext().getServerSocketFactory();
163 SSLServerSocket sslServerSocket = (SSLServerSocket) factory.createServerSocket(port, 1, localIp);
164 sslServerSocket.setUseClientMode(false);
165 enableCipherSuites(sslServerSocket);
166 sock = sslServerSocket;
167 } else {
168 sock = ServerSocketFactory.getDefault().createServerSocket(port, 1, localIp);
169 }
170 sock.setSoTimeout(DATA_CHANNEL_TIMEOUT);
171 return sock;
172 }
173
174 /***
175 * Enables the configured cipher suites in the passed server socket.
176 *
177 * @param sslServerSocket The server socket.
178 */
179 private void enableCipherSuites(SSLServerSocket sslServerSocket) {
180 String[] cipherSuites = ctx.getOptions().getStringArray(FtpConstants.OPT_SSL_CIPHER_SUITES, null);
181 if (cipherSuites != null) {
182 if (cipherSuites.length == 1 && FtpConstants.WILDCARD.equals(cipherSuites[0])) {
183 sslServerSocket.setEnabledCipherSuites(sslServerSocket.getSupportedCipherSuites());
184 } else {
185 sslServerSocket.setEnabledCipherSuites(cipherSuites);
186 }
187 }
188 }
189 }