/*
* ftp4j - A pure Java FTP client library
*
* Copyright (C) 2008-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see .
*/
package it.sauronsoftware.ftp4j.connectors;
import it.sauronsoftware.ftp4j.FTPConnector;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
/**
* This one connects a remote ftp host through a SOCKS4/4a proxy server.
*
* The connector's default value for the
* useSuggestedAddressForDataConnections flag is false.
*
* @author Carlo Pelliccia
*/
public class SOCKS4Connector extends FTPConnector {
/**
* The socks4 proxy host name.
*/
private String socks4host;
/**
* The socks4 proxy port.
*/
private int socks4port;
/**
* The socks4 proxy user (optional).
*/
private String socks4user;
/**
* It builds the connector.
*
* @param socks4host
* The socks4 proxy host name.
* @param socks4port
* The socks4 proxy port.
* @param socks4user
* The socks4 proxy user (optional, can be set to null).
*/
public SOCKS4Connector(String socks4host, int socks4port, String socks4user) {
this.socks4host = socks4host;
this.socks4port = socks4port;
this.socks4user = socks4user;
}
/**
* It builds the connector.
*
* @param socks4host
* The socks4 proxy host name.
* @param socks4port
* The socks4 proxy port.
*/
public SOCKS4Connector(String socks4host, int socks4port) {
this(socks4host, socks4port, null);
}
private Socket socksConnect(String host, int port, boolean forDataTransfer) throws IOException {
// Socks 4 or 4a?
boolean socks4a = false;
byte[] address;
try {
address = InetAddress.getByName(host).getAddress();
} catch (Exception e) {
// Cannot resolve host, switch to version 4a.
socks4a = true;
address = new byte[] { 0x00, 0x00, 0x00, 0x01 };
}
// A connection status flag.
boolean connected = false;
// The socket for the connection with the proxy.
Socket socket = null;
InputStream in = null;
OutputStream out = null;
// FTPConnection routine.
try {
if (forDataTransfer) {
socket = tcpConnectForDataTransferChannel(socks4host, socks4port);
} else {
socket = tcpConnectForCommunicationChannel(socks4host, socks4port);
}
in = socket.getInputStream();
out = socket.getOutputStream();
// Send the request.
// Version 4.
out.write(0x04);
// CONNECT method.
out.write(0x01);
// Remote port number.
out.write(port >> 8);
out.write(port);
// Remote host address.
out.write(address);
// The user.
if (socks4user != null) {
out.write(socks4user.getBytes("UTF-8"));
}
// End of user.
out.write(0x00);
// Version 4a?
if (socks4a) {
out.write(host.getBytes("UTF-8"));
out.write(0x00);
}
// Get and parse the response.
int aux = read(in);
if (aux != 0x00) {
throw new IOException("SOCKS4Connector: invalid proxy response");
}
aux = read(in);
switch (aux) {
case 0x5a:
in.skip(6);
connected = true;
break;
case 0x5b:
throw new IOException(
"SOCKS4Connector: connection refused/failed");
case 0x5c:
throw new IOException(
"SOCKS4Connector: cannot validate the user");
case 0x5d:
throw new IOException("SOCKS4Connector: invalid user");
default:
throw new IOException("SOCKS4Connector: invalid proxy response");
}
} catch (IOException e) {
throw e;
} finally {
if (!connected) {
if (out != null) {
try {
out.close();
} catch (Throwable t) {
;
}
}
if (in != null) {
try {
in.close();
} catch (Throwable t) {
;
}
}
if (socket != null) {
try {
socket.close();
} catch (Throwable t) {
;
}
}
}
}
return socket;
}
private int read(InputStream in) throws IOException {
int aux = in.read();
if (aux < 0) {
throw new IOException(
"SOCKS4Connector: connection closed by the proxy");
}
return aux;
}
public Socket connectForCommunicationChannel(String host, int port)
throws IOException {
return socksConnect(host, port, false);
}
public Socket connectForDataTransferChannel(String host, int port)
throws IOException {
return socksConnect(host, port, true);
}
}