proxy: modernize code by using (ByteArrayOutput|DataInput)Stream

This commit is contained in:
Florian Schmaus 2019-11-18 21:08:03 +01:00
parent b6ad737aa4
commit 961e56a47c
3 changed files with 113 additions and 88 deletions

View File

@ -16,6 +16,8 @@
*/ */
package org.jivesoftware.smack.proxy; package org.jivesoftware.smack.proxy;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -24,6 +26,8 @@ import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.OutputStreamUtil;
/** /**
* Socket factory for socks4 proxy. * Socket factory for socks4 proxy.
* *
@ -45,10 +49,11 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout); socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024]; ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
int index = 0; byte[] inBuf;
/* /*
1) CONNECT 1) CONNECT
@ -68,26 +73,22 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
of all zero bits. of all zero bits.
*/ */
buf[index++] = 4; outBuf.write(4);
buf[index++] = 1; outBuf.write(1);
buf[index++] = (byte) (port >>> 8); outBuf.write(port >>> 8);
buf[index++] = (byte) (port & 0xff); outBuf.write(port & 0xff);
InetAddress inetAddress = InetAddress.getByName(proxy_host); InetAddress inetAddress = InetAddress.getByName(proxy_host);
byte[] byteAddress = inetAddress.getAddress(); byte[] byteAddress = inetAddress.getAddress();
for (int i = 0; i < byteAddress.length; i++) { outBuf.write(byteAddress);
buf[index++] = byteAddress[i];
}
if (user != null) { if (user != null) {
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8); byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
System.arraycopy(userBytes, 0, buf, index, user.length()); outBuf.write(userBytes);
index += user.length();
} }
buf[index++] = 0; outBuf.write(0);
out.write(buf, 0, index); OutputStreamUtil.writeResetAndFlush(outBuf, out);
out.flush();
/* /*
The SOCKS server checks to see whether such a request should be granted The SOCKS server checks to see whether such a request should be granted
@ -116,26 +117,17 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
The remaining fields are ignored. The remaining fields are ignored.
*/ */
int len = 6; inBuf = new byte[6];
int s = 0; dis.readFully(inBuf);
while (s < len) { if (inBuf[0] != 0) {
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) {
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
"server returns VN " + buf[0]); "server returns VN " + inBuf[0]);
} }
if (buf[1] != 90) { if (inBuf[1] != 90) {
String message = "ProxySOCKS4: server returns CD " + buf[1]; String message = "ProxySOCKS4: server returns CD " + inBuf[1];
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message); throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message);
} }
byte[] temp = new byte[2]; inBuf = new byte[2];
in.read(temp, 0, 2); dis.readFully(inBuf);
} }
} }

View File

