/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.net;
import java.io.
IOException;
import java.io.
InputStream;
import java.io.
OutputStream;
import java.io.
BufferedOutputStream;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.security.
PrivilegedExceptionAction;
import sun.net.
SocksProxy;
import sun.net.www.
ParseUtil;
/* import org.ietf.jgss.*; */
/**
* SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
* This is a subclass of PlainSocketImpl.
* Note this class should <b>NOT</b> be public.
*/
class
SocksSocketImpl extends
PlainSocketImpl implements
SocksConsts {
private
String server = null;
private int
serverPort =
DEFAULT_PORT;
private
InetSocketAddress external_address;
private boolean
useV4 = false;
private
Socket cmdsock = null;
private
InputStream cmdIn = null;
private
OutputStream cmdOut = null;
/* true if the Proxy has been set programatically */
private boolean
applicationSetProxy; /* false */
SocksSocketImpl() {
// Nothing needed
}
SocksSocketImpl(
String server, int
port) {
this.
server =
server;
this.
serverPort = (
port == -1 ?
DEFAULT_PORT :
port);
}
SocksSocketImpl(
Proxy proxy) {
SocketAddress a =
proxy.
address();
if (
a instanceof
InetSocketAddress) {
InetSocketAddress ad = (
InetSocketAddress)
a;
// Use getHostString() to avoid reverse lookups
server =
ad.
getHostString();
serverPort =
ad.
getPort();
}
}
void
setV4() {
useV4 = true;
}
private synchronized void
privilegedConnect(final
String host,
final int
port,
final int
timeout)
throws
IOException
{
try {
AccessController.
doPrivileged(
new java.security.
PrivilegedExceptionAction<
Void>() {
public
Void run() throws
IOException {
superConnectServer(
host,
port,
timeout);
cmdIn =
getInputStream();
cmdOut =
getOutputStream();
return null;
}
});
} catch (java.security.
PrivilegedActionException pae) {
throw (
IOException)
pae.
getException();
}
}
private void
superConnectServer(
String host, int
port,
int
timeout) throws
IOException {
super.connect(new
InetSocketAddress(
host,
port),
timeout);
}
private static int
remainingMillis(long
deadlineMillis) throws
IOException {
if (
deadlineMillis == 0L)
return 0;
final long
remaining =
deadlineMillis -
System.
currentTimeMillis();
if (
remaining > 0)
return (int)
remaining;
throw new
SocketTimeoutException();
}
private int
readSocksReply(
InputStream in, byte[]
data) throws
IOException {
return
readSocksReply(
in,
data, 0L);
}
private int
readSocksReply(
InputStream in, byte[]
data, long
deadlineMillis) throws
IOException {
int
len =
data.length;
int
received = 0;
for (int
attempts = 0;
received <
len &&
attempts < 3;
attempts++) {
int
count;
try {
count = ((
SocketInputStream)
in).
read(
data,
received,
len -
received,
remainingMillis(
deadlineMillis));
} catch (
SocketTimeoutException e) {
throw new
SocketTimeoutException("Connect timed out");
}
if (
count < 0)
throw new
SocketException("Malformed reply from SOCKS server");
received +=
count;
}
return
received;
}
/**
* Provides the authentication machanism required by the proxy.
*/
private boolean
authenticate(byte
method,
InputStream in,
BufferedOutputStream out) throws
IOException {
return
authenticate(
method,
in,
out, 0L);
}
private boolean
authenticate(byte
method,
InputStream in,
BufferedOutputStream out,
long
deadlineMillis) throws
IOException {
// No Authentication required. We're done then!
if (
method ==
NO_AUTH)
return true;
/**
* User/Password authentication. Try, in that order :
* - The application provided Authenticator, if any
* - the user.name & no password (backward compatibility behavior).
*/
if (
method ==
USER_PASSW) {
String userName;
String password = null;
final
InetAddress addr =
InetAddress.
getByName(
server);
PasswordAuthentication pw =
java.security.
AccessController.
doPrivileged(
new java.security.
PrivilegedAction<
PasswordAuthentication>() {
public
PasswordAuthentication run() {
return
Authenticator.
requestPasswordAuthentication(
server,
addr,
serverPort, "SOCKS5", "SOCKS authentication", null);
}
});
if (
pw != null) {
userName =
pw.
getUserName();
password = new
String(
pw.
getPassword());
} else {
userName = java.security.
AccessController.
doPrivileged(
new sun.security.action.
GetPropertyAction("user.name"));
}
if (
userName == null)
return false;
out.
write(1);
out.
write(
userName.
length());
try {
out.
write(
userName.
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
if (
password != null) {
out.
write(
password.
length());
try {
out.
write(
password.
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
} else
out.
write(0);
out.
flush();
byte[]
data = new byte[2];
int
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 2 ||
data[1] != 0) {
/* RFC 1929 specifies that the connection MUST be closed if
authentication fails */
out.
close();
in.
close();
return false;
}
/* Authentication succeeded */
return true;
}
/**
* GSSAPI authentication mechanism.
* Unfortunately the RFC seems out of sync with the Reference
* implementation. I'll leave this in for future completion.
*/
// if (method == GSSAPI) {
// try {
// GSSManager manager = GSSManager.getInstance();
// GSSName name = manager.createName("SERVICE:socks@"+server,
// null);
// GSSContext context = manager.createContext(name, null, null,
// GSSContext.DEFAULT_LIFETIME);
// context.requestMutualAuth(true);
// context.requestReplayDet(true);
// context.requestSequenceDet(true);
// context.requestCredDeleg(true);
// byte []inToken = new byte[0];
// while (!context.isEstablished()) {
// byte[] outToken
// = context.initSecContext(inToken, 0, inToken.length);
// // send the output token if generated
// if (outToken != null) {
// out.write(1);
// out.write(1);
// out.writeShort(outToken.length);
// out.write(outToken);
// out.flush();
// data = new byte[2];
// i = readSocksReply(in, data, deadlineMillis);
// if (i != 2 || data[1] == 0xff) {
// in.close();
// out.close();
// return false;
// }
// i = readSocksReply(in, data, deadlineMillis);
// int len = 0;
// len = ((int)data[0] & 0xff) << 8;
// len += data[1];
// data = new byte[len];
// i = readSocksReply(in, data, deadlineMillis);
// if (i == len)
// return true;
// in.close();
// out.close();
// }
// }
// } catch (GSSException e) {
// /* RFC 1961 states that if Context initialisation fails the connection
// MUST be closed */
// e.printStackTrace();
// in.close();
// out.close();
// }
// }
return false;
}
private void
connectV4(
InputStream in,
OutputStream out,
InetSocketAddress endpoint,
long
deadlineMillis) throws
IOException {
if (!(
endpoint.
getAddress() instanceof
Inet4Address)) {
throw new
SocketException("SOCKS V4 requires IPv4 only addresses");
}
out.
write(
PROTO_VERS4);
out.
write(
CONNECT);
out.
write((
endpoint.
getPort() >> 8) & 0xff);
out.
write((
endpoint.
getPort() >> 0) & 0xff);
out.
write(
endpoint.
getAddress().
getAddress());
String userName =
getUserName();
try {
out.
write(
userName.
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
out.
write(0);
out.
flush();
byte[]
data = new byte[8];
int
n =
readSocksReply(
in,
data,
deadlineMillis);
if (
n != 8)
throw new
SocketException("Reply from SOCKS server has bad length: " +
n);
if (
data[0] != 0 &&
data[0] != 4)
throw new
SocketException("Reply from SOCKS server has bad version");
SocketException ex = null;
switch (
data[1]) {
case 90:
// Success!
external_address =
endpoint;
break;
case 91:
ex = new
SocketException("SOCKS request rejected");
break;
case 92:
ex = new
SocketException("SOCKS server couldn't reach destination");
break;
case 93:
ex = new
SocketException("SOCKS authentication failed");
break;
default:
ex = new
SocketException("Reply from SOCKS server contains bad status");
break;
}
if (
ex != null) {
in.
close();
out.
close();
throw
ex;
}
}
/**
* Connects the Socks Socket to the specified endpoint. It will first
* connect to the SOCKS proxy and negotiate the access. If the proxy
* grants the connections, then the connect is successful and all
* further traffic will go to the "real" endpoint.
*
* @param endpoint the {@code SocketAddress} to connect to.
* @param timeout the timeout value in milliseconds
* @throws IOException if the connection can't be established.
* @throws SecurityException if there is a security manager and it
* doesn't allow the connection
* @throws IllegalArgumentException if endpoint is null or a
* SocketAddress subclass not supported by this socket
*/
@
Override
protected void
connect(
SocketAddress endpoint, int
timeout) throws
IOException {
final long
deadlineMillis;
if (
timeout == 0) {
deadlineMillis = 0L;
} else {
long
finish =
System.
currentTimeMillis() +
timeout;
deadlineMillis =
finish < 0 ?
Long.
MAX_VALUE :
finish;
}
SecurityManager security =
System.
getSecurityManager();
if (
endpoint == null || !(
endpoint instanceof
InetSocketAddress))
throw new
IllegalArgumentException("Unsupported address type");
InetSocketAddress epoint = (
InetSocketAddress)
endpoint;
if (
security != null) {
if (
epoint.
isUnresolved())
security.
checkConnect(
epoint.
getHostName(),
epoint.
getPort());
else
security.
checkConnect(
epoint.
getAddress().
getHostAddress(),
epoint.
getPort());
}
if (
server == null) {
// This is the general case
// server is not null only when the socket was created with a
// specified proxy in which case it does bypass the ProxySelector
ProxySelector sel = java.security.
AccessController.
doPrivileged(
new java.security.
PrivilegedAction<
ProxySelector>() {
public
ProxySelector run() {
return
ProxySelector.
getDefault();
}
});
if (
sel == null) {
/*
* No default proxySelector --> direct connection
*/
super.connect(
epoint,
remainingMillis(
deadlineMillis));
return;
}
URI uri;
// Use getHostString() to avoid reverse lookups
String host =
epoint.
getHostString();
// IPv6 litteral?
if (
epoint.
getAddress() instanceof
Inet6Address &&
(!
host.
startsWith("[")) && (
host.
indexOf(":") >= 0)) {
host = "[" +
host + "]";
}
try {
uri = new
URI("socket://" +
ParseUtil.
encodePath(
host) + ":"+
epoint.
getPort());
} catch (
URISyntaxException e) {
// This shouldn't happen
assert false :
e;
uri = null;
}
Proxy p = null;
IOException savedExc = null;
java.util.
Iterator<
Proxy>
iProxy = null;
iProxy =
sel.
select(
uri).
iterator();
if (
iProxy == null || !(
iProxy.
hasNext())) {
super.connect(
epoint,
remainingMillis(
deadlineMillis));
return;
}
while (
iProxy.
hasNext()) {
p =
iProxy.
next();
if (
p == null ||
p.
type() !=
Proxy.
Type.
SOCKS) {
super.connect(
epoint,
remainingMillis(
deadlineMillis));
return;
}
if (!(
p.
address() instanceof
InetSocketAddress))
throw new
SocketException("Unknown address type for proxy: " +
p);
// Use getHostString() to avoid reverse lookups
server = ((
InetSocketAddress)
p.
address()).
getHostString();
serverPort = ((
InetSocketAddress)
p.
address()).
getPort();
if (
p instanceof
SocksProxy) {
if (((
SocksProxy)
p).
protocolVersion() == 4) {
useV4 = true;
}
}
// Connects to the SOCKS server
try {
privilegedConnect(
server,
serverPort,
remainingMillis(
deadlineMillis));
// Worked, let's get outta here
break;
} catch (
IOException e) {
// Ooops, let's notify the ProxySelector
sel.
connectFailed(
uri,
p.
address(),
e);
server = null;
serverPort = -1;
savedExc =
e;
// Will continue the while loop and try the next proxy
}
}
/*
* If server is still null at this point, none of the proxy
* worked
*/
if (
server == null) {
throw new
SocketException("Can't connect to SOCKS proxy:"
+
savedExc.
getMessage());
}
} else {
// Connects to the SOCKS server
try {
privilegedConnect(
server,
serverPort,
remainingMillis(
deadlineMillis));
} catch (
IOException e) {
throw new
SocketException(
e.
getMessage());
}
}
// cmdIn & cmdOut were initialized during the privilegedConnect() call
BufferedOutputStream out = new
BufferedOutputStream(
cmdOut, 512);
InputStream in =
cmdIn;
if (
useV4) {
// SOCKS Protocol version 4 doesn't know how to deal with
// DOMAIN type of addresses (unresolved addresses here)
if (
epoint.
isUnresolved())
throw new
UnknownHostException(
epoint.
toString());
connectV4(
in,
out,
epoint,
deadlineMillis);
return;
}
// This is SOCKS V5
out.
write(
PROTO_VERS);
out.
write(2);
out.
write(
NO_AUTH);
out.
write(
USER_PASSW);
out.
flush();
byte[]
data = new byte[2];
int
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 2 || ((int)
data[0]) !=
PROTO_VERS) {
// Maybe it's not a V5 sever after all
// Let's try V4 before we give up
// SOCKS Protocol version 4 doesn't know how to deal with
// DOMAIN type of addresses (unresolved addresses here)
if (
epoint.
isUnresolved())
throw new
UnknownHostException(
epoint.
toString());
connectV4(
in,
out,
epoint,
deadlineMillis);
return;
}
if (((int)
data[1]) ==
NO_METHODS)
throw new
SocketException("SOCKS : No acceptable methods");
if (!
authenticate(
data[1],
in,
out,
deadlineMillis)) {
throw new
SocketException("SOCKS : authentication failed");
}
out.
write(
PROTO_VERS);
out.
write(
CONNECT);
out.
write(0);
/* Test for IPV4/IPV6/Unresolved */
if (
epoint.
isUnresolved()) {
out.
write(
DOMAIN_NAME);
out.
write(
epoint.
getHostName().
length());
try {
out.
write(
epoint.
getHostName().
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
out.
write((
epoint.
getPort() >> 8) & 0xff);
out.
write((
epoint.
getPort() >> 0) & 0xff);
} else if (
epoint.
getAddress() instanceof
Inet6Address) {
out.
write(
IPV6);
out.
write(
epoint.
getAddress().
getAddress());
out.
write((
epoint.
getPort() >> 8) & 0xff);
out.
write((
epoint.
getPort() >> 0) & 0xff);
} else {
out.
write(
IPV4);
out.
write(
epoint.
getAddress().
getAddress());
out.
write((
epoint.
getPort() >> 8) & 0xff);
out.
write((
epoint.
getPort() >> 0) & 0xff);
}
out.
flush();
data = new byte[4];
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 4)
throw new
SocketException("Reply from SOCKS server has bad length");
SocketException ex = null;
int
len;
byte[]
addr;
switch (
data[1]) {
case
REQUEST_OK:
// success!
switch(
data[3]) {
case
IPV4:
addr = new byte[4];
i =
readSocksReply(
in,
addr,
deadlineMillis);
if (
i != 4)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
break;
case
DOMAIN_NAME:
len =
data[1];
byte[]
host = new byte[
len];
i =
readSocksReply(
in,
host,
deadlineMillis);
if (
i !=
len)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
break;
case
IPV6:
len =
data[1];
addr = new byte[
len];
i =
readSocksReply(
in,
addr,
deadlineMillis);
if (
i !=
len)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data,
deadlineMillis);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
break;
default:
ex = new
SocketException("Reply from SOCKS server contains wrong code");
break;
}
break;
case
GENERAL_FAILURE:
ex = new
SocketException("SOCKS server general failure");
break;
case
NOT_ALLOWED:
ex = new
SocketException("SOCKS: Connection not allowed by ruleset");
break;
case
NET_UNREACHABLE:
ex = new
SocketException("SOCKS: Network unreachable");
break;
case
HOST_UNREACHABLE:
ex = new
SocketException("SOCKS: Host unreachable");
break;
case
CONN_REFUSED:
ex = new
SocketException("SOCKS: Connection refused");
break;
case
TTL_EXPIRED:
ex = new
SocketException("SOCKS: TTL expired");
break;
case
CMD_NOT_SUPPORTED:
ex = new
SocketException("SOCKS: Command not supported");
break;
case
ADDR_TYPE_NOT_SUP:
ex = new
SocketException("SOCKS: address type not supported");
break;
}
if (
ex != null) {
in.
close();
out.
close();
throw
ex;
}
external_address =
epoint;
}
private void
bindV4(
InputStream in,
OutputStream out,
InetAddress baddr,
int
lport) throws
IOException {
if (!(
baddr instanceof
Inet4Address)) {
throw new
SocketException("SOCKS V4 requires IPv4 only addresses");
}
super.bind(
baddr,
lport);
byte[]
addr1 =
baddr.
getAddress();
/* Test for AnyLocal */
InetAddress naddr =
baddr;
if (
naddr.
isAnyLocalAddress()) {
naddr =
AccessController.
doPrivileged(
new
PrivilegedAction<
InetAddress>() {
public
InetAddress run() {
return
cmdsock.
getLocalAddress();
}
});
addr1 =
naddr.
getAddress();
}
out.
write(
PROTO_VERS4);
out.
write(
BIND);
out.
write((super.getLocalPort() >> 8) & 0xff);
out.
write((super.getLocalPort() >> 0) & 0xff);
out.
write(
addr1);
String userName =
getUserName();
try {
out.
write(
userName.
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
out.
write(0);
out.
flush();
byte[]
data = new byte[8];
int
n =
readSocksReply(
in,
data);
if (
n != 8)
throw new
SocketException("Reply from SOCKS server has bad length: " +
n);
if (
data[0] != 0 &&
data[0] != 4)
throw new
SocketException("Reply from SOCKS server has bad version");
SocketException ex = null;
switch (
data[1]) {
case 90:
// Success!
external_address = new
InetSocketAddress(
baddr,
lport);
break;
case 91:
ex = new
SocketException("SOCKS request rejected");
break;
case 92:
ex = new
SocketException("SOCKS server couldn't reach destination");
break;
case 93:
ex = new
SocketException("SOCKS authentication failed");
break;
default:
ex = new
SocketException("Reply from SOCKS server contains bad status");
break;
}
if (
ex != null) {
in.
close();
out.
close();
throw
ex;
}
}
/**
* Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
* means "accept incoming connection from", so the SocketAddress is the
* the one of the host we do accept connection from.
*
* @param saddr the Socket address of the remote host.
* @exception IOException if an I/O error occurs when binding this socket.
*/
protected synchronized void
socksBind(
InetSocketAddress saddr) throws
IOException {
if (
socket != null) {
// this is a client socket, not a server socket, don't
// call the SOCKS proxy for a bind!
return;
}
// Connects to the SOCKS server
if (
server == null) {
// This is the general case
// server is not null only when the socket was created with a
// specified proxy in which case it does bypass the ProxySelector
ProxySelector sel = java.security.
AccessController.
doPrivileged(
new java.security.
PrivilegedAction<
ProxySelector>() {
public
ProxySelector run() {
return
ProxySelector.
getDefault();
}
});
if (
sel == null) {
/*
* No default proxySelector --> direct connection
*/
return;
}
URI uri;
// Use getHostString() to avoid reverse lookups
String host =
saddr.
getHostString();
// IPv6 litteral?
if (
saddr.
getAddress() instanceof
Inet6Address &&
(!
host.
startsWith("[")) && (
host.
indexOf(":") >= 0)) {
host = "[" +
host + "]";
}
try {
uri = new
URI("serversocket://" +
ParseUtil.
encodePath(
host) + ":"+
saddr.
getPort());
} catch (
URISyntaxException e) {
// This shouldn't happen
assert false :
e;
uri = null;
}
Proxy p = null;
Exception savedExc = null;
java.util.
Iterator<
Proxy>
iProxy = null;
iProxy =
sel.
select(
uri).
iterator();
if (
iProxy == null || !(
iProxy.
hasNext())) {
return;
}
while (
iProxy.
hasNext()) {
p =
iProxy.
next();
if (
p == null ||
p.
type() !=
Proxy.
Type.
SOCKS) {
return;
}
if (!(
p.
address() instanceof
InetSocketAddress))
throw new
SocketException("Unknown address type for proxy: " +
p);
// Use getHostString() to avoid reverse lookups
server = ((
InetSocketAddress)
p.
address()).
getHostString();
serverPort = ((
InetSocketAddress)
p.
address()).
getPort();
if (
p instanceof
SocksProxy) {
if (((
SocksProxy)
p).
protocolVersion() == 4) {
useV4 = true;
}
}
// Connects to the SOCKS server
try {
AccessController.
doPrivileged(
new
PrivilegedExceptionAction<
Void>() {
public
Void run() throws
Exception {
cmdsock = new
Socket(new
PlainSocketImpl());
cmdsock.
connect(new
InetSocketAddress(
server,
serverPort));
cmdIn =
cmdsock.
getInputStream();
cmdOut =
cmdsock.
getOutputStream();
return null;
}
});
} catch (
Exception e) {
// Ooops, let's notify the ProxySelector
sel.
connectFailed(
uri,
p.
address(),new
SocketException(
e.
getMessage()));
server = null;
serverPort = -1;
cmdsock = null;
savedExc =
e;
// Will continue the while loop and try the next proxy
}
}
/*
* If server is still null at this point, none of the proxy
* worked
*/
if (
server == null ||
cmdsock == null) {
throw new
SocketException("Can't connect to SOCKS proxy:"
+
savedExc.
getMessage());
}
} else {
try {
AccessController.
doPrivileged(
new
PrivilegedExceptionAction<
Void>() {
public
Void run() throws
Exception {
cmdsock = new
Socket(new
PlainSocketImpl());
cmdsock.
connect(new
InetSocketAddress(
server,
serverPort));
cmdIn =
cmdsock.
getInputStream();
cmdOut =
cmdsock.
getOutputStream();
return null;
}
});
} catch (
Exception e) {
throw new
SocketException(
e.
getMessage());
}
}
BufferedOutputStream out = new
BufferedOutputStream(
cmdOut, 512);
InputStream in =
cmdIn;
if (
useV4) {
bindV4(
in,
out,
saddr.
getAddress(),
saddr.
getPort());
return;
}
out.
write(
PROTO_VERS);
out.
write(2);
out.
write(
NO_AUTH);
out.
write(
USER_PASSW);
out.
flush();
byte[]
data = new byte[2];
int
i =
readSocksReply(
in,
data);
if (
i != 2 || ((int)
data[0]) !=
PROTO_VERS) {
// Maybe it's not a V5 sever after all
// Let's try V4 before we give up
bindV4(
in,
out,
saddr.
getAddress(),
saddr.
getPort());
return;
}
if (((int)
data[1]) ==
NO_METHODS)
throw new
SocketException("SOCKS : No acceptable methods");
if (!
authenticate(
data[1],
in,
out)) {
throw new
SocketException("SOCKS : authentication failed");
}
// We're OK. Let's issue the BIND command.
out.
write(
PROTO_VERS);
out.
write(
BIND);
out.
write(0);
int
lport =
saddr.
getPort();
if (
saddr.
isUnresolved()) {
out.
write(
DOMAIN_NAME);
out.
write(
saddr.
getHostName().
length());
try {
out.
write(
saddr.
getHostName().
getBytes("ISO-8859-1"));
} catch (java.io.
UnsupportedEncodingException uee) {
assert false;
}
out.
write((
lport >> 8) & 0xff);
out.
write((
lport >> 0) & 0xff);
} else if (
saddr.
getAddress() instanceof
Inet4Address) {
byte[]
addr1 =
saddr.
getAddress().
getAddress();
out.
write(
IPV4);
out.
write(
addr1);
out.
write((
lport >> 8) & 0xff);
out.
write((
lport >> 0) & 0xff);
out.
flush();
} else if (
saddr.
getAddress() instanceof
Inet6Address) {
byte[]
addr1 =
saddr.
getAddress().
getAddress();
out.
write(
IPV6);
out.
write(
addr1);
out.
write((
lport >> 8) & 0xff);
out.
write((
lport >> 0) & 0xff);
out.
flush();
} else {
cmdsock.
close();
throw new
SocketException("unsupported address type : " +
saddr);
}
data = new byte[4];
i =
readSocksReply(
in,
data);
SocketException ex = null;
int
len,
nport;
byte[]
addr;
switch (
data[1]) {
case
REQUEST_OK:
// success!
switch(
data[3]) {
case
IPV4:
addr = new byte[4];
i =
readSocksReply(
in,
addr);
if (
i != 4)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
nport = ((int)
data[0] & 0xff) << 8;
nport += ((int)
data[1] & 0xff);
external_address =
new
InetSocketAddress(new
Inet4Address("",
addr) ,
nport);
break;
case
DOMAIN_NAME:
len =
data[1];
byte[]
host = new byte[
len];
i =
readSocksReply(
in,
host);
if (
i !=
len)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
nport = ((int)
data[0] & 0xff) << 8;
nport += ((int)
data[1] & 0xff);
external_address = new
InetSocketAddress(new
String(
host),
nport);
break;
case
IPV6:
len =
data[1];
addr = new byte[
len];
i =
readSocksReply(
in,
addr);
if (
i !=
len)
throw new
SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
i =
readSocksReply(
in,
data);
if (
i != 2)
throw new
SocketException("Reply from SOCKS server badly formatted");
nport = ((int)
data[0] & 0xff) << 8;
nport += ((int)
data[1] & 0xff);
external_address =
new
InetSocketAddress(new
Inet6Address("",
addr),
nport);
break;
}
break;
case
GENERAL_FAILURE:
ex = new
SocketException("SOCKS server general failure");
break;
case
NOT_ALLOWED:
ex = new
SocketException("SOCKS: Bind not allowed by ruleset");
break;
case
NET_UNREACHABLE:
ex = new
SocketException("SOCKS: Network unreachable");
break;
case
HOST_UNREACHABLE:
ex = new
SocketException("SOCKS: Host unreachable");
break;
case
CONN_REFUSED:
ex = new
SocketException("SOCKS: Connection refused");
break;
case
TTL_EXPIRED:
ex = new
SocketException("SOCKS: TTL expired");
break;
case
CMD_NOT_SUPPORTED:
ex = new
SocketException("SOCKS: Command not supported");
break;
case
ADDR_TYPE_NOT_SUP:
ex = new
SocketException("SOCKS: address type not supported");
break;
}
if (
ex != null) {
in.
close();
out.
close();
cmdsock.
close();
cmdsock = null;
throw
ex;
}
cmdIn =
in;
cmdOut =
out;
}
/**
* Accepts a connection from a specific host.
*
* @param s the accepted connection.
* @param saddr the socket address of the host we do accept
* connection from
* @exception IOException if an I/O error occurs when accepting the
* connection.
*/
protected void
acceptFrom(
SocketImpl s,
InetSocketAddress saddr) throws
IOException {
if (
cmdsock == null) {
// Not a Socks ServerSocket.
return;
}
InputStream in =
cmdIn;
// Sends the "SOCKS BIND" request.
socksBind(
saddr);
in.
read();
int
i =
in.
read();
in.
read();
SocketException ex = null;
int
nport;
byte[]
addr;
InetSocketAddress real_end = null;
switch (
i) {
case
REQUEST_OK:
// success!
i =
in.
read();
switch(
i) {
case
IPV4:
addr = new byte[4];
readSocksReply(
in,
addr);
nport =
in.
read() << 8;
nport +=
in.
read();
real_end =
new
InetSocketAddress(new
Inet4Address("",
addr) ,
nport);
break;
case
DOMAIN_NAME:
int
len =
in.
read();
addr = new byte[
len];
readSocksReply(
in,
addr);
nport =
in.
read() << 8;
nport +=
in.
read();
real_end = new
InetSocketAddress(new
String(
addr),
nport);
break;
case
IPV6:
addr = new byte[16];
readSocksReply(
in,
addr);
nport =
in.
read() << 8;
nport +=
in.
read();
real_end =
new
InetSocketAddress(new
Inet6Address("",
addr),
nport);
break;
}
break;
case
GENERAL_FAILURE:
ex = new
SocketException("SOCKS server general failure");
break;
case
NOT_ALLOWED:
ex = new
SocketException("SOCKS: Accept not allowed by ruleset");
break;
case
NET_UNREACHABLE:
ex = new
SocketException("SOCKS: Network unreachable");
break;
case
HOST_UNREACHABLE:
ex = new
SocketException("SOCKS: Host unreachable");
break;
case
CONN_REFUSED:
ex = new
SocketException("SOCKS: Connection refused");
break;
case
TTL_EXPIRED:
ex = new
SocketException("SOCKS: TTL expired");
break;
case
CMD_NOT_SUPPORTED:
ex = new
SocketException("SOCKS: Command not supported");
break;
case
ADDR_TYPE_NOT_SUP:
ex = new
SocketException("SOCKS: address type not supported");
break;
}
if (
ex != null) {
cmdIn.
close();
cmdOut.
close();
cmdsock.
close();
cmdsock = null;
throw
ex;
}
/**
* This is where we have to do some fancy stuff.
* The datastream from the socket "accepted" by the proxy will
* come through the cmdSocket. So we have to swap the socketImpls
*/
if (
s instanceof
SocksSocketImpl) {
((
SocksSocketImpl)
s).
external_address =
real_end;
}
if (
s instanceof
PlainSocketImpl) {
PlainSocketImpl psi = (
PlainSocketImpl)
s;
psi.
setInputStream((
SocketInputStream)
in);
psi.
setFileDescriptor(
cmdsock.
getImpl().
getFileDescriptor());
psi.
setAddress(
cmdsock.
getImpl().
getInetAddress());
psi.
setPort(
cmdsock.
getImpl().
getPort());
psi.
setLocalPort(
cmdsock.
getImpl().
getLocalPort());
} else {
s.
fd =
cmdsock.
getImpl().
fd;
s.
address =
cmdsock.
getImpl().
address;
s.
port =
cmdsock.
getImpl().
port;
s.
localport =
cmdsock.
getImpl().
localport;
}
// Need to do that so that the socket won't be closed
// when the ServerSocket is closed by the user.
// It kinds of detaches the Socket because it is now
// used elsewhere.
cmdsock = null;
}
/**
* Returns the value of this socket's {@code address} field.
*
* @return the value of this socket's {@code address} field.
* @see java.net.SocketImpl#address
*/
@
Override
protected
InetAddress getInetAddress() {
if (
external_address != null)
return
external_address.
getAddress();
else
return super.getInetAddress();
}
/**
* Returns the value of this socket's {@code port} field.
*
* @return the value of this socket's {@code port} field.
* @see java.net.SocketImpl#port
*/
@
Override
protected int
getPort() {
if (
external_address != null)
return
external_address.
getPort();
else
return super.getPort();
}
@
Override
protected int
getLocalPort() {
if (
socket != null)
return super.getLocalPort();
if (
external_address != null)
return
external_address.
getPort();
else
return super.getLocalPort();
}
@
Override
protected void
close() throws
IOException {
if (
cmdsock != null)
cmdsock.
close();
cmdsock = null;
super.close();
}
private
String getUserName() {
String userName = "";
if (
applicationSetProxy) {
try {
userName =
System.
getProperty("user.name");
} catch (
SecurityException se) { /* swallow Exception */ }
} else {
userName = java.security.
AccessController.
doPrivileged(
new sun.security.action.
GetPropertyAction("user.name"));
}
return
userName;
}
}