/*
* @(#)SocksSocketImplFactory.java 1.7 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, 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
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package java.net;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.io.InputStream;
import java.io.OutputStream;
/**
* This factory creates an SocketImpl that implements the SOCKS protocol
* (both V5 & V4). It implements RFC 1928.
*
* @see java.net.Socket#setSocketImplFactory(SocketImplFactory)
* @see java.net.ServerSocket#setSocketImplFactory(SocketImplFactory)
*/
class SocksSocketImplFactory implements SocketImplFactory, SocksConsts {
private String server;
private int port = -1;
private boolean useV4 = false;
/**
* Creates a SocksSocketImplFactory with a specific server & port.
* This should point to a SOCKS v5 proxy server.
*
* @param server the server hostname
* @param port the port number. -1 for the default SOCKS port.
*/
SocksSocketImplFactory(String server, int port) {
this.server = server;
this.port = port == -1 ? DEFAULT_PORT : port;
guessVersion();
}
/**
* Creates a SocksSocketImplFactory with a specific server & port.
*
* @param server the server hostname
* @param port the port number. -1 for the default SOCKS port.
* @param v4 <code>true</code> if the protocol should be version 4
* <code>false</code> for version 5.
*/
SocksSocketImplFactory(String server, int port, boolean v4) {
this.server = server;
this.port = port == -1 ? DEFAULT_PORT : port;
this.useV4 = v4;
}
/*
* Checks whether the System properties changed.
* If they did, we need to renegociate the protocol version
*/
private synchronized void checkProps() {
boolean changed = false;
int newport = DEFAULT_PORT;
String socksPort = null;
String socksHost =
(String) java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("socksProxyHost"));
socksPort =
(String) java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("socksProxyPort"));
if (socksPort != null) {
try {
newport = Integer.parseInt(socksPort);
} catch (Exception e) {
newport = DEFAULT_PORT;
}
}
if (socksHost != null && !socksHost.equals(this.server)) {
this.server = socksHost;
changed = true;
}
if (newport != this.port) {
this.port = newport;
changed = true;
}
if (changed) {
guessVersion();
}
}
private void guessVersion() {
Socket s;
// Connects to the SOCKS server
try {
s = (Socket) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
Socket so = new Socket(new PlainSocketImpl());
so.connect(new InetSocketAddress(server, port));
return so;
}
});
} catch (Exception e) {
e.printStackTrace();
return;
}
InputStream in = null;
OutputStream out = null;
try {
// If it's taking too long to get an answer, then it's probably
// the wrong version
s.setSoTimeout(1000);
out = s.getOutputStream();
in = s.getInputStream();
// Try V5 first
out.write(PROTO_VERS);
out.write(2);
out.write(NO_AUTH);
out.write(USER_PASSW);
out.flush();
int i = in.read();
if (i == PROTO_VERS) {
// All rigth it's V5
useV4 = false;
i = in.read();
} else {
// V5 doesn't work, let's assume it's V4
useV4 = true;
}
in.close();
out.close();
s.close();
} catch (java.net.SocketTimeoutException te) {
// Timeout. Took too long let's assume it's V4 then
useV4 = true;
try {
in.close();
out.close();
s.close();
} catch (Exception e2) {
}
} catch (java.io.IOException ioe) {
// Nothing much we can do here, but we tried
}
}
/**
* Creates a new <code>SocketImpl</code> instance.
*
* @return a new instance of <code>SocketImpl</code>.
* @see java.net.SocketImpl
*/
public SocketImpl createSocketImpl() {
checkProps();
SocksSocketImpl impl = new SocksSocketImpl(server, port);
if (useV4)
impl.setV4();
return impl;
}
}