View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.ftp.core.utils;
21  
22  import io.netty.channel.Channel;
23  import org.waarp.common.crypto.ssl.WaarpSslUtility;
24  import org.waarp.common.logging.SysErrLogger;
25  import org.waarp.common.logging.WaarpLogger;
26  import org.waarp.common.logging.WaarpLoggerFactory;
27  import org.waarp.common.utility.WaarpShutdownHook;
28  import org.waarp.ftp.core.config.FtpConfiguration;
29  
30  import java.net.InetAddress;
31  import java.net.InetSocketAddress;
32  import java.net.UnknownHostException;
33  import java.util.Timer;
34  
35  /**
36   * Some useful functions related to Channel of Netty
37   */
38  public class FtpChannelUtils implements Runnable {
39    /**
40     * Internal Logger
41     */
42    private static final WaarpLogger logger =
43        WaarpLoggerFactory.getLogger(FtpChannelUtils.class);
44  
45    /**
46     * Get the Remote InetAddress
47     *
48     * @param channel
49     *
50     * @return the remote InetAddress
51     */
52    public static InetAddress getRemoteInetAddress(final Channel channel) {
53      InetSocketAddress socketAddress =
54          (InetSocketAddress) channel.remoteAddress();
55      if (socketAddress == null) {
56        socketAddress = new InetSocketAddress(20);
57      }
58      return socketAddress.getAddress();
59    }
60  
61    /**
62     * Get the Local InetAddress
63     *
64     * @param channel
65     *
66     * @return the local InetAddress
67     */
68    public static InetAddress getLocalInetAddress(final Channel channel) {
69      final InetSocketAddress socketAddress =
70          (InetSocketAddress) channel.localAddress();
71      return socketAddress.getAddress();
72    }
73  
74    /**
75     * Get the Remote InetSocketAddress
76     *
77     * @param channel
78     *
79     * @return the remote InetSocketAddress
80     */
81    public static InetSocketAddress getRemoteInetSocketAddress(
82        final Channel channel) {
83      return (InetSocketAddress) channel.remoteAddress();
84    }
85  
86    /**
87     * Get the Local InetSocketAddress
88     *
89     * @param channel
90     *
91     * @return the local InetSocketAddress
92     */
93    public static InetSocketAddress getLocalInetSocketAddress(
94        final Channel channel) {
95      return (InetSocketAddress) channel.localAddress();
96    }
97  
98    /**
99     * Get the InetSocketAddress corresponding to the FTP format of address
100    *
101    * @param arg
102    *
103    * @return the InetSocketAddress or null if an error occurs
104    */
105   public static InetSocketAddress getInetSocketAddress(final String arg) {
106     final String[] elements = arg.split(",");
107     if (elements.length != 6) {
108       return null;
109     }
110     final byte[] address = { 0, 0, 0, 0 };
111     final int[] iElements = new int[6];
112     for (int i = 0; i < 6; i++) {
113       try {
114         iElements[i] = Integer.parseInt(elements[i]);
115       } catch (final NumberFormatException e) {
116         return null;
117       }
118       if (iElements[i] < 0 || iElements[i] > 255) {
119         return null;
120       }
121     }
122     for (int i = 0; i < 4; i++) {
123       address[i] = (byte) iElements[i];
124     }
125     final int port = iElements[4] << 8 | iElements[5];
126     final InetAddress inetAddress;
127     try {
128       inetAddress = InetAddress.getByAddress(address);
129     } catch (final UnknownHostException e) {
130       return null;
131     }
132     return new InetSocketAddress(inetAddress, port);
133   }
134 
135   /**
136    * Return the Address in the format compatible with FTP argument
137    *
138    * @param address
139    * @param port
140    *
141    * @return the String representation of the address
142    */
143   public static String getAddress(final String address, final int port) {
144     return address.replace('.', ',') + ',' + (port >> 8) + ',' + (port & 0xFF);
145   }
146 
147   /**
148    * Return the Address in the format compatible with FTP argument
149    *
150    * @param address
151    *
152    * @return the String representation of the address
153    */
154   public static String getAddress(final InetSocketAddress address) {
155     final InetAddress servAddr = address.getAddress();
156     final int servPort = address.getPort();
157     return servAddr.getHostAddress().replace('.', ',') + ',' + (servPort >> 8) +
158            ',' + (servPort & 0xFF);
159   }
160 
161   /**
162    * Get the (RFC2428) InetSocketAddress corresponding to the FTP format of
163    * address (RFC2428)
164    *
165    * @param arg
166    *
167    * @return the InetSocketAddress or null if an error occurs
168    */
169   public static InetSocketAddress get2428InetSocketAddress(final String arg) {
170     // Format: #a#net-addr#tcp-port# where a = 1 IPV4 or 2 IPV6, other will
171     // not be supported
172     if (arg == null || arg.length() == 0) {
173       // bad args
174       return null;
175     }
176     final String delim = arg.substring(0, 1);
177     final String[] infos = arg.split('\\' + delim);
178     if (infos.length != 3 && infos.length != 4) {
179       // bad format
180       logger.error("Bad address format: " + infos.length);
181       return null;
182     }
183     int start = 0;
184     if (infos.length == 4) {
185       start = 1;
186     }
187     boolean isIPV4 = true;
188     if ("1".equals(infos[start])) {
189       isIPV4 = true;
190     } else if ("2".equals(infos[start])) {
191       isIPV4 = false;
192     } else {
193       // not supported
194       logger.error("Bad 1 or 2 format in address: " + infos[start]);
195       return null;
196     }
197     start++;
198     final InetAddress inetAddress;
199     if (isIPV4) {
200       // IPV4
201       try {
202         inetAddress = InetAddress.getByName(infos[start]);
203       } catch (final UnknownHostException e) {
204         logger.error("Bad IPV4 format: {}", e.getMessage());
205         return null;
206       }
207     } else {
208       // IPV6
209       try {
210         inetAddress = InetAddress.getByName(infos[start]);
211       } catch (final UnknownHostException e) {
212         logger.error("Bad IPV6 format: {}", e.getMessage());
213         return null;
214       }
215     }
216     start++;
217     final int port;
218     try {
219       port = Integer.parseInt(infos[start]);
220     } catch (final NumberFormatException e) {
221       logger.error("Bad port number format: " + infos[start]);
222       return null;
223     }
224     return new InetSocketAddress(inetAddress, port);
225   }
226 
227   /**
228    * Return the (RFC2428) Address in the format compatible with FTP (RFC2428)
229    *
230    * @param address
231    *
232    * @return the String representation of the address
233    */
234   public static String get2428Address(final InetSocketAddress address) {
235     final InetAddress servAddr = address.getAddress();
236     final int servPort = address.getPort();
237     final StringBuilder builder = new StringBuilder();
238     final String hostaddress = servAddr.getHostAddress();
239     builder.append('|');
240     if (hostaddress.contains(":")) {
241       builder.append('2'); // IPV6
242     } else {
243       builder.append('1'); // IPV4
244     }
245     builder.append('|').append(hostaddress).append('|').append(servPort)
246            .append('|');
247     return builder.toString();
248   }
249 
250   /**
251    * Terminate all registered command channels
252    *
253    * @param configuration
254    *
255    * @return the number of previously registered command channels
256    */
257   static int terminateCommandChannels(final FtpConfiguration configuration) {
258     final int result =
259         configuration.getFtpInternalConfiguration().getCommandChannelGroup()
260                      .size();
261     configuration.getFtpInternalConfiguration().getCommandChannelGroup()
262                  .close();
263     return result;
264   }
265 
266   /**
267    * Terminate all registered data channels
268    *
269    * @param configuration
270    *
271    * @return the number of previously registered data channels
272    */
273   private static int terminateDataChannels(
274       final FtpConfiguration configuration) {
275     final int result =
276         configuration.getFtpInternalConfiguration().getDataChannelGroup()
277                      .size();
278     configuration.getFtpInternalConfiguration().getDataChannelGroup().close();
279     return result;
280   }
281 
282   /**
283    * Return the current number of command connections
284    *
285    * @param configuration
286    *
287    * @return the current number of command connections
288    */
289   public static int nbCommandChannels(final FtpConfiguration configuration) {
290     return configuration.getFtpInternalConfiguration().getCommandChannelGroup()
291                         .size();
292   }
293 
294   /**
295    * Return the current number of data connections
296    *
297    * @param configuration
298    *
299    * @return the current number of data connections
300    */
301   public static int nbDataChannels(final FtpConfiguration configuration) {
302     return configuration.getFtpInternalConfiguration().getDataChannelGroup()
303                         .size();
304   }
305 
306   /**
307    * Return the number of still positive command connections
308    *
309    * @param configuration
310    *
311    * @return the number of positive command connections
312    */
313   public static int validCommandChannels(final FtpConfiguration configuration) {
314     int result = 0;
315     Channel channel;
316     for (final Channel value : configuration.getFtpInternalConfiguration()
317                                             .getCommandChannelGroup()) {
318       channel = value;
319       if (channel.parent() != null) {
320         // Child Channel
321         if (channel.isActive()) {
322           // Normal channel
323           result++;
324         } else {
325           WaarpSslUtility.closingSslChannel(channel);
326         }
327       } else {
328         // Parent channel
329         result++;
330       }
331     }
332     return result;
333   }
334 
335   /**
336    * Exit global ChannelFactory
337    *
338    * @param configuration
339    */
340   protected static void exit(final FtpConfiguration configuration) {
341     configuration.setShutdown(true);
342     final long delay = configuration.getTimeoutCon() / 2;
343     logger.warn("Exit: Give a delay of " + delay + " ms");
344     configuration.inShutdownProcess();
345     try {
346       Thread.sleep(delay);
347     } catch (final InterruptedException e) {//NOSONAR
348       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
349     }
350     final Timer timer = new Timer(true);
351     final FtpTimerTask timerTask = new FtpTimerTask(FtpTimerTask.TIMER_CONTROL);
352     timerTask.setConfiguration(configuration);
353     timer.schedule(timerTask, delay / 2);
354     configuration.getFtpInternalConfiguration().getGlobalTrafficShapingHandler()
355                  .release();
356     configuration.releaseResources();
357     logger.info("Exit Shutdown Data");
358     terminateDataChannels(configuration);
359     logger.warn("Exit end of Data Shutdown");
360   }
361 
362   /**
363    * This function is the top function to be called when the server is to be
364    * shutdown.
365    *
366    * @param configuration
367    */
368   public static void teminateServer(final FtpConfiguration configuration) {
369     FtpShutdownHook.configuration = configuration;
370     WaarpShutdownHook.terminate(false);
371   }
372 
373   /**
374    * Add a command channel into the list
375    *
376    * @param channel
377    * @param configuration
378    */
379   public static void addCommandChannel(final Channel channel,
380                                        final FtpConfiguration configuration) {
381     configuration.getFtpInternalConfiguration().getCommandChannelGroup()
382                  .add(channel);
383   }
384 
385   /**
386    * Add a data channel into the list
387    *
388    * @param channel
389    * @param configuration
390    */
391   public static void addDataChannel(final Channel channel,
392                                     final FtpConfiguration configuration) {
393     configuration.getFtpInternalConfiguration().getDataChannelGroup()
394                  .add(channel);
395   }
396 
397   /**
398    * Used to run Exit command
399    */
400   private final FtpConfiguration configuration;
401 
402   public FtpChannelUtils(final FtpConfiguration configuration) {
403     this.configuration = configuration;
404   }
405 
406   @Override
407   public void run() {
408     exit(configuration);
409   }
410 
411 }