@ -16,6 +16,8 @@
*/ */
package org.jivesoftware.smack.proxy; package org.jivesoftware.smack.proxy;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -23,6 +25,8 @@ import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.OutputStreamUtil;
/** /**
* Socket factory for Socks5 proxy. * Socket factory for Socks5 proxy.
* *
@ -46,10 +50,11 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout); socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024]; ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
int index = 0; byte[] inBuf;
/* /*
+----+----------+----------+ +----+----------+----------+
@ -72,14 +77,13 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
o X'FF' NO ACCEPTABLE METHODS o X'FF' NO ACCEPTABLE METHODS
*/ */
buf[index++] = 5; outBuf.write(5);
buf[index++] = 2; outBuf.write(2);
buf[index++] = 0; // NO AUTHENTICATION REQUIRED outBuf.write(0); // NO AUTHENTICATION REQUIRED
buf[index++] = 2; // USERNAME/PASSWORD outBuf.write(2); // USERNAME/PASSWORD
out.write(buf, 0, index); OutputStreamUtil.writeResetAndFlush(outBuf, out);
out.flush();
/* /*
The server selects from one of the methods given in METHODS, and The server selects from one of the methods given in METHODS, and
@ -91,10 +95,11 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
| 1 | 1 | | 1 | 1 |
+----+--------+ +----+--------+
*/ */
fill(in, buf, 2); inBuf = new byte[2];
dis.readFully(inBuf);
boolean check = false; boolean check = false;
switch (buf[1] & 0xff) { switch (inBuf[1] & 0xff) {
case 0: // NO AUTHENTICATION REQUIRED case 0: // NO AUTHENTICATION REQUIRED
check = true; check = true;
break; break;
@ -122,19 +127,16 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
PASSWD field that follows. The PASSWD field contains the password PASSWD field that follows. The PASSWD field contains the password
association with the given UNAME. association with the given UNAME.
*/ */
index = 0; outBuf.write(1);
buf[index++] = 1;
buf[index++] = (byte) user.length();
byte[] userBytes = user.getBytes(StandardCharsets.UTF_8); byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
System.arraycopy(userBytes, 0, buf, index, OutputStreamUtil.writeByteSafe(outBuf, userBytes.length, "Username to long");
user.length()); outBuf.write(userBytes);
index += user.length();
byte[] passwordBytes = passwd.getBytes(StandardCharsets.UTF_8); byte[] passwordBytes = passwd.getBytes(StandardCharsets.UTF_8);
buf[index++] = (byte) passwordBytes.length; OutputStreamUtil.writeByteSafe(outBuf, passwordBytes.length, "Password to long");
System.arraycopy(passwordBytes, 0, buf, index, outBuf.write(passwordBytes);
passwd.length());
index += passwd.length(); OutputStreamUtil.writeResetAndFlush(outBuf, out);
out.write(buf, 0, index);
/* /*
The server verifies the supplied UNAME and PASSWD, and sends the The server verifies the supplied UNAME and PASSWD, and sends the
@ -150,8 +152,9 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
`failure' (STATUS value other than X'00') status, it MUST close the `failure' (STATUS value other than X'00') status, it MUST close the
connection. connection.
*/ */
fill(in, buf, 2); inBuf = new byte[2];
if (buf[1] == 0) { dis.readFully(inBuf);
if (inBuf[1] == 0) {
check = true; check = true;
} }
break; break;
@ -189,22 +192,19 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
order order
*/ */
index = 0; outBuf.write(5);
buf[index++] = 5; outBuf.write(1); // CONNECT
buf[index++] = 1; // CONNECT outBuf.write(0);
buf[index++] = 0;
byte[] hostb = host.getBytes(StandardCharsets.UTF_8); byte[] hostb = host.getBytes(StandardCharsets.UTF_8);
int len = hostb.length; int len = hostb.length;
buf[index++] = 3; // DOMAINNAME outBuf.write(3); // DOMAINNAME
buf[index++] = (byte) len; OutputStreamUtil.writeByteSafe(outBuf, len, "Hostname too long");
System.arraycopy(hostb, 0, buf, index, len); outBuf.write(hostb);
index += len; outBuf.write(port >>> 8);
buf[index++] = (byte) (port >>> 8); outBuf.write(port & 0xff);
buf[index++] = (byte) (port & 0xff);
out.write(buf, 0, index); OutputStreamUtil.writeResetAndFlush(outBuf, out);
out.flush();
/* /*
The SOCKS request information is sent by the client as soon as it has The SOCKS request information is sent by the client as soon as it has
@ -241,39 +241,33 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
o BND.PORT server bound port in network octet order o BND.PORT server bound port in network octet order
*/ */
fill(in, buf, 4); inBuf = new byte[4];
dis.readFully(inBuf);
if (buf[1] != 0) { if (inBuf[1] != 0) {
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
"server returns " + buf[1]); "server returns " + inBuf[1]);
} }
switch (buf[3] & 0xff) { final int addressBytes;
// TODO: Use Byte.toUnsignedInt() once Smack's minimum Android SDK level is 26 or higher.
final int atyp = inBuf[3] & 0xff;
switch (atyp) {
case 1: case 1:
fill(in, buf, 6); addressBytes = 4;
break; break;
case 3: case 3:
fill(in, buf, 1); byte domainnameLengthByte = dis.readByte();
fill(in, buf, (buf[0] & 0xff) + 2); // TODO: Use Byte.toUnsignedInt() once Smack's minimum Android SDK level is 26 or higher.
addressBytes = domainnameLengthByte & 0xff;
break; break;
case 4: case 4:
fill(in, buf, 18); addressBytes = 16;
break; break;
default: default:
throw new IOException("Unknown ATYP value: " + atyp);
} }
inBuf = new byte[addressBytes + 2];
dis.readFully(inBuf);
} }
private static void fill(InputStream in, byte[] buf, int len)
throws IOException {
int s = 0;
while (s < len) {
int i = in.read(buf, s, len - s);
if (i <= 0) {
throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream " +
"is closed");
}
s += i;
}
}
} }

View File

@ -0,0 +1,39 @@
/**
*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamUtil {
public static void writeByteSafe(OutputStream outputStream, int i, String message) throws IOException {
if (i < 0 || i > 0xff) {
throw new IOException(message + ". The value " + i + " is not within the allowed range for bytes");
}
outputStream.write(i);
}
public static void writeResetAndFlush(ByteArrayOutputStream byteArrayOutputStream, OutputStream outputStream)
throws IOException {
byteArrayOutputStream.writeTo(outputStream);
byteArrayOutputStream.reset();
outputStream.flush();
}
}