backport socks codec to 3 branch

This commit is contained in:
alexey 2012-12-20 15:34:12 +04:00 committed by Norman Maurer
parent ca7702f38c
commit 9fcd31a4f4
35 changed files with 3065 additions and 1 deletions

View File

@ -329,7 +329,7 @@
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java15</artifactId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
<ignores>
@ -343,6 +343,8 @@
<ignore>java.net.StandardProtocolFamily</ignore>
<!-- Java Object Serialization -->
<ignore>java.io.ObjectStreamClass</ignore>
<!-- Socks Codec-->
<ignore>java.net.IDN</ignore>
</ignores>
</configuration>
<executions>

View File

@ -0,0 +1,83 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.util.CharsetUtil;
import java.nio.charset.CharsetEncoder;
/**
* An socks auth request.
*
* @see {@link SocksAuthResponse}
* @see {@link SocksAuthRequestDecoder}
*/
public final class SocksAuthRequest extends SocksRequest {
private static final CharsetEncoder asciiEncoder = CharsetUtil.getEncoder(CharsetUtil.US_ASCII);
private static final SubnegotiationVersion SUBNEGOTIATION_VERSION = SubnegotiationVersion.AUTH_PASSWORD;
private final String username;
private final String password;
public SocksAuthRequest(String username, String password) {
super(SocksRequestType.AUTH);
if (username == null) {
throw new NullPointerException("username");
}
if (password == null) {
throw new NullPointerException("username");
}
if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) {
throw new IllegalArgumentException(" username: " + username + " or password: " + password +
" values should be in pure ascii");
}
if (username.length() > 255) {
throw new IllegalArgumentException(username + " exceeds 255 char limit");
}
if (password.length() > 255) {
throw new IllegalArgumentException(password + " exceeds 255 char limit");
}
this.username = username;
this.password = password;
}
/**
* Returns username that needs to be authenticated
*
* @return username that needs to be authenticated
*/
public String getUsername() {
return username;
}
/**
* Returns password that needs to be validated
*
* @return password that needs to be validated
*/
public String getPassword() {
return password;
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) throws Exception {
channelBuffer.writeByte(SUBNEGOTIATION_VERSION.getByteValue());
channelBuffer.writeByte(username.length());
channelBuffer.writeBytes(username.getBytes("US-ASCII"));
channelBuffer.writeByte(password.length());
channelBuffer.writeBytes(password.getBytes("US-ASCII"));
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import org.jboss.netty.util.CharsetUtil;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksAuthRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksAuthRequestDecoder extends ReplayingDecoder<SocksAuthRequestDecoder.State> {
private static final String name = "SOCKS_AUTH_REQUEST_DECODER";
public static String getName() {
return name;
}
private SocksMessage.SubnegotiationVersion version;
private int fieldLength;
private String username;
private String password;
private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST;
public SocksAuthRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.SubnegotiationVersion.fromByte(buffer.readByte());
if (version != SocksMessage.SubnegotiationVersion.AUTH_PASSWORD) {
break;
}
checkpoint(State.READ_USERNAME);
}
case READ_USERNAME: {
fieldLength = buffer.readByte();
username = buffer.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
checkpoint(State.READ_PASSWORD);
}
case READ_PASSWORD: {
fieldLength = buffer.readByte();
password = buffer.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
msg = new SocksAuthRequest(username, password);
}
}
ctx.getPipeline().remove(this);
return msg;
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_USERNAME,
READ_PASSWORD
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An socks auth response.
*
* @see SocksAuthRequest
* @see SocksAuthResponseDecoder
*/
public final class SocksAuthResponse extends SocksResponse {
private static final SubnegotiationVersion SUBNEGOTIATION_VERSION = SubnegotiationVersion.AUTH_PASSWORD;
private final AuthStatus authStatus;
public SocksAuthResponse(AuthStatus authStatus) {
super(SocksResponseType.AUTH);
if (authStatus == null) {
throw new NullPointerException("authStatus");
}
this.authStatus = authStatus;
}
/**
* Returns the {@link AuthStatus} of this {@link SocksAuthResponse}
*/
public AuthStatus getAuthStatus() {
return authStatus;
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) {
channelBuffer.writeByte(SUBNEGOTIATION_VERSION.getByteValue());
channelBuffer.writeByte(authStatus.getByteValue());
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksAuthResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksAuthResponseDecoder extends ReplayingDecoder<SocksAuthResponseDecoder.State> {
private static final String name = "SOCKS_AUTH_RESPONSE_DECODER";
public static String getName() {
return name;
}
private SocksMessage.SubnegotiationVersion version;
private SocksMessage.AuthStatus authStatus;
private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE;
public SocksAuthResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.SubnegotiationVersion.fromByte(buffer.readByte());
if (version != SocksMessage.SubnegotiationVersion.AUTH_PASSWORD) {
break;
}
checkpoint(State.READ_AUTH_RESPONSE);
}
case READ_AUTH_RESPONSE: {
authStatus = SocksMessage.AuthStatus.fromByte(buffer.readByte());
msg = new SocksAuthResponse(authStatus);
}
}
ctx.getPipeline().remove(this);
return msg;
}
public enum State {
CHECK_PROTOCOL_VERSION,
READ_AUTH_RESPONSE
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.util.NetUtil;
import org.jboss.netty.util.internal.DetectionUtil;
import java.net.IDN;
/**
* An socks cmd request.
*
* @see {@link SocksCmdResponse}
* @see {@link SocksCmdRequestDecoder}
*/
public final class SocksCmdRequest extends SocksRequest {
private final CmdType cmdType;
private final AddressType addressType;
private final String host;
private final int port;
public SocksCmdRequest(CmdType cmdType, AddressType addressType, String host, int port) {
super(SocksRequestType.CMD);
if (DetectionUtil.javaVersion() < 6) {
throw new IllegalStateException("Only supported with Java version 6+");
}
if (cmdType == null) {
throw new NullPointerException("cmdType");
}
if (addressType == null) {
throw new NullPointerException("addressType");
}
if (host == null) {
throw new NullPointerException("host");
}
switch (addressType) {
case IPv4:
if (!NetUtil.isValidIpV4Address(host)) {
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
}
break;
case DOMAIN:
if (IDN.toASCII(host).length() > 255) {
throw new IllegalArgumentException(host + " IDN: " + IDN.toASCII(host) + " exceeds 255 char limit");
}
break;
case IPv6:
if (!NetUtil.isValidIpV6Address(host)) {
throw new IllegalArgumentException(host + " is not a valid IPv6 address");
}
break;
case UNKNOWN:
break;
}
if (port < 0 && port >= 65535) {
throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536");
}
this.cmdType = cmdType;
this.addressType = addressType;
this.host = IDN.toASCII(host);
this.port = port;
}
/**
* Returns the {@link SocksMessage.CmdType} of this {@link SocksCmdRequest}
*/
public CmdType getCmdType() {
return cmdType;
}
/**
* Returns the {@link AddressType} of this {@link SocksCmdRequest}
*/
public AddressType getAddressType() {
return addressType;
}
/**
* Returns host that is used as a parameter in {@link SocksMessage.CmdType}
*/
public String getHost() {
return IDN.toUnicode(host);
}
/**
* Returns port that is used as a parameter in {@link SocksMessage.CmdType}
*/
public int getPort() {
return port;
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) throws Exception {
channelBuffer.writeByte(getProtocolVersion().getByteValue());
channelBuffer.writeByte(cmdType.getByteValue());
channelBuffer.writeByte(0x00);
channelBuffer.writeByte(addressType.getByteValue());
switch (addressType) {
case IPv4: {
channelBuffer.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
channelBuffer.writeShort(port);
break;
}
case DOMAIN: {
channelBuffer.writeByte(host.length());
channelBuffer.writeBytes(host.getBytes("US-ASCII"));
channelBuffer.writeShort(port);
break;
}
case IPv6: {
channelBuffer.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
channelBuffer.writeShort(port);
break;
}
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import org.jboss.netty.util.CharsetUtil;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksCmdRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksCmdRequestDecoder extends ReplayingDecoder<SocksCmdRequestDecoder.State> {
private static final String name = "SOCKS_CMD_REQUEST_DECODER";
public static String getName() {
return name;
}
private SocksMessage.ProtocolVersion version;
private int fieldLength;
private SocksMessage.CmdType cmdType;
private SocksMessage.AddressType addressType;
private byte reserved;
private String host;
private int port;
private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST;
public SocksCmdRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.ProtocolVersion.fromByte(buffer.readByte());
if (version != SocksMessage.ProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdType = SocksMessage.CmdType.fromByte(buffer.readByte());
reserved = buffer.readByte();
addressType = SocksMessage.AddressType.fromByte(buffer.readByte());
checkpoint(State.READ_CMD_ADDRESS);
}
case READ_CMD_ADDRESS: {
switch (addressType) {
case IPv4: {
host = SocksCommonUtils.intToIp(buffer.readInt());
port = buffer.readUnsignedShort();
msg = new SocksCmdRequest(cmdType, addressType, host, port);
break;
}
case DOMAIN: {
fieldLength = buffer.readByte();
host = buffer.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
port = buffer.readUnsignedShort();
msg = new SocksCmdRequest(cmdType, addressType, host, port);
break;
}
case IPv6: {
host = SocksCommonUtils.ipv6toStr(buffer.readBytes(16).array());
port = buffer.readUnsignedShort();
msg = new SocksCmdRequest(cmdType, addressType, host, port);
break;
}
case UNKNOWN:
break;
}
}
}
ctx.getPipeline().remove(this);
return msg;
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_CMD_HEADER,
READ_CMD_ADDRESS
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An socks cmd response.
*
* @see SocksCmdRequest
* @see SocksCmdResponseDecoder
*/
public final class SocksCmdResponse extends SocksResponse {
private final CmdStatus cmdStatus;
private final AddressType addressType;
// All arrays are initialized on construction time to 0/false/null remove array Initialization
private static final byte[] IPv4_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00};
private static final byte[] IPv6_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
public SocksCmdResponse(CmdStatus cmdStatus, AddressType addressType) {
super(SocksResponseType.CMD);
if (cmdStatus == null) {
throw new NullPointerException("cmdStatus");
}
if (addressType == null) {
throw new NullPointerException("addressType");
}
this.cmdStatus = cmdStatus;
this.addressType = addressType;
}
/**
* Returns the {@link CmdStatus} of this {@link SocksCmdResponse}
*
* @return The {@link CmdStatus} of this {@link SocksCmdResponse}
*/
public CmdStatus getCmdStatus() {
return cmdStatus;
}
/**
* Returns the {@link AddressType} of this {@link SocksCmdResponse}
*
* @return The {@link AddressType} of this {@link SocksCmdResponse}
*/
public AddressType getAddressType() {
return addressType;
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) {
channelBuffer.writeByte(getProtocolVersion().getByteValue());
channelBuffer.writeByte(cmdStatus.getByteValue());
channelBuffer.writeByte(0x00);
channelBuffer.writeByte(addressType.getByteValue());
switch (addressType) {
case IPv4: {
channelBuffer.writeBytes(IPv4_HOSTNAME_ZEROED);
channelBuffer.writeShort(0);
break;
}
case DOMAIN: {
channelBuffer.writeByte(1); // domain length
channelBuffer.writeByte(0); // domain value
channelBuffer.writeShort(0); // port value
break;
}
case IPv6: {
channelBuffer.writeBytes(IPv6_HOSTNAME_ZEROED);
channelBuffer.writeShort(0);
break;
}
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import org.jboss.netty.util.CharsetUtil;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksCmdResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksCmdResponseDecoder extends ReplayingDecoder<SocksCmdResponseDecoder.State> {
private static final String name = "SOCKS_CMD_RESPONSE_DECODER";
public static String getName() {
return name;
}
private SocksMessage.ProtocolVersion version;
private int fieldLength;
private SocksMessage.CmdStatus cmdStatus;
private SocksMessage.AddressType addressType;
private byte reserved;
private String host;
private int port;
private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE;
public SocksCmdResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.ProtocolVersion.fromByte(buffer.readByte());
if (version != SocksMessage.ProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdStatus = SocksMessage.CmdStatus.fromByte(buffer.readByte());
reserved = buffer.readByte();
addressType = SocksMessage.AddressType.fromByte(buffer.readByte());
checkpoint(State.READ_CMD_ADDRESS);
}
case READ_CMD_ADDRESS: {
switch (addressType) {
case IPv4: {
host = SocksCommonUtils.intToIp(buffer.readInt());
port = buffer.readUnsignedShort();
msg = new SocksCmdResponse(cmdStatus, addressType);
break;
}
case DOMAIN: {
fieldLength = buffer.readByte();
host = buffer.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
port = buffer.readUnsignedShort();
msg = new SocksCmdResponse(cmdStatus, addressType);
break;
}
case IPv6: {
host = SocksCommonUtils.ipv6toStr(buffer.readBytes(16).array());
port = buffer.readUnsignedShort();
msg = new SocksCmdResponse(cmdStatus, addressType);
break;
}
case UNKNOWN:
break;
}
}
}
ctx.getPipeline().remove(this);
return msg;
}
public enum State {
CHECK_PROTOCOL_VERSION,
READ_CMD_HEADER,
READ_CMD_ADDRESS
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
final class SocksCommonUtils {
public static final SocksRequest UNKNOWN_SOCKS_REQUEST = new UnknownSocksRequest();
public static final SocksResponse UNKNOWN_SOCKS_RESPONSE = new UnknownSocksResponse();
private static final int SECOND_ADDRESS_OCTET_SHIFT = 16;
private static final int FIRST_ADDRESS_OCTET_SHIFT = 24;
private static final int THIRD_ADDRESS_OCTET_SHIFT = 8;
private static final int XOR_DEFAULT_VALUE = 0xff;
/**
* A constructor to stop this class being constructed.
*/
private SocksCommonUtils() {
//NOOP
}
public static String intToIp(int i) {
return String.valueOf(i >> FIRST_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
(i >> SECOND_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
(i >> THIRD_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
(i & XOR_DEFAULT_VALUE);
}
private static final char[] ipv6conseqZeroFiller = {':', ':'};
private static final char ipv6hextetSeparator = ':';
/*
* Convert numeric IPv6 to compressed format, where
* the longest sequence of 0's (with 2 or more 0's) is replaced with "::"
*/
public static String ipv6toCompressedForm(byte[] src) {
assert src.length == 16;
//Find the longest sequence of 0's
//start of compressed region (hextet index)
int cmprHextet = -1;
//length of compressed region
int cmprSize = 0;
for (int hextet = 0; hextet < 8;) {
int curByte = hextet * 2;
int size = 0;
while (curByte < src.length && src[curByte] == 0
&& src[curByte + 1] == 0) {
curByte += 2;
size++;
}
if (size > cmprSize) {
cmprHextet = hextet;
cmprSize = size;
}
hextet = curByte / 2 + 1;
}
if (cmprHextet == -1 || cmprSize < 2) {
//No compression can be applied
return ipv6toStr(src);
}
StringBuilder sb = new StringBuilder(39);
ipv6toStr(sb, src, 0, cmprHextet);
sb.append(ipv6conseqZeroFiller);
ipv6toStr(sb, src, cmprHextet + cmprSize, 8);
return sb.toString();
}
/*
* Convert numeric IPv6 to standard (non-compressed) format.
*
* Borrowed from Inet6Address.java #numericToTextFormat(byte[])
* Changed StringBuffer -> StringBuilder and ":" -> ':' for performance.
*/
public static String ipv6toStr(byte[] src) {
assert src.length == 16;
StringBuilder sb = new StringBuilder(39);
ipv6toStr(sb, src, 0, 8);
return sb.toString();
}
private static void ipv6toStr(StringBuilder sb, byte[] src,
int fromHextet, int toHextet) {
for (int i = fromHextet; i < toHextet; i++) {
sb.append(Integer.toHexString(src[i << 1] << 8 & 0xff00
| src[(i << 1) + 1] & 0xff));
if (i < toHextet - 1) {
sb.append(ipv6hextetSeparator);
}
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import java.util.Collections;
import java.util.List;
/**
* An socks init request.
*
* @see {@link SocksInitResponse}
* @see {@link SocksInitRequestDecoder}
*/
public final class SocksInitRequest extends SocksRequest {
private final List<AuthScheme> authSchemes;
public SocksInitRequest(List<AuthScheme> authSchemes) {
super(SocksRequestType.INIT);
if (authSchemes == null) {
throw new NullPointerException("authSchemes");
}
this.authSchemes = authSchemes;
}
/**
* Returns the List<{@link AuthScheme}> of this {@link SocksInitRequest}
*/
public List<AuthScheme> getAuthSchemes() {
return Collections.unmodifiableList(authSchemes);
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) {
channelBuffer.writeByte(getProtocolVersion().getByteValue());
channelBuffer.writeByte(authSchemes.size());
for (AuthScheme authScheme : authSchemes) {
channelBuffer.writeByte(authScheme.getByteValue());
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import java.util.ArrayList;
import java.util.List;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksInitRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksInitRequestDecoder extends ReplayingDecoder<SocksInitRequestDecoder.State> {
private static final String name = "SOCKS_INIT_REQUEST_DECODER";
public static String getName() {
return name;
}
private final List<SocksMessage.AuthScheme> authSchemes = new ArrayList<SocksMessage.AuthScheme>();
private SocksMessage.ProtocolVersion version;
private byte authSchemeNum;
private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST;
public SocksInitRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.ProtocolVersion.fromByte(buffer.readByte());
if (version != SocksMessage.ProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_AUTH_SCHEMES);
}
case READ_AUTH_SCHEMES: {
authSchemes.clear();
authSchemeNum = buffer.readByte();
for (int i = 0; i < authSchemeNum; i++) {
authSchemes.add(SocksMessage.AuthScheme.fromByte(buffer.readByte()));
}
msg = new SocksInitRequest(authSchemes);
break;
}
}
ctx.getPipeline().remove(this);
return msg;
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_AUTH_SCHEMES
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An socks init response.
*
* @see {@link SocksInitRequest}
* @see {@link SocksInitResponseDecoder}
*/
public final class SocksInitResponse extends SocksResponse {
private final AuthScheme authScheme;
public SocksInitResponse(AuthScheme authScheme) {
super(SocksResponseType.INIT);
if (authScheme == null) {
throw new NullPointerException("authScheme");
}
this.authScheme = authScheme;
}
/**
* Returns the {@link AuthScheme} of this {@link SocksInitResponse}
*/
public AuthScheme getAuthScheme() {
return authScheme;
}
@Override
public void encodeAsByteBuf(ChannelBuffer channelBuffer) {
channelBuffer.writeByte(getProtocolVersion().getByteValue());
channelBuffer.writeByte(authScheme.getByteValue());
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
/**
* Decodes {@link ChannelBuffer}s into {@link SocksInitResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksInitResponseDecoder extends ReplayingDecoder<SocksInitResponseDecoder.State> {
private static final String name = "SOCKS_INIT_RESPONSE_DECODER";
public static String getName() {
return name;
}
private SocksMessage.ProtocolVersion version;
private SocksMessage.AuthScheme authScheme;
private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE;
public SocksInitResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, State state) throws Exception {
switch (state) {
case CHECK_PROTOCOL_VERSION: {
version = SocksMessage.ProtocolVersion.fromByte(buffer.readByte());
if (version != SocksMessage.ProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_PREFFERED_AUTH_TYPE);
}
case READ_PREFFERED_AUTH_TYPE: {
authScheme = SocksMessage.AuthScheme.fromByte(buffer.readByte());
msg = new SocksInitResponse(authScheme);
break;
}
}
ctx.getPipeline().remove(this);
return msg;
}
public enum State {
CHECK_PROTOCOL_VERSION,
READ_PREFFERED_AUTH_TYPE
}
}

View File

@ -0,0 +1,252 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An abstract class that defines a SocksMessage, providing common properties for
* {@link SocksRequest} and {@link SocksResponse}.
*
* @see SocksRequest
* @see SocksResponse
*/
public abstract class SocksMessage {
private final MessageType messageType;
private final ProtocolVersion protocolVersion = ProtocolVersion.SOCKS5;
protected SocksMessage(MessageType messageType) {
if (messageType == null) {
throw new NullPointerException("messageType");
}
this.messageType = messageType;
}
/**
* Returns the {@link MessageType} of this {@link SocksMessage}
*
* @return The {@link MessageType} of this {@link SocksMessage}
*/
public MessageType getMessageType() {
return messageType;
}
public enum MessageType {
REQUEST,
RESPONSE,
UNKNOWN
}
public enum AuthScheme {
NO_AUTH((byte) 0x00),
AUTH_GSSAPI((byte) 0x01),
AUTH_PASSWORD((byte) 0x02),
UNKNOWN((byte) 0xff);
private final byte b;
AuthScheme(byte b) {
this.b = b;
}
public static AuthScheme fromByte(byte b) {
for (AuthScheme code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte getByteValue() {
return b;
}
}
public enum CmdType {
CONNECT((byte) 0x01),
BIND((byte) 0x02),
UDP((byte) 0x03),
UNKNOWN((byte) 0xff);
private final byte b;
CmdType(byte b) {
this.b = b;
}
public static CmdType fromByte(byte b) {
for (CmdType code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte getByteValue() {
return b;
}
}
public enum AddressType {
IPv4((byte) 0x01),
DOMAIN((byte) 0x03),
IPv6((byte) 0x04),
UNKNOWN((byte) 0xff);
private final byte b;
AddressType(byte b) {
this.b = b;
}
public static AddressType fromByte(byte b) {
for (AddressType code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte getByteValue() {
return b;
}
}
public enum AuthStatus {
SUCCESS((byte) 0x00),
FAILURE((byte) 0xff);
private final byte b;
AuthStatus(byte b) {
this.b = b;
}
public static AuthStatus fromByte(byte b) {
for (AuthStatus code : values()) {
if (code.b == b) {
return code;
}
}
return FAILURE;
}
public byte getByteValue() {
return b;
}
}
public enum CmdStatus {
SUCCESS((byte) 0x00),
FAILURE((byte) 0x01),
FORBIDDEN((byte) 0x02),
NETWORK_UNREACHABLE((byte) 0x03),
HOST_UNREACHABLE((byte) 0x04),
REFUSED((byte) 0x05),
TTL_EXPIRED((byte) 0x06),
COMMAND_NOT_SUPPORTED((byte) 0x07),
ADDRESS_NOT_SUPPORTED((byte) 0x08),
UNASSIGNED((byte) 0xff);
private final byte b;
CmdStatus(byte b) {
this.b = b;
}
public static CmdStatus fromByte(byte b) {
for (CmdStatus code : values()) {
if (code.b == b) {
return code;
}
}
return UNASSIGNED;
}
public byte getByteValue() {
return b;
}
}
public enum ProtocolVersion {
SOCKS4a((byte) 0x04),
SOCKS5((byte) 0x05),
UNKNOWN((byte) 0xff);
private final byte b;
ProtocolVersion(byte b) {
this.b = b;
}
public static ProtocolVersion fromByte(byte b) {
for (ProtocolVersion code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte getByteValue() {
return b;
}
}
public enum SubnegotiationVersion {
AUTH_PASSWORD((byte) 0x01),
UNKNOWN((byte) 0xff);
private final byte b;
SubnegotiationVersion(byte b) {
this.b = b;
}
public static SubnegotiationVersion fromByte(byte b) {
for (SubnegotiationVersion code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte getByteValue() {
return b;
}
}
/**
* Returns the {@link ProtocolVersion} of this {@link SocksMessage}
*
* @return The {@link ProtocolVersion} of this {@link SocksMessage}
*/
public ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
/**
* Encode socks message into its byte representation and write it into byteBuf
*
* @see ChannelBuffer
*/
public abstract void encodeAsByteBuf(ChannelBuffer channelBuffer) throws Exception;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
/**
* Encodes an {@link SocksMessage} into a {@link ChannelBuffer}.
* {@link OneToOneEncoder} implementation.
* Use this with {@link SocksInitRequest}, {@link SocksInitResponse}, {@link SocksAuthRequest},
* {@link SocksAuthResponse}, {@link SocksCmdRequest} and {@link SocksCmdResponse}
*/
@ChannelHandler.Sharable
public class SocksMessageEncoder extends OneToOneEncoder {
private static final String name = "SOCKS_MESSAGE_ENCODER";
private static final int DEFAULT_ENCODER_BUFFER_SIZE = 1024;
public static String getName() {
return name;
}
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
ChannelBuffer buffer = null;
if (msg instanceof SocksMessage) {
buffer = ChannelBuffers.buffer(DEFAULT_ENCODER_BUFFER_SIZE);
((SocksMessage) msg).encodeAsByteBuf(buffer);
}
return buffer;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
/**
* An abstract class that defines a SocksRequest, providing common properties for
* {@link SocksInitRequest}, {@link SocksAuthRequest}, {@link SocksCmdRequest} and {@link UnknownSocksRequest}.
*
* @see {@link SocksInitRequest}
* @see {@link SocksAuthRequest}
* @see {@link SocksCmdRequest}
* @see {@link UnknownSocksRequest}
*/
public abstract class SocksRequest extends SocksMessage {
private final SocksRequestType socksRequestType;
protected SocksRequest(SocksRequestType socksRequestType) {
super(MessageType.REQUEST);
if (socksRequestType == null) {
throw new NullPointerException("socksRequestType");
}
this.socksRequestType = socksRequestType;
}
/**
* Returns socks request type
*
* @return socks request type
*/
public SocksRequestType getSocksRequestType() {
return socksRequestType;
}
/**
* Type of socks request
*/
public enum SocksRequestType {
INIT,
AUTH,
CMD,
UNKNOWN
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
/**
* An abstract class that defines a SocksResponse, providing common properties for
* {@link SocksInitResponse}, {@link SocksAuthResponse}, {@link SocksCmdResponse} and {@link UnknownSocksResponse}.
*
* @see {@link SocksInitResponse}
* @see {@link SocksAuthResponse}
* @see {@link SocksCmdResponse}
* @see {@link UnknownSocksResponse}
*/
public abstract class SocksResponse extends SocksMessage {
private final SocksResponseType socksResponseType;
protected SocksResponse(SocksResponseType socksResponseType) {
super(MessageType.RESPONSE);
if (socksResponseType == null) {
throw new NullPointerException("socksResponseType");
}
this.socksResponseType = socksResponseType;
}
/**
* Returns socks response type
*
* @return socks response type
*/
public SocksResponseType getSocksResponseType() {
return socksResponseType;
}
/**
* Type of socks response
*/
public enum SocksResponseType {
INIT,
AUTH,
CMD,
UNKNOWN
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An unknown socks message.
*
* @see {@link UnknownSocksRequest}
* @see {@link UnknownSocksResponse}
*/
public final class UnknownSocksMessage extends SocksMessage {
public UnknownSocksMessage() {
super(MessageType.UNKNOWN);
}
@Override
public void encodeAsByteBuf(ChannelBuffer byteBuf) {
// NOOP
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An unknown socks request.
*
* @see {@link SocksInitRequestDecoder}
* @see {@link SocksAuthRequestDecoder}
* @see {@link SocksCmdRequestDecoder}
*/
public final class UnknownSocksRequest extends SocksRequest {
public UnknownSocksRequest() {
super(SocksRequestType.UNKNOWN);
}
@Override
public void encodeAsByteBuf(ChannelBuffer buffer) {
// NOOP
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* An unknown socks response.
*
* @see {@link SocksInitResponseDecoder}
* @see {@link SocksAuthResponseDecoder}
* @see {@link SocksCmdResponseDecoder}
*/
public final class UnknownSocksResponse extends SocksResponse {
public UnknownSocksResponse() {
super(SocksResponseType.UNKNOWN);
}
@Override
public void encodeAsByteBuf(ChannelBuffer buffer) {
// NOOP
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.
*/
/**
* Encoder, decoder and their related message types for Socks.
*
* Be aware it is only supported on Java 6+.
*/
package org.jboss.netty.handler.codec.socks;

View File

@ -0,0 +1,593 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.util;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.StringTokenizer;
/**
* A class that holds a number of network-related constants.
* <p/>
* This class borrowed some of its methods from a modified fork of the
* <a href="http://svn.apache.org/repos/asf/harmony/enhanced/java/branches/java6/classlib/modules/luni/
* src/main/java/org/apache/harmony/luni/util/Inet6Util.java">Inet6Util class</a> which was part of Apache Harmony.
*/
public final class NetUtil {
/**
* The {@link InetAddress} representing the host machine
* <p/>
* We cache this because some machines take almost forever to return from
* {@link InetAddress}.getLocalHost(). This may be due to incorrect
* configuration of the hosts and DNS client configuration files.
*/
public static final InetAddress LOCALHOST;
/**
* The loopback {@link NetworkInterface} on the current machine
*/
public static final NetworkInterface LOOPBACK_IF;
/**
* The SOMAXCONN value of the current machine. If failed to get the value, 3072 is used as a
* default value.
*/
public static final int SOMAXCONN;
/**
* The logger being used by this class
*/
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(NetUtil.class);
static {
//Start the process of discovering localhost
InetAddress localhost;
try {
localhost = InetAddress.getLocalHost();
validateHost(localhost);
} catch (IOException e) {
// The default local host names did not work. Try hard-coded IPv4 address.
try {
localhost = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
validateHost(localhost);
} catch (IOException e1) {
// The hard-coded IPv4 address did not work. Try hard coded IPv6 address.
try {
localhost = InetAddress.getByAddress(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
validateHost(localhost);
} catch (IOException e2) {
throw new Error("Failed to resolve localhost - incorrect network configuration?", e2);
}
}
}
LOCALHOST = localhost;
//Prepare to get the local NetworkInterface
NetworkInterface loopbackInterface;
try {
//Automatically get the loopback interface
loopbackInterface = NetworkInterface.getByInetAddress(LOCALHOST);
} catch (SocketException e) {
//No? Alright. There is a backup!
loopbackInterface = null;
}
//Check to see if a network interface was not found
if (loopbackInterface == null) {
try {
//Start iterating over all network interfaces
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
interfaces.hasMoreElements();) {
//Get the "next" interface
NetworkInterface networkInterface = interfaces.nextElement();
//Check to see if the interface is a loopback interface
if (networkInterface.isLoopback()) {
//Phew! The loopback interface was found.
loopbackInterface = networkInterface;
//No need to keep iterating
break;
}
}
} catch (SocketException e) {
//Nope. Can't do anything else, sorry!
logger.error("Failed to enumerate network interfaces", e);
}
}
//Set the loopback interface constant
LOOPBACK_IF = loopbackInterface;
int somaxconn = 3072;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader("/proc/sys/net/core/somaxconn"));
somaxconn = Integer.parseInt(in.readLine());
} catch (Exception e) {
// Failed to get SOMAXCONN
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
// Ignored.
}
}
}
SOMAXCONN = somaxconn;
}
private static void validateHost(InetAddress host) throws IOException {
ServerSocket ss = null;
Socket s1 = null;
Socket s2 = null;
try {
ss = new ServerSocket();
ss.setReuseAddress(false);
ss.bind(new InetSocketAddress(host, 0));
s1 = new Socket(host, ss.getLocalPort());
s2 = ss.accept();
} finally {
if (s2 != null) {
try {
s2.close();
} catch (IOException e) {
// Ignore
}
}
if (s1 != null) {
try {
s1.close();
} catch (IOException e) {
// Ignore
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
// Ignore
}
}
}
}
/**
* Creates an byte[] based on an ipAddressString. No error handling is
* performed here.
*/
public static byte[] createByteArrayFromIpAddressString(
String ipAddressString) {
if (isValidIpV4Address(ipAddressString)) {
StringTokenizer tokenizer = new StringTokenizer(ipAddressString,
".");
String token;
int tempInt;
byte[] byteAddress = new byte[4];
for (int i = 0; i < 4; i++) {
token = tokenizer.nextToken();
tempInt = Integer.parseInt(token);
byteAddress[i] = (byte) tempInt;
}
return byteAddress;
}
if (isValidIpV6Address(ipAddressString)) {
if (ipAddressString.charAt(0) == '[') {
ipAddressString = ipAddressString.substring(1, ipAddressString
.length() - 1);
}
StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.",
true);
ArrayList<String> hexStrings = new ArrayList<String>();
ArrayList<String> decStrings = new ArrayList<String>();
String token = "";
String prevToken = "";
int doubleColonIndex = -1; // If a double colon exists, we need to
// insert 0s.
// Go through the tokens, including the seperators ':' and '.'
// When we hit a : or . the previous token will be added to either
// the hex list or decimal list. In the case where we hit a ::
// we will save the index of the hexStrings so we can add zeros
// in to fill out the string
while (tokenizer.hasMoreTokens()) {
prevToken = token;
token = tokenizer.nextToken();
if (":".equals(token)) {
if (":".equals(prevToken)) {
doubleColonIndex = hexStrings.size();
} else if (!prevToken.isEmpty()) {
hexStrings.add(prevToken);
}
} else if (".".equals(token)) {
decStrings.add(prevToken);
}
}
if (":".equals(prevToken)) {
if (":".equals(token)) {
doubleColonIndex = hexStrings.size();
} else {
hexStrings.add(token);
}
} else if (".".equals(prevToken)) {
decStrings.add(token);
}
// figure out how many hexStrings we should have
// also check if it is a IPv4 address
int hexStringsLength = 8;
// If we have an IPv4 address tagged on at the end, subtract
// 4 bytes, or 2 hex words from the total
if (!decStrings.isEmpty()) {
hexStringsLength -= 2;
}
// if we hit a double Colon add the appropriate hex strings
if (doubleColonIndex != -1) {
int numberToInsert = hexStringsLength - hexStrings.size();
for (int i = 0; i < numberToInsert; i++) {
hexStrings.add(doubleColonIndex, "0");
}
}
byte[] ipByteArray = new byte[16];
// Finally convert these strings to bytes...
for (int i = 0; i < hexStrings.size(); i++) {
convertToBytes(hexStrings.get(i), ipByteArray, i * 2);
}
// Now if there are any decimal values, we know where they go...
for (int i = 0; i < decStrings.size(); i++) {
ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings
.get(i)) & 255);
}
return ipByteArray;
}
return null;
}
/**
* Converts a 4 character hex word into a 2 byte word equivalent
*/
private static void convertToBytes(String hexWord, byte[] ipByteArray,
int byteIndex) {
int hexWordLength = hexWord.length();
int hexWordIndex = 0;
ipByteArray[byteIndex] = 0;
ipByteArray[byteIndex + 1] = 0;
int charValue;
// high order 4 bits of first byte
if (hexWordLength > 3) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex] |= charValue << 4;
}
// low order 4 bits of the first byte
if (hexWordLength > 2) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex] |= charValue;
}
// high order 4 bits of second byte
if (hexWordLength > 1) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex + 1] |= charValue << 4;
}
// low order 4 bits of the first byte
charValue = getIntValue(hexWord.charAt(hexWordIndex));
ipByteArray[byteIndex + 1] |= charValue & 15;
}
static int getIntValue(char c) {
switch (c) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
}
c = Character.toLowerCase(c);
switch (c) {
case 'a':
return 10;
case 'b':
return 11;
case 'c':
return 12;
case 'd':
return 13;
case 'e':
return 14;
case 'f':
return 15;
}
return 0;
}
public static boolean isValidIpV6Address(String ipAddress) {
int length = ipAddress.length();
boolean doubleColon = false;
int numberOfColons = 0;
int numberOfPeriods = 0;
int numberOfPercent = 0;
StringBuilder word = new StringBuilder();
char c = 0;
char prevChar;
int offset = 0; // offset for [] ip addresses
if (length < 2) {
return false;
}
for (int i = 0; i < length; i++) {
prevChar = c;
c = ipAddress.charAt(i);
switch (c) {
// case for an open bracket [x:x:x:...x]
case '[':
if (i != 0) {
return false; // must be first character
}
if (ipAddress.charAt(length - 1) != ']') {
return false; // must have a close ]
}
offset = 1;
if (length < 4) {
return false;
}
break;
// case for a closed bracket at end of IP [x:x:x:...x]
case ']':
if (i != length - 1) {
return false; // must be last charcter
}
if (ipAddress.charAt(0) != '[') {
return false; // must have a open [
}
break;
// case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d
case '.':
numberOfPeriods++;
if (numberOfPeriods > 3) {
return false;
}
if (!isValidIp4Word(word.toString())) {
return false;
}
if (numberOfColons != 6 && !doubleColon) {
return false;
}
// a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
// IPv4 ending, otherwise 7 :'s is bad
if (numberOfColons == 7 && ipAddress.charAt(offset) != ':'
&& ipAddress.charAt(1 + offset) != ':') {
return false;
}
word.delete(0, word.length());
break;
case ':':
// FIX "IP6 mechanism syntax #ip6-bad1"
// An IPV6 address cannot start with a single ":".
// Either it can starti with "::" or with a number.
if (i == offset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) {
return false;
}
// END FIX "IP6 mechanism syntax #ip6-bad1"
numberOfColons++;
if (numberOfColons > 7) {
return false;
}
if (numberOfPeriods > 0) {
return false;
}
if (prevChar == ':') {
if (doubleColon) {
return false;
}
doubleColon = true;
}
word.delete(0, word.length());
break;
case '%':
if (numberOfColons == 0) {
return false;
}
numberOfPercent++;
// validate that the stuff after the % is valid
if (i + 1 >= length) {
// in this case the percent is there but no number is
// available
return false;
}
try {
Integer.parseInt(ipAddress.substring(i + 1));
} catch (NumberFormatException e) {
// right now we just support an integer after the % so if
// this is not
// what is there then return
return false;
}
break;
default:
if (numberOfPercent == 0) {
if (word != null && word.length() > 3) {
return false;
}
if (!isValidHexChar(c)) {
return false;
}
}
word.append(c);
}
}
// Check if we have an IPv4 ending
if (numberOfPeriods > 0) {
// There is a test case with 7 colons and valid ipv4 this should resolve it
if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && numberOfColons < 7)) {
return false;
}
} else {
// If we're at then end and we haven't had 7 colons then there is a
// problem unless we encountered a doubleColon
if (numberOfColons != 7 && !doubleColon) {
return false;
}
// If we have an empty word at the end, it means we ended in either
// a : or a .
// If we did not end in :: then this is invalid
if (numberOfPercent == 0) {
if (word.length() == 0 && ipAddress.charAt(length - 1 - offset) == ':'
&& ipAddress.charAt(length - 2 - offset) != ':') {
return false;
}
}
}
return true;
}
public static boolean isValidIp4Word(String word) {
char c;
if (word.length() < 1 || word.length() > 3) {
return false;
}
for (int i = 0; i < word.length(); i++) {
c = word.charAt(i);
if (!(c >= '0' && c <= '9')) {
return false;
}
}
if (Integer.parseInt(word) > 255) {
return false;
}
return true;
}
static boolean isValidHexChar(char c) {
return c >= '0' && c <= '9' || c >= 'A' && c <= 'F'
|| c >= 'a' && c <= 'f';
}
/**
* Takes a string and parses it to see if it is a valid IPV4 address.
*
* @return true, if the string represents an IPV4 address in dotted
* notation, false otherwise
*/
public static boolean isValidIpV4Address(String value) {
int periods = 0;
int i;
int length = value.length();
if (length > 15) {
return false;
}
char c;
StringBuilder word = new StringBuilder();
for (i = 0; i < length; i++) {
c = value.charAt(i);
if (c == '.') {
periods++;
if (periods > 3) {
return false;
}
if (word.length() == 0) {
return false;
}
if (Integer.parseInt(word.toString()) > 255) {
return false;
}
word.delete(0, word.length());
} else if (!Character.isDigit(c)) {
return false;
} else {
if (word.length() > 2) {
return false;
}
word.append(c);
}
}
if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) {
return false;
}
if (periods != 3) {
return false;
}
return true;
}
/**
* A constructor to stop this class being constructed.
*/
private NetUtil() {
// Unused
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
public class SocksAuthRequestDecoderTest {
private static final Logger logger = LoggerFactory.getLogger(SocksAuthRequestDecoderTest.class);
@Test
public void testAuthRequestDecoder() throws Exception{
String username = "test";
String password = "test";
SocksAuthRequest msg = new SocksAuthRequest(username, password);
SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder();
DecoderEmbedder<SocksAuthRequest> embedder = new DecoderEmbedder<SocksAuthRequest>(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = embedder.poll();
assertEquals(msg.getUsername(), username);
assertEquals(msg.getUsername(), password);
assertNull(embedder.poll());
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksAuthRequestTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksAuthRequest(null, "");
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksAuthRequest("", null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
@Test
public void testUsernameOrPasswordIsNotAscii() {
try {
new SocksAuthRequest("παράδειγμα.δοκιμή", "password");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksAuthRequest("username", "παράδειγμα.δοκιμή");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testUsernameOrPasswordLengthIsLessThan255Chars() {
try {
new SocksAuthRequest(
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword",
"password");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksAuthRequest("password",
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
public class SocksAuthResponseDecoderTest {
private static final Logger logger = LoggerFactory.getLogger(SocksAuthResponseDecoderTest.class);
private static void testSocksAuthResponseDecoderWithDifferentParams(SocksMessage.AuthStatus authStatus)
throws Exception{
logger.debug("Testing SocksAuthResponseDecoder with authStatus: "+ authStatus);
SocksAuthResponse msg = new SocksAuthResponse(authStatus);
SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder();
DecoderEmbedder<SocksAuthResponse> embedder = new DecoderEmbedder<SocksAuthResponse>(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = embedder.poll();
assertSame(msg.getAuthStatus(), authStatus);
assertNull(embedder.poll());
}
@Test
public void testSocksCmdResponseDecoder() throws Exception {
for (SocksMessage.AuthStatus authStatus: SocksMessage.AuthStatus.values()) {
testSocksAuthResponseDecoderWithDifferentParams(authStatus);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksAuthResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksAuthResponse(null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.net.util.IPAddressUtil;
import static org.junit.Assert.*;
public class SocksCmdRequestDecoderTest {
private static final Logger logger = LoggerFactory.getLogger(SocksCmdRequestDecoderTest.class);
private static void testSocksCmdRequestDecoderWithDifferentParams(SocksMessage.CmdType cmdType,
SocksMessage.AddressType addressType, String host, int port) throws Exception {
logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: "
+ host + " port: " + port);
SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port);
SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder();
DecoderEmbedder<SocksRequest> embedder = new DecoderEmbedder<SocksRequest>(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (msg.getAddressType() == SocksMessage.AddressType.UNKNOWN) {
assertTrue(embedder.poll() instanceof UnknownSocksRequest);
} else {
msg = (SocksCmdRequest) embedder.poll();
assertSame(msg.getCmdType(), cmdType);
assertSame(msg.getAddressType(), addressType);
assertEquals(msg.getHost(), host);
assertEquals(msg.getPort(), port);
}
assertNull(embedder.poll());
}
@Test
public void testCmdRequestDecoderIPv4() throws Exception{
String[] hosts = { "127.0.0.1" };
int[] ports = {0, 32769, 65535 };
for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.IPv4, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderIPv6() throws Exception{
String[] hosts = { SocksCommonUtils.ipv6toStr(IPAddressUtil.textToNumericFormatV6("::1")) };
int[] ports = {0, 32769, 65535};
for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.IPv6, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderDomain() throws Exception{
String[] hosts = {"google.com" ,
"مثال.إختبار",
"παράδειγμα.δοκιμή",
"مثال.آزمایشی",
"пример.испытание",
"בײַשפּיל.טעסט",
"例子.测试",
"例子.測試",
"उदाहरण.परीक्षा",
"例え.テスト",
"실례.테스트",
"உதாரணம்.பரிட்சை"};
int[] ports = {0, 32769, 65535};
for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.DOMAIN, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderUnknown() throws Exception{
String host = "google.com";
int port = 80;
for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.UNKNOWN, host, port);
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksCmdRequestTest {
@Test
public void testConstructorParamsAreNotNull(){
try {
new SocksCmdRequest(null, SocksMessage.AddressType.UNKNOWN, "", 0);
} catch (Exception e){
assertTrue(e instanceof NullPointerException);
}
try {
new SocksCmdRequest(SocksMessage.CmdType.UNKNOWN, null, "", 0);
} catch (Exception e){
assertTrue(e instanceof NullPointerException);
}
try {
new SocksCmdRequest(SocksMessage.CmdType.UNKNOWN, SocksMessage.AddressType.UNKNOWN, null, 0);
} catch (Exception e){
assertTrue(e instanceof NullPointerException);
}
}
@Test
public void testIPv4CorrectAddress(){
try {
new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.IPv4, "54.54.1111.253", 0);
} catch (Exception e){
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testIPv6CorrectAddress(){
try {
new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.IPv6, "xxx:xxx:xxx", 0);
} catch (Exception e){
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testIDNNotExceeds255CharsLimit(){
try {
new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή", 0);
} catch (Exception e){
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testValidPortRange(){
try {
new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδει", -1);
} catch (Exception e){
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδει", 65536);
} catch (Exception e){
assertTrue(e instanceof IllegalArgumentException);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
public class SocksCmdResponseDecoderTest {
private static final Logger logger = LoggerFactory.getLogger(SocksCmdResponseDecoderTest.class);
private static void testSocksCmdResponseDecoderWithDifferentParams(SocksMessage.CmdStatus cmdStatus,
SocksMessage.AddressType addressType) throws Exception {
logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType);
SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType);
SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder();
DecoderEmbedder<SocksResponse> embedder = new DecoderEmbedder<SocksResponse>(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (addressType == SocksMessage.AddressType.UNKNOWN) {
assertTrue(embedder.poll() instanceof UnknownSocksResponse);
} else {
msg = embedder.poll();
assertEquals(((SocksCmdResponse) msg).getCmdStatus(), cmdStatus);
}
assertNull(embedder.poll());
}
@Test
public void testSocksCmdResponseDecoder() throws Exception {
for (SocksMessage.CmdStatus cmdStatus: SocksMessage.CmdStatus.values()) {
for (SocksMessage.AddressType addressType: SocksMessage.AddressType.values()) {
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, addressType);
}
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksCmdResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksCmdResponse(null, SocksMessage.AddressType.UNKNOWN);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksCmdResponse(SocksMessage.CmdStatus.UNASSIGNED, null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
final class SocksCommonTestUtils {
private static final int DEFAULT_ENCODER_BUFFER_SIZE = 1024;
/**
* A constructor to stop this class being constructed.
*/
private SocksCommonTestUtils() {
//NOOP
}
public static void writeMessageIntoEmbedder(DecoderEmbedder embedder, SocksMessage msg)
throws Exception {
ChannelBuffer buf = ChannelBuffers.buffer(DEFAULT_ENCODER_BUFFER_SIZE);
msg.encodeAsByteBuf(buf);
embedder.offer(buf);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksInitRequestTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksInitRequest(null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.handler.codec.socks;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksInitResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksInitResponse(null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -0,0 +1,297 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.jboss.netty.util;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import static org.junit.Assert.*;
public class NetUtilTest {
private final static Map<String, byte[]> validIpV4Hosts = new HashMap<String, byte[]>() {
private static final long serialVersionUID = 2629792739366724032L;
{
put("192.168.1.0", new byte[]{
(byte) 0xc0, (byte) 0xa8, 0x01, 0x00}
);
put("10.255.255.254", new byte[]{
0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xfe
});
put("172.18.5.4", new byte[]{
(byte) 0xac, 0x12, 0x05, 0x04
});
put("0.0.0.0", new byte[]{
0x00, 0x00, 0x00, 0x00
});
put("127.0.0.1", new byte[]{
0x7f, 0x00, 0x00, 0x01
});
}
};
private final static Map<String, byte[]> invalidIpV4Hosts = new HashMap<String, byte[]>() {
private static final long serialVersionUID = 1299215199895717282L;
{
put("1.256.3.4", null);
put("256.0.0.1", null);
put("1.1.1.1.1", null);
}
};
private final static Map<String, byte[]> validIpV6Hosts = new HashMap<String, byte[]>() {
private static final long serialVersionUID = 3999763170377573184L;
{
put("::ffff:5.6.7.8", new byte[]{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, (byte) 0xff, (byte) 0xff,
0x05, 0x06, 0x07, 0x08}
);
put("fdf8:f53b:82e4::53", new byte[]{
(byte) 0xfd, (byte) 0xf8, (byte) 0xf5, 0x3b,
(byte) 0x82, (byte) 0xe4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x53}
);
put("fe80::200:5aee:feaa:20a2", new byte[]{
(byte) 0xfe, (byte) 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x5a, (byte) 0xee,
(byte) 0xfe, (byte) 0xaa, 0x20, (byte) 0xa2}
);
put("2001::1", new byte[]{
0x20, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01}
);
put("2001:0000:4136:e378:8000:63bf:3fff:fdd2", new byte[]{
0x20, 0x01, 0x00, 0x00,
0x41, 0x36, (byte) 0xe3, 0x78,
(byte) 0x80, 0x00, 0x63, (byte) 0xbf,
0x3f, (byte) 0xff, (byte) 0xfd, (byte) 0xd2}
);
put("2001:0002:6c::430", new byte[]{
0x20, 0x01, 0x00, 0x02,
0x00, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x30}
);
put("2001:10:240:ab::a", new byte[]{
0x20, 0x01, 0x00, 0x10,
0x02, 0x40, 0x00, (byte) 0xab,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0a});
put("2002:cb0a:3cdd:1::1", new byte[]{
0x20, 0x02, (byte) 0xcb, 0x0a,
0x3c, (byte) 0xdd, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01}
);
put("2001:db8:8:4::2", new byte[]{
0x20, 0x01, 0x0d, (byte) 0xb8,
0x00, 0x08, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02}
);
put("ff01:0:0:0:0:0:0:2", new byte[]{
(byte) 0xff, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02}
);
put("[fdf8:f53b:82e4::53]", new byte[]{
(byte) 0xfd, (byte) 0xf8, (byte) 0xf5, 0x3b,
(byte) 0x82, (byte) 0xe4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x53}
);
put("[fe80::200:5aee:feaa:20a2]", new byte[]{
(byte) 0xfe, (byte) 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x5a, (byte) 0xee,
(byte) 0xfe, (byte) 0xaa, 0x20, (byte) 0xa2}
);
put("[2001::1]", new byte[]{
0x20, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01}
);
put("[2001:0000:4136:e378:8000:63bf:3fff:fdd2]", new byte[]{
0x20, 0x01, 0x00, 0x00,
0x41, 0x36, (byte) 0xe3, 0x78,
(byte) 0x80, 0x00, 0x63, (byte) 0xbf,
0x3f, (byte) 0xff, (byte) 0xfd, (byte) 0xd2}
);
put("0:1:2:3:4:5:6:789a", new byte[]{
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 0x00, 0x03,
0x00, 0x04, 0x00, 0x05,
0x00, 0x06, 0x78, (byte) 0x9a}
);
put("0:1:2:3::f", new byte[]{
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0f}
);
put("0:0:0:0:0:0:10.0.0.1", new byte[]{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x01}
);
put("::ffff:192.168.0.1", new byte[]{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, (byte) 0xff, (byte) 0xff,
(byte) 0xc0, (byte) 0xa8, 0x00, 0x01}
);
}
};
private final static Map<String, byte[]> invalidIpV6Hosts = new HashMap<String, byte[]>() {
private static final long serialVersionUID = -5870810805409009696L;
{
// Test method with garbage.
put("Obvious Garbage", null);
// Test method with preferred style, too many :
put("0:1:2:3:4:5:6:7:8", null);
// Test method with preferred style, not enough :
put("0:1:2:3:4:5:6", null);
// Test method with preferred style, bad digits.
put("0:1:2:3:4:5:6:x", null);
// Test method with preferred style, adjacent :
put("0:1:2:3:4:5:6::7", null);
// Test method with preferred style, too many digits.
put("0:1:2:3:4:5:6:789abcdef", null);
// Test method with compressed style, bad digits.
put("0:1:2:3::x", null);
// Test method with compressed style, too many adjacent :
put("0:1:2:::3", null);
// Test method with compressed style, too many digits.
put("0:1:2:3::abcde", null);
// Test method with preferred style, too many :
put("0:1:2:3:4:5:6:7:8", null);
// Test method with compressed style, not enough :
put("0:1", null);
// Test method with ipv4 style, bad ipv6 digits.
put("0:0:0:0:0:x:10.0.0.1", null);
// Test method with ipv4 style, bad ipv4 digits.
put("0:0:0:0:0:0:10.0.0.x", null);
// Test method with ipv4 style, adjacent :
put("0:0:0:0:0::0:10.0.0.1", null);
// Test method with ipv4 style, too many ipv6 digits.
put("0:0:0:0:0:00000:10.0.0.1", null);
// Test method with ipv4 style, too many :
put("0:0:0:0:0:0:0:10.0.0.1", null);
// Test method with ipv4 style, not enough :
put("0:0:0:0:0:10.0.0.1", null);
// Test method with ipv4 style, too many .
put("0:0:0:0:0:0:10.0.0.0.1", null);
// Test method with ipv4 style, not enough .
put("0:0:0:0:0:0:10.0.1", null);
// Test method with ipv4 style, adjacent .
put("0:0:0:0:0:0:10..0.0.1", null);
// Test method with compressed ipv4 style, bad ipv6 digits.
put("::fffx:192.168.0.1", null);
// Test method with compressed ipv4 style, bad ipv4 digits.
put("::ffff:192.168.0.x", null);
// Test method with compressed ipv4 style, too many adjacent :
put(":::ffff:192.168.0.1", null);
// Test method with compressed ipv4 style, too many ipv6 digits.
put("::fffff:192.168.0.1", null);
// Test method with compressed ipv4 style, too many ipv4 digits.
put("::ffff:1923.168.0.1", null);
// Test method with compressed ipv4 style, not enough :
put(":ffff:192.168.0.1", null);
// Test method with compressed ipv4 style, too many .
put("::ffff:192.168.0.1.2", null);
// Test method with compressed ipv4 style, not enough .
put("::ffff:192.168.0", null);
// Test method with compressed ipv4 style, adjacent .
put("::ffff:192.168..0.1", null);
// Test method, garbage.
put("absolute, and utter garbage", null);
// Test method, bad ipv6 digits.
put("x:0:0:0:0:0:10.0.0.1", null);
// Test method, bad ipv4 digits.
put("0:0:0:0:0:0:x.0.0.1", null);
// Test method, too many ipv6 digits.
put("00000:0:0:0:0:0:10.0.0.1", null);
// Test method, too many ipv4 digits.
put("0:0:0:0:0:0:10.0.0.1000", null);
// Test method, too many :
put("0:0:0:0:0:0:0:10.0.0.1", null);
// Test method, not enough :
put("0:0:0:0:0:10.0.0.1", null);
// Test method, too many .
put("0:0:0:0:0:0:10.0.0.0.1", null);
// Test method, not enough .
put("0:0:0:0:0:0:10.0.1", null);
// Test method, adjacent .
put("0:0:0:0:0:0:10.0.0..1", null);
}
};
@Test
public void testLocalhost() {
assertNotNull(NetUtil.LOCALHOST);
}
@Test
public void testLoopback() {
assertNotNull(NetUtil.LOOPBACK_IF);
}
@Test
public void testIsValidIpV4Address() {
for (String host : validIpV4Hosts.keySet()) {
assertTrue(NetUtil.isValidIpV4Address(host));
}
for (String host : invalidIpV4Hosts.keySet()) {
assertFalse(NetUtil.isValidIpV4Address(host));
}
}
@Test
public void testIsValidIpV6Address() {
for (String host : validIpV6Hosts.keySet()) {
assertTrue(NetUtil.isValidIpV6Address(host));
}
for (String host : invalidIpV6Hosts.keySet()) {
assertFalse(NetUtil.isValidIpV6Address(host));
}
}
@Test
public void testCreateByteArrayFromIpAddressString() {
for (Entry<String, byte[]> stringEntry : validIpV4Hosts.entrySet()) {
assertArrayEquals(stringEntry.getValue(), NetUtil.createByteArrayFromIpAddressString(stringEntry.getKey()));
}
for (Entry<String, byte[]> stringEntry : invalidIpV4Hosts.entrySet()) {
assertArrayEquals(stringEntry.getValue(), NetUtil.createByteArrayFromIpAddressString(stringEntry.getKey()));
}
for (Entry<String, byte[]> stringEntry : validIpV6Hosts.entrySet()) {
assertArrayEquals(stringEntry.getValue(), NetUtil.createByteArrayFromIpAddressString(stringEntry.getKey()));
}
for (Entry<String, byte[]> stringEntry : invalidIpV6Hosts.entrySet()) {
assertArrayEquals(stringEntry.getValue(), NetUtil.createByteArrayFromIpAddressString(stringEntry.getKey()));
}
}
}