mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-25 15:52:06 +01:00
proxy: make it the caller's reponsibility to close the socket
This makes the code shorter as there is now a single place where the socket should be closed.
This commit is contained in:
parent
9d626bf787
commit
7afd1fdf46
4 changed files with 123 additions and 151 deletions
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2016 Florian Schmaus.
|
||||
* Copyright 2015-2019 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -21,6 +21,16 @@ import java.net.Socket;
|
|||
|
||||
public interface ProxySocketConnection {
|
||||
|
||||
/**
|
||||
* Initiate a connection to the given host on the given port. Note that the caller is responsible for closing the
|
||||
* socket in case this method throws.
|
||||
*
|
||||
* @param socket the socket to use to initiate the connection to the proxy.
|
||||
* @param host the host to connect to.
|
||||
* @param port the port to connect to.
|
||||
* @param timeout the timeout in milliseconds.
|
||||
* @throws IOException in case an I/O error occurs.
|
||||
*/
|
||||
void connect(Socket socket, String host, int port, int timeout)
|
||||
throws IOException;
|
||||
|
||||
|
|
|
@ -39,20 +39,17 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
|
|||
@Override
|
||||
public void connect(Socket socket, String host, int port, int timeout)
|
||||
throws IOException {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
String proxy_host = proxy.getProxyAddress();
|
||||
int proxy_port = proxy.getProxyPort();
|
||||
String user = proxy.getProxyUsername();
|
||||
|
||||
try {
|
||||
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
|
||||
in = socket.getInputStream();
|
||||
out = socket.getOutputStream();
|
||||
socket.setTcpNoDelay(true);
|
||||
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
|
||||
InputStream in = socket.getInputStream();
|
||||
OutputStream out = socket.getOutputStream();
|
||||
socket.setTcpNoDelay(true);
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int index = 0;
|
||||
byte[] buf = new byte[1024];
|
||||
int index = 0;
|
||||
|
||||
/*
|
||||
1) CONNECT
|
||||
|
@ -72,25 +69,25 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
|
|||
of all zero bits.
|
||||
*/
|
||||
|
||||
buf[index++] = 4;
|
||||
buf[index++] = 1;
|
||||
buf[index++] = 4;
|
||||
buf[index++] = 1;
|
||||
|
||||
buf[index++] = (byte) (port >>> 8);
|
||||
buf[index++] = (byte) (port & 0xff);
|
||||
buf[index++] = (byte) (port >>> 8);
|
||||
buf[index++] = (byte) (port & 0xff);
|
||||
|
||||
InetAddress inetAddress = InetAddress.getByName(proxy_host);
|
||||
byte[] byteAddress = inetAddress.getAddress();
|
||||
for (int i = 0; i < byteAddress.length; i++) {
|
||||
buf[index++] = byteAddress[i];
|
||||
}
|
||||
InetAddress inetAddress = InetAddress.getByName(proxy_host);
|
||||
byte[] byteAddress = inetAddress.getAddress();
|
||||
for (int i = 0; i < byteAddress.length; i++) {
|
||||
buf[index++] = byteAddress[i];
|
||||
}
|
||||
|
||||
if (user != null) {
|
||||
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
|
||||
System.arraycopy(userBytes, 0, buf, index, user.length());
|
||||
index += user.length();
|
||||
}
|
||||
buf[index++] = 0;
|
||||
out.write(buf, 0, index);
|
||||
if (user != null) {
|
||||
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
|
||||
System.arraycopy(userBytes, 0, buf, index, user.length());
|
||||
index += user.length();
|
||||
}
|
||||
buf[index++] = 0;
|
||||
out.write(buf, 0, index);
|
||||
|
||||
/*
|
||||
The SOCKS server checks to see whether such a request should be granted
|
||||
|
@ -119,43 +116,26 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
|
|||
The remaining fields are ignored.
|
||||
*/
|
||||
|
||||
int len = 6;
|
||||
int s = 0;
|
||||
while (s < len) {
|
||||
int i = in.read(buf, s, len - s);
|
||||
if (i <= 0) {
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
|
||||
"stream is closed");
|
||||
}
|
||||
s += i;
|
||||
}
|
||||
if (buf[0] != 0) {
|
||||
int len = 6;
|
||||
int s = 0;
|
||||
while (s < len) {
|
||||
int i = in.read(buf, s, len - s);
|
||||
if (i <= 0) {
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
|
||||
"server returns VN " + buf[0]);
|
||||
"stream is closed");
|
||||
}
|
||||
if (buf[1] != 90) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception eee) {
|
||||
}
|
||||
String message = "ProxySOCKS4: server returns CD " + buf[1];
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message);
|
||||
}
|
||||
byte[] temp = new byte[2];
|
||||
in.read(temp, 0, 2);
|
||||
s += i;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw e;
|
||||
if (buf[0] != 0) {
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
|
||||
"server returns VN " + buf[0]);
|
||||
}
|
||||
catch (Exception e) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception eee) {
|
||||
}
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, e.toString());
|
||||
if (buf[1] != 90) {
|
||||
String message = "ProxySOCKS4: server returns CD " + buf[1];
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message);
|
||||
}
|
||||
byte[] temp = new byte[2];
|
||||
in.read(temp, 0, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,9 +22,6 @@ import java.io.OutputStream;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.util.CloseableUtil;
|
||||
|
||||
/**
|
||||
* Socket factory for Socks5 proxy.
|
||||
|
@ -32,7 +29,6 @@ import org.jivesoftware.smack.util.CloseableUtil;
|
|||
* @author Atul Aggarwal
|
||||
*/
|
||||
public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
||||
private static final Logger LOGGER = Logger.getLogger(Socks5ProxySocketConnection.class.getName());
|
||||
|
||||
private final ProxyInfo proxy;
|
||||
|
||||
|
@ -43,22 +39,19 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
@Override
|
||||
public void connect(Socket socket, String host, int port, int timeout)
|
||||
throws IOException {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
String proxy_host = proxy.getProxyAddress();
|
||||
int proxy_port = proxy.getProxyPort();
|
||||
String user = proxy.getProxyUsername();
|
||||
String passwd = proxy.getProxyPassword();
|
||||
|
||||
try {
|
||||
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
|
||||
in = socket.getInputStream();
|
||||
out = socket.getOutputStream();
|
||||
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
|
||||
InputStream in = socket.getInputStream();
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
socket.setTcpNoDelay(true);
|
||||
socket.setTcpNoDelay(true);
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int index = 0;
|
||||
byte[] buf = new byte[1024];
|
||||
int index = 0;
|
||||
|
||||
/*
|
||||
+----+----------+----------+
|
||||
|
@ -81,13 +74,13 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
o X'FF' NO ACCEPTABLE METHODS
|
||||
*/
|
||||
|
||||
buf[index++] = 5;
|
||||
buf[index++] = 5;
|
||||
|
||||
buf[index++] = 2;
|
||||
buf[index++] = 0; // NO AUTHENTICATION REQUIRED
|
||||
buf[index++] = 2; // USERNAME/PASSWORD
|
||||
buf[index++] = 2;
|
||||
buf[index++] = 0; // NO AUTHENTICATION REQUIRED
|
||||
buf[index++] = 2; // USERNAME/PASSWORD
|
||||
|
||||
out.write(buf, 0, index);
|
||||
out.write(buf, 0, index);
|
||||
|
||||
/*
|
||||
The server selects from one of the methods given in METHODS, and
|
||||
|
@ -99,17 +92,17 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
| 1 | 1 |
|
||||
+----+--------+
|
||||
*/
|
||||
fill(in, buf, 2);
|
||||
fill(in, buf, 2);
|
||||
|
||||
boolean check = false;
|
||||
switch (buf[1] & 0xff) {
|
||||
case 0: // NO AUTHENTICATION REQUIRED
|
||||
check = true;
|
||||
boolean check = false;
|
||||
switch (buf[1] & 0xff) {
|
||||
case 0: // NO AUTHENTICATION REQUIRED
|
||||
check = true;
|
||||
break;
|
||||
case 2: // USERNAME/PASSWORD
|
||||
if (user == null || passwd == null) {
|
||||
break;
|
||||
case 2: // USERNAME/PASSWORD
|
||||
if (user == null || passwd == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Once the SOCKS V5 server has started, and the client has selected the
|
||||
|
@ -130,20 +123,19 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
PASSWD field that follows. The PASSWD field contains the password
|
||||
association with the given UNAME.
|
||||
*/
|
||||
index = 0;
|
||||
buf[index++] = 1;
|
||||
buf[index++] = (byte) user.length();
|
||||
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
|
||||
System.arraycopy(userBytes, 0, buf, index,
|
||||
user.length());
|
||||
index += user.length();
|
||||
byte[] passwordBytes = passwd.getBytes(StandardCharsets.UTF_8);
|
||||
buf[index++] = (byte) passwordBytes.length;
|
||||
System.arraycopy(passwordBytes, 0, buf, index,
|
||||
passwd.length());
|
||||
index += passwd.length();
|
||||
|
||||
out.write(buf, 0, index);
|
||||
index = 0;
|
||||
buf[index++] = 1;
|
||||
buf[index++] = (byte) user.length();
|
||||
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
|
||||
System.arraycopy(userBytes, 0, buf, index,
|
||||
user.length());
|
||||
index += user.length();
|
||||
byte[] passwordBytes = passwd.getBytes(StandardCharsets.UTF_8);
|
||||
buf[index++] = (byte) passwordBytes.length;
|
||||
System.arraycopy(passwordBytes, 0, buf, index,
|
||||
passwd.length());
|
||||
index += passwd.length();
|
||||
out.write(buf, 0, index);
|
||||
|
||||
/*
|
||||
The server verifies the supplied UNAME and PASSWD, and sends the
|
||||
|
@ -159,19 +151,18 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
`failure' (STATUS value other than X'00') status, it MUST close the
|
||||
connection.
|
||||
*/
|
||||
fill(in, buf, 2);
|
||||
if (buf[1] == 0) {
|
||||
check = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
fill(in, buf, 2);
|
||||
if (buf[1] == 0) {
|
||||
check = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (!check) {
|
||||
CloseableUtil.maybeClose(socket, LOGGER);
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
|
||||
"fail in SOCKS5 proxy");
|
||||
}
|
||||
if (!check) {
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
|
||||
"fail in SOCKS5 proxy");
|
||||
}
|
||||
|
||||
/*
|
||||
The SOCKS request is formed as follows:
|
||||
|
@ -199,21 +190,21 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
order
|
||||
*/
|
||||
|
||||
index = 0;
|
||||
buf[index++] = 5;
|
||||
buf[index++] = 1; // CONNECT
|
||||
buf[index++] = 0;
|
||||
index = 0;
|
||||
buf[index++] = 5;
|
||||
buf[index++] = 1; // CONNECT
|
||||
buf[index++] = 0;
|
||||
|
||||
byte[] hostb = host.getBytes(StandardCharsets.UTF_8);
|
||||
int len = hostb.length;
|
||||
buf[index++] = 3; // DOMAINNAME
|
||||
buf[index++] = (byte) len;
|
||||
System.arraycopy(hostb, 0, buf, index, len);
|
||||
index += len;
|
||||
buf[index++] = (byte) (port >>> 8);
|
||||
buf[index++] = (byte) (port & 0xff);
|
||||
byte[] hostb = host.getBytes(StandardCharsets.UTF_8);
|
||||
int len = hostb.length;
|
||||
buf[index++] = 3; // DOMAINNAME
|
||||
buf[index++] = (byte) len;
|
||||
System.arraycopy(hostb, 0, buf, index, len);
|
||||
index += len;
|
||||
buf[index++] = (byte) (port >>> 8);
|
||||
buf[index++] = (byte) (port & 0xff);
|
||||
|
||||
out.write(buf, 0, index);
|
||||
out.write(buf, 0, index);
|
||||
|
||||
/*
|
||||
The SOCKS request information is sent by the client as soon as it has
|
||||
|
@ -250,35 +241,25 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
o BND.PORT server bound port in network octet order
|
||||
*/
|
||||
|
||||
fill(in, buf, 4);
|
||||
fill(in, buf, 4);
|
||||
|
||||
if (buf[1] != 0) {
|
||||
CloseableUtil.maybeClose(socket, LOGGER);
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
|
||||
"server returns " + buf[1]);
|
||||
}
|
||||
if (buf[1] != 0) {
|
||||
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
|
||||
"server returns " + buf[1]);
|
||||
}
|
||||
|
||||
switch (buf[3] & 0xff) {
|
||||
case 1:
|
||||
fill(in, buf, 6);
|
||||
break;
|
||||
case 3:
|
||||
fill(in, buf, 1);
|
||||
fill(in, buf, (buf[0] & 0xff) + 2);
|
||||
break;
|
||||
case 4:
|
||||
fill(in, buf, 18);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
CloseableUtil.maybeClose(socket, LOGGER);
|
||||
// TODO convert to IOException(e) when minimum Android API level is 9 or higher
|
||||
throw new IOException(e.getLocalizedMessage());
|
||||
switch (buf[3] & 0xff) {
|
||||
case 1:
|
||||
fill(in, buf, 6);
|
||||
break;
|
||||
case 3:
|
||||
fill(in, buf, 1);
|
||||
fill(in, buf, (buf[0] & 0xff) + 2);
|
||||
break;
|
||||
case 4:
|
||||
fill(in, buf, 18);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -606,6 +606,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
try {
|
||||
proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout);
|
||||
} catch (IOException e) {
|
||||
CloseableUtil.maybeClose(socket, LOGGER);
|
||||
hostAddress.setException(e);
|
||||
failedAddresses.add(hostAddress);
|
||||
continue;
|
||||
|
|
Loading…
Reference in a new issue