Add socksx package which supports SOCKS 4/4a/5

Motivation:

SOCKS 4 and 5 are very different protocols although they share the same
name.  It is not possible to incorporate the two protocol versions into
a single package.

Modifications:

- Add a new package called 'socksx' to supercede 'socks' package.
- Add SOCKS 4/4a support to the 'socksx' package

Result:

codec-socks now supports all SOCKS versions
This commit is contained in:
bk1te 2014-08-12 07:12:52 +04:00 committed by Trustin Lee
parent dcd3cadeaa
commit 1a05004523
66 changed files with 3829 additions and 77 deletions

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 io.netty.handler.codec.socksx;
import io.netty.buffer.ByteBuf;
/**
* An abstract class that defines a SocksMessage, providing common properties for
* {@link SocksV5Request} and {@link SocksV5Response}.
*
* @see SocksV5Request
* @see SocksV5Response
*/
public abstract class SocksMessage {
private final SocksMessageType type;
private final SocksProtocolVersion protocolVersion;
protected SocksMessage(SocksProtocolVersion protocolVersion, SocksMessageType type) {
if (protocolVersion == null) {
throw new NullPointerException("protocolVersion");
}
if (type == null) {
throw new NullPointerException("type");
}
this.protocolVersion = protocolVersion;
this.type = type;
}
/**
* Returns the {@link SocksMessageType} of this {@link SocksMessage}
*
* @return The {@link SocksMessageType} of this {@link SocksMessage}
*/
public SocksMessageType type() {
return type;
}
/**
* Returns the {@link SocksProtocolVersion} of this {@link SocksMessage}
*
* @return The {@link SocksProtocolVersion} of this {@link SocksMessage}
*/
public SocksProtocolVersion protocolVersion() {
return protocolVersion;
}
/**
* @deprecated Do not use; this method was intended for an internal use only.
*/
@Deprecated
public abstract void encodeAsByteBuf(ByteBuf byteBuf);
}

View File

