001    /*
002     * ftp4j - A pure Java FTP client library
003     * 
004     * Copyright (C) 2008 Carlo Pelliccia (www.sauronsoftware.it)
005     * 
006     * This program is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation, either version 3 of the License, or
009     * (at your option) any later version.
010     *
011     * This program is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014     * GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License
017     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018     */
019    package it.sauronsoftware.ftp4j.connectors;
020    
021    import it.sauronsoftware.ftp4j.FTPConnection;
022    import it.sauronsoftware.ftp4j.FTPConnector;
023    
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.OutputStream;
027    import java.net.InetAddress;
028    import java.net.Socket;
029    
030    /**
031     * This one connects a remote ftp host through a SOCKS4/4a proxy server.
032     * 
033     * @author Carlo Pelliccia
034     */
035    public class SOCKS4Connector implements FTPConnector {
036    
037            /**
038             * The socks4 proxy host name.
039             */
040            private String socks4host;
041    
042            /**
043             * The socks4 proxy port.
044             */
045            private int socks4port;
046    
047            /**
048             * The socks4 proxy user (optional).
049             */
050            private String socks4user;
051    
052            /**
053             * It builds the connector.
054             * 
055             * @param socks4host
056             *            The socks4 proxy host name.
057             * @param socks4port
058             *            The socks4 proxy port.
059             * @param socks4user
060             *            The socks4 proxy user (optional, can be set to null).
061             */
062            public SOCKS4Connector(String socks4host, int socks4port, String socks4user) {
063                    this.socks4host = socks4host;
064                    this.socks4port = socks4port;
065                    this.socks4user = socks4user;
066            }
067    
068            /**
069             * It builds the connector.
070             * 
071             * @param socks4host
072             *            The socks4 proxy host name.
073             * @param socks4port
074             *            The socks4 proxy port.
075             */
076            public SOCKS4Connector(String socks4host, int socks4port) {
077                    this(socks4host, socks4port, null);
078            }
079    
080            private FTPConnection connect(String host, int port) throws IOException {
081                    // Socks 4 or 4a?
082                    boolean socks4a = false;
083                    byte[] address;
084                    try {
085                            address = InetAddress.getByName(host).getAddress();
086                    } catch (Exception e) {
087                            // Cannot resolve host, switch to version 4a.
088                            socks4a = true;
089                            address = new byte[] { 0x00, 0x00, 0x00, 0x01 };
090                    }
091                    // A connection status flag.
092                    boolean connected = false;
093                    // The socket for the connection with the proxy.
094                    Socket socket = null;
095                    InputStream in = null;
096                    OutputStream out = null;
097                    // FTPConnection routine.
098                    try {
099                            socket = new Socket(socks4host, socks4port);
100                            in = socket.getInputStream();
101                            out = socket.getOutputStream();
102                            // Send the request.
103                            // Version 4.
104                            out.write(0x04);
105                            // CONNECT method.
106                            out.write(0x01);
107                            // Remote port number.
108                            out.write(port >> 8);
109                            out.write(port);
110                            // Remote host address.
111                            out.write(address);
112                            // The user.
113                            if (socks4user != null) {
114                                    out.write(socks4user.getBytes("UTF-8"));
115                            }
116                            // End of user.
117                            out.write(0x00);
118                            // Version 4a?
119                            if (socks4a) {
120                                    out.write(host.getBytes("UTF-8"));
121                                    out.write(0x00);
122                            }
123                            // Get and parse the response.
124                            int aux = read(in);
125                            if (aux != 0x00) {
126                                    throw new IOException("SOCKS4Connector: invalid proxy response");
127                            }
128                            aux = read(in);
129                            switch (aux) {
130                            case 0x5a:
131                                    in.skip(6);
132                                    connected = true;
133                                    break;
134                            case 0x5b:
135                                    throw new IOException(
136                                                    "SOCKS4Connector: connection refused/failed");
137                            case 0x5c:
138                                    throw new IOException(
139                                                    "SOCKS4Connector: cannot validate the user");
140                            case 0x5d:
141                                    throw new IOException("SOCKS4Connector: invalid user");
142                            default:
143                                    throw new IOException("SOCKS4Connector: invalid proxy response");
144                            }
145                    } catch (IOException e) {
146                            throw e;
147                    } finally {
148                            if (!connected) {
149                                    if (out != null) {
150                                            try {
151                                                    out.close();
152                                            } catch (Throwable t) {
153                                                    ;
154                                            }
155                                    }
156                                    if (in != null) {
157                                            try {
158                                                    in.close();
159                                            } catch (Throwable t) {
160                                                    ;
161                                            }
162                                    }
163                                    if (socket != null) {
164                                            try {
165                                                    socket.close();
166                                            } catch (Throwable t) {
167                                                    ;
168                                            }
169                                    }
170                            }
171                    }
172                    return new SocketConnection(socket, in, out);
173            }
174    
175            private int read(InputStream in) throws IOException {
176                    int aux = in.read();
177                    if (aux < 0) {
178                            throw new IOException(
179                                            "SOCKS4Connector: connection closed by the proxy");
180                    }
181                    return aux;
182            }
183    
184            public FTPConnection connectForCommunicationChannel(String host, int port)
185                            throws IOException {
186                    return connect(host, port);
187            }
188    
189            public FTPConnection connectForDataTransferChannel(String host, int port)
190                            throws IOException {
191                    return connect(host, port);
192            }
193    
194    }