diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml new file mode 100644 index 0000000000..e094d2b2ae --- /dev/null +++ b/codec-socks/pom.xml @@ -0,0 +1,43 @@ + + + + + 4.0.0 + + io.netty + netty-parent + 4.0.0.Alpha8-SNAPSHOT + + + netty-codec-socks + jar + + Netty/Codec/Socks + + + + ${project.groupId} + netty-codec + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + + + \ No newline at end of file diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequest.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequest.java new file mode 100644 index 0000000000..b1edaf331d --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequest.java @@ -0,0 +1,89 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; + +import java.nio.charset.CharsetEncoder; + +/** + * An socks auth request. + * + * @see SocksAuthResponse + * @see SocksAuthRequestDecoder + */ +public final class SocksAuthRequest extends SocksRequest { + private static final CharsetEncoder asciiEncoder = CharsetUtil.getEncoder(CharsetUtil.US_ASCII); + private final String username; + private final String password; + + /** + * + * @param username + * @param password + * @throws NullPointerException + * @throws IllegalArgumentException + */ + public SocksAuthRequest(String username, String password) { + super(SocksRequestType.AUTH); + if (username == null) { + throw new NullPointerException("username"); + } + if (password == null) { + throw new NullPointerException("username"); + } + if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) { + throw new IllegalArgumentException(" username: " + username + " or password: " + password + + " values should be in pure ascii"); + } + if (username.length() > 255) { + throw new IllegalArgumentException(username + " exceeds 255 char limit"); + } + if (password.length() > 255) { + throw new IllegalArgumentException(password + " exceeds 255 char limit"); + } + this.username = username; + this.password = password; + } + + /** + * Returns username that needs to be authenticated + * + * @return username that needs to be authenticated + */ + public String getUsername() { + return username; + } + + /** + * Returns password that needs to be validated + * + * @return password that needs to be validated + */ + public String getPassword() { + return password; + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(username.length()); + byteBuf.writeBytes(username.getBytes(CharsetUtil.US_ASCII)); + byteBuf.writeByte(password.length()); + byteBuf.writeBytes(password.getBytes(CharsetUtil.US_ASCII)); + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequestDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequestDecoder.java new file mode 100644 index 0000000000..661c464140 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthRequestDecoder.java @@ -0,0 +1,74 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; +import io.netty.util.CharsetUtil; + +/** + * Decodes {@link ByteBuf}s into {@link SocksAuthRequest}. + * Before returning SocksRequest decoder removes itself from pipeline. + */ +public class SocksAuthRequestDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_AUTH_REQUEST_DECODER"; + + public static String getName() { + return name; + } + + private SocksMessage.ProtocolVersion version; + private int fieldLength; + private String username; + private String password; + private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST; + + public SocksAuthRequestDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksRequest decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte((byte) byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.SOCKS5) { + 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 SocksAuthRequest(username, password); + } + } + ctx.pipeline().remove(this); + return msg; + } + + enum State { + CHECK_PROTOCOL_VERSION, + READ_USERNAME, + READ_PASSWORD + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponse.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponse.java new file mode 100644 index 0000000000..1515e87f76 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponse.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An socks auth response. + * + * @see SocksAuthRequest + * @see SocksAuthResponseDecoder + */ +public final class SocksAuthResponse extends SocksResponse { + + private final AuthStatus authStatus; + + /** + * + * @param authStatus + * @throws NullPointerException + */ + + public SocksAuthResponse(AuthStatus authStatus) { + super(SocksResponseType.AUTH); + if (authStatus == null) { + throw new NullPointerException("authStatus"); + } + this.authStatus = authStatus; + } + + /** + * Returns the {@link AuthStatus} of this {@link SocksAuthResponse} + * + * @return The {@link AuthStatus} of this {@link SocksAuthResponse} + */ + public AuthStatus getAuthStatus() { + return authStatus; + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(authStatus.getByteValue()); + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponseDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponseDecoder.java new file mode 100644 index 0000000000..754770d03e --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksAuthResponseDecoder.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; + +/** + * Decodes {@link ByteBuf}s into {@link SocksAuthResponse}. + * Before returning SocksResponse decoder removes itself from pipeline. + */ +public class SocksAuthResponseDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_AUTH_RESPONSE_DECODER"; + + public static String getName() { + return name; + } + + private SocksMessage.ProtocolVersion version; + private SocksMessage.AuthStatus authStatus; + private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE; + + public SocksAuthResponseDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksResponse decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception { + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte(byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.SOCKS5) { + break; + } + checkpoint(State.READ_AUTH_RESPONSE); + } + case READ_AUTH_RESPONSE: { + authStatus = SocksMessage.AuthStatus.fromByte(byteBuf.readByte()); + msg = new SocksAuthResponse(authStatus); + } + } + channelHandlerContext.pipeline().remove(this); + return msg; + } + + public enum State { + CHECK_PROTOCOL_VERSION, + READ_AUTH_RESPONSE + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequest.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequest.java new file mode 100644 index 0000000000..e8cd500524 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequest.java @@ -0,0 +1,147 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; +import sun.net.util.IPAddressUtil; + +import java.net.IDN; + +/** + * An socks cmd request. + * + * @see SocksCmdResponse + * @see SocksCmdRequestDecoder + */ +public final class SocksCmdRequest extends SocksRequest { + private final CmdType cmdType; + private final AddressType addressType; + private final String host; + private final int port; + + /** + * + * @param cmdType + * @param addressType + * @param host + * @param port + * @throws NullPointerException + * @throws IllegalArgumentException + */ + public SocksCmdRequest(CmdType cmdType, AddressType addressType, String host, int port) { + super(SocksRequestType.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 (!IPAddressUtil.isIPv4LiteralAddress(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 (!IPAddressUtil.isIPv6LiteralAddress(host)) { + throw new IllegalArgumentException(host + " is not a valid IPv6 address"); + } + break; + case UNKNOWN: + break; + } + if ((port < 0) && (port >= 65535)) { + throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536"); + } + this.cmdType = cmdType; + this.addressType = addressType; + this.host = IDN.toASCII(host); + this.port = port; + } + + /** + * Returns the {@link CmdType} of this {@link SocksCmdRequest} + * + * @return The {@link CmdType} of this {@link SocksCmdRequest} + */ + public CmdType getCmdType() { + return cmdType; + } + + /** + * Returns the {@link AddressType} of this {@link SocksCmdRequest} + * + * @return The {@link AddressType} of this {@link SocksCmdRequest} + */ + public AddressType getAddressType() { + return addressType; + } + + /** + * Returns host that is used as a parameter in {@link CmdType} + * + * @return host that is used as a parameter in {@link CmdType} + */ + public String getHost() { + return IDN.toUnicode(host); + } + + /** + * Returns port that is used as a parameter in {@link CmdType} + * + * @return port that is used as a parameter in {@link CmdType} + */ + public int getPort() { + return port; + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(cmdType.getByteValue()); + byteBuf.writeByte(0x00); + byteBuf.writeByte(addressType.getByteValue()); + switch (addressType) { + case IPv4: { + byteBuf.writeBytes(IPAddressUtil.textToNumericFormatV4(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(IPAddressUtil.textToNumericFormatV6(host)); + byteBuf.writeShort(port); + break; + } + } + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequestDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequestDecoder.java new file mode 100644 index 0000000000..a5243a1ccb --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdRequestDecoder.java @@ -0,0 +1,99 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; +import io.netty.util.CharsetUtil; + +/** + * Decodes {@link ByteBuf}s into {@link SocksCmdRequest}. + * Before returning SocksRequest decoder removes itself from pipeline. + */ +public class SocksCmdRequestDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_CMD_REQUEST_DECODER"; + + public static String getName() { + return name; + } + + private SocksMessage.ProtocolVersion version; + private int fieldLength; + private SocksMessage.CmdType cmdType; + private SocksMessage.AddressType addressType; + private byte reserved; + private String host; + private int port; + private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST; + + public SocksCmdRequestDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksRequest decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte(byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.SOCKS5) { + break; + } + checkpoint(State.READ_CMD_HEADER); + } + case READ_CMD_HEADER: { + cmdType = SocksMessage.CmdType.fromByte(byteBuf.readByte()); + reserved = byteBuf.readByte(); + addressType = SocksMessage.AddressType.fromByte(byteBuf.readByte()); + checkpoint(State.READ_CMD_ADDRESS); + } + case READ_CMD_ADDRESS: { + switch (addressType) { + case IPv4: { + host = SocksCommonUtils.intToIp(byteBuf.readInt()); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdRequest(cmdType, addressType, host, port); + break; + } + case DOMAIN: { + fieldLength = byteBuf.readByte(); + host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdRequest(cmdType, addressType, host, port); + break; + } + case IPv6: { + host = SocksCommonUtils.ipv6toStr(byteBuf.readBytes(16).array()); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdRequest(cmdType, addressType, host, port); + break; + } + case UNKNOWN: + break; + } + } + } + ctx.pipeline().remove(this); + return msg; + } + + enum State { + CHECK_PROTOCOL_VERSION, + READ_CMD_HEADER, + READ_CMD_ADDRESS + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponse.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponse.java new file mode 100644 index 0000000000..6946fd23f3 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponse.java @@ -0,0 +1,98 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An socks cmd response. + * + * @see SocksCmdRequest + * @see SocksCmdResponseDecoder + */ +public final class SocksCmdResponse extends SocksResponse { + private final CmdStatus cmdStatus; + + private final AddressType addressType; + // All arrays are initialized on construction time to 0/false/null remove array Initialization + private static final byte[] IPv4_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00}; + private static final byte[] IPv6_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + + /** + * + * @param cmdStatus + * @param addressType + * @throws NullPointerException + */ + public SocksCmdResponse(CmdStatus cmdStatus, AddressType addressType) { + super(SocksResponseType.CMD); + if (cmdStatus == null) { + throw new NullPointerException("cmdStatus"); + } + if (addressType == null) { + throw new NullPointerException("addressType"); + } + this.cmdStatus = cmdStatus; + this.addressType = addressType; + } + + /** + * Returns the {@link CmdStatus} of this {@link SocksCmdResponse} + * + * @return The {@link CmdStatus} of this {@link SocksCmdResponse} + */ + public CmdStatus getCmdStatus() { + return cmdStatus; + } + + /** + * Returns the {@link AddressType} of this {@link SocksCmdResponse} + * + * @return The {@link AddressType} of this {@link SocksCmdResponse} + */ + public AddressType getAddressType() { + return addressType; + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(cmdStatus.getByteValue()); + byteBuf.writeByte(0x00); + byteBuf.writeByte(addressType.getByteValue()); + switch (addressType) { + case IPv4: { + byteBuf.writeBytes(IPv4_HOSTNAME_ZEROED); + byteBuf.writeShort(0); + break; + } + case DOMAIN: { + byteBuf.writeByte(1); // domain length + byteBuf.writeByte(0); // domain value + byteBuf.writeShort(0); // port value + break; + } + case IPv6: { + byteBuf.writeBytes(IPv6_HOSTNAME_ZEROED); + byteBuf.writeShort(0); + break; + } + } + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponseDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponseDecoder.java new file mode 100644 index 0000000000..cff0880479 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksCmdResponseDecoder.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; +import io.netty.util.CharsetUtil; + +/** + * Decodes {@link ByteBuf}s into {@link SocksCmdResponse}. + * Before returning SocksResponse decoder removes itself from pipeline. + */ +public class SocksCmdResponseDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_CMD_RESPONSE_DECODER"; + + public static String getName() { + return name; + } + + private SocksMessage.ProtocolVersion version; + private int fieldLength; + private SocksMessage.CmdStatus cmdStatus; + private SocksMessage.AddressType addressType; + private byte reserved; + private String host; + private int port; + private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE; + + + public SocksCmdResponseDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksResponse decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte(byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.SOCKS5) { + break; + } + checkpoint(State.READ_CMD_HEADER); + } + case READ_CMD_HEADER: { + cmdStatus = SocksMessage.CmdStatus.fromByte(byteBuf.readByte()); + reserved = byteBuf.readByte(); + addressType = SocksMessage.AddressType.fromByte(byteBuf.readByte()); + checkpoint(State.READ_CMD_ADDRESS); + } + case READ_CMD_ADDRESS: { + switch (addressType) { + case IPv4: { + host = SocksCommonUtils.intToIp(byteBuf.readInt()); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdResponse(cmdStatus, addressType); + break; + } + case DOMAIN: { + fieldLength = byteBuf.readByte(); + host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdResponse(cmdStatus, addressType); + break; + } + case IPv6: { + host = SocksCommonUtils.ipv6toStr(byteBuf.readBytes(16).array()); + port = byteBuf.readUnsignedShort(); + msg = new SocksCmdResponse(cmdStatus, addressType); + break; + } + case UNKNOWN: + break; + } + } + } + ctx.pipeline().remove(this); + return msg; + } + + public enum State { + CHECK_PROTOCOL_VERSION, + READ_CMD_HEADER, + READ_CMD_ADDRESS + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksCommonUtils.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksCommonUtils.java new file mode 100644 index 0000000000..4768c2222e --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksCommonUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +final class SocksCommonUtils { + public static final SocksRequest UNKNOWN_SOCKS_REQUEST = new UnknownSocksRequest(); + public static final SocksResponse UNKNOWN_SOCKS_RESPONSE = new UnknownSocksResponse(); + + private static final int SECOND_ADDRESS_OCTET_SHIFT = 16; + private static final int FIRST_ADDRESS_OCTET_SHIFT = 24; + private static final int THIRD_ADDRESS_OCTET_SHIFT = 8; + private static final int XOR_DEFAULT_VALUE = 0xff; + + /** + * A constructor to stop this class being constructed. + */ + private SocksCommonUtils() { + //NOOP + } + + public static String intToIp(int i) { + return new StringBuilder().append((i >> FIRST_ADDRESS_OCTET_SHIFT) & XOR_DEFAULT_VALUE).append(".") + .append((i >> SECOND_ADDRESS_OCTET_SHIFT) & XOR_DEFAULT_VALUE).append(".") + .append((i >> THIRD_ADDRESS_OCTET_SHIFT) & XOR_DEFAULT_VALUE).append(".") + .append(i & XOR_DEFAULT_VALUE).toString(); + } + + private static final char[] ipv6conseqZeroFiller = {':', ':'}; + private static final char ipv6hextetSeparator = ':'; + + /* + * Convert numeric IPv6 to compressed format, where + * the longest sequence of 0's (with 2 or more 0's) is replaced with "::" + */ + public static String ipv6toCompressedForm(byte[] src) { + assert src.length == 16; + //Find the longest sequence of 0's + //start of compressed region (hextet index) + int cmprHextet = -1; + //length of compressed region + int cmprSize = 0; + for (int hextet = 0; hextet < 8;) { + int curByte = hextet * 2; + int size = 0; + while (curByte < src.length && src[curByte] == 0 + && src[curByte + 1] == 0) { + curByte += 2; + size++; + } + if (size > cmprSize) { + cmprHextet = hextet; + cmprSize = size; + } + hextet = (curByte / 2) + 1; + } + if (cmprHextet == -1 || cmprSize < 2) { + //No compression can be applied + return ipv6toStr(src); + } + StringBuilder sb = new StringBuilder(39); + ipv6toStr(sb, src, 0, cmprHextet); + sb.append(ipv6conseqZeroFiller); + ipv6toStr(sb, src, cmprHextet + cmprSize, 8); + return sb.toString(); + } + + /* + * Convert numeric IPv6 to standard (non-compressed) format. + * + * Borrowed from Inet6Address.java #numericToTextFormat(byte[]) + * Changed StringBuffer -> StringBuilder and ":" -> ':' for performance. + */ + public static String ipv6toStr(byte[] src) { + assert src.length == 16; + StringBuilder sb = new StringBuilder(39); + ipv6toStr(sb, src, 0, 8); + return sb.toString(); + } + + private static void ipv6toStr(StringBuilder sb, byte[] src, + int fromHextet, int toHextet) { + for (int i = fromHextet; i < toHextet; i++) { + sb.append(Integer.toHexString(((src[i << 1] << 8) & 0xff00) + | (src[(i << 1) + 1] & 0xff))); + if (i < toHextet - 1) { + sb.append(ipv6hextetSeparator); + } + } + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequest.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequest.java new file mode 100644 index 0000000000..d4bbfdf6a6 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequest.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; + +import java.util.Collections; +import java.util.List; + +/** + * An socks init request. + * + * @see SocksInitResponse + * @see SocksInitRequestDecoder + */ +public final class SocksInitRequest extends SocksRequest { + private final List authSchemes; + + public SocksInitRequest(List authSchemes) { + super(SocksRequestType.INIT); + if (authSchemes == null) { + throw new NullPointerException("authSchemes"); + } + this.authSchemes = authSchemes; + } + + /** + * Returns the List<{@link AuthScheme}> of this {@link SocksInitRequest} + * + * @return The List<{@link AuthScheme}> of this {@link SocksInitRequest} + */ + public List getAuthSchemes() { + return Collections.unmodifiableList(authSchemes); + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(authSchemes.size()); + for (AuthScheme authScheme : authSchemes) { + byteBuf.writeByte(authScheme.getByteValue()); + } + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequestDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequestDecoder.java new file mode 100644 index 0000000000..04a51bf8e5 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitRequestDecoder.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Decodes {@link ByteBuf}s into {@link SocksInitRequest}. + * Before returning SocksRequest decoder removes itself from pipeline. + */ +public class SocksInitRequestDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_INIT_REQUEST_DECODER"; + + public static String getName() { + return name; + } + + private final List authSchemes = new ArrayList(); + private SocksMessage.ProtocolVersion version; + private byte authSchemeNum; + private SocksRequest msg = SocksCommonUtils.UNKNOWN_SOCKS_REQUEST; + + public SocksInitRequestDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksRequest decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte(byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.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(SocksMessage.AuthScheme.fromByte(byteBuf.readByte())); + } + msg = new SocksInitRequest(authSchemes); + break; + } + } + ctx.pipeline().remove(this); + return msg; + } + + enum State { + CHECK_PROTOCOL_VERSION, + READ_AUTH_SCHEMES + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponse.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponse.java new file mode 100644 index 0000000000..77f78caa46 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponse.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An socks init response. + * + * @see SocksInitRequest + * @see SocksInitResponseDecoder + */ +public final class SocksInitResponse extends SocksResponse { + private final AuthScheme authScheme; + + /** + * + * @param authScheme + * @throws NullPointerException + */ + public SocksInitResponse(AuthScheme authScheme) { + super(SocksResponseType.INIT); + if (authScheme == null) { + throw new NullPointerException("authScheme"); + } + this.authScheme = authScheme; + } + + /** + * Returns the {@link AuthScheme} of this {@link SocksInitResponse} + * + * @return The {@link AuthScheme} of this {@link SocksInitResponse} + */ + public AuthScheme getAuthScheme() { + return authScheme; + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + byteBuf.writeByte(getProtocolVersion().getByteValue()); + byteBuf.writeByte(authScheme.getByteValue()); + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponseDecoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponseDecoder.java new file mode 100644 index 0000000000..b1bf2c4c1f --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksInitResponseDecoder.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; + +/** + * Decodes {@link ByteBuf}s into {@link SocksInitResponse}. + * Before returning SocksResponse decoder removes itself from pipeline. + */ +public class SocksInitResponseDecoder extends ReplayingDecoder { + private static final String name = "SOCKS_INIT_RESPONSE_DECODER"; + + public static String getName() { + return name; + } + + private SocksMessage.ProtocolVersion version; + private SocksMessage.AuthScheme authScheme; + + private SocksResponse msg = SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE; + + public SocksInitResponseDecoder() { + super(State.CHECK_PROTOCOL_VERSION); + } + + @Override + public SocksResponse decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + switch (state()) { + case CHECK_PROTOCOL_VERSION: { + version = SocksMessage.ProtocolVersion.fromByte(byteBuf.readByte()); + if (version != SocksMessage.ProtocolVersion.SOCKS5) { + break; + } + checkpoint(State.READ_PREFFERED_AUTH_TYPE); + } + case READ_PREFFERED_AUTH_TYPE: { + authScheme = SocksMessage.AuthScheme.fromByte(byteBuf.readByte()); + msg = new SocksInitResponse(authScheme); + break; + } + } + ctx.pipeline().remove(this); + return msg; + } + + public enum State { + CHECK_PROTOCOL_VERSION, + READ_PREFFERED_AUTH_TYPE + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksMessage.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksMessage.java new file mode 100644 index 0000000000..34303ed547 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksMessage.java @@ -0,0 +1,234 @@ +/* + * 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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An abstract class that defines a SocksMessage, providing common properties for + * {@link SocksRequest} and {@link SocksResponse}. + * + * @see SocksRequest + * @see SocksResponse + */ + +public abstract class SocksMessage { + private final MessageType messageType; + private final ProtocolVersion protocolVersion = ProtocolVersion.SOCKS5; + + /** + * + * @param messageType + * @throws NullPointerException + */ + public SocksMessage(MessageType messageType) { + if (messageType == null) { + throw new NullPointerException("messageType"); + } + this.messageType = messageType; + } + + /** + * Returns the {@link MessageType} of this {@link SocksMessage} + * + * @return The {@link MessageType} of this {@link SocksMessage} + */ + public MessageType getMessageType() { + return messageType; + } + + public enum MessageType { + REQUEST, + RESPONSE, + UNKNOWN + } + + public enum AuthScheme { + NO_AUTH((byte) 0x00), + AUTH_GSSAPI((byte) 0x01), + AUTH_PASSWORD((byte) 0x02), + UNKNOWN((byte) 0xff); + + private final byte b; + + private AuthScheme(byte b) { + this.b = b; + } + + public static AuthScheme fromByte(byte b) { + for (AuthScheme code : values()) { + if (code.b == b) { + return code; + } + } + return UNKNOWN; + } + + public byte getByteValue() { + return this.b; + } + } + + public enum CmdType { + CONNECT((byte) 0x01), + BIND((byte) 0x02), + UDP((byte) 0x03), + UNKNOWN((byte) 0xff); + + private final byte b; + + private CmdType(byte b) { + this.b = b; + } + + public static CmdType fromByte(byte b) { + for (CmdType code : values()) { + if (code.b == b) { + return code; + } + } + return UNKNOWN; + } + + public byte getByteValue() { + return this.b; + } + } + + public enum AddressType { + IPv4((byte) 0x01), + DOMAIN((byte) 0x03), + IPv6((byte) 0x04), + UNKNOWN((byte) 0xff); + + private final byte b; + + private AddressType(byte b) { + this.b = b; + } + + public static AddressType fromByte(byte b) { + for (AddressType code : values()) { + if (code.b == b) { + return code; + } + } + return UNKNOWN; + } + + public byte getByteValue() { + return this.b; + } + } + + public enum AuthStatus { + SUCCESS((byte) 0x00), + FAILURE((byte) 0xff); + + private final byte b; + + private AuthStatus(byte b) { + this.b = b; + } + + public static AuthStatus fromByte(byte b) { + for (AuthStatus code : values()) { + if (code.b == b) { + return code; + } + } + return FAILURE; + } + + public byte getByteValue() { + return this.b; + } + } + + public enum CmdStatus { + SUCCESS((byte) 0x00), + FAILURE((byte) 0x01), + FORBIDDEN((byte) 0x02), + NETWORK_UNREACHABLE((byte) 0x03), + HOST_UNREACHABLE((byte) 0x04), + REFUSED((byte) 0x05), + TTL_EXPIRED((byte) 0x06), + COMMAND_NOT_SUPPORTED((byte) 0x07), + ADDRESS_NOT_SUPPORTED((byte) 0x08), + UNASSIGNED((byte) 0xff); + + private final byte b; + + private CmdStatus(byte b) { + this.b = b; + } + + public static CmdStatus fromByte(byte b) { + for (CmdStatus code : values()) { + if (code.b == b) { + return code; + } + } + return UNASSIGNED; + } + + public byte getByteValue() { + return this.b; + } + } + + public enum ProtocolVersion { + SOCKS4a((byte) 0x04), + SOCKS5((byte) 0x05), + UNKNOWN((byte) 0xff); + + private final byte b; + + private ProtocolVersion(byte b) { + this.b = b; + } + + public static ProtocolVersion fromByte(byte b) { + for (ProtocolVersion code : values()) { + if (code.b == b) { + return code; + } + } + return UNKNOWN; + } + + public byte getByteValue() { + return this.b; + } + } + + /** + * Returns the {@link ProtocolVersion} of this {@link SocksMessage} + * + * @return The {@link ProtocolVersion} of this {@link SocksMessage} + */ + public ProtocolVersion getProtocolVersion() { + return protocolVersion; + } + + /** + * Encode socks message into its byte representation and write it into byteBuf + * + * @param byteBuf + * @see ByteBuf + */ + public abstract void encodeAsByteBuf(ByteBuf byteBuf); +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksMessageEncoder.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksMessageEncoder.java new file mode 100644 index 0000000000..9432ada2fe --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksMessageEncoder.java @@ -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.codec.socks; + +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 SocksInitRequest}, {@link SocksInitResponse}, {@link SocksAuthRequest}, + * {@link SocksAuthResponse}, {@link SocksCmdRequest} and {@link SocksCmdResponse} + */ +@ChannelHandler.Sharable +public class SocksMessageEncoder extends MessageToByteEncoder { + private static final String name = "SOCKS_MESSAGE_ENCODER"; + + public static String getName() { + return name; + } + + public SocksMessageEncoder() { + super(SocksMessage.class); + } + + @Override + public void encode(ChannelHandlerContext ctx, SocksMessage msg, ByteBuf out) throws Exception { + msg.encodeAsByteBuf(out); + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksRequest.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksRequest.java new file mode 100644 index 0000000000..4197915678 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksRequest.java @@ -0,0 +1,61 @@ +/* + * 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.codec.socks; + +/** + * An abstract class that defines a SocksRequest, providing common properties for + * {@link SocksInitRequest}, {@link SocksAuthRequest}, {@link SocksCmdRequest} and {@link UnknownSocksRequest}. + * + * @see SocksInitRequest + * @see SocksAuthRequest + * @see SocksCmdRequest + * @see UnknownSocksRequest + */ +public abstract class SocksRequest extends SocksMessage { + private final SocksRequestType socksRequestType; + + /** + * + * @param socksRequestType + * @throws NullPointerException + */ + public SocksRequest(SocksRequestType socksRequestType) { + super(MessageType.REQUEST); + if (socksRequestType == null) { + throw new NullPointerException("socksRequestType"); + } + this.socksRequestType = socksRequestType; + } + + /** + * Returns socks request type + * + * @return socks request type + */ + public SocksRequestType getSocksRequestType() { + return socksRequestType; + } + + /** + * Type of socks request + */ + public enum SocksRequestType { + INIT, + AUTH, + CMD, + UNKNOWN + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/SocksResponse.java b/codec-socks/src/main/java/io/netty/codec/socks/SocksResponse.java new file mode 100644 index 0000000000..2b55e90d22 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/SocksResponse.java @@ -0,0 +1,61 @@ +/* + * 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.codec.socks; + +/** + * An abstract class that defines a SocksResponse, providing common properties for + * {@link SocksInitResponse}, {@link SocksAuthResponse}, {@link SocksCmdResponse} and {@link UnknownSocksResponse}. + * + * @see SocksInitResponse + * @see SocksAuthResponse + * @see SocksCmdResponse + * @see UnknownSocksResponse + */ +public abstract class SocksResponse extends SocksMessage { + private final SocksResponseType socksResponseType; + + /** + * + * @param socksResponseType + * @throws NullPointerException + */ + public SocksResponse(SocksResponseType socksResponseType) { + super(MessageType.RESPONSE); + if (socksResponseType == null) { + throw new NullPointerException("socksResponseType"); + } + this.socksResponseType = socksResponseType; + } + + /** + * Returns socks response type + * + * @return socks response type + */ + public SocksResponseType getSocksResponseType() { + return socksResponseType; + } + + /** + * Type of socks response + */ + public enum SocksResponseType { + INIT, + AUTH, + CMD, + UNKNOWN + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksMessage.java b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksMessage.java new file mode 100644 index 0000000000..bbf04ff8bf --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksMessage.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An unknown socks message. + * + * @see UnknownSocksRequest + * @see UnknownSocksResponse + */ +public final class UnknownSocksMessage extends SocksMessage { + + public UnknownSocksMessage() { + super(MessageType.UNKNOWN); + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + // NOOP + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksRequest.java b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksRequest.java new file mode 100644 index 0000000000..477b1d939e --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksRequest.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An unknown socks request. + * + * @see SocksInitRequestDecoder + * @see SocksAuthRequestDecoder + * @see SocksCmdRequestDecoder + */ +public final class UnknownSocksRequest extends SocksRequest { + + public UnknownSocksRequest() { + super(SocksRequestType.UNKNOWN); + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + // NOOP + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksResponse.java b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksResponse.java new file mode 100644 index 0000000000..6f6b27ef16 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/UnknownSocksResponse.java @@ -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.codec.socks; + +import io.netty.buffer.ByteBuf; + +/** + * An unknown socks response. + * + * @see SocksInitResponseDecoder + * @see SocksAuthResponseDecoder + * @see SocksCmdResponseDecoder + */ +public final class UnknownSocksResponse extends SocksResponse { + + public UnknownSocksResponse() { + super(SocksResponseType.UNKNOWN); + } + + @Override + public void encodeAsByteBuf(ByteBuf byteBuf) { + // NOOP + } +} diff --git a/codec-socks/src/main/java/io/netty/codec/socks/package-info.java b/codec-socks/src/main/java/io/netty/codec/socks/package-info.java new file mode 100644 index 0000000000..fd07765ff4 --- /dev/null +++ b/codec-socks/src/main/java/io/netty/codec/socks/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.codec.socks; diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestDecoderTest.java new file mode 100644 index 0000000000..e7dbdc517a --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestDecoderTest.java @@ -0,0 +1,39 @@ +/* + * 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.codec.socks; + +import io.netty.channel.embedded.EmbeddedByteChannel; +import org.junit.Test; +import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SocksAuthRequestDecoderTest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocksAuthRequestDecoderTest.class); + @Test + public void testAuthRequestDecoder() { + String username = "test"; + String password = "test"; + SocksAuthRequest msg = new SocksAuthRequest(username, password); + SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder(); + EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); + msg = (SocksAuthRequest) embedder.readInbound(); + assertTrue(msg.getUsername().equals(username)); + assertTrue(msg.getUsername().equals(password)); + assertNull(embedder.readInbound()); + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestTest.java new file mode 100644 index 0000000000..79ae1adb8d --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthRequestTest.java @@ -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.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksAuthRequestTest { + @Test + public void testConstructorParamsAreNotNull() { + try { + new SocksAuthRequest(null, ""); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + try { + new SocksAuthRequest("", null); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + } + + @Test + public void testUsernameOrPasswordIsNotAscii() { + try { + new SocksAuthRequest("παράδειγμα.δοκιμή", "password"); + } catch (Exception e) { + assertTrue(e instanceof IllegalArgumentException); + } + try { + new SocksAuthRequest("username", "παράδειγμα.δοκιμή"); + } catch (Exception e) { + assertTrue(e instanceof IllegalArgumentException); + } + } + + @Test + public void testUsernameOrPasswordLengthIsLessThan255Chars() { + try { + new SocksAuthRequest( + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword", + "password"); + } catch (Exception e) { + assertTrue(e instanceof IllegalArgumentException); + } + try { + new SocksAuthRequest("password", + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpassword"); + } catch (Exception e) { + assertTrue(e instanceof IllegalArgumentException); + } + } + +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseDecoderTest.java new file mode 100644 index 0000000000..e82794738f --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseDecoderTest.java @@ -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.codec.socks; + +import io.netty.channel.embedded.EmbeddedByteChannel; +import org.junit.Test; +import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SocksAuthResponseDecoderTest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocksAuthResponseDecoderTest.class); + private void testSocksAuthResponseDecoderWithDifferentParams(SocksMessage.AuthStatus authStatus){ + logger.debug("Testing SocksAuthResponseDecoder with authStatus: "+ authStatus); + SocksAuthResponse msg = new SocksAuthResponse(authStatus); + SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder(); + EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); + msg = (SocksAuthResponse) embedder.readInbound(); + assertTrue(msg.getAuthStatus().equals(authStatus)); + assertNull(embedder.readInbound()); + } + + @Test + public void testSocksCmdResponseDecoder(){ + for (SocksMessage.AuthStatus authStatus: SocksMessage.AuthStatus.values()){ + testSocksAuthResponseDecoderWithDifferentParams(authStatus); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseTest.java new file mode 100644 index 0000000000..ae9f43669b --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksAuthResponseTest.java @@ -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.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksAuthResponseTest { + @Test + public void testConstructorParamsAreNotNull() { + try { + new SocksAuthResponse(null); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestDecoderTest.java new file mode 100644 index 0000000000..9d89e59102 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestDecoderTest.java @@ -0,0 +1,104 @@ +/* + * 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.codec.socks; + +import io.netty.channel.embedded.EmbeddedByteChannel; +import org.junit.Test; +import org.slf4j.LoggerFactory; +import sun.net.util.IPAddressUtil; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SocksCmdRequestDecoderTest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocksCmdRequestDecoderTest.class); + + private void testSocksCmdRequestDecoderWithDifferentParams(SocksMessage.CmdType cmdType, SocksMessage.AddressType addressType, String host, int port) { + logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: " + host + " port: " + port); + SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port); + SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder(); + EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); + if (msg.getAddressType() == SocksMessage.AddressType.UNKNOWN) { + assertTrue(embedder.readInbound() instanceof UnknownSocksRequest); + } else { + msg = (SocksCmdRequest) embedder.readInbound(); + assertTrue(msg.getCmdType().equals(cmdType)); + assertTrue(msg.getAddressType().equals(addressType)); + assertTrue(msg.getHost().equals(host)); + assertTrue(msg.getPort() == port); + } + assertNull(embedder.readInbound()); + } + + @Test + public void testCmdRequestDecoderIPv4() { + String[] hosts = {"127.0.0.1",}; + int[] ports = {0, 32769, 65535 }; + for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) { + for (String host : hosts) { + for (int port : ports) { + testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.IPv4, host, port); + } + } + } + } + + @Test + public void testCmdRequestDecoderIPv6() { + String[] hosts = {SocksCommonUtils.ipv6toStr(IPAddressUtil.textToNumericFormatV6("::1"))}; + int[] ports = {0, 32769, 65535}; + for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) { + for (String host : hosts) { + for (int port : ports) { + testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.IPv6, host, port); + } + } + } + } + + @Test + public void testCmdRequestDecoderDomain() { + String[] hosts = {"google.com" , + "مثال.إختبار", + "παράδειγμα.δοκιμή", + "مثال.آزمایشی", + "пример.испытание", + "בײַשפּיל.טעסט", + "例子.测试", + "例子.測試", + "उदाहरण.परीक्षा", + "例え.テスト", + "실례.테스트", + "உதாரணம்.பரிட்சை"}; + int[] ports = {0, 32769, 65535}; + for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) { + for (String host : hosts) { + for (int port : ports) { + testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.DOMAIN, host, port); + } + } + } + } + + @Test + public void testCmdRequestDecoderUnknown() { + String host = "google.com"; + int port = 80; + for (SocksMessage.CmdType cmdType : SocksMessage.CmdType.values()) { + testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksMessage.AddressType.UNKNOWN, host, port); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestTest.java new file mode 100644 index 0000000000..cf6fa6a374 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdRequestTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksCmdRequestTest { + @Test + public void testConstructorParamsAreNotNull(){ + try { + new SocksCmdRequest(null, SocksMessage.AddressType.UNKNOWN, "", 0); + } catch (Exception e){ + assertTrue(e instanceof NullPointerException); + } + + try { + new SocksCmdRequest(SocksMessage.CmdType.UNKNOWN, null, "", 0); + } catch (Exception e){ + assertTrue(e instanceof NullPointerException); + } + + try { + new SocksCmdRequest(SocksMessage.CmdType.UNKNOWN, SocksMessage.AddressType.UNKNOWN, null, 0); + } catch (Exception e){ + assertTrue(e instanceof NullPointerException); + } + } + + @Test + public void testIPv4CorrectAddress(){ + try { + new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.IPv4, "54.54.1111.253", 0); + } catch (Exception e){ + assertTrue(e instanceof IllegalArgumentException); + } + } + + @Test + public void testIPv6CorrectAddress(){ + try { + new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.IPv6, "xxx:xxx:xxx", 0); + } catch (Exception e){ + assertTrue(e instanceof IllegalArgumentException); + } + } + + @Test + public void testIDNNotExceeds255CharsLimit(){ + try { + new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN, + "παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" + + "παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" + + "παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" + + "παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή", 0); + } catch (Exception e){ + assertTrue(e instanceof IllegalArgumentException); + } + } + + @Test + public void testValidPortRange(){ + try { + new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN, + "παράδειγμα.δοκιμήπαράδει", -1); + } catch (Exception e){ + assertTrue(e instanceof IllegalArgumentException); + } + + try { + new SocksCmdRequest(SocksMessage.CmdType.BIND, SocksMessage.AddressType.DOMAIN, + "παράδειγμα.δοκιμήπαράδει", 65536); + } catch (Exception e){ + assertTrue(e instanceof IllegalArgumentException); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseDecoderTest.java new file mode 100644 index 0000000000..bac94b8353 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseDecoderTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import io.netty.channel.embedded.EmbeddedByteChannel; +import org.junit.Test; +import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SocksCmdResponseDecoderTest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocksCmdResponseDecoderTest.class); + + private void testSocksCmdResponseDecoderWithDifferentParams(SocksMessage.CmdStatus cmdStatus, SocksMessage.AddressType addressType){ + logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType); + SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType); + SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder(); + EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); + if (addressType == SocksMessage.AddressType.UNKNOWN){ + assertTrue(embedder.readInbound() instanceof UnknownSocksResponse); + } else { + msg = (SocksCmdResponse) embedder.readInbound(); + assertTrue(((SocksCmdResponse) msg).getCmdStatus().equals(cmdStatus)); + } + assertNull(embedder.readInbound()); + } + + @Test + public void testSocksCmdResponseDecoder(){ + for (SocksMessage.CmdStatus cmdStatus: SocksMessage.CmdStatus.values()){ + for (SocksMessage.AddressType addressType: SocksMessage.AddressType.values()){ + testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, addressType); + } + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseTest.java new file mode 100644 index 0000000000..a3e7fcfd23 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksCmdResponseTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksCmdResponseTest { + @Test + public void testConstructorParamsAreNotNull() { + try { + new SocksCmdResponse(null, SocksMessage.AddressType.UNKNOWN); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + try { + new SocksCmdResponse(SocksMessage.CmdStatus.UNASSIGNED, null); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksCommonTestUtils.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksCommonTestUtils.java new file mode 100644 index 0000000000..bfb7f93cb1 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksCommonTestUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.codec.socks; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedByteChannel; + +class SocksCommonTestUtils { + /** + * A constructor to stop this class being constructed. + */ + private SocksCommonTestUtils() { + //NOOP + } + + public static void writeMessageIntoEmbedder(EmbeddedByteChannel embedder, SocksMessage msg) { + ByteBuf buf = Unpooled.buffer(); + msg.encodeAsByteBuf(buf); + embedder.writeInbound(buf); + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksInitRequestTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksInitRequestTest.java new file mode 100644 index 0000000000..737a5f07ec --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksInitRequestTest.java @@ -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.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksInitRequestTest { + @Test + public void testConstructorParamsAreNotNull(){ + try { + new SocksInitRequest(null); + } catch (Exception e){ + assertTrue(e instanceof NullPointerException); + } + } +} diff --git a/codec-socks/src/test/java/io/netty/codec/socks/SocksInitResponseTest.java b/codec-socks/src/test/java/io/netty/codec/socks/SocksInitResponseTest.java new file mode 100644 index 0000000000..91c971f5b0 --- /dev/null +++ b/codec-socks/src/test/java/io/netty/codec/socks/SocksInitResponseTest.java @@ -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.codec.socks; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class SocksInitResponseTest { + @Test + public void testConstructorParamsAreNotNull() { + try { + new SocksInitResponse(null); + } catch (Exception e) { + assertTrue(e instanceof NullPointerException); + } + } +} diff --git a/example/pom.xml b/example/pom.xml index b0543b6a3e..e5a199c1bf 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -14,41 +14,47 @@ ~ License for the specific language governing permissions and limitations ~ under the License. --> - + - 4.0.0 - - io.netty - netty-parent - 4.0.0.Alpha8-SNAPSHOT - + 4.0.0 + + io.netty + netty-parent + 4.0.0.Alpha8-SNAPSHOT + - netty-example - jar + netty-example + jar - Netty/Example + Netty/Example - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-handler - ${project.version} - - - ${project.groupId} - netty-codec-http - ${project.version} - + + + ${project.groupId} + netty-transport + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + + + ${project.groupId} + netty-codec-http + ${project.version} + + + ${project.groupId} + netty-codec-socks + ${project.version} + - - com.google.protobuf - protobuf-java - - + + com.google.protobuf + protobuf-java + + diff --git a/example/src/main/java/io/netty/example/socksproxy/CallbackNotifier.java b/example/src/main/java/io/netty/example/socksproxy/CallbackNotifier.java new file mode 100644 index 0000000000..1d19b4bfbd --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/CallbackNotifier.java @@ -0,0 +1,24 @@ +/* + * 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.channel.ChannelHandlerContext; + +public interface CallbackNotifier { + void onSuccess(final ChannelHandlerContext outboundCtx); + + void onFailure(final ChannelHandlerContext outboundCtx, final Throwable cause); +} diff --git a/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java b/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java new file mode 100644 index 0000000000..06a6a449a3 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.example.socksproxy; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundByteHandlerAdapter; + + +public final class DirectClientHandler extends ChannelInboundByteHandlerAdapter { + private static final String name = "DIRECT_CLIENT_HANDLER"; + + public static String getName() { + return name; + } + private final CallbackNotifier cb; + + public DirectClientHandler(CallbackNotifier cb) { + this.cb = cb; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.pipeline().remove(this); + cb.onSuccess(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception { + cb.onFailure(ctx, throwable); + } + + @Override + public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/DirectClientInitializer.java b/example/src/main/java/io/netty/example/socksproxy/DirectClientInitializer.java new file mode 100644 index 0000000000..678a7d715a --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/DirectClientInitializer.java @@ -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.example.socksproxy; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; + + +public final class DirectClientInitializer extends ChannelInitializer { + + private final CallbackNotifier callbackNotifier; + + public DirectClientInitializer(CallbackNotifier callbackNotifier) { + this.callbackNotifier = callbackNotifier; + } + + @Override + public void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline channelPipeline = socketChannel.pipeline(); + channelPipeline.addLast(DirectClientHandler.getName(), new DirectClientHandler(callbackNotifier)); + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java new file mode 100644 index 0000000000..8d0aa39200 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java @@ -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.example.socksproxy; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundByteHandlerAdapter; + + +public final class RelayHandler extends ChannelInboundByteHandlerAdapter { + private static final String name = "RELAY_HANDLER"; + + public static String getName() { + return name; + } + + private final Channel relayChannel; + + public RelayHandler(Channel relayChannel) { + this.relayChannel = relayChannel; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + + @Override + public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf out = relayChannel.outboundByteBuffer(); + out.discardReadBytes(); + out.writeBytes(in); + in.clear(); + if (relayChannel.isActive()) { + relayChannel.flush(); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (relayChannel.isActive()) { + SocksServerUtils.closeOnFlush(relayChannel); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + +} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServer.java b/example/src/main/java/io/netty/example/socksproxy/SocksServer.java new file mode 100644 index 0000000000..91f6344cf8 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServer.java @@ -0,0 +1,47 @@ +/* + * 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.bootstrap.ServerBootstrap; +import io.netty.channel.socket.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +public final class SocksServer { + private final int localPort; + + public SocksServer(int localPort) { + this.localPort = localPort; + } + + public void run() throws Exception { + System.err.println( + "Listening on*:" + localPort + "..."); + ServerBootstrap b = new ServerBootstrap(); + try { + b.group(new NioEventLoopGroup(), new NioEventLoopGroup()) + .channel(NioServerSocketChannel.class) + .localAddress(localPort) + .childHandler(new SocksServerInitializer()); + b.bind().sync().channel().closeFuture().sync(); + } finally { + b.shutdown(); + } + } + + public static void main(String[] args) throws Exception { + new SocksServer(1080).run(); + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java new file mode 100644 index 0000000000..581558dc00 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java @@ -0,0 +1,82 @@ +/* + * 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.bootstrap.Bootstrap; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.codec.socks.SocksCmdRequest; +import io.netty.codec.socks.SocksCmdResponse; +import io.netty.codec.socks.SocksMessage; + + +@ChannelHandler.Sharable +public final class SocksServerConnectHandler extends ChannelInboundMessageHandlerAdapter { + private static final String name = "SOCKS_SERVER_CONNECT_HANDLER"; + + public static String getName() { + return name; + } + + private final Bootstrap b; + + public SocksServerConnectHandler() { + super(SocksCmdRequest.class); + b = new Bootstrap(); + } + + @Override + public void messageReceived(final ChannelHandlerContext ctx, final SocksCmdRequest request) throws Exception { + CallbackNotifier cb = new CallbackNotifier() { + public void onSuccess(final ChannelHandlerContext outboundCtx) { + ctx.channel().write(new SocksCmdResponse(SocksMessage.CmdStatus.SUCCESS, request.getAddressType())) + .addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture channelFuture) throws Exception { + ctx.pipeline().remove(SocksServerConnectHandler.getName()); + outboundCtx.channel().pipeline().addLast(new RelayHandler(ctx.channel())); + ctx.channel().pipeline().addLast(new RelayHandler(outboundCtx.channel())); + } + }); + } + + public void onFailure(ChannelHandlerContext outboundCtx, Throwable cause) { + ctx.channel().write(new SocksCmdResponse(SocksMessage.CmdStatus.FAILURE, request.getAddressType())); + 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 DirectClientInitializer(cb)) + .remoteAddress(request.getHost(), request.getPort()); + ChannelFuture f = b.connect(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + SocksServerUtils.closeOnFlush(ctx.channel()); + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java new file mode 100644 index 0000000000..cc502a8906 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java @@ -0,0 +1,80 @@ +/* + * 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.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.codec.socks.SocksCmdRequestDecoder; +import io.netty.codec.socks.SocksInitResponse; +import io.netty.codec.socks.SocksMessage; +import io.netty.codec.socks.SocksRequest; +import io.netty.codec.socks.SocksAuthResponse; +import io.netty.codec.socks.SocksCmdRequest; +import io.netty.codec.socks.SocksCmdResponse; + + + +@ChannelHandler.Sharable +public final class SocksServerHandler extends ChannelInboundMessageHandlerAdapter { + private static final String name = "SOCKS_SERVER_HANDLER"; + + public static String getName() { + return name; + } + + public SocksServerHandler() { + super(SocksRequest.class); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception { + switch (socksRequest.getSocksRequestType()) { + case INIT: { +// auth support example +// ctx.pipeline().addFirst("socksAuthRequestDecoder",new SocksAuthRequestDecoder()); +// ctx.write(new SocksInitResponse(SocksMessage.AuthScheme.AUTH_PASSWORD)); + ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder()); + ctx.write(new SocksInitResponse(SocksMessage.AuthScheme.NO_AUTH)); + break; + } + case AUTH: + ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder()); + ctx.write(new SocksAuthResponse(SocksMessage.AuthStatus.SUCCESS)); + break; + case CMD: + SocksCmdRequest req = (SocksCmdRequest) socksRequest; + if (req.getCmdType() == SocksMessage.CmdType.CONNECT) { + ctx.pipeline().addLast(SocksServerConnectHandler.getName(), new SocksServerConnectHandler()); + ctx.pipeline().remove(this); + ctx.nextInboundMessageBuffer().add(socksRequest); + ctx.fireInboundBufferUpdated(); + } else { + ctx.close(); + } + break; + case UNKNOWN: + ctx.close(); + break; + } + } + + @Override + public void exceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable throwable) throws Exception { + throwable.printStackTrace(); + SocksServerUtils.closeOnFlush(ctx.channel()); + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java new file mode 100644 index 0000000000..10c454c760 --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java @@ -0,0 +1,39 @@ +/* + * 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.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.codec.socks.SocksInitRequestDecoder; +import io.netty.codec.socks.SocksMessageEncoder; + +public final class SocksServerInitializer extends ChannelInitializer { + private SocksMessageEncoder socksMessageEncoder = new SocksMessageEncoder(); + private SocksServerHandler socksServerHandler = new SocksServerHandler(); + + public SocksServerInitializer() { + super(); + } + + @Override + public void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline channelPipeline = socketChannel.pipeline(); + channelPipeline.addLast(SocksInitRequestDecoder.getName(), new SocksInitRequestDecoder()); + channelPipeline.addLast(SocksMessageEncoder.getName(), socksMessageEncoder); + channelPipeline.addLast(SocksServerHandler.getName(), socksServerHandler); + } +} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java new file mode 100644 index 0000000000..4e0e7b147d --- /dev/null +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.example.socksproxy; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; + +public final class SocksServerUtils { + + private SocksServerUtils() { + //NOOP + } + + /** + * Closes the specified channel after all queued write requests are flushed. + */ + public static void closeOnFlush(Channel ch) { + if (ch.isActive()) { + ch.flush().addListener(ChannelFutureListener.CLOSE); + } + } +} diff --git a/pom.xml b/pom.xml index 187ba69cce..cf9a189627 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ buffer codec codec-http + codec-socks transport handler metrics-yammer @@ -278,6 +279,7 @@ sun.misc.Unsafe sun.misc.Cleaner + sun.net.util.IPAddressUtil java.util.zip.Deflater