@ -0,0 +1,52 @@
/*
* 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 io.netty.handler.codec.socksx;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* Encodes an {@link SocksMessage} into a {@link ByteBuf}.
* {@link MessageToByteEncoder} implementation.
* Use this with {@link SocksV4CmdRequest},
* {@link SocksV4CmdRequest},
* {@link SocksV5InitRequest},
* {@link SocksV5InitResponse},
* {@link SocksV5AuthRequest},
* {@link SocksV5AuthResponse},
* {@link SocksV5CmdRequest} and
* {@link SocksV5CmdResponse}
*/
@ChannelHandler.Sharable
public class SocksMessageEncoder extends MessageToByteEncoder<SocksMessage> {
private static final String name = "SOCKS_MESSAGE_ENCODER";
@Override
@SuppressWarnings("deprecation")
protected void encode(ChannelHandlerContext ctx, SocksMessage msg, ByteBuf out) throws Exception {
msg.encodeAsByteBuf(out);
}
private static class SocksMessageEncoderHolder {
public static final SocksMessageEncoder HOLDER_INSTANCE = new SocksMessageEncoder();
}
public static SocksMessageEncoder getInstance() {
return SocksMessageEncoderHolder.HOLDER_INSTANCE;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx;
public enum SocksMessageType {
REQUEST,
RESPONSE,
UNKNOWN
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx;
public enum SocksProtocolVersion {
SOCKS4a((byte) 0x04),
SOCKS5((byte) 0x05),
UNKNOWN((byte) 0xff);
private final byte b;
SocksProtocolVersion(byte b) {
this.b = b;
}
/**
* @deprecated Use {@link #valueOf(byte)} instead.
*/
@Deprecated
public static SocksProtocolVersion fromByte(byte b) {
return valueOf(b);
}
public static SocksProtocolVersion valueOf(byte b) {
for (SocksProtocolVersion code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

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 io.netty.handler.codec.socksx;
/**
* An abstract class that defines a SocksRequest, providing common properties for
* {@link SocksV5InitRequest},
* {@link SocksV5AuthRequest},
* {@link SocksV5CmdRequest}
* and {@link UnknownSocksV5Request}.
*
* @see io.netty.handler.codec.socks.SocksInitRequest
* @see io.netty.handler.codec.socks.SocksAuthRequest
* @see io.netty.handler.codec.socks.SocksCmdRequest
* @see io.netty.handler.codec.socks.UnknownSocksRequest
*/
public abstract class SocksRequest extends SocksMessage {
protected SocksRequest(SocksProtocolVersion protocolVersion) {
super(protocolVersion, SocksMessageType.REQUEST);
}
}

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.
*/
package io.netty.handler.codec.socksx;
public abstract class SocksResponse extends SocksMessage {
protected SocksResponse(SocksProtocolVersion protocolVersion) {
super(protocolVersion, SocksMessageType.RESPONSE);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2014 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.
*/
package io.netty.handler.codec.socksx;

View File

@ -0,0 +1,116 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;
import io.netty.util.internal.SystemPropertyUtil;
import java.net.IDN;
/**
* An socksv4a cmd request.
*
* @see SocksV4Response
* @see SocksV4CmdRequestDecoder
*/
public final class SocksV4CmdRequest extends SocksV4Request {
private final String userId;
private final SocksV4CmdType cmdType;
private final String host;
private final int port;
private static final byte[] IPv4_DOMAIN_MARKER = {0x00, 0x00, 0x00, 0x01};
public SocksV4CmdRequest(String userId, SocksV4CmdType cmdType, String host, int port) {
if (userId == null) {
throw new NullPointerException("username");
}
if (cmdType == null) {
throw new NullPointerException("cmdType");
}
if (host == null) {
throw new NullPointerException("host");
}
if (port <= 0 || port >= 65536) {
throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536");
}
this.userId = userId;
this.cmdType = cmdType;
this.host = IDN.toASCII(host);
this.port = port;
}
public SocksV4CmdRequest(SocksV4CmdType cmdType, String host, int port) {
this("", cmdType, host, port);
}
/**
* Returns the {@link SocksV4CmdType} of this {@link SocksV4Request}
*
* @return The {@link SocksV4CmdType} of this {@link SocksV4Request}
*/
public SocksV4CmdType cmdType() {
return cmdType;
}
/**
* Returns host that is used as a parameter in {@link SocksV4CmdType}
*
* @return host that is used as a parameter in {@link SocksV4CmdType}
*/
public String host() {
return IDN.toUnicode(host);
}
/**
* Returns userId that is used as a parameter in {@link SocksV4CmdType}
*
* @return userId that is used as a parameter in {@link SocksV4CmdType}
*/
public String userId() {
return userId;
}
/**
* Returns port that is used as a parameter in {@link SocksV4CmdType}
*
* @return port that is used as a parameter in {@link SocksV4CmdType}
*/
public int port() {
return port;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(protocolVersion().byteValue());
byteBuf.writeByte(cmdType.byteValue());
byteBuf.writeShort(port);
if (NetUtil.isValidIpV4Address(host)) {
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
byteBuf.writeBytes(userId.getBytes());
byteBuf.writeZero(1);
} else {
byteBuf.writeBytes(IPv4_DOMAIN_MARKER);
byteBuf.writeBytes(userId.getBytes());
byteBuf.writeZero(1);
byteBuf.writeBytes(host.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeZero(1);
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v4.SocksV4CmdRequestDecoder.State;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV4CmdRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksV4CmdRequestDecoder extends ReplayingDecoder<State> {
private static final String name = "SOCKS_CMD_REQUEST_DECODER";
private SocksProtocolVersion version;
private SocksV4CmdType cmdType;
@SuppressWarnings("UnusedDeclaration")
private byte reserved;
private String host;
private int port;
private String userId;
private SocksV4Request msg = UnknownSocksV4Request.getInstance();
public SocksV4CmdRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
if (version != SocksProtocolVersion.SOCKS4a) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdType = SocksV4CmdType.valueOf(byteBuf.readByte());
port = byteBuf.readUnsignedShort();
host = SocksV4CommonUtils.intToIp(byteBuf.readInt());
checkpoint(State.READ_CMD_USERID);
}
case READ_CMD_USERID: {
userId = readNullTerminatedString(byteBuf);
checkpoint(State.READ_CMD_DOMAIN);
}
case READ_CMD_DOMAIN: {
// Check for Socks4a protocol marker 0,0,0,x
if (!host.equals("0.0.0.0") && host.startsWith("0.0.0.")) {
host = readNullTerminatedString(byteBuf);
}
msg = new SocksV4CmdRequest(userId, cmdType, host, port);
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
private static String readNullTerminatedString(ByteBuf byteBuf) throws Exception {
byte NULL_BYTE = (byte) 0x00;
// Could be used for DoS
String string = byteBuf.readBytes(byteBuf.bytesBefore(NULL_BYTE)).toString(CharsetUtil.US_ASCII);
// Read NULL-byte
byteBuf.readByte();
return string;
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_CMD_HEADER,
READ_CMD_USERID,
READ_CMD_DOMAIN
}
}

View File

@ -0,0 +1,114 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
import io.netty.util.NetUtil;
import java.net.IDN;
/**
* A socks cmd response.
*
* @see SocksV4CmdRequest
* @see SocksV4CmdResponseDecoder
*/
public final class SocksV4CmdResponse extends SocksV4Response {
private final SocksV4CmdStatus cmdStatus;
private final String host;
private final int port;
// 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 };
public SocksV4CmdResponse(SocksV4CmdStatus cmdStatus) {
this(cmdStatus, null, 0);
}
/**
* Constructs new response and includes provided host and port as part of it.
*
* @param cmdStatus status of the response
* @param host host (BND.ADDR field) is address that server used when connecting to the target host.
* When null a value of 4/8 0x00 octets will be used for IPv4/IPv6 and a single 0x00 byte will be
* used for domain addressType. Value is converted to ASCII using {@link java.net.IDN#toASCII(String)}.
* @param port port (BND.PORT field) that the server assigned to connect to the target host
* @throws NullPointerException in case cmdStatus or addressType are missing
* @throws IllegalArgumentException in case host or port cannot be validated
* @see java.net.IDN#toASCII(String)
*/
public SocksV4CmdResponse(SocksV4CmdStatus cmdStatus, String host, int port) {
if (cmdStatus == null) {
throw new NullPointerException("cmdStatus");
}
if (host != null) {
if (!NetUtil.isValidIpV4Address(host)) {
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
}
}
if (port < 0 || port > 65535) {
throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535");
}
this.cmdStatus = cmdStatus;
this.host = host;
this.port = port;
}
/**
* Returns the {@link SocksV4CmdStatus} of this {@link SocksV4Response}
*
* @return The {@link SocksV4CmdStatus} of this {@link SocksV4Response}
*/
public SocksV4CmdStatus cmdStatus() {
return cmdStatus;
}
/**
* Returns host that is used as a parameter in {@link io.netty.handler.codec.socks.v4.SocksV4CmdType}.
* Host (BND.ADDR field in response) is address that server used when connecting to the target host.
* This is typically different from address which client uses to connect to the SOCKS server.
*
* @return host that is used as a parameter in {@link io.netty.handler.codec.socks.v4.SocksV4CmdType}
* or null when there was no host specified during response construction
*/
public String host() {
if (host != null) {
return IDN.toUnicode(host);
} else {
return null;
}
}
/**
* Returns port that is used as a parameter in {@link io.netty.handler.codec.socks.v4.SocksV4CmdType}.
* Port (BND.PORT field in response) is port that the server assigned to connect to the target host.
*
* @return port that is used as a parameter in {@link io.netty.handler.codec.socks.v4.SocksV4CmdType}
*/
public int port() {
return port;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeZero(1);
byteBuf.writeByte(cmdStatus.byteValue());
byteBuf.writeShort(port);
byte[] hostContent = host == null ?
IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
byteBuf.writeBytes(hostContent);
}
}

View File

@ -0,0 +1,73 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socks.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v4.SocksV4CmdResponseDecoder.State;
import io.netty.util.CharsetUtil;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV4CmdResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksV4CmdResponseDecoder extends ReplayingDecoder<State> {
private static final String name = "SOCKS_CMD_RESPONSE_DECODER";
private SocksProtocolVersion version;
private SocksV4CmdStatus cmdStatus;
private String host;
private int port;
private SocksV4Response msg = UnknownSocksV4Response.getInstance();
public SocksV4CmdResponseDecoder() {
super(State.CHECK_NULL_BYTE);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_NULL_BYTE: {
if (byteBuf.readByte() != (byte) 0x00) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdStatus = SocksV4CmdStatus.valueOf(byteBuf.readByte());
checkpoint(State.READ_CMD_ADDRESS);
}
case READ_CMD_ADDRESS: {
port = byteBuf.readUnsignedShort();
host = SocksV4CommonUtils.intToIp(byteBuf.readInt());
msg = new SocksV4CmdResponse(cmdStatus, host, port);
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_NULL_BYTE,
READ_CMD_HEADER,
READ_CMD_ADDRESS
}
}

View File

@ -0,0 +1,43 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
public enum SocksV4CmdStatus {
SUCCESS((byte) 0x5a),
REJECTED_OR_FAILED((byte) 0x5b),
IDENTD_UNREACHABLE((byte) 0x5c),
IDENTD_AUTH_FAILURE((byte) 0x5d),
UNASSIGNED((byte) 0xff);
private final byte b;
SocksV4CmdStatus(byte b) {
this.b = b;
}
public static SocksV4CmdStatus valueOf(byte b) {
for (SocksV4CmdStatus code : values()) {
if (code.b == b) {
return code;
}
}
return UNASSIGNED;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
public enum SocksV4CmdType {
CONNECT((byte) 0x01),
BIND((byte) 0x02),
UNKNOWN((byte) 0xff);
private final byte b;
SocksV4CmdType(byte b) {
this.b = b;
}
public static SocksV4CmdType valueOf(byte b) {
for (SocksV4CmdType code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,102 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.util.internal.StringUtil;
public final class SocksV4CommonUtils {
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 SocksV4CommonUtils() {
// 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();
}
/**
* Converts numeric IPv6 to standard (non-compressed) format.
*/
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) {
int i;
toHextet --;
for (i = fromHextet; i < toHextet; i++) {
appendHextet(sb, src, i);
sb.append(ipv6hextetSeparator);
}
appendHextet(sb, src, i);
}
private static void appendHextet(StringBuilder sb, byte[] src, int i) {
StringUtil.toHexString(sb, src, i << 1, 2);
}
}

View File

@ -0,0 +1,34 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.handler.codec.socksx.SocksMessage;
import io.netty.handler.codec.socksx.SocksMessageType;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.SocksRequest;
/**
* An abstract class that defines a SocksRequest, providing common properties for
* {@link SocksV4CmdRequest}.
*
* @see SocksV4CmdRequest
* @see UnknownSocksV4Request
*/
public abstract class SocksV4Request extends SocksRequest {
protected SocksV4Request() {
super(SocksProtocolVersion.SOCKS4a);
}
}

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 io.netty.handler.codec.socksx.v4;
import io.netty.handler.codec.socksx.SocksMessage;
import io.netty.handler.codec.socksx.SocksMessageType;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.SocksResponse;
import io.netty.handler.codec.socksx.v5.SocksV5ResponseType;
/**
* An abstract class that defines a SocksResponse, providing common properties for
* {@link SocksV4CmdResponse}.
*
* @see SocksV4CmdResponse
* @see UnknownSocksV4Response
*/
public abstract class SocksV4Response extends SocksResponse {
protected SocksV4Response() {
super(SocksProtocolVersion.SOCKS4a);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
/**
* An unknown socks request.
*
* @see SocksV4CmdRequestDecoder
*/
public final class UnknownSocksV4Request extends SocksV4Request {
public UnknownSocksV4Request() {
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
// NOOP
}
private static class UnknownSocksV4RequestHolder {
public static final UnknownSocksV4Request HOLDER_INSTANCE = new UnknownSocksV4Request();
}
public static UnknownSocksV4Request getInstance() {
return UnknownSocksV4RequestHolder.HOLDER_INSTANCE;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
/**
* An unknown socks response.
*
* @see SocksV4CmdResponseDecoder
*/
public final class UnknownSocksV4Response extends SocksV4Response {
public UnknownSocksV4Response() {
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
// NOOP
}
private static class UnknownSocksV4ResponseHolder {
public static final UnknownSocksV4Response HOLDER_INSTANCE = new UnknownSocksV4Response();
}
public static UnknownSocksV4Response getInstance() {
return UnknownSocksV4ResponseHolder.HOLDER_INSTANCE;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2014 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.
*/
package io.netty.handler.codec.socksx.v4;

View File

@ -0,0 +1,44 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5AddressType {
IPv4((byte) 0x01),
DOMAIN((byte) 0x03),
IPv6((byte) 0x04),
UNKNOWN((byte) 0xff);
private final byte b;
SocksV5AddressType(byte b) {
this.b = b;
}
public static SocksV5AddressType valueOf(byte b) {
for (SocksV5AddressType code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import java.nio.charset.CharsetEncoder;
/**
* An socks auth request.
*
* @see SocksV5AuthResponse
* @see SocksV5AuthRequestDecoder
*/
public final class SocksV5AuthRequest extends SocksV5Request {
private static final CharsetEncoder asciiEncoder = CharsetUtil.getEncoder(CharsetUtil.US_ASCII);
private static final SocksV5SubnegotiationVersion SUBNEGOTIATION_VERSION =
SocksV5SubnegotiationVersion.AUTH_PASSWORD;
private final String username;
private final String password;
public SocksV5AuthRequest(String username, String password) {
super(SocksV5RequestType.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 username() {
return username;
}
/**
* Returns password that needs to be validated
*
* @return password that needs to be validated
*/
public String password() {
return password;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue());
byteBuf.writeByte(username.length());
byteBuf.writeBytes(username.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeByte(password.length());
byteBuf.writeBytes(password.getBytes(CharsetUtil.US_ASCII));
}
}

View File

@ -0,0 +1,71 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.v5.SocksV5AuthRequestDecoder.State;
import io.netty.util.CharsetUtil;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5AuthRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksV5AuthRequestDecoder extends ReplayingDecoder<State> {
private SocksV5SubnegotiationVersion version;
private int fieldLength;
private String username;
private String password;
private SocksV5Request msg = UnknownSocksV5Request.getInstance();
public SocksV5AuthRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksV5SubnegotiationVersion.valueOf(byteBuf.readByte());
if (version != SocksV5SubnegotiationVersion.AUTH_PASSWORD) {
break;
}
checkpoint(State.READ_USERNAME);
}
case READ_USERNAME: {
fieldLength = byteBuf.readByte();
username = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
checkpoint(State.READ_PASSWORD);
}
case READ_PASSWORD: {
fieldLength = byteBuf.readByte();
password = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
msg = new SocksV5AuthRequest(username, password);
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_USERNAME,
READ_PASSWORD
}
}

View File

@ -0,0 +1,53 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
/**
* An socks auth response.
*
* @see SocksV5AuthRequest
* @see SocksV5AuthResponseDecoder
*/
public final class SocksV5AuthResponse extends SocksV5Response {
private static final SocksV5SubnegotiationVersion SUBNEGOTIATION_VERSION =
SocksV5SubnegotiationVersion.AUTH_PASSWORD;
private final SocksV5AuthStatus authStatus;
public SocksV5AuthResponse(SocksV5AuthStatus authStatus) {
super(SocksV5ResponseType.AUTH);
if (authStatus == null) {
throw new NullPointerException("authStatus");
}
this.authStatus = authStatus;
}
/**
* Returns the {@link SocksV5AuthStatus} of this {@link SocksV5AuthResponse}
*
* @return The {@link SocksV5AuthStatus} of this {@link SocksV5AuthResponse}
*/
public SocksV5AuthStatus authStatus() {
return authStatus;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue());
byteBuf.writeByte(authStatus.byteValue());
}
}

View File

@ -0,0 +1,62 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.v5.SocksV5AuthResponseDecoder.State;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5AuthResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksV5AuthResponseDecoder extends ReplayingDecoder<State> {
private SocksV5SubnegotiationVersion version;
private SocksV5AuthStatus authStatus;
private SocksV5Response msg = UnknownSocksV5Response.getInstance();
public SocksV5AuthResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out)
throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksV5SubnegotiationVersion.valueOf(byteBuf.readByte());
if (version != SocksV5SubnegotiationVersion.AUTH_PASSWORD) {
break;
}
checkpoint(State.READ_AUTH_RESPONSE);
}
case READ_AUTH_RESPONSE: {
authStatus = SocksV5AuthStatus.valueOf(byteBuf.readByte());
msg = new SocksV5AuthResponse(authStatus);
}
}
channelHandlerContext.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_AUTH_RESPONSE
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5AuthScheme {
NO_AUTH((byte) 0x00),
AUTH_GSSAPI((byte) 0x01),
AUTH_PASSWORD((byte) 0x02),
UNKNOWN((byte) 0xff);
private final byte b;
SocksV5AuthScheme(byte b) {
this.b = b;
}
public static SocksV5AuthScheme valueOf(byte b) {
for (SocksV5AuthScheme code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5AuthStatus {
SUCCESS((byte) 0x00),
FAILURE((byte) 0xff);
private final byte b;
SocksV5AuthStatus(byte b) {
this.b = b;
}
public static SocksV5AuthStatus valueOf(byte b) {
for (SocksV5AuthStatus code : values()) {
if (code.b == b) {
return code;
}
}
return FAILURE;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,138 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;
import java.net.IDN;
/**
* An socks cmd request.
*
* @see SocksV5CmdResponse
* @see SocksV5CmdRequestDecoder
*/
public final class SocksV5CmdRequest extends SocksV5Request {
private final SocksV5CmdType cmdType;
private final SocksV5AddressType addressType;
private final String host;
private final int port;
public SocksV5CmdRequest(SocksV5CmdType cmdType, SocksV5AddressType addressType, String host, int port) {
super(SocksV5RequestType.CMD);
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 >= 65536) {
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 SocksV5CmdType} of this {@link SocksV5CmdRequest}
*
* @return The {@link SocksV5CmdType} of this {@link SocksV5CmdRequest}
*/
public SocksV5CmdType cmdType() {
return cmdType;
}
/**
* Returns the {@link SocksV5AddressType} of this {@link SocksV5CmdRequest}
*
* @return The {@link SocksV5AddressType} of this {@link SocksV5CmdRequest}
*/
public SocksV5AddressType addressType() {
return addressType;
}
/**
* Returns host that is used as a parameter in {@link SocksV5CmdType}
*
* @return host that is used as a parameter in {@link SocksV5CmdType}
*/
public String host() {
return IDN.toUnicode(host);
}
/**
* Returns port that is used as a parameter in {@link SocksV5CmdType}
*
* @return port that is used as a parameter in {@link SocksV5CmdType}
*/
public int port() {
return port;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(protocolVersion().byteValue());
byteBuf.writeByte(cmdType.byteValue());
byteBuf.writeByte(0x00);
byteBuf.writeByte(addressType.byteValue());
switch (addressType) {
case IPv4: {
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
byteBuf.writeShort(port);
break;
}
case DOMAIN: {
byteBuf.writeByte(host.length());
byteBuf.writeBytes(host.getBytes(CharsetUtil.US_ASCII));
byteBuf.writeShort(port);
break;
}
case IPv6: {
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
byteBuf.writeShort(port);
break;
}
}
}
}

View File

@ -0,0 +1,97 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v5.SocksV5CmdRequestDecoder.State;
import io.netty.util.CharsetUtil;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5CmdRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksV5CmdRequestDecoder extends ReplayingDecoder<State> {
private SocksProtocolVersion version;
private int fieldLength;
private SocksV5CmdType cmdType;
private SocksV5AddressType addressType;
@SuppressWarnings("UnusedDeclaration")
private byte reserved;
private String host;
private int port;
private SocksV5Request msg = UnknownSocksV5Request.getInstance();
public SocksV5CmdRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
if (version != SocksProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdType = SocksV5CmdType.valueOf(byteBuf.readByte());
reserved = byteBuf.readByte();
addressType = SocksV5AddressType.valueOf(byteBuf.readByte());
checkpoint(State.READ_CMD_ADDRESS);
}
case READ_CMD_ADDRESS: {
switch (addressType) {
case IPv4: {
host = SocksV5CommonUtils.intToIp(byteBuf.readInt());
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdRequest(cmdType, addressType, host, port);
break;
}
case DOMAIN: {
fieldLength = byteBuf.readByte();
host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdRequest(cmdType, addressType, host, port);
break;
}
case IPv6: {
host = SocksV5CommonUtils.ipv6toStr(byteBuf.readBytes(16).array());
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdRequest(cmdType, addressType, host, port);
break;
}
case UNKNOWN:
break;
}
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_CMD_HEADER,
READ_CMD_ADDRESS
}
}

View File

@ -0,0 +1,177 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;
import java.net.IDN;
/**
* A socks cmd response.
*
* @see SocksV5CmdRequest
* @see SocksV5CmdResponseDecoder
*/
public final class SocksV5CmdResponse extends SocksV5Response {
private final SocksV5CmdStatus cmdStatus;
private final SocksV5AddressType addressType;
private final String host;
private final int port;
// All arrays are initialized on construction time to 0/false/null remove array Initialization
private static final byte[] DOMAIN_ZEROED = {0x00};
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 SocksV5CmdResponse(SocksV5CmdStatus cmdStatus, SocksV5AddressType addressType) {
this(cmdStatus, addressType, null, 0);
}
/**
* Constructs new response and includes provided host and port as part of it.
*
* @param cmdStatus status of the response
* @param addressType type of host parameter
* @param host host (BND.ADDR field) is address that server used when connecting to the target host.
* When null a value of 4/8 0x00 octets will be used for IPv4/IPv6 and a single 0x00 byte will be
* used for domain addressType. Value is converted to ASCII using {@link IDN#toASCII(String)}.
* @param port port (BND.PORT field) that the server assigned to connect to the target host
* @throws NullPointerException in case cmdStatus or addressType are missing
* @throws IllegalArgumentException in case host or port cannot be validated
* @see IDN#toASCII(String)
*/
public SocksV5CmdResponse(SocksV5CmdStatus cmdStatus, SocksV5AddressType addressType, String host, int port) {
super(SocksV5ResponseType.CMD);
if (cmdStatus == null) {
throw new NullPointerException("cmdStatus");
}
if (addressType == null) {
throw new NullPointerException("addressType");
}
if (host != null) {
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;
}
host = IDN.toASCII(host);
}
if (port < 0 || port > 65535) {
throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535");
}
this.cmdStatus = cmdStatus;
this.addressType = addressType;
this.host = host;
this.port = port;
}
/**
* Returns the {@link SocksV5CmdStatus} of this {@link SocksV5CmdResponse}
*
* @return The {@link SocksV5CmdStatus} of this {@link SocksV5CmdResponse}
*/
public SocksV5CmdStatus cmdStatus() {
return cmdStatus;
}
/**
* Returns the {@link SocksV5AddressType} of this {@link SocksV5CmdResponse}
*
* @return The {@link SocksV5AddressType} of this {@link SocksV5CmdResponse}
*/
public SocksV5AddressType addressType() {
return addressType;
}
/**
* Returns host that is used as a parameter in {@link SocksV5CmdType}.
* Host (BND.ADDR field in response) is address that server used when connecting to the target host.
* This is typically different from address which client uses to connect to the SOCKS server.
*
* @return host that is used as a parameter in {@link SocksV5CmdType}
* or null when there was no host specified during response construction
*/
public String host() {
if (host != null) {
return IDN.toUnicode(host);
} else {
return null;
}
}
/**
* Returns port that is used as a parameter in {@link SocksV5CmdType}.
* Port (BND.PORT field in response) is port that the server assigned to connect to the target host.
*
* @return port that is used as a parameter in {@link SocksV5CmdType}
*/
public int port() {
return port;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(protocolVersion().byteValue());
byteBuf.writeByte(cmdStatus.byteValue());
byteBuf.writeByte(0x00);
byteBuf.writeByte(addressType.byteValue());
switch (addressType) {
case IPv4: {
byte[] hostContent = host == null ?
IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
byteBuf.writeBytes(hostContent);
byteBuf.writeShort(port);
break;
}
case DOMAIN: {
byte[] hostContent = host == null ?
DOMAIN_ZEROED : host.getBytes(CharsetUtil.US_ASCII);
byteBuf.writeByte(hostContent.length); // domain length
byteBuf.writeBytes(hostContent); // domain value
byteBuf.writeShort(port); // port value
break;
}
case IPv6: {
byte[] hostContent = host == null
? IPv6_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
byteBuf.writeBytes(hostContent);
byteBuf.writeShort(port);
break;
}
}
}
}

View File

@ -0,0 +1,96 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v5.SocksV5CmdResponseDecoder.State;
import io.netty.util.CharsetUtil;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5CmdResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksV5CmdResponseDecoder extends ReplayingDecoder<State> {
private SocksProtocolVersion version;
private int fieldLength;
private SocksV5CmdStatus cmdStatus;
private SocksV5AddressType addressType;
private byte reserved;
private String host;
private int port;
private SocksV5Response msg = UnknownSocksV5Response.getInstance();
public SocksV5CmdResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
if (version != SocksProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_CMD_HEADER);
}
case READ_CMD_HEADER: {
cmdStatus = SocksV5CmdStatus.valueOf(byteBuf.readByte());
reserved = byteBuf.readByte();
addressType = SocksV5AddressType.valueOf(byteBuf.readByte());
checkpoint(State.READ_CMD_ADDRESS);
}
case READ_CMD_ADDRESS: {
switch (addressType) {
case IPv4: {
host = SocksV5CommonUtils.intToIp(byteBuf.readInt());
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdResponse(cmdStatus, addressType, host, port);
break;
}
case DOMAIN: {
fieldLength = byteBuf.readByte();
host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdResponse(cmdStatus, addressType, host, port);
break;
}
case IPv6: {
host = SocksV5CommonUtils.ipv6toStr(byteBuf.readBytes(16).array());
port = byteBuf.readUnsignedShort();
msg = new SocksV5CmdResponse(cmdStatus, addressType, host, port);
break;
}
case UNKNOWN:
break;
}
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_CMD_HEADER,
READ_CMD_ADDRESS
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5CmdStatus {
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;
SocksV5CmdStatus(byte b) {
this.b = b;
}
public static SocksV5CmdStatus valueOf(byte b) {
for (SocksV5CmdStatus code : values()) {
if (code.b == b) {
return code;
}
}
return UNASSIGNED;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5CmdType {
CONNECT((byte) 0x01),
BIND((byte) 0x02),
UDP((byte) 0x03),
UNKNOWN((byte) 0xff);
private final byte b;
SocksV5CmdType(byte b) {
this.b = b;
}
public static SocksV5CmdType valueOf(byte b) {
for (SocksV5CmdType code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

View File

@ -0,0 +1,102 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.util.internal.StringUtil;
public final class SocksV5CommonUtils {
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 SocksV5CommonUtils() {
// 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();
}
/**
* Converts numeric IPv6 to standard (non-compressed) format.
*/
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) {
int i;
toHextet --;
for (i = fromHextet; i < toHextet; i++) {
appendHextet(sb, src, i);
sb.append(ipv6hextetSeparator);
}
appendHextet(sb, src, i);
}
private static void appendHextet(StringBuilder sb, byte[] src, int i) {
StringUtil.toHexString(sb, src, i << 1, 2);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import java.util.Collections;
import java.util.List;
/**
* An socks init request.
*
* @see SocksV5InitResponse
* @see SocksV5InitRequestDecoder
*/
public final class SocksV5InitRequest extends SocksV5Request {
private final List<SocksV5AuthScheme> authSchemes;
public SocksV5InitRequest(List<SocksV5AuthScheme> authSchemes) {
super(SocksV5RequestType.INIT);
if (authSchemes == null) {
throw new NullPointerException("authSchemes");
}
this.authSchemes = authSchemes;
}
/**
* Returns the List<{@link SocksV5AuthScheme}> of this {@link SocksV5InitRequest}
*
* @return The List<{@link SocksV5AuthScheme}> of this {@link SocksV5InitRequest}
*/
public List<SocksV5AuthScheme> authSchemes() {
return Collections.unmodifiableList(authSchemes);
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(protocolVersion().byteValue());
byteBuf.writeByte(authSchemes.size());
for (SocksV5AuthScheme authScheme : authSchemes) {
byteBuf.writeByte(authScheme.byteValue());
}
}
}

View File

@ -0,0 +1,69 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v5.SocksV5InitRequestDecoder.State;
import java.util.ArrayList;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5InitRequest}.
* Before returning SocksRequest decoder removes itself from pipeline.
*/
public class SocksV5InitRequestDecoder extends ReplayingDecoder<State> {
private final List<SocksV5AuthScheme> authSchemes = new ArrayList<SocksV5AuthScheme>();
private SocksProtocolVersion version;
private byte authSchemeNum;
private SocksV5Request msg = UnknownSocksV5Request.getInstance();
public SocksV5InitRequestDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
if (version != SocksProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_AUTH_SCHEMES);
}
case READ_AUTH_SCHEMES: {
authSchemes.clear();
authSchemeNum = byteBuf.readByte();
for (int i = 0; i < authSchemeNum; i++) {
authSchemes.add(SocksV5AuthScheme.valueOf(byteBuf.readByte()));
}
msg = new SocksV5InitRequest(authSchemes);
break;
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_AUTH_SCHEMES
}
}

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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
/**
* An socks init response.
*
* @see SocksV5InitRequest
* @see SocksV5InitResponseDecoder
*/
public final class SocksV5InitResponse extends SocksV5Response {
private final SocksV5AuthScheme authScheme;
public SocksV5InitResponse(SocksV5AuthScheme authScheme) {
super(SocksV5ResponseType.INIT);
if (authScheme == null) {
throw new NullPointerException("authScheme");
}
this.authScheme = authScheme;
}
/**
* Returns the {@link SocksV5AuthScheme} of this {@link SocksV5InitResponse}
*
* @return The {@link SocksV5AuthScheme} of this {@link SocksV5InitResponse}
*/
public SocksV5AuthScheme authScheme() {
return authScheme;
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
byteBuf.writeByte(protocolVersion().byteValue());
byteBuf.writeByte(authScheme.byteValue());
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v5.SocksV5InitResponseDecoder.State;
import java.util.List;
/**
* Decodes {@link ByteBuf}s into {@link SocksV5InitResponse}.
* Before returning SocksResponse decoder removes itself from pipeline.
*/
public class SocksV5InitResponseDecoder extends ReplayingDecoder<State> {
private SocksProtocolVersion version;
private SocksV5AuthScheme authScheme;
private SocksV5Response msg = UnknownSocksV5Response.getInstance();
public SocksV5InitResponseDecoder() {
super(State.CHECK_PROTOCOL_VERSION);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
switch (state()) {
case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
if (version != SocksProtocolVersion.SOCKS5) {
break;
}
checkpoint(State.READ_PREFFERED_AUTH_TYPE);
}
case READ_PREFFERED_AUTH_TYPE: {
authScheme = SocksV5AuthScheme.valueOf(byteBuf.readByte());
msg = new SocksV5InitResponse(authScheme);
break;
}
}
ctx.pipeline().remove(this);
out.add(msg);
}
enum State {
CHECK_PROTOCOL_VERSION,
READ_PREFFERED_AUTH_TYPE
}
}

View File

@ -0,0 +1,54 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.handler.codec.socksx.SocksMessage;
import io.netty.handler.codec.socksx.SocksMessageType;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.SocksRequest;
/**
* An abstract class that defines a SocksRequest, providing common properties for
* {@link SocksV5InitRequest},
* {@link SocksV5AuthRequest},
* {@link SocksV5CmdRequest} and
* {@link UnknownSocksV5Request}.
*
* @see SocksV5InitRequest
* @see SocksV5AuthRequest
* @see SocksV5CmdRequest
* @see UnknownSocksV5Request
*/
public abstract class SocksV5Request extends SocksRequest {
private final SocksV5RequestType requestType;
protected SocksV5Request(SocksV5RequestType requestType) {
super(SocksProtocolVersion.SOCKS5);
if (requestType == null) {
throw new NullPointerException("requestType");
}
this.requestType = requestType;
}
/**
* Returns socks request type
*
* @return socks request type
*/
public SocksV5RequestType requestType() {
return requestType;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
/**
* Type of socks request
*/
public enum SocksV5RequestType {
INIT,
AUTH,
CMD,
UNKNOWN
}

View File

@ -0,0 +1,54 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.handler.codec.socksx.SocksMessage;
import io.netty.handler.codec.socksx.SocksMessageType;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.SocksResponse;
/**
* An abstract class that defines a SocksResponse, providing common properties for
* {@link SocksV5InitResponse},
* {@link SocksV5AuthResponse},
* {@link SocksV5CmdResponse}
* and {@link UnknownSocksV5Response}.
*
* @see SocksV5InitResponse
* @see SocksV5AuthResponse
* @see SocksV5CmdResponse
* @see UnknownSocksV5Response
*/
public abstract class SocksV5Response extends SocksResponse {
private final SocksV5ResponseType responseType;
protected SocksV5Response(SocksV5ResponseType responseType) {
super(SocksProtocolVersion.SOCKS5);
if (responseType == null) {
throw new NullPointerException("responseType");
}
this.responseType = responseType;
}
/**
* Returns socks response type
*
* @return socks response type
*/
public SocksV5ResponseType responseType() {
return responseType;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
/**
* Type of socks response
*/
public enum SocksV5ResponseType {
INIT,
AUTH,
CMD,
UNKNOWN
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2013 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 io.netty.handler.codec.socksx.v5;
public enum SocksV5SubnegotiationVersion {
AUTH_PASSWORD((byte) 0x01),
UNKNOWN((byte) 0xff);
private final byte b;
SocksV5SubnegotiationVersion(byte b) {
this.b = b;
}
public static SocksV5SubnegotiationVersion valueOf(byte b) {
for (SocksV5SubnegotiationVersion code : values()) {
if (code.b == b) {
return code;
}
}
return UNKNOWN;
}
public byte byteValue() {
return b;
}
}

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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
/**
* An unknown socks request.
*
* @see SocksV5InitRequestDecoder
* @see SocksV5AuthRequestDecoder
* @see SocksV5CmdRequestDecoder
*/
public final class UnknownSocksV5Request extends SocksV5Request {
public UnknownSocksV5Request() {
super(SocksV5RequestType.UNKNOWN);
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
// NOOP
}
private static class UnknownSocksV5RequestHolder {
public static final UnknownSocksV5Request HOLDER_INSTANCE = new UnknownSocksV5Request();
}
public static UnknownSocksV5Request getInstance() {
return UnknownSocksV5RequestHolder.HOLDER_INSTANCE;
}
}

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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
/**
* An unknown socks response.
*
* @see SocksV5InitResponseDecoder
* @see SocksV5AuthResponseDecoder
* @see SocksV5CmdResponseDecoder
*/
public final class UnknownSocksV5Response extends SocksV5Response {
public UnknownSocksV5Response() {
super(SocksV5ResponseType.UNKNOWN);
}
@Override
public void encodeAsByteBuf(ByteBuf byteBuf) {
// NOOP
}
private static class UnknownSocksV5ResponseHolder {
public static final UnknownSocksV5Response HOLDER_INSTANCE = new UnknownSocksV5Response();
}
public static UnknownSocksV5Response getInstance() {
return UnknownSocksV5ResponseHolder.HOLDER_INSTANCE;
}
}

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
package io.netty.handler.codec.socksx.v5;
// TODO: Combine decoders into one.

View File

@ -0,0 +1,70 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.socksx.v5.SocksV5AddressType;
import io.netty.handler.codec.socksx.v5.SocksV5CmdRequest;
import io.netty.handler.codec.socksx.v5.SocksV5CmdType;
import io.netty.handler.codec.socksx.v5.UnknownSocksV5Request;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class SocksV4CmdRequestDecoderTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksV4CmdRequestDecoderTest.class);
private static void testSocksV4CmdRequestDecoderWithDifferentParams(String userId,
SocksV4CmdType cmdType,
String host,
int port) {
logger.debug("Testing cmdType: " + cmdType + " userId: " + userId + " host: " + host +
" port: " + port);
SocksV4CmdRequest msg = new SocksV4CmdRequest(userId, cmdType, host, port);
SocksV4CmdRequestDecoder decoder = new SocksV4CmdRequestDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
Object obj = embedder.readInbound();
msg = (SocksV4CmdRequest) obj;
assertSame(msg.cmdType(), cmdType);
assertEquals(msg.userId(), userId);
assertEquals(msg.host(), host);
assertEquals(msg.port(), port);
assertNull(embedder.readInbound());
}
@Test
public void testCmdRequestDecoder() {
String[] hosts = {"127.0.0.1", };
String[] userIds = {"test", };
int[] ports = {1, 32769, 65535};
for (SocksV4CmdType cmdType : SocksV4CmdType.values()) {
for (String userId : userIds) {
for (String host : hosts) {
for (int port : ports) {
testSocksV4CmdRequestDecoderWithDifferentParams(userId, cmdType, host, port);
}
}
}
}
}
}

View File

@ -0,0 +1,23 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SocksV4CmdRequestTest {
private static final Logger logger = LoggerFactory.getLogger(SocksV4CmdRequestTest.class);
}

View File

@ -0,0 +1,58 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.socksx.v5.SocksV5AddressType;
import io.netty.handler.codec.socksx.v5.SocksV5CmdStatus;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class SocksV4CmdResponseDecoderTest {
private static final Logger logger = LoggerFactory.getLogger(SocksV4CmdResponseDecoderTest.class);
private static void testSocksCmdResponseDecoderWithDifferentParams(
SocksV4CmdStatus cmdStatus, String host, int port) {
logger.debug("Testing cmdStatus: " + cmdStatus);
SocksV4Response msg = new SocksV4CmdResponse(cmdStatus, host, port);
SocksV4CmdResponseDecoder decoder = new SocksV4CmdResponseDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = (SocksV4Response) embedder.readInbound();
assertEquals(((SocksV4CmdResponse) msg).cmdStatus(), cmdStatus);
if (host != null) {
assertEquals(((SocksV4CmdResponse) msg).host(), host);
}
assertEquals(((SocksV4CmdResponse) msg).port(), port);
assertNull(embedder.readInbound());
}
/**
* Verifies that sent socks messages are decoded correctly.
*/
@Test
public void testSocksCmdResponseDecoder() {
for (SocksV4CmdStatus cmdStatus : SocksV4CmdStatus.values()) {
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, null, 0);
}
}
}

View File

@ -0,0 +1,23 @@
/*
* 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 io.netty.handler.codec.socksx.v4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SocksV4CmdResponseTest {
private static final Logger logger = LoggerFactory.getLogger(SocksV4CmdResponseTest.class);
}

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 io.netty.handler.codec.socksx.v4;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.socksx.SocksMessage;
final class SocksV4CommonTestUtils {
/**
* A constructor to stop this class being constructed.
*/
private SocksV4CommonTestUtils() {
//NOOP
}
@SuppressWarnings("deprecation")
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, SocksMessage msg) {
ByteBuf buf = Unpooled.buffer();
msg.encodeAsByteBuf(buf);
embedder.writeInbound(buf);
}
}

View File

@ -0,0 +1,38 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.channel.embedded.EmbeddedChannel;
import org.junit.Test;
import static org.junit.Assert.*;
public class SocksV5AuthRequestDecoderTest {
@Test
public void testAuthRequestDecoder() {
String username = "test";
String password = "test";
SocksV5AuthRequest msg = new SocksV5AuthRequest(username, password);
SocksV5AuthRequestDecoder decoder = new SocksV5AuthRequestDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = (SocksV5AuthRequest) embedder.readInbound();
assertEquals(msg.username(), username);
assertEquals(msg.username(), password);
assertNull(embedder.readInbound());
}
}

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 io.netty.handler.codec.socksx.v5;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksV5AuthRequestTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5AuthRequest(null, "");
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksV5AuthRequest("", null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
@Test
public void testUsernameOrPasswordIsNotAscii() {
try {
new SocksV5AuthRequest("παράδειγμα.δοκιμή", "password");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksV5AuthRequest("username", "παράδειγμα.δοκιμή");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testUsernameOrPasswordLengthIsLessThan255Chars() {
try {
new SocksV5AuthRequest(
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword",
"password");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksV5AuthRequest("password",
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpassword");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import static org.junit.Assert.*;
public class SocksV5AuthResponseDecoderTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
SocksV5AuthResponseDecoderTest.class);
private static void testSocksAuthResponseDecoderWithDifferentParams(SocksV5AuthStatus authStatus) {
logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus);
SocksV5AuthResponse msg = new SocksV5AuthResponse(authStatus);
SocksV5AuthResponseDecoder decoder = new SocksV5AuthResponseDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = (SocksV5AuthResponse) embedder.readInbound();
assertSame(msg.authStatus(), authStatus);
assertNull(embedder.readInbound());
}
@Test
public void testSocksCmdResponseDecoder() {
for (SocksV5AuthStatus authStatus: SocksV5AuthStatus.values()) {
testSocksAuthResponseDecoderWithDifferentParams(authStatus);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksV5AuthResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5AuthResponse(null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -0,0 +1,109 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import sun.net.util.IPAddressUtil;
import static org.junit.Assert.*;
public class SocksV5CmdRequestDecoderTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksV5CmdRequestDecoderTest.class);
private static void testSocksCmdRequestDecoderWithDifferentParams(SocksV5CmdType cmdType,
SocksV5AddressType addressType,
String host,
int port) {
logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: " + host +
" port: " + port);
SocksV5CmdRequest msg = new SocksV5CmdRequest(cmdType, addressType, host, port);
SocksV5CmdRequestDecoder decoder = new SocksV5CmdRequestDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (msg.addressType() == SocksV5AddressType.UNKNOWN) {
assertTrue(embedder.readInbound() instanceof UnknownSocksV5Request);
} else {
msg = (SocksV5CmdRequest) embedder.readInbound();
assertSame(msg.cmdType(), cmdType);
assertSame(msg.addressType(), addressType);
assertEquals(msg.host(), host);
assertEquals(msg.port(), port);
}
assertNull(embedder.readInbound());
}
@Test
public void testCmdRequestDecoderIPv4() {
String[] hosts = {"127.0.0.1", };
int[] ports = {1, 32769, 65535 };
for (SocksV5CmdType cmdType : SocksV5CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksV5AddressType.IPv4, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderIPv6() {
String[] hosts = {SocksV5CommonUtils.ipv6toStr(IPAddressUtil.textToNumericFormatV6("::1"))};
int[] ports = {1, 32769, 65535};
for (SocksV5CmdType cmdType : SocksV5CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksV5AddressType.IPv6, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderDomain() {
String[] hosts = {"google.com" ,
"مثال.إختبار",
"παράδειγμα.δοκιμή",
"مثال.آزمایشی",
"пример.испытание",
"בײַשפּיל.טעסט",
"例子.测试",
"例子.測試",
"उदाहरण.परीक्षा",
"例え.テスト",
"실례.테스트",
"உதாரணம்.பரிட்சை"};
int[] ports = {1, 32769, 65535};
for (SocksV5CmdType cmdType : SocksV5CmdType.values()) {
for (String host : hosts) {
for (int port : ports) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksV5AddressType.DOMAIN, host, port);
}
}
}
}
@Test
public void testCmdRequestDecoderUnknown() {
String host = "google.com";
int port = 80;
for (SocksV5CmdType cmdType : SocksV5CmdType.values()) {
testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksV5AddressType.UNKNOWN, host, port);
}
}
}

View File

@ -0,0 +1,91 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import org.junit.Test;
import static org.junit.Assert.*;
public class SocksV5CmdRequestTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5CmdRequest(null, SocksV5AddressType.UNKNOWN, "", 1);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksV5CmdRequest(SocksV5CmdType.UNKNOWN, null, "", 1);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksV5CmdRequest(SocksV5CmdType.UNKNOWN, SocksV5AddressType.UNKNOWN, null, 1);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
@Test
public void testIPv4CorrectAddress() {
try {
new SocksV5CmdRequest(SocksV5CmdType.BIND, SocksV5AddressType.IPv4, "54.54.1111.253", 1);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testIPv6CorrectAddress() {
try {
new SocksV5CmdRequest(SocksV5CmdType.BIND, SocksV5AddressType.IPv6, "xxx:xxx:xxx", 1);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testIDNNotExceeds255CharsLimit() {
try {
new SocksV5CmdRequest(SocksV5CmdType.BIND, SocksV5AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή", 1);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
@Test
public void testValidPortRange() {
try {
new SocksV5CmdRequest(SocksV5CmdType.BIND, SocksV5AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδει", 0);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksV5CmdRequest(SocksV5CmdType.BIND, SocksV5AddressType.DOMAIN,
"παράδειγμα.δοκιμήπαράδει", 65536);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import static org.junit.Assert.*;
public class SocksV5CmdResponseDecoderTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksV5CmdResponseDecoderTest.class);
private static void testSocksCmdResponseDecoderWithDifferentParams(
SocksV5CmdStatus cmdStatus, SocksV5AddressType addressType, String host, int port) {
logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType);
SocksV5Response msg = new SocksV5CmdResponse(cmdStatus, addressType, host, port);
SocksV5CmdResponseDecoder decoder = new SocksV5CmdResponseDecoder();
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksV5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (addressType == SocksV5AddressType.UNKNOWN) {
assertTrue(embedder.readInbound() instanceof UnknownSocksV5Response);
} else {
msg = (SocksV5Response) embedder.readInbound();
assertEquals(((SocksV5CmdResponse) msg).cmdStatus(), cmdStatus);
if (host != null) {
assertEquals(((SocksV5CmdResponse) msg).host(), host);
}
assertEquals(((SocksV5CmdResponse) msg).port(), port);
}
assertNull(embedder.readInbound());
}
/**
* Verifies that sent socks messages are decoded correctly.
*/
@Test
public void testSocksCmdResponseDecoder() {
for (SocksV5CmdStatus cmdStatus : SocksV5CmdStatus.values()) {
for (SocksV5AddressType addressType : SocksV5AddressType.values()) {
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, addressType, null, 0);
}
}
}
/**
* Verifies that invalid bound host will fail with IllegalArgumentException during encoding.
*/
@Test(expected = IllegalArgumentException.class)
public void testInvalidAddress() {
testSocksCmdResponseDecoderWithDifferentParams(SocksV5CmdStatus.SUCCESS, SocksV5AddressType.IPv4, "1", 80);
}
/**
* Verifies that send socks messages are decoded correctly when bound host and port are set.
*/
@Test
public void testSocksCmdResponseDecoderIncludingHost() {
for (SocksV5CmdStatus cmdStatus : SocksV5CmdStatus.values()) {
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksV5AddressType.IPv4,
"127.0.0.1", 80);
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksV5AddressType.DOMAIN,
"testDomain.com", 80);
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksV5AddressType.IPv6,
"2001:db8:85a3:42:1000:8a2e:370:7334", 80);
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksV5AddressType.IPv6,
"1111:111:11:1:0:0:0:1", 80);
}
}
}

View File

@ -0,0 +1,141 @@
/*
* 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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
import static org.junit.Assert.*;
public class SocksV5CmdResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5CmdResponse(null, SocksV5AddressType.UNKNOWN);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
try {
new SocksV5CmdResponse(SocksV5CmdStatus.UNASSIGNED, null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
/**
* Verifies content of the response when domain is not specified.
*/
@Test
public void testEmptyDomain() {
SocksV5CmdResponse socksV5CmdResponse = new SocksV5CmdResponse(
SocksV5CmdStatus.SUCCESS, SocksV5AddressType.DOMAIN);
assertNull(socksV5CmdResponse.host());
assertEquals(0, socksV5CmdResponse.port());
ByteBuf buffer = Unpooled.buffer(20);
socksV5CmdResponse.encodeAsByteBuf(buffer);
byte[] expected = {
0x05, // version
0x00, // success reply
0x00, // reserved
0x03, // address type domain
0x01, // length of domain
0x00, // domain value
0x00, // port value
0x00
};
assertByteBufEquals(expected, buffer);
}
/**
* Verifies content of the response when IPv4 address is specified.
*/
@Test
public void testIPv4Host() {
SocksV5CmdResponse socksV5CmdResponse = new SocksV5CmdResponse(
SocksV5CmdStatus.SUCCESS, SocksV5AddressType.IPv4, "127.0.0.1", 80);
assertEquals("127.0.0.1", socksV5CmdResponse.host());
assertEquals(80, socksV5CmdResponse.port());
ByteBuf buffer = Unpooled.buffer(20);
socksV5CmdResponse.encodeAsByteBuf(buffer);
byte[] expected = {
0x05, // version
0x00, // success reply
0x00, // reserved
0x01, // address type IPv4
0x7F, // address 127.0.0.1
0x00,
0x00,
0x01,
0x00, // port
0x50
};
assertByteBufEquals(expected, buffer);
}
/**
* Verifies that empty domain is allowed Response.
*/
@Test
public void testEmptyBoundAddress() {
SocksV5CmdResponse socksV5CmdResponse = new SocksV5CmdResponse(
SocksV5CmdStatus.SUCCESS, SocksV5AddressType.DOMAIN, "", 80);
assertEquals("", socksV5CmdResponse.host());
assertEquals(80, socksV5CmdResponse.port());
ByteBuf buffer = Unpooled.buffer(20);
socksV5CmdResponse.encodeAsByteBuf(buffer);
byte[] expected = {
0x05, // version
0x00, // success reply
0x00, // reserved
0x03, // address type domain
0x00, // domain length
0x00, // port
0x50
};
assertByteBufEquals(expected, buffer);
}
/**
* Verifies that Response cannot be constructed with invalid IP.
*/
@Test(expected = IllegalArgumentException.class)
public void testInvalidBoundAddress() {
new SocksV5CmdResponse(SocksV5CmdStatus.SUCCESS, SocksV5AddressType.IPv4, "127.0.0", 1000);
}
private static void assertByteBufEquals(byte[] expected, ByteBuf actual) {
byte[] actualBytes = new byte[actual.readableBytes()];
actual.readBytes(actualBytes);
assertEquals("Generated response has incorrect length", expected.length, actualBytes.length);
assertArrayEquals("Generated response differs from expected", expected, actualBytes);
}
@Test
public void testValidPortRange() {
try {
new SocksV5CmdResponse(SocksV5CmdStatus.SUCCESS, SocksV5AddressType.IPv4, "127.0.0", 0);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
new SocksV5CmdResponse(SocksV5CmdStatus.SUCCESS, SocksV5AddressType.IPv4, "127.0.0", 65536);
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
}

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 io.netty.handler.codec.socksx.v5;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.socksx.SocksMessage;
final class SocksV5CommonTestUtils {
/**
* A constructor to stop this class being constructed.
*/
private SocksV5CommonTestUtils() {
//NOOP
}
@SuppressWarnings("deprecation")
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, SocksMessage msg) {
ByteBuf buf = Unpooled.buffer();
msg.encodeAsByteBuf(buf);
embedder.writeInbound(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 io.netty.handler.codec.socksx.v5;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksV5InitRequestTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5InitRequest(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 io.netty.handler.codec.socksx.v5;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class SocksV5InitResponseTest {
@Test
public void testConstructorParamsAreNotNull() {
try {
new SocksV5InitResponse(null);
} catch (Exception e) {
assertTrue(e instanceof NullPointerException);
}
}
}

View File

@ -72,7 +72,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-tcnative</artifactId>
<classifier>${os.detected.classifier}</classifier>
<classifier>${os.detected.classifier}</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.npn</groupId>

View File

@ -0,0 +1,52 @@
/*
* 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 io.netty.example.socksproxy;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.socksx.SocksMessageEncoder;
import io.netty.handler.codec.socksx.SocksProtocolVersion;
import io.netty.handler.codec.socksx.v4.SocksV4CmdRequestDecoder;
import io.netty.handler.codec.socksx.v5.SocksV5InitRequestDecoder;
import java.util.List;
public class SocksPortUnificationServerHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
ChannelPipeline p = ctx.pipeline();
SocksProtocolVersion version = SocksProtocolVersion.valueOf(in.readByte());
System.out.println(version);
in.resetReaderIndex();
switch (version) {
case SOCKS4a:
p.addLast(new SocksV4CmdRequestDecoder());
break;
case SOCKS5:
p.addLast(new SocksV5InitRequestDecoder());
break;
case UNKNOWN:
in.clear();
ctx.close();
return;
}
p.addLast(SocksMessageEncoder.getInstance());
p.addLast(SocksServerHandler.getInstance());
p.remove(this);
}
}

View File

@ -27,61 +27,124 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.socks.SocksCmdRequest;
import io.netty.handler.codec.socks.SocksCmdResponse;
import io.netty.handler.codec.socks.SocksCmdStatus;
import io.netty.handler.codec.socksx.SocksMessage;
import io.netty.handler.codec.socksx.SocksRequest;
import io.netty.handler.codec.socksx.v4.SocksV4CmdRequest;
import io.netty.handler.codec.socksx.v4.SocksV4CmdResponse;
import io.netty.handler.codec.socksx.v4.SocksV4CmdStatus;
import io.netty.handler.codec.socksx.v5.SocksV5CmdRequest;
import io.netty.handler.codec.socksx.v5.SocksV5CmdResponse;
import io.netty.handler.codec.socksx.v5.SocksV5CmdStatus;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
@ChannelHandler.Sharable
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksCmdRequest> {
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksRequest> {
private final Bootstrap b = new Bootstrap();
@Override
public void channelRead0(final ChannelHandlerContext ctx, final SocksCmdRequest request) throws Exception {
Promise<Channel> promise = ctx.executor().newPromise();
promise.addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(final Future<Channel> future) throws Exception {
final Channel outboundChannel = future.getNow();
if (future.isSuccess()) {
ctx.channel().writeAndFlush(new SocksCmdResponse(SocksCmdStatus.SUCCESS, request.addressType()))
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) {
ctx.pipeline().remove(SocksServerConnectHandler.this);
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
}
});
} else {
ctx.channel().writeAndFlush(new SocksCmdResponse(SocksCmdStatus.FAILURE, request.addressType()));
SocksServerUtils.closeOnFlush(ctx.channel());
public void channelRead0(final ChannelHandlerContext ctx, final SocksRequest message) throws Exception {
if (message instanceof SocksV4CmdRequest) {
final SocksV4CmdRequest request = (SocksV4CmdRequest) message;
Promise<Channel> promise = ctx.executor().newPromise();
promise.addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(final Future<Channel> future) throws Exception {
final Channel outboundChannel = future.getNow();
if (future.isSuccess()) {
ctx.channel().writeAndFlush(new SocksV4CmdResponse(SocksV4CmdStatus.SUCCESS))
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) {
ctx.pipeline().remove(SocksServerConnectHandler.this);
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
}
});
} else {
ctx.channel().writeAndFlush(
new SocksV4CmdResponse(SocksV4CmdStatus.REJECTED_OR_FAILED)
);
SocksServerUtils.closeOnFlush(ctx.channel());
}
}
});
final Channel inboundChannel = ctx.channel();
b.group(inboundChannel.eventLoop())
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new DirectClientHandler(promise));
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// Connection established use handler provided results
} else {
// Close the connection if the connection attempt has failed.
ctx.channel().writeAndFlush(
new SocksV4CmdResponse(SocksV4CmdStatus.REJECTED_OR_FAILED)
);
SocksServerUtils.closeOnFlush(ctx.channel());
}
}
}
});
});
} else if (message instanceof SocksV5CmdRequest) {
final SocksV5CmdRequest request = (SocksV5CmdRequest) message;
Promise<Channel> promise = ctx.executor().newPromise();
promise.addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(final Future<Channel> future) throws Exception {
final Channel outboundChannel = future.getNow();
if (future.isSuccess()) {
ctx.channel().writeAndFlush(
new SocksV5CmdResponse(SocksV5CmdStatus.SUCCESS, request.addressType())
).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) {
ctx.pipeline().remove(SocksServerConnectHandler.this);
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
}
}
);
} else {
ctx.channel().writeAndFlush(
new SocksV5CmdResponse(SocksV5CmdStatus.FAILURE, request.addressType()));
SocksServerUtils.closeOnFlush(ctx.channel());
}
}
});
final Channel inboundChannel = ctx.channel();
final Channel inboundChannel = ctx.channel();
b.group(inboundChannel.eventLoop())
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new DirectClientHandler(promise));
b.group(inboundChannel.eventLoop())
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new DirectClientHandler(promise));
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// Connection established use handler provided results
} else {
// Close the connection if the connection attempt has failed.
ctx.channel().writeAndFlush(
new SocksCmdResponse(SocksCmdStatus.FAILURE, request.addressType()));
SocksServerUtils.closeOnFlush(ctx.channel());
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// Connection established use handler provided results
} else {
// Close the connection if the connection attempt has failed.
ctx.channel().writeAndFlush(
new SocksV5CmdResponse(SocksV5CmdStatus.FAILURE, request.addressType()));
SocksServerUtils.closeOnFlush(ctx.channel());
}
}
}
});
});
} else {
ctx.close();
}
}
@Override

View File

@ -18,37 +18,27 @@ package io.netty.example.socksproxy;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socks.SocksAuthResponse;
import io.netty.handler.codec.socks.SocksAuthScheme;
import io.netty.handler.codec.socks.SocksAuthStatus;
import io.netty.handler.codec.socks.SocksCmdRequest;
import io.netty.handler.codec.socks.SocksCmdRequestDecoder;
import io.netty.handler.codec.socks.SocksCmdType;
import io.netty.handler.codec.socks.SocksInitResponse;
import io.netty.handler.codec.socks.SocksRequest;
import io.netty.handler.codec.socksx.SocksRequest;
import io.netty.handler.codec.socksx.v4.SocksV4CmdRequest;
import io.netty.handler.codec.socksx.v4.SocksV4CmdType;
import io.netty.handler.codec.socksx.v5.SocksV5AuthScheme;
import io.netty.handler.codec.socksx.v5.SocksV5CmdRequestDecoder;
import io.netty.handler.codec.socksx.v5.SocksV5InitResponse;
import io.netty.handler.codec.socksx.v5.SocksV5Request;
import io.netty.handler.codec.socksx.v5.SocksV5AuthResponse;
import io.netty.handler.codec.socksx.v5.SocksV5AuthStatus;
import io.netty.handler.codec.socksx.v5.SocksV5CmdRequest;
import io.netty.handler.codec.socksx.v5.SocksV5CmdType;
@ChannelHandler.Sharable
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksRequest> {
@Override
public void channelRead0(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception {
switch (socksRequest.requestType()) {
case INIT: {
// auth support example
//ctx.pipeline().addFirst(new SocksAuthRequestDecoder());
//ctx.write(new SocksInitResponse(SocksAuthScheme.AUTH_PASSWORD));
ctx.pipeline().addFirst(new SocksCmdRequestDecoder());
ctx.write(new SocksInitResponse(SocksAuthScheme.NO_AUTH));
break;
}
case AUTH:
ctx.pipeline().addFirst(new SocksCmdRequestDecoder());
ctx.write(new SocksAuthResponse(SocksAuthStatus.SUCCESS));
break;
case CMD:
SocksCmdRequest req = (SocksCmdRequest) socksRequest;
if (req.cmdType() == SocksCmdType.CONNECT) {
switch (socksRequest.protocolVersion()) {
case SOCKS4a:
SocksV4CmdRequest socksV4CmdRequest = (SocksV4CmdRequest) socksRequest;
if (socksV4CmdRequest.cmdType() == SocksV4CmdType.CONNECT) {
ctx.pipeline().addLast(new SocksServerConnectHandler());
ctx.pipeline().remove(this);
ctx.fireChannelRead(socksRequest);
@ -56,6 +46,35 @@ public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksR
ctx.close();
}
break;
case SOCKS5:
switch (((SocksV5Request) socksRequest).requestType()) {
case INIT: {
// auth support example
//ctx.pipeline().addFirst(new SocksV5AuthRequestDecoder());
//ctx.write(new SocksV5InitResponse(SocksV5AuthScheme.AUTH_PASSWORD));
ctx.pipeline().addFirst(new SocksV5CmdRequestDecoder());
ctx.write(new SocksV5InitResponse(SocksV5AuthScheme.NO_AUTH));
break;
}
case AUTH:
ctx.pipeline().addFirst(new SocksV5CmdRequestDecoder());
ctx.write(new SocksV5AuthResponse(SocksV5AuthStatus.SUCCESS));
break;
case CMD:
SocksV5CmdRequest socksV5CmdRequest = (SocksV5CmdRequest) socksRequest;
if (socksV5CmdRequest.cmdType() == SocksV5CmdType.CONNECT) {
ctx.pipeline().addLast(new SocksServerConnectHandler());
ctx.pipeline().remove(this);
ctx.fireChannelRead(socksRequest);
} else {
ctx.close();
}
break;
case UNKNOWN:
ctx.close();
break;
}
break;
case UNKNOWN:
ctx.close();
break;
@ -72,4 +91,12 @@ public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksR
throwable.printStackTrace();
SocksServerUtils.closeOnFlush(ctx.channel());
}
private static class SocksServerHandlerHolder {
public static final SocksServerHandler HOLDER_INSTANCE = new SocksServerHandler();
}
public static SocksServerHandler getInstance() {
return SocksServerHandlerHolder.HOLDER_INSTANCE;
}
}

View File

@ -18,19 +18,14 @@ package io.netty.example.socksproxy;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.socks.SocksInitRequestDecoder;
import io.netty.handler.codec.socks.SocksMessageEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public final class SocksServerInitializer extends ChannelInitializer<SocketChannel> {
private final SocksMessageEncoder socksMessageEncoder = new SocksMessageEncoder();
private final SocksServerHandler socksServerHandler = new SocksServerHandler();
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline p = socketChannel.pipeline();
p.addLast(new SocksInitRequestDecoder());
p.addLast(socksMessageEncoder);
p.addLast(socksServerHandler);
p.addFirst(new LoggingHandler(LogLevel.DEBUG));
p.addLast(new SocksPortUnificationServerHandler());
}
}