Revamp io.netty.handler.codec.socksx
While implementing netty-handler-proxy, I realized various issues in our current socksx package. Here's the list of the modifications and their background: - Split message types into interfaces and default implementations - so that a user can implement an alternative message implementations - Use classes instead of enums when a user might want to define a new constant - so that a user can extend SOCKS5 protocol, such as: - defining a new error code - defining a new address type - Rename the message classes - to avoid abbreviated class names. e.g: - Cmd -> Command - Init -> Initial - so that the class names align better with the protocol specifications. e.g: - AuthRequest -> PasswordAuthRequest - AuthScheme -> AuthMethod - Rename the property names of the messages - so that the property names align better when the field names in the protocol specifications - Improve the decoder implementations - Give a user more control over when a decoder has to be removed - Use DecoderResult and DecoderResultProvider to handle decode failure gracefully. i.e. no more Unknown* message classes - Add SocksPortUnifinicationServerHandler since it's useful to the users who write a SOCKS server - Cleaned up and moved from the socksproxy example
This commit is contained in:
parent
865a83c15d
commit
976db9269d
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -16,27 +16,25 @@
|
||||
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
public enum SocksProtocolVersion {
|
||||
SOCKS4a((byte) 0x04),
|
||||
SOCKS5((byte) 0x05),
|
||||
UNKNOWN((byte) 0xff);
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
|
||||
private final byte b;
|
||||
/**
|
||||
* An abstract {@link SocksMessage}.
|
||||
*/
|
||||
public abstract class AbstractSocksMessage implements SocksMessage {
|
||||
|
||||
SocksProtocolVersion(byte b) {
|
||||
this.b = b;
|
||||
private DecoderResult decoderResult = DecoderResult.SUCCESS;
|
||||
|
||||
@Override
|
||||
public DecoderResult decoderResult() {
|
||||
return decoderResult;
|
||||
}
|
||||
|
||||
public static SocksProtocolVersion valueOf(byte b) {
|
||||
for (SocksProtocolVersion code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
@Override
|
||||
public void setDecoderResult(DecoderResult decoderResult) {
|
||||
if (decoderResult == null) {
|
||||
throw new NullPointerException("decoderResult");
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
this.decoderResult = decoderResult;
|
||||
}
|
||||
}
|
@ -15,40 +15,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
import io.netty.handler.codec.DecoderResultProvider;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SocksMessage, providing common properties for
|
||||
* {@link SocksRequest} and {@link SocksResponse}.
|
||||
* An interface that all SOCKS protocol messages implement.
|
||||
*/
|
||||
public abstract class SocksMessage {
|
||||
private final SocksMessageType type;
|
||||
private final SocksProtocolVersion protocolVersion;
|
||||
|
||||
protected SocksMessage(SocksProtocolVersion protocolVersion, SocksMessageType type) {
|
||||
if (protocolVersion == null) {
|
||||
throw new NullPointerException("protocolVersion");
|
||||
}
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type");
|
||||
}
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.type = type;
|
||||
}
|
||||
public interface SocksMessage extends DecoderResultProvider {
|
||||
|
||||
/**
|
||||
* Returns the {@link SocksMessageType} of this {@link SocksMessage}
|
||||
*
|
||||
* @return The {@link SocksMessageType} of this {@link SocksMessage}
|
||||
* Returns the protocol version of this message.
|
||||
*/
|
||||
public SocksMessageType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link SocksProtocolVersion} of this {@link SocksMessage}
|
||||
*
|
||||
* @return The {@link SocksProtocolVersion} of this {@link SocksMessage}
|
||||
*/
|
||||
public SocksProtocolVersion protocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
SocksVersion version();
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
public enum SocksMessageType {
|
||||
REQUEST,
|
||||
RESPONSE,
|
||||
UNKNOWN
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ServerEncoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AddressEncoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Detects the version of the current SOCKS connection and initializes the pipeline with
|
||||
* {@link Socks4ServerDecoder} or {@link Socks5InitialRequestDecoder}.
|
||||
*/
|
||||
public class SocksPortUnificationServerHandler extends ByteToMessageDecoder {
|
||||
|
||||
private static final InternalLogger logger =
|
||||
InternalLoggerFactory.getInstance(SocksPortUnificationServerHandler.class);
|
||||
|
||||
private final Socks5ServerEncoder socks5encoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default configuration.
|
||||
*/
|
||||
public SocksPortUnificationServerHandler() {
|
||||
this(Socks5ServerEncoder.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified {@link Socks5ServerEncoder}.
|
||||
* This constructor is useful when a user wants to use an alternative {@link Socks5AddressEncoder}.
|
||||
*/
|
||||
public SocksPortUnificationServerHandler(Socks5ServerEncoder socks5encoder) {
|
||||
if (socks5encoder == null) {
|
||||
throw new NullPointerException("socks5encoder");
|
||||
}
|
||||
|
||||
this.socks5encoder = socks5encoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
final int readerIndex = in.readerIndex();
|
||||
if (in.writerIndex() == readerIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelPipeline p = ctx.pipeline();
|
||||
final byte versionVal = in.getByte(readerIndex);
|
||||
SocksVersion version = SocksVersion.valueOf(versionVal);
|
||||
|
||||
switch (version) {
|
||||
case SOCKS4a:
|
||||
logKnownVersion(ctx, version);
|
||||
p.addAfter(ctx.name(), null, Socks4ServerEncoder.INSTANCE);
|
||||
p.addAfter(ctx.name(), null, new Socks4ServerDecoder());
|
||||
break;
|
||||
case SOCKS5:
|
||||
logKnownVersion(ctx, version);
|
||||
p.addAfter(ctx.name(), null, socks5encoder);
|
||||
p.addAfter(ctx.name(), null, new Socks5InitialRequestDecoder());
|
||||
break;
|
||||
default:
|
||||
logUnknownVersion(ctx, versionVal);
|
||||
in.skipBytes(in.readableBytes());
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
|
||||
p.remove(this);
|
||||
}
|
||||
|
||||
private static void logKnownVersion(ChannelHandlerContext ctx, SocksVersion version) {
|
||||
logger.debug("{} Protocol version: {}({})", ctx.channel(), version);
|
||||
}
|
||||
|
||||
private static void logUnknownVersion(ChannelHandlerContext ctx, byte versionVal) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} Unknown protocol version: {}", ctx.channel(), versionVal & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
|
||||
import io.netty.handler.codec.socksx.v4.Socks4Request;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5Request;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SOCKS request, providing common properties for
|
||||
* {@link Socks4Request} and {@link Socks5Request}.
|
||||
*/
|
||||
public abstract class SocksRequest extends SocksMessage {
|
||||
|
||||
protected SocksRequest(SocksProtocolVersion protocolVersion) {
|
||||
super(protocolVersion, SocksMessageType.REQUEST);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
import io.netty.handler.codec.socksx.v4.Socks4Response;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5Response;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SOCKS response, providing common properties for
|
||||
* {@link Socks4Response} and {@link Socks5Response}.
|
||||
*/
|
||||
public abstract class SocksResponse extends SocksMessage {
|
||||
protected SocksResponse(SocksProtocolVersion protocolVersion) {
|
||||
super(protocolVersion, SocksMessageType.RESPONSE);
|
||||
}
|
||||
}
|
64
codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksVersion.java
Executable file
64
codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksVersion.java
Executable file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx;
|
||||
|
||||
/**
|
||||
* The version of SOCKS protocol.
|
||||
*/
|
||||
public enum SocksVersion {
|
||||
/**
|
||||
* SOCKS protocol version 4a (or 4)
|
||||
*/
|
||||
SOCKS4a((byte) 0x04),
|
||||
/**
|
||||
* SOCKS protocol version 5
|
||||
*/
|
||||
SOCKS5((byte) 0x05),
|
||||
/**
|
||||
* Unknown protocol version
|
||||
*/
|
||||
UNKNOWN((byte) 0xff);
|
||||
|
||||
/**
|
||||
* Returns the {@link SocksVersion} that corresponds to the specified version field value,
|
||||
* as defined in the protocol specification.
|
||||
*
|
||||
* @return {@link #UNKNOWN} if the specified value does not represent a known SOCKS protocol version
|
||||
*/
|
||||
public static SocksVersion valueOf(byte b) {
|
||||
if (b == SOCKS4a.byteValue()) {
|
||||
return SOCKS4a;
|
||||
}
|
||||
if (b == SOCKS5.byteValue()) {
|
||||
return SOCKS5;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
private final byte b;
|
||||
|
||||
SocksVersion(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the version field, as defined in the protocol specification.
|
||||
*/
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -13,23 +13,18 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.socksx.AbstractSocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
|
||||
/**
|
||||
* An unknown socks response.
|
||||
*
|
||||
* @see Socks4CmdResponseDecoder
|
||||
* An abstract {@link Socks4Message}.
|
||||
*/
|
||||
public final class UnknownSocks4Response extends Socks4Response {
|
||||
|
||||
public static final UnknownSocks4Response INSTANCE = new UnknownSocks4Response();
|
||||
|
||||
private UnknownSocks4Response() { }
|
||||
|
||||
public abstract class AbstractSocks4Message extends AbstractSocksMessage implements Socks4Message {
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
// NOOP
|
||||
public final SocksVersion version() {
|
||||
return SocksVersion.SOCKS4a;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* The default {@link Socks4CommandRequest}.
|
||||
*/
|
||||
public class DefaultSocks4CommandRequest extends AbstractSocks4Message implements Socks4CommandRequest {
|
||||
|
||||
private final Socks4CommandType type;
|
||||
private final String dstAddr;
|
||||
private final int dstPort;
|
||||
private final String userId;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param type the type of the request
|
||||
* @param dstAddr the {@code DSTIP} field of the request
|
||||
* @param dstPort the {@code DSTPORT} field of the request
|
||||
*/
|
||||
public DefaultSocks4CommandRequest(Socks4CommandType type, String dstAddr, int dstPort) {
|
||||
this(type, dstAddr, dstPort, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param type the type of the request
|
||||
* @param dstAddr the {@code DSTIP} field of the request
|
||||
* @param dstPort the {@code DSTPORT} field of the request
|
||||
* @param userId the {@code USERID} field of the request
|
||||
*/
|
||||
public DefaultSocks4CommandRequest(Socks4CommandType type, String dstAddr, int dstPort, String userId) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type");
|
||||
}
|
||||
if (dstAddr == null) {
|
||||
throw new NullPointerException("dstAddr");
|
||||
}
|
||||
if (dstPort <= 0 || dstPort >= 65536) {
|
||||
throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 1~65535)");
|
||||
}
|
||||
if (userId == null) {
|
||||
throw new NullPointerException("userId");
|
||||
}
|
||||
|
||||
this.userId = userId;
|
||||
this.type = type;
|
||||
this.dstAddr = IDN.toASCII(dstAddr);
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks4CommandType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dstAddr() {
|
||||
return dstAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dstPort() {
|
||||
return dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", type: ");
|
||||
} else {
|
||||
buf.append("(type: ");
|
||||
}
|
||||
buf.append(type());
|
||||
buf.append(", dstAddr: ");
|
||||
buf.append(dstAddr());
|
||||
buf.append(", dstPort: ");
|
||||
buf.append(dstPort());
|
||||
buf.append(", userId: ");
|
||||
buf.append(userId());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link Socks4CommandResponse}.
|
||||
*/
|
||||
public class DefaultSocks4CommandResponse extends AbstractSocks4Message implements Socks4CommandResponse {
|
||||
|
||||
private final Socks4CommandStatus status;
|
||||
private final String dstAddr;
|
||||
private final int dstPort;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param status the status of the response
|
||||
*/
|
||||
public DefaultSocks4CommandResponse(Socks4CommandStatus status) {
|
||||
this(status, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param status the status of the response
|
||||
* @param dstAddr the {@code DSTIP} field of the response
|
||||
* @param dstPort the {@code DSTPORT} field of the response
|
||||
*/
|
||||
public DefaultSocks4CommandResponse(Socks4CommandStatus status, String dstAddr, int dstPort) {
|
||||
if (status == null) {
|
||||
throw new NullPointerException("cmdStatus");
|
||||
}
|
||||
if (dstAddr != null) {
|
||||
if (!NetUtil.isValidIpV4Address(dstAddr)) {
|
||||
throw new IllegalArgumentException(
|
||||
"dstAddr: " + dstAddr + " (expected: a valid IPv4 address)");
|
||||
}
|
||||
}
|
||||
if (dstPort < 0 || dstPort > 65535) {
|
||||
throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 0~65535)");
|
||||
}
|
||||
|
||||
this.status = status;
|
||||
this.dstAddr = dstAddr;
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks4CommandStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dstAddr() {
|
||||
return dstAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dstPort() {
|
||||
return dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(96);
|
||||
buf.append(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", dstAddr: ");
|
||||
} else {
|
||||
buf.append("(dstAddr: ");
|
||||
}
|
||||
buf.append(dstAddr());
|
||||
buf.append(", dstPort: ");
|
||||
buf.append(dstPort());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder.State;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks4CommandResponse} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove this decoder later. On failed decode, this decoder will discard the
|
||||
* received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks4ClientDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
START,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
public Socks4ClientDecoder() {
|
||||
super(State.START);
|
||||
setSingleDecode(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case START: {
|
||||
final int version = in.readUnsignedByte();
|
||||
if (version != 0) {
|
||||
throw new DecoderException("unsupported reply version: " + version + " (expected: 0)");
|
||||
}
|
||||
|
||||
final Socks4CommandStatus status = Socks4CommandStatus.valueOf(in.readByte());
|
||||
final int dstPort = in.readUnsignedShort();
|
||||
final String dstAddr = NetUtil.intToIpAddress(in.readInt());
|
||||
|
||||
out.add(new DefaultSocks4CommandResponse(status, dstAddr, dstPort));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
Socks4CommandResponse m = new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
/**
|
||||
* Encodes a {@link Socks4CommandRequest} into a {@link ByteBuf}.
|
||||
*/
|
||||
@Sharable
|
||||
public final class Socks4ClientEncoder extends MessageToByteEncoder<Socks4CommandRequest> {
|
||||
|
||||
/**
|
||||
* The singleton instance of {@link Socks4ClientEncoder}
|
||||
*/
|
||||
public static final Socks4ClientEncoder INSTANCE = new Socks4ClientEncoder();
|
||||
|
||||
private static final byte[] IPv4_DOMAIN_MARKER = {0x00, 0x00, 0x00, 0x01};
|
||||
|
||||
private Socks4ClientEncoder() { }
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Socks4CommandRequest msg, ByteBuf out) throws Exception {
|
||||
out.writeByte(msg.version().byteValue());
|
||||
out.writeByte(msg.type().byteValue());
|
||||
out.writeShort(msg.dstPort());
|
||||
if (NetUtil.isValidIpV4Address(msg.dstAddr())) {
|
||||
out.writeBytes(NetUtil.createByteArrayFromIpAddressString(msg.dstAddr()));
|
||||
ByteBufUtil.writeAscii(out, msg.userId());
|
||||
out.writeByte(0);
|
||||
} else {
|
||||
out.writeBytes(IPv4_DOMAIN_MARKER);
|
||||
ByteBufUtil.writeAscii(out, msg.userId());
|
||||
out.writeByte(0);
|
||||
ByteBufUtil.writeAscii(out, msg.dstAddr());
|
||||
out.writeByte(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* An socksv4a cmd request.
|
||||
*
|
||||
* @see Socks4Response
|
||||
* @see Socks4CmdRequestDecoder
|
||||
*/
|
||||
public final class Socks4CmdRequest extends Socks4Request {
|
||||
private final String userId;
|
||||
private final Socks4CmdType cmdType;
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
private static final byte[] IPv4_DOMAIN_MARKER = {0x00, 0x00, 0x00, 0x01};
|
||||
|
||||
public Socks4CmdRequest(String userId, Socks4CmdType cmdType, String host, int port) {
|
||||
if (userId == null) {
|
||||
throw new NullPointerException("username");
|
||||
}
|
||||
if (cmdType == null) {
|
||||
throw new NullPointerException("cmdType");
|
||||
}
|
||||
if (host == null) {
|
||||
throw new NullPointerException("host");
|
||||
}
|
||||
if (port <= 0 || port >= 65536) {
|
||||
throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536");
|
||||
}
|
||||
this.userId = userId;
|
||||
this.cmdType = cmdType;
|
||||
this.host = IDN.toASCII(host);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Socks4CmdRequest(Socks4CmdType cmdType, String host, int port) {
|
||||
this("", cmdType, host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks4CmdType} of this {@link Socks4Request}
|
||||
*
|
||||
* @return The {@link Socks4CmdType} of this {@link Socks4Request}
|
||||
*/
|
||||
public Socks4CmdType cmdType() {
|
||||
return cmdType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns host that is used as a parameter in {@link Socks4CmdType}
|
||||
*
|
||||
* @return host that is used as a parameter in {@link Socks4CmdType}
|
||||
*/
|
||||
public String host() {
|
||||
return IDN.toUnicode(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns userId that is used as a parameter in {@link Socks4CmdType}
|
||||
*
|
||||
* @return userId that is used as a parameter in {@link Socks4CmdType}
|
||||
*/
|
||||
public String userId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns port that is used as a parameter in {@link Socks4CmdType}
|
||||
*
|
||||
* @return port that is used as a parameter in {@link Socks4CmdType}
|
||||
*/
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(protocolVersion().byteValue());
|
||||
byteBuf.writeByte(cmdType.byteValue());
|
||||
byteBuf.writeShort(port);
|
||||
if (NetUtil.isValidIpV4Address(host)) {
|
||||
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
|
||||
byteBuf.writeBytes(userId.getBytes());
|
||||
byteBuf.writeZero(1);
|
||||
} else {
|
||||
byteBuf.writeBytes(IPv4_DOMAIN_MARKER);
|
||||
byteBuf.writeBytes(userId.getBytes());
|
||||
byteBuf.writeZero(1);
|
||||
byteBuf.writeBytes(host.getBytes(CharsetUtil.US_ASCII));
|
||||
byteBuf.writeZero(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequestDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks4CmdRequest}.
|
||||
* Before returning SocksRequest decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks4CmdRequestDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
private SocksProtocolVersion version;
|
||||
private Socks4CmdType cmdType;
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private byte reserved;
|
||||
private String host;
|
||||
private int port;
|
||||
private String userId;
|
||||
private Socks4Request msg = UnknownSocks4Request.INSTANCE;
|
||||
|
||||
public Socks4CmdRequestDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
|
||||
if (version != SocksProtocolVersion.SOCKS4a) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_CMD_HEADER);
|
||||
}
|
||||
case READ_CMD_HEADER: {
|
||||
cmdType = Socks4CmdType.valueOf(byteBuf.readByte());
|
||||
port = byteBuf.readUnsignedShort();
|
||||
host = Socks4CommonUtils.intToIp(byteBuf.readInt());
|
||||
checkpoint(State.READ_CMD_USERID);
|
||||
}
|
||||
case READ_CMD_USERID: {
|
||||
userId = readNullTerminatedString(byteBuf);
|
||||
checkpoint(State.READ_CMD_DOMAIN);
|
||||
}
|
||||
case READ_CMD_DOMAIN: {
|
||||
// Check for Socks4a protocol marker 0,0,0,x
|
||||
if (!"0.0.0.0".equals(host) && host.startsWith("0.0.0.")) {
|
||||
host = readNullTerminatedString(byteBuf);
|
||||
}
|
||||
msg = new Socks4CmdRequest(userId, cmdType, host, port);
|
||||
}
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
private static String readNullTerminatedString(ByteBuf byteBuf) throws Exception {
|
||||
byte NULL_BYTE = 0x00;
|
||||
// Could be used for DoS
|
||||
String string = byteBuf.readBytes(byteBuf.bytesBefore(NULL_BYTE)).toString(CharsetUtil.US_ASCII);
|
||||
// Read NULL-byte
|
||||
byteBuf.readByte();
|
||||
return string;
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_CMD_HEADER,
|
||||
READ_CMD_USERID,
|
||||
READ_CMD_DOMAIN
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* A socks cmd response.
|
||||
*
|
||||
* @see Socks4CmdRequest
|
||||
* @see Socks4CmdResponseDecoder
|
||||
*/
|
||||
public final class Socks4CmdResponse extends Socks4Response {
|
||||
private final Socks4CmdStatus cmdStatus;
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
// All arrays are initialized on construction time to 0/false/null remove array Initialization
|
||||
private static final byte[] IPv4_HOSTNAME_ZEROED = { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
public Socks4CmdResponse(Socks4CmdStatus cmdStatus) {
|
||||
this(cmdStatus, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new response and includes provided host and port as part of it.
|
||||
*
|
||||
* @param cmdStatus status of the response
|
||||
* @param host host (BND.ADDR field) is address that server used when connecting to the target host.
|
||||
* When null a value of 4/8 0x00 octets will be used for IPv4/IPv6 and a single 0x00 byte will be
|
||||
* used for domain addressType. Value is converted to ASCII using {@link IDN#toASCII(String)}.
|
||||
* @param port port (BND.PORT field) that the server assigned to connect to the target host
|
||||
* @throws NullPointerException in case cmdStatus or addressType are missing
|
||||
* @throws IllegalArgumentException in case host or port cannot be validated
|
||||
* @see IDN#toASCII(String)
|
||||
*/
|
||||
public Socks4CmdResponse(Socks4CmdStatus cmdStatus, String host, int port) {
|
||||
if (cmdStatus == null) {
|
||||
throw new NullPointerException("cmdStatus");
|
||||
}
|
||||
if (host != null) {
|
||||
if (!NetUtil.isValidIpV4Address(host)) {
|
||||
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
|
||||
}
|
||||
}
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535");
|
||||
}
|
||||
this.cmdStatus = cmdStatus;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks4CmdStatus} of this {@link Socks4Response}
|
||||
*
|
||||
* @return The {@link Socks4CmdStatus} of this {@link Socks4Response}
|
||||
*/
|
||||
public Socks4CmdStatus cmdStatus() {
|
||||
return cmdStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns host that is used as a parameter in {@link Socks4CmdType}.
|
||||
* Host (BND.ADDR field in response) is address that server used when connecting to the target host.
|
||||
* This is typically different from address which client uses to connect to the SOCKS server.
|
||||
*
|
||||
* @return host that is used as a parameter in {@link Socks4CmdType}
|
||||
* or null when there was no host specified during response construction
|
||||
*/
|
||||
public String host() {
|
||||
if (host != null) {
|
||||
return IDN.toUnicode(host);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns port that is used as a parameter in {@link Socks4CmdType}.
|
||||
* Port (BND.PORT field in response) is port that the server assigned to connect to the target host.
|
||||
*
|
||||
* @return port that is used as a parameter in {@link Socks4CmdType}
|
||||
*/
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeZero(1);
|
||||
byteBuf.writeByte(cmdStatus.byteValue());
|
||||
byteBuf.writeShort(port);
|
||||
byte[] hostContent = host == null ?
|
||||
IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
|
||||
byteBuf.writeBytes(hostContent);
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks4CmdResponse}.
|
||||
* Before returning SocksResponse decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks4CmdResponseDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
private Socks4CmdStatus cmdStatus;
|
||||
|
||||
private String host;
|
||||
private int port;
|
||||
private Socks4Response msg = UnknownSocks4Response.INSTANCE;
|
||||
|
||||
public Socks4CmdResponseDecoder() {
|
||||
super(State.CHECK_NULL_BYTE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_NULL_BYTE: {
|
||||
if (byteBuf.readByte() != (byte) 0x00) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_CMD_HEADER);
|
||||
}
|
||||
case READ_CMD_HEADER: {
|
||||
cmdStatus = Socks4CmdStatus.valueOf(byteBuf.readByte());
|
||||
checkpoint(State.READ_CMD_ADDRESS);
|
||||
}
|
||||
case READ_CMD_ADDRESS: {
|
||||
port = byteBuf.readUnsignedShort();
|
||||
host = Socks4CommonUtils.intToIp(byteBuf.readInt());
|
||||
msg = new Socks4CmdResponse(cmdStatus, host, port);
|
||||
}
|
||||
}
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_NULL_BYTE,
|
||||
READ_CMD_HEADER,
|
||||
READ_CMD_ADDRESS
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
public enum Socks4CmdStatus {
|
||||
SUCCESS((byte) 0x5a),
|
||||
REJECTED_OR_FAILED((byte) 0x5b),
|
||||
IDENTD_UNREACHABLE((byte) 0x5c),
|
||||
IDENTD_AUTH_FAILURE((byte) 0x5d),
|
||||
UNASSIGNED((byte) 0xff);
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks4CmdStatus(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks4CmdStatus valueOf(byte b) {
|
||||
for (Socks4CmdStatus code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNASSIGNED;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
@ -15,27 +15,28 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
public enum Socks4CmdType {
|
||||
CONNECT((byte) 0x01),
|
||||
BIND((byte) 0x02),
|
||||
UNKNOWN((byte) 0xff);
|
||||
/**
|
||||
* A SOCKS4a {@code CONNECT} or {@code BIND} request.
|
||||
*/
|
||||
public interface Socks4CommandRequest extends Socks4Message {
|
||||
|
||||
private final byte b;
|
||||
/**
|
||||
* Returns the type of this request.
|
||||
*/
|
||||
Socks4CommandType type();
|
||||
|
||||
Socks4CmdType(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
/**
|
||||
* Returns the {@code USERID} field of this request.
|
||||
*/
|
||||
String userId();
|
||||
|
||||
public static Socks4CmdType valueOf(byte b) {
|
||||
for (Socks4CmdType code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
/**
|
||||
* Returns the {@code DSTIP} field of this request.
|
||||
*/
|
||||
String dstAddr();
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
/**
|
||||
* Returns the {@code DSTPORT} field of this request.
|
||||
*/
|
||||
int dstPort();
|
||||
}
|
@ -15,21 +15,23 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* An unknown socks request.
|
||||
*
|
||||
* @see Socks4CmdRequestDecoder
|
||||
* A SOCKS4a response.
|
||||
*/
|
||||
public final class UnknownSocks4Request extends Socks4Request {
|
||||
public interface Socks4CommandResponse extends Socks4Message {
|
||||
|
||||
public static final UnknownSocks4Request INSTANCE = new UnknownSocks4Request();
|
||||
/**
|
||||
* Returns the status of this response.
|
||||
*/
|
||||
Socks4CommandStatus status();
|
||||
|
||||
private UnknownSocks4Request() { }
|
||||
/**
|
||||
* Returns the {@code DSTIP} field of this response.
|
||||
*/
|
||||
String dstAddr();
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
// NOOP
|
||||
}
|
||||
/**
|
||||
* Returns the {@code DSTPORT} field of this response.
|
||||
*/
|
||||
int dstPort();
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
/**
|
||||
* The status of {@link Socks4CommandResponse}.
|
||||
*/
|
||||
public class Socks4CommandStatus implements Comparable<Socks4CommandStatus> {
|
||||
|
||||
public static final Socks4CommandStatus SUCCESS = new Socks4CommandStatus(0x5a, "SUCCESS");
|
||||
public static final Socks4CommandStatus REJECTED_OR_FAILED = new Socks4CommandStatus(0x5b, "REJECTED_OR_FAILED");
|
||||
public static final Socks4CommandStatus IDENTD_UNREACHABLE = new Socks4CommandStatus(0x5c, "IDENTD_UNREACHABLE");
|
||||
public static final Socks4CommandStatus IDENTD_AUTH_FAILURE = new Socks4CommandStatus(0x5d, "IDENTD_AUTH_FAILURE");
|
||||
|
||||
public static Socks4CommandStatus valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x5a:
|
||||
return SUCCESS;
|
||||
case 0x5b:
|
||||
return REJECTED_OR_FAILED;
|
||||
case 0x5c:
|
||||
return IDENTD_UNREACHABLE;
|
||||
case 0x5d:
|
||||
return IDENTD_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
return new Socks4CommandStatus(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks4CommandStatus(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks4CommandStatus(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return byteValue == 0x5a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks4CommandStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks4CommandStatus) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks4CommandStatus o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
/**
|
||||
* The type of {@link Socks4CommandRequest}.
|
||||
*/
|
||||
public class Socks4CommandType implements Comparable<Socks4CommandType> {
|
||||
|
||||
public static final Socks4CommandType CONNECT = new Socks4CommandType(0x01, "CONNECT");
|
||||
public static final Socks4CommandType BIND = new Socks4CommandType(0x02, "BIND");
|
||||
|
||||
public static Socks4CommandType valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x01:
|
||||
return CONNECT;
|
||||
case 0x02:
|
||||
return BIND;
|
||||
}
|
||||
|
||||
return new Socks4CommandType(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks4CommandType(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks4CommandType(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks4CommandType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks4CommandType) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks4CommandType o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
final class Socks4CommonUtils {
|
||||
|
||||
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 Socks4CommonUtils() {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
public static String intToIp(int i) {
|
||||
return String.valueOf(i >> FIRST_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i >> SECOND_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i >> THIRD_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i & XOR_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
private static final char[] ipv6conseqZeroFiller = {':', ':'};
|
||||
private static final char ipv6hextetSeparator = ':';
|
||||
|
||||
/**
|
||||
* Convert numeric IPv6 to compressed format, where
|
||||
* the longest sequence of 0's (with 2 or more 0's) is replaced with "::"
|
||||
*/
|
||||
public static String ipv6toCompressedForm(byte[] src) {
|
||||
assert src.length == 16;
|
||||
//Find the longest sequence of 0's
|
||||
//start of compressed region (hextet index)
|
||||
int cmprHextet = -1;
|
||||
//length of compressed region
|
||||
int cmprSize = 0;
|
||||
for (int hextet = 0; hextet < 8;) {
|
||||
int curByte = hextet * 2;
|
||||
int size = 0;
|
||||
while (curByte < src.length && src[curByte] == 0
|
||||
&& src[curByte + 1] == 0) {
|
||||
curByte += 2;
|
||||
size++;
|
||||
}
|
||||
if (size > cmprSize) {
|
||||
cmprHextet = hextet;
|
||||
cmprSize = size;
|
||||
}
|
||||
hextet = curByte / 2 + 1;
|
||||
}
|
||||
if (cmprHextet == -1 || cmprSize < 2) {
|
||||
//No compression can be applied
|
||||
return ipv6toStr(src);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(39);
|
||||
ipv6toStr(sb, src, 0, cmprHextet);
|
||||
sb.append(ipv6conseqZeroFiller);
|
||||
ipv6toStr(sb, src, cmprHextet + cmprSize, 8);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts numeric IPv6 to standard (non-compressed) format.
|
||||
*/
|
||||
public static String ipv6toStr(byte[] src) {
|
||||
assert src.length == 16;
|
||||
StringBuilder sb = new StringBuilder(39);
|
||||
ipv6toStr(sb, src, 0, 8);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void ipv6toStr(StringBuilder sb, byte[] src, int fromHextet, int toHextet) {
|
||||
int i;
|
||||
toHextet --;
|
||||
for (i = fromHextet; i < toHextet; i++) {
|
||||
appendHextet(sb, src, i);
|
||||
sb.append(ipv6hextetSeparator);
|
||||
}
|
||||
|
||||
appendHextet(sb, src, i);
|
||||
}
|
||||
|
||||
private static void appendHextet(StringBuilder sb, byte[] src, int i) {
|
||||
StringUtil.toHexString(sb, src, i << 1, 2);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -14,14 +14,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
|
||||
/**
|
||||
* Type of socks response
|
||||
* A tag interface that all SOCKS4a protocol messages implement.
|
||||
*/
|
||||
public enum Socks5ResponseType {
|
||||
INIT,
|
||||
AUTH,
|
||||
CMD,
|
||||
UNKNOWN
|
||||
public interface Socks4Message extends SocksMessage {
|
||||
// Tag interface
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
|
||||
/**
|
||||
* Encodes a {@link Socks4Request} and {@link Socks4Response} into a {@link ByteBuf}.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public final class Socks4MessageEncoder extends MessageToByteEncoder<SocksMessage> {
|
||||
|
||||
public static final Socks4MessageEncoder INSTANCE = new Socks4MessageEncoder();
|
||||
|
||||
private Socks4MessageEncoder() { }
|
||||
|
||||
@Override
|
||||
public boolean acceptOutboundMessage(Object msg) throws Exception {
|
||||
return super.acceptOutboundMessage(msg) &&
|
||||
((SocksMessage) msg).protocolVersion() == SocksProtocolVersion.SOCKS4a;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, SocksMessage msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof Socks4Response) {
|
||||
((Socks4Response) msg).encodeAsByteBuf(out);
|
||||
} else if (msg instanceof Socks4Request) {
|
||||
((Socks4Request) msg).encodeAsByteBuf(out);
|
||||
} else {
|
||||
// Should not reach here.
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.socks.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.SocksRequest;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SocksRequest, providing common properties for
|
||||
* {@link Socks4CmdRequest}.
|
||||
*
|
||||
* @see Socks4CmdRequest
|
||||
* @see UnknownSocks4Request
|
||||
*/
|
||||
public abstract class Socks4Request extends SocksRequest {
|
||||
protected Socks4Request() {
|
||||
super(SocksProtocolVersion.SOCKS4a);
|
||||
}
|
||||
|
||||
/**
|
||||
* We could have defined this method in {@link SocksMessage} as a protected method, but we did not,
|
||||
* because we do not want to expose this method to users.
|
||||
*/
|
||||
abstract void encodeAsByteBuf(ByteBuf byteBuf);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.socks.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.SocksResponse;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SocksResponse, providing common properties for
|
||||
* {@link Socks4CmdResponse}.
|
||||
*
|
||||
* @see Socks4CmdResponse
|
||||
* @see UnknownSocks4Response
|
||||
*/
|
||||
public abstract class Socks4Response extends SocksResponse {
|
||||
|
||||
protected Socks4Response() {
|
||||
super(SocksProtocolVersion.SOCKS4a);
|
||||
}
|
||||
|
||||
/**
|
||||
* We could have defined this method in {@link SocksMessage} as a protected method, but we did not,
|
||||
* because we do not want to expose this method to users.
|
||||
*/
|
||||
abstract void encodeAsByteBuf(ByteBuf byteBuf);
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks4CommandRequest} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove this decoder later. On failed decode, this decoder will discard the
|
||||
* received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks4ServerDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
private static final int MAX_FIELD_LENGTH = 255;
|
||||
|
||||
enum State {
|
||||
START,
|
||||
READ_USERID,
|
||||
READ_DOMAIN,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
private Socks4CommandType type;
|
||||
private String dstAddr;
|
||||
private int dstPort;
|
||||
private String userId;
|
||||
|
||||
public Socks4ServerDecoder() {
|
||||
super(State.START);
|
||||
setSingleDecode(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case START: {
|
||||
final int version = in.readUnsignedByte();
|
||||
if (version != SocksVersion.SOCKS4a.byteValue()) {
|
||||
throw new DecoderException("unsupported protocol version: " + version);
|
||||
}
|
||||
|
||||
type = Socks4CommandType.valueOf(in.readByte());
|
||||
dstPort = in.readUnsignedShort();
|
||||
dstAddr = NetUtil.intToIpAddress(in.readInt());
|
||||
checkpoint(State.READ_USERID);
|
||||
}
|
||||
case READ_USERID: {
|
||||
userId = readString("userid", in);
|
||||
checkpoint(State.READ_DOMAIN);
|
||||
}
|
||||
case READ_DOMAIN: {
|
||||
// Check for Socks4a protocol marker 0.0.0.x
|
||||
if (!"0.0.0.0".equals(dstAddr) && dstAddr.startsWith("0.0.0.")) {
|
||||
dstAddr = readString("dstAddr", in);
|
||||
}
|
||||
out.add(new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
Socks4CommandRequest m = new DefaultSocks4CommandRequest(
|
||||
type != null? type : Socks4CommandType.CONNECT,
|
||||
dstAddr != null? dstAddr : "",
|
||||
dstPort != 0? dstPort : 65535,
|
||||
userId != null? userId : "");
|
||||
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a variable-length NUL-terminated string as defined in SOCKS4.
|
||||
*/
|
||||
private static String readString(String fieldName, ByteBuf in) {
|
||||
int length = in.bytesBefore(MAX_FIELD_LENGTH + 1, (byte) 0);
|
||||
if (length < 0) {
|
||||
throw new DecoderException("field '" + fieldName + "' longer than " + MAX_FIELD_LENGTH + " chars");
|
||||
}
|
||||
|
||||
String value = in.readSlice(length).toString(CharsetUtil.US_ASCII);
|
||||
in.skipBytes(1); // Skip the NUL.
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
/**
|
||||
* Encodes a {@link Socks4CommandResponse} into a {@link ByteBuf}.
|
||||
*/
|
||||
@Sharable
|
||||
public final class Socks4ServerEncoder extends MessageToByteEncoder<Socks4CommandResponse> {
|
||||
|
||||
public static final Socks4ServerEncoder INSTANCE = new Socks4ServerEncoder();
|
||||
|
||||
private static final byte[] IPv4_HOSTNAME_ZEROED = { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
private Socks4ServerEncoder() { }
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Socks4CommandResponse msg, ByteBuf out) throws Exception {
|
||||
out.writeByte(0);
|
||||
out.writeByte(msg.status().byteValue());
|
||||
out.writeShort(msg.dstPort());
|
||||
out.writeBytes(msg.dstAddr() == null? IPv4_HOSTNAME_ZEROED
|
||||
: NetUtil.createByteArrayFromIpAddressString(msg.dstAddr()));
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.socksx.AbstractSocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
|
||||
/**
|
||||
* An abstract {@link Socks5Message}.
|
||||
*/
|
||||
public abstract class AbstractSocks5Message extends AbstractSocksMessage implements Socks5Message {
|
||||
@Override
|
||||
public final SocksVersion version() {
|
||||
return SocksVersion.SOCKS5;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5CommandRequest}.
|
||||
*/
|
||||
public final class DefaultSocks5CommandRequest extends AbstractSocks5Message implements Socks5CommandRequest {
|
||||
|
||||
private final Socks5CommandType type;
|
||||
private final Socks5AddressType dstAddrType;
|
||||
private final String dstAddr;
|
||||
private final int dstPort;
|
||||
|
||||
public DefaultSocks5CommandRequest(
|
||||
Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) {
|
||||
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type");
|
||||
}
|
||||
if (dstAddrType == null) {
|
||||
throw new NullPointerException("dstAddrType");
|
||||
}
|
||||
if (dstAddr == null) {
|
||||
throw new NullPointerException("dstAddr");
|
||||
}
|
||||
|
||||
if (dstAddrType == Socks5AddressType.IPv4) {
|
||||
if (!NetUtil.isValidIpV4Address(dstAddr)) {
|
||||
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv4 address)");
|
||||
}
|
||||
} else if (dstAddrType == Socks5AddressType.DOMAIN) {
|
||||
dstAddr = IDN.toASCII(dstAddr);
|
||||
if (dstAddr.length() > 255) {
|
||||
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: less than 256 chars)");
|
||||
}
|
||||
} else if (dstAddrType == Socks5AddressType.IPv6) {
|
||||
if (!NetUtil.isValidIpV6Address(dstAddr)) {
|
||||
throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv6 address");
|
||||
}
|
||||
}
|
||||
|
||||
if (dstPort <= 0 || dstPort >= 65536) {
|
||||
throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 1~65535)");
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.dstAddrType = dstAddrType;
|
||||
this.dstAddr = dstAddr;
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5CommandType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5AddressType dstAddrType() {
|
||||
return dstAddrType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dstAddr() {
|
||||
return dstAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dstPort() {
|
||||
return dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", type: ");
|
||||
} else {
|
||||
buf.append("(type: ");
|
||||
}
|
||||
buf.append(type());
|
||||
buf.append(", dstAddrType: ");
|
||||
buf.append(dstAddrType());
|
||||
buf.append(", dstAddr: ");
|
||||
buf.append(dstAddr());
|
||||
buf.append(", dstPort: ");
|
||||
buf.append(dstPort());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5CommandResponse}.
|
||||
*/
|
||||
public final class DefaultSocks5CommandResponse extends AbstractSocks5Message implements Socks5CommandResponse {
|
||||
|
||||
private final Socks5CommandStatus status;
|
||||
private final Socks5AddressType bndAddrType;
|
||||
private final String bndAddr;
|
||||
private final int bndPort;
|
||||
|
||||
public DefaultSocks5CommandResponse(Socks5CommandStatus status, Socks5AddressType bndAddrType) {
|
||||
this(status, bndAddrType, null, 0);
|
||||
}
|
||||
|
||||
public DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus status, Socks5AddressType bndAddrType, String bndAddr, int bndPort) {
|
||||
|
||||
if (status == null) {
|
||||
throw new NullPointerException("status");
|
||||
}
|
||||
if (bndAddrType == null) {
|
||||
throw new NullPointerException("bndAddrType");
|
||||
}
|
||||
if (bndAddr != null) {
|
||||
if (bndAddrType == Socks5AddressType.IPv4) {
|
||||
if (!NetUtil.isValidIpV4Address(bndAddr)) {
|
||||
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv4 address)");
|
||||
}
|
||||
} else if (bndAddrType == Socks5AddressType.DOMAIN) {
|
||||
bndAddr = IDN.toASCII(bndAddr);
|
||||
if (bndAddr.length() > 255) {
|
||||
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: less than 256 chars)");
|
||||
}
|
||||
} else if (bndAddrType == Socks5AddressType.IPv6) {
|
||||
if (!NetUtil.isValidIpV6Address(bndAddr)) {
|
||||
throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv6 address)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bndPort < 0 || bndPort > 65535) {
|
||||
throw new IllegalArgumentException("bndPort: " + bndPort + " (expected: 0~65535)");
|
||||
}
|
||||
this.status = status;
|
||||
this.bndAddrType = bndAddrType;
|
||||
this.bndAddr = bndAddr;
|
||||
this.bndPort = bndPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5CommandStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5AddressType bndAddrType() {
|
||||
return bndAddrType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String bndAddr() {
|
||||
return bndAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bndPort() {
|
||||
return bndPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", status: ");
|
||||
} else {
|
||||
buf.append("(status: ");
|
||||
}
|
||||
buf.append(status());
|
||||
buf.append(", bndAddrType: ");
|
||||
buf.append(bndAddrType());
|
||||
buf.append(", bndAddr: ");
|
||||
buf.append(bndAddr());
|
||||
buf.append(", bndPort: ");
|
||||
buf.append(bndPort());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5InitialRequest}.
|
||||
*/
|
||||
public class DefaultSocks5InitialRequest extends AbstractSocks5Message implements Socks5InitialRequest {
|
||||
|
||||
private final List<Socks5AuthMethod> authMethods;
|
||||
|
||||
public DefaultSocks5InitialRequest(Socks5AuthMethod... authMethods) {
|
||||
if (authMethods == null) {
|
||||
throw new NullPointerException("authMethods");
|
||||
}
|
||||
|
||||
List<Socks5AuthMethod> list = new ArrayList<Socks5AuthMethod>(authMethods.length);
|
||||
for (Socks5AuthMethod m: authMethods) {
|
||||
if (m == null) {
|
||||
break;
|
||||
}
|
||||
list.add(m);
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw new IllegalArgumentException("authMethods is empty");
|
||||
}
|
||||
|
||||
this.authMethods = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
public DefaultSocks5InitialRequest(Iterable<Socks5AuthMethod> authMethods) {
|
||||
if (authMethods == null) {
|
||||
throw new NullPointerException("authSchemes");
|
||||
}
|
||||
|
||||
List<Socks5AuthMethod> list = new ArrayList<Socks5AuthMethod>();
|
||||
for (Socks5AuthMethod m: authMethods) {
|
||||
if (m == null) {
|
||||
break;
|
||||
}
|
||||
list.add(m);
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw new IllegalArgumentException("authMethods is empty");
|
||||
}
|
||||
|
||||
this.authMethods = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Socks5AuthMethod> authMethods() {
|
||||
return authMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", authMethods: ");
|
||||
} else {
|
||||
buf.append("(authMethods: ");
|
||||
}
|
||||
buf.append(authMethods());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5InitialResponse}.
|
||||
*/
|
||||
public class DefaultSocks5InitialResponse extends AbstractSocks5Message implements Socks5InitialResponse {
|
||||
|
||||
private final Socks5AuthMethod authMethod;
|
||||
|
||||
public DefaultSocks5InitialResponse(Socks5AuthMethod authMethod) {
|
||||
if (authMethod == null) {
|
||||
throw new NullPointerException("authMethod");
|
||||
}
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5AuthMethod authMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", authMethod: ");
|
||||
} else {
|
||||
buf.append("(authMethod: ");
|
||||
}
|
||||
buf.append(authMethod());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5PasswordAuthRequest}.
|
||||
*/
|
||||
public class DefaultSocks5PasswordAuthRequest extends AbstractSocks5Message implements Socks5PasswordAuthRequest {
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public DefaultSocks5PasswordAuthRequest(String username, String password) {
|
||||
if (username == null) {
|
||||
throw new NullPointerException("username");
|
||||
}
|
||||
if (password == null) {
|
||||
throw new NullPointerException("password");
|
||||
}
|
||||
|
||||
if (username.length() > 255) {
|
||||
throw new IllegalArgumentException("username: **** (expected: less than 256 chars)");
|
||||
}
|
||||
if (password.length() > 255) {
|
||||
throw new IllegalArgumentException("password: **** (expected: less than 256 chars)");
|
||||
}
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String username() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String password() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", username: ");
|
||||
} else {
|
||||
buf.append("(username: ");
|
||||
}
|
||||
buf.append(username());
|
||||
buf.append(", password: ****)");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link Socks5PasswordAuthResponse}.
|
||||
*/
|
||||
public class DefaultSocks5PasswordAuthResponse extends AbstractSocks5Message implements Socks5PasswordAuthResponse {
|
||||
|
||||
private final Socks5PasswordAuthStatus status;
|
||||
|
||||
public DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus status) {
|
||||
if (status == null) {
|
||||
throw new NullPointerException("status");
|
||||
}
|
||||
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socks5PasswordAuthStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this));
|
||||
|
||||
DecoderResult decoderResult = decoderResult();
|
||||
if (!decoderResult.isSuccess()) {
|
||||
buf.append("(decoderResult: ");
|
||||
buf.append(decoderResult);
|
||||
buf.append(", status: ");
|
||||
} else {
|
||||
buf.append("(status: ");
|
||||
}
|
||||
buf.append(status());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
/**
|
||||
* Decodes a SOCKS5 address field into its string representation.
|
||||
*
|
||||
* @see Socks5CommandRequestDecoder
|
||||
* @see Socks5CommandResponseDecoder
|
||||
*/
|
||||
public interface Socks5AddressDecoder {
|
||||
|
||||
Socks5AddressDecoder DEFAULT = new Socks5AddressDecoder() {
|
||||
|
||||
private static final int IPv6_LEN = 16;
|
||||
|
||||
@Override
|
||||
public String decodeAddress(Socks5AddressType addrType, ByteBuf in) throws Exception {
|
||||
if (addrType == Socks5AddressType.IPv4) {
|
||||
return NetUtil.intToIpAddress(in.readInt());
|
||||
}
|
||||
if (addrType == Socks5AddressType.DOMAIN) {
|
||||
final int length = in.readUnsignedByte();
|
||||
final String domain = in.toString(in.readerIndex(), length, CharsetUtil.US_ASCII);
|
||||
in.skipBytes(length);
|
||||
return domain;
|
||||
}
|
||||
if (addrType == Socks5AddressType.IPv6) {
|
||||
if (in.hasArray()) {
|
||||
final int readerIdx = in.readerIndex();
|
||||
in.readerIndex(readerIdx + IPv6_LEN);
|
||||
return NetUtil.bytesToIpAddress(in.array(), in.arrayOffset() + readerIdx, IPv6_LEN);
|
||||
} else {
|
||||
byte[] tmp = new byte[IPv6_LEN];
|
||||
in.readBytes(tmp);
|
||||
return NetUtil.bytesToIpAddress(tmp, 0, IPv6_LEN);
|
||||
}
|
||||
} else {
|
||||
throw new DecoderException("unsupported address type: " + (addrType.byteValue() & 0xFF));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a SOCKS5 address field into its string representation.
|
||||
*
|
||||
* @param addrType the type of the address
|
||||
* @param in the input buffer which contains the SOCKS5 address field at its reader index
|
||||
*/
|
||||
String decodeAddress(Socks5AddressType addrType, ByteBuf in) throws Exception;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
/**
|
||||
* Encodes a SOCKS5 address into binary representation.
|
||||
*
|
||||
* @see Socks5ClientEncoder
|
||||
* @see Socks5ServerEncoder
|
||||
*/
|
||||
public interface Socks5AddressEncoder {
|
||||
|
||||
Socks5AddressEncoder DEFAULT = new Socks5AddressEncoder() {
|
||||
@Override
|
||||
public void encodeAddress(Socks5AddressType addrType, String addrValue, ByteBuf out) throws Exception {
|
||||
final byte typeVal = addrType.byteValue();
|
||||
if (typeVal == Socks5AddressType.IPv4.byteValue()) {
|
||||
if (addrValue != null) {
|
||||
out.writeBytes(NetUtil.createByteArrayFromIpAddressString(addrValue));
|
||||
} else {
|
||||
out.writeInt(0);
|
||||
}
|
||||
} else if (typeVal == Socks5AddressType.DOMAIN.byteValue()) {
|
||||
if (addrValue != null) {
|
||||
byte[] bndAddr = addrValue.getBytes(CharsetUtil.US_ASCII);
|
||||
out.writeByte(bndAddr.length);
|
||||
out.writeBytes(bndAddr);
|
||||
} else {
|
||||
out.writeByte(1);
|
||||
out.writeByte(0);
|
||||
}
|
||||
} else if (typeVal == Socks5AddressType.IPv6.byteValue()) {
|
||||
if (addrValue != null) {
|
||||
out.writeBytes(NetUtil.createByteArrayFromIpAddressString(addrValue));
|
||||
} else {
|
||||
out.writeLong(0);
|
||||
out.writeLong(0);
|
||||
}
|
||||
} else {
|
||||
throw new EncoderException("unsupported addrType: " + (addrType.byteValue() & 0xFF));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes a SOCKS5 address.
|
||||
*
|
||||
* @param addrType the type of the address
|
||||
* @param addrValue the string representation of the address
|
||||
* @param out the output buffer where the encoded SOCKS5 address field will be written to
|
||||
*/
|
||||
void encodeAddress(Socks5AddressType addrType, String addrValue, ByteBuf out) throws Exception;
|
||||
}
|
@ -16,29 +16,74 @@
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5AddressType {
|
||||
IPv4((byte) 0x01),
|
||||
DOMAIN((byte) 0x03),
|
||||
IPv6((byte) 0x04),
|
||||
UNKNOWN((byte) 0xff);
|
||||
/**
|
||||
* The type of address in {@link Socks5CommandRequest} and {@link Socks5CommandResponse}.
|
||||
*/
|
||||
public class Socks5AddressType implements Comparable<Socks5AddressType> {
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks5AddressType(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
public static final Socks5AddressType IPv4 = new Socks5AddressType(0x01, "IPv4");
|
||||
public static final Socks5AddressType DOMAIN = new Socks5AddressType(0x03, "DOMAIN");
|
||||
public static final Socks5AddressType IPv6 = new Socks5AddressType(0x04, "IPv6");
|
||||
|
||||
public static Socks5AddressType valueOf(byte b) {
|
||||
for (Socks5AddressType code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
switch (b) {
|
||||
case 0x01:
|
||||
return IPv4;
|
||||
case 0x03:
|
||||
return DOMAIN;
|
||||
case 0x04:
|
||||
return IPv6;
|
||||
}
|
||||
return UNKNOWN;
|
||||
|
||||
return new Socks5AddressType(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks5AddressType(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks5AddressType(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks5AddressType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks5AddressType) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks5AddressType o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* The authentication method of SOCKS5.
|
||||
*/
|
||||
public class Socks5AuthMethod implements Comparable<Socks5AuthMethod> {
|
||||
|
||||
public static final Socks5AuthMethod NO_AUTH = new Socks5AuthMethod(0x00, "NO_AUTH");
|
||||
public static final Socks5AuthMethod GSSAPI = new Socks5AuthMethod(0x01, "GSSAPI");
|
||||
public static final Socks5AuthMethod PASSWORD = new Socks5AuthMethod(0x02, "PASSWORD");
|
||||
|
||||
/**
|
||||
* Indicates that the server does not accept any authentication methods the client proposed.
|
||||
*/
|
||||
public static final Socks5AuthMethod UNACCEPTED = new Socks5AuthMethod(0xff, "UNACCEPTED");
|
||||
|
||||
public static Socks5AuthMethod valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x00:
|
||||
return NO_AUTH;
|
||||
case 0x01:
|
||||
return GSSAPI;
|
||||
case 0x02:
|
||||
return PASSWORD;
|
||||
case (byte) 0xFF:
|
||||
return UNACCEPTED;
|
||||
}
|
||||
|
||||
return new Socks5AuthMethod(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks5AuthMethod(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks5AuthMethod(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks5AuthMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks5AuthMethod) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks5AuthMethod o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
|
||||
/**
|
||||
* An socks auth request.
|
||||
*
|
||||
* @see Socks5AuthResponse
|
||||
* @see Socks5AuthRequestDecoder
|
||||
*/
|
||||
public final class Socks5AuthRequest extends Socks5Request {
|
||||
private static final CharsetEncoder asciiEncoder = CharsetUtil.getEncoder(CharsetUtil.US_ASCII);
|
||||
private static final Socks5SubnegotiationVersion SUBNEGOTIATION_VERSION =
|
||||
Socks5SubnegotiationVersion.AUTH_PASSWORD;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public Socks5AuthRequest(String username, String password) {
|
||||
super(Socks5RequestType.AUTH);
|
||||
if (username == null) {
|
||||
throw new NullPointerException("username");
|
||||
}
|
||||
if (password == null) {
|
||||
throw new NullPointerException("username");
|
||||
}
|
||||
if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) {
|
||||
throw new IllegalArgumentException(" username: " + username + " or password: " + password +
|
||||
" values should be in pure ascii");
|
||||
}
|
||||
if (username.length() > 255) {
|
||||
throw new IllegalArgumentException(username + " exceeds 255 char limit");
|
||||
}
|
||||
if (password.length() > 255) {
|
||||
throw new IllegalArgumentException(password + " exceeds 255 char limit");
|
||||
}
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns username that needs to be authenticated
|
||||
*
|
||||
* @return username that needs to be authenticated
|
||||
*/
|
||||
public String username() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns password that needs to be validated
|
||||
*
|
||||
* @return password that needs to be validated
|
||||
*/
|
||||
public String password() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue());
|
||||
byteBuf.writeByte(username.length());
|
||||
byteBuf.writeBytes(username.getBytes(CharsetUtil.US_ASCII));
|
||||
byteBuf.writeByte(password.length());
|
||||
byteBuf.writeBytes(password.getBytes(CharsetUtil.US_ASCII));
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthRequestDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5AuthRequest}.
|
||||
* Before returning SocksRequest decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5AuthRequestDecoder extends ReplayingDecoder<State> {
|
||||
private Socks5SubnegotiationVersion version;
|
||||
private int fieldLength;
|
||||
private String username;
|
||||
private String password;
|
||||
private Socks5Request msg = UnknownSocks5Request.INSTANCE;
|
||||
|
||||
public Socks5AuthRequestDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = Socks5SubnegotiationVersion.valueOf(byteBuf.readByte());
|
||||
if (version != Socks5SubnegotiationVersion.AUTH_PASSWORD) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_USERNAME);
|
||||
}
|
||||
case READ_USERNAME: {
|
||||
fieldLength = byteBuf.readByte();
|
||||
username = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
|
||||
checkpoint(State.READ_PASSWORD);
|
||||
}
|
||||
case READ_PASSWORD: {
|
||||
fieldLength = byteBuf.readByte();
|
||||
password = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
|
||||
msg = new Socks5AuthRequest(username, password);
|
||||
}
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_USERNAME,
|
||||
READ_PASSWORD
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* An socks auth response.
|
||||
*
|
||||
* @see Socks5AuthRequest
|
||||
* @see Socks5AuthResponseDecoder
|
||||
*/
|
||||
public final class Socks5AuthResponse extends Socks5Response {
|
||||
private static final Socks5SubnegotiationVersion SUBNEGOTIATION_VERSION =
|
||||
Socks5SubnegotiationVersion.AUTH_PASSWORD;
|
||||
private final Socks5AuthStatus authStatus;
|
||||
|
||||
public Socks5AuthResponse(Socks5AuthStatus authStatus) {
|
||||
super(Socks5ResponseType.AUTH);
|
||||
if (authStatus == null) {
|
||||
throw new NullPointerException("authStatus");
|
||||
}
|
||||
this.authStatus = authStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AuthStatus} of this {@link Socks5AuthResponse}
|
||||
*
|
||||
* @return The {@link Socks5AuthStatus} of this {@link Socks5AuthResponse}
|
||||
*/
|
||||
public Socks5AuthStatus authStatus() {
|
||||
return authStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue());
|
||||
byteBuf.writeByte(authStatus.byteValue());
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5AuthResponse}.
|
||||
* Before returning SocksResponse decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5AuthResponseDecoder extends ReplayingDecoder<State> {
|
||||
private Socks5SubnegotiationVersion version;
|
||||
private Socks5AuthStatus authStatus;
|
||||
private Socks5Response msg = UnknownSocks5Response.INSTANCE;
|
||||
|
||||
public Socks5AuthResponseDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out)
|
||||
throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = Socks5SubnegotiationVersion.valueOf(byteBuf.readByte());
|
||||
if (version != Socks5SubnegotiationVersion.AUTH_PASSWORD) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_AUTH_RESPONSE);
|
||||
}
|
||||
case READ_AUTH_RESPONSE: {
|
||||
authStatus = Socks5AuthStatus.valueOf(byteBuf.readByte());
|
||||
msg = new Socks5AuthResponse(authStatus);
|
||||
}
|
||||
}
|
||||
channelHandlerContext.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_AUTH_RESPONSE
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5AuthScheme {
|
||||
NO_AUTH((byte) 0x00),
|
||||
AUTH_GSSAPI((byte) 0x01),
|
||||
AUTH_PASSWORD((byte) 0x02),
|
||||
UNKNOWN((byte) 0xff);
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks5AuthScheme(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks5AuthScheme valueOf(byte b) {
|
||||
for (Socks5AuthScheme code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5AuthStatus {
|
||||
SUCCESS((byte) 0x00),
|
||||
FAILURE((byte) 0xff);
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks5AuthStatus(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks5AuthStatus valueOf(byte b) {
|
||||
for (Socks5AuthStatus code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* Encodes a client-side {@link Socks5Message} into a {@link ByteBuf}.
|
||||
*/
|
||||
@Sharable
|
||||
public class Socks5ClientEncoder extends MessageToByteEncoder<Socks5Message> {
|
||||
|
||||
public static final Socks5ClientEncoder DEFAULT = new Socks5ClientEncoder();
|
||||
|
||||
private final Socks5AddressEncoder addressEncoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default {@link Socks5AddressEncoder}.
|
||||
*/
|
||||
protected Socks5ClientEncoder() {
|
||||
this(Socks5AddressEncoder.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified {@link Socks5AddressEncoder}.
|
||||
*/
|
||||
public Socks5ClientEncoder(Socks5AddressEncoder addressEncoder) {
|
||||
if (addressEncoder == null) {
|
||||
throw new NullPointerException("addressEncoder");
|
||||
}
|
||||
|
||||
this.addressEncoder = addressEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AddressEncoder} of this encoder.
|
||||
*/
|
||||
protected final Socks5AddressEncoder addressEncoder() {
|
||||
return addressEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof Socks5InitialRequest) {
|
||||
encodeAuthMethodRequest((Socks5InitialRequest) msg, out);
|
||||
} else if (msg instanceof Socks5PasswordAuthRequest) {
|
||||
encodePasswordAuthRequest((Socks5PasswordAuthRequest) msg, out);
|
||||
} else if (msg instanceof Socks5CommandRequest) {
|
||||
encodeCommandRequest((Socks5CommandRequest) msg, out);
|
||||
} else {
|
||||
throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg));
|
||||
}
|
||||
}
|
||||
|
||||
private static void encodeAuthMethodRequest(Socks5InitialRequest msg, ByteBuf out) {
|
||||
out.writeByte(msg.version().byteValue());
|
||||
|
||||
final List<Socks5AuthMethod> authMethods = msg.authMethods();
|
||||
final int numAuthMethods = authMethods.size();
|
||||
out.writeByte(numAuthMethods);
|
||||
|
||||
if (authMethods instanceof RandomAccess) {
|
||||
for (int i = 0; i < numAuthMethods; i ++) {
|
||||
out.writeByte(authMethods.get(i).byteValue());
|
||||
}
|
||||
} else {
|
||||
for (Socks5AuthMethod a: authMethods) {
|
||||
out.writeByte(a.byteValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void encodePasswordAuthRequest(Socks5PasswordAuthRequest msg, ByteBuf out) {
|
||||
out.writeByte(0x01);
|
||||
|
||||
final String username = msg.username();
|
||||
out.writeByte(username.length());
|
||||
ByteBufUtil.writeAscii(out, username);
|
||||
|
||||
final String password = msg.password();
|
||||
out.writeByte(password.length());
|
||||
ByteBufUtil.writeAscii(out, password);
|
||||
}
|
||||
|
||||
private void encodeCommandRequest(Socks5CommandRequest msg, ByteBuf out) throws Exception {
|
||||
out.writeByte(msg.version().byteValue());
|
||||
out.writeByte(msg.type().byteValue());
|
||||
out.writeByte(0x00);
|
||||
|
||||
final Socks5AddressType dstAddrType = msg.dstAddrType();
|
||||
out.writeByte(dstAddrType.byteValue());
|
||||
addressEncoder.encodeAddress(dstAddrType, msg.dstAddr(), out);
|
||||
out.writeShort(msg.dstPort());
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* An socks cmd request.
|
||||
*
|
||||
* @see Socks5CmdResponse
|
||||
* @see Socks5CmdRequestDecoder
|
||||
*/
|
||||
public final class Socks5CmdRequest extends Socks5Request {
|
||||
private final Socks5CmdType cmdType;
|
||||
private final Socks5AddressType addressType;
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
public Socks5CmdRequest(Socks5CmdType cmdType, Socks5AddressType addressType, String host, int port) {
|
||||
super(Socks5RequestType.CMD);
|
||||
if (cmdType == null) {
|
||||
throw new NullPointerException("cmdType");
|
||||
}
|
||||
if (addressType == null) {
|
||||
throw new NullPointerException("addressType");
|
||||
}
|
||||
if (host == null) {
|
||||
throw new NullPointerException("host");
|
||||
}
|
||||
switch (addressType) {
|
||||
case IPv4:
|
||||
if (!NetUtil.isValidIpV4Address(host)) {
|
||||
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
|
||||
}
|
||||
break;
|
||||
case DOMAIN:
|
||||
if (IDN.toASCII(host).length() > 255) {
|
||||
throw new IllegalArgumentException(host + " IDN: " + IDN.toASCII(host) + " exceeds 255 char limit");
|
||||
}
|
||||
break;
|
||||
case IPv6:
|
||||
if (!NetUtil.isValidIpV6Address(host)) {
|
||||
throw new IllegalArgumentException(host + " is not a valid IPv6 address");
|
||||
}
|
||||
break;
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
if (port <= 0 || port >= 65536) {
|
||||
throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536");
|
||||
}
|
||||
this.cmdType = cmdType;
|
||||
this.addressType = addressType;
|
||||
this.host = IDN.toASCII(host);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5CmdType} of this {@link Socks5CmdRequest}
|
||||
*
|
||||
* @return The {@link Socks5CmdType} of this {@link Socks5CmdRequest}
|
||||
*/
|
||||
public Socks5CmdType cmdType() {
|
||||
return cmdType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AddressType} of this {@link Socks5CmdRequest}
|
||||
*
|
||||
* @return The {@link Socks5AddressType} of this {@link Socks5CmdRequest}
|
||||
*/
|
||||
public Socks5AddressType addressType() {
|
||||
return addressType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns host that is used as a parameter in {@link Socks5CmdType}
|
||||
*
|
||||
* @return host that is used as a parameter in {@link Socks5CmdType}
|
||||
*/
|
||||
public String host() {
|
||||
return IDN.toUnicode(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns port that is used as a parameter in {@link Socks5CmdType}
|
||||
*
|
||||
* @return port that is used as a parameter in {@link Socks5CmdType}
|
||||
*/
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(protocolVersion().byteValue());
|
||||
byteBuf.writeByte(cmdType.byteValue());
|
||||
byteBuf.writeByte(0x00);
|
||||
byteBuf.writeByte(addressType.byteValue());
|
||||
switch (addressType) {
|
||||
case IPv4: {
|
||||
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
|
||||
byteBuf.writeShort(port);
|
||||
break;
|
||||
}
|
||||
|
||||
case DOMAIN: {
|
||||
byteBuf.writeByte(host.length());
|
||||
byteBuf.writeBytes(host.getBytes(CharsetUtil.US_ASCII));
|
||||
byteBuf.writeShort(port);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPv6: {
|
||||
byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host));
|
||||
byteBuf.writeShort(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequestDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5CmdRequest}.
|
||||
* Before returning SocksRequest decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5CmdRequestDecoder extends ReplayingDecoder<State> {
|
||||
private SocksProtocolVersion version;
|
||||
private int fieldLength;
|
||||
private Socks5CmdType cmdType;
|
||||
private Socks5AddressType addressType;
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private byte reserved;
|
||||
private String host;
|
||||
private int port;
|
||||
private Socks5Request msg = UnknownSocks5Request.INSTANCE;
|
||||
|
||||
public Socks5CmdRequestDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
|
||||
if (version != SocksProtocolVersion.SOCKS5) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_CMD_HEADER);
|
||||
}
|
||||
case READ_CMD_HEADER: {
|
||||
cmdType = Socks5CmdType.valueOf(byteBuf.readByte());
|
||||
reserved = byteBuf.readByte();
|
||||
addressType = Socks5AddressType.valueOf(byteBuf.readByte());
|
||||
checkpoint(State.READ_CMD_ADDRESS);
|
||||
}
|
||||
case READ_CMD_ADDRESS: {
|
||||
switch (addressType) {
|
||||
case IPv4: {
|
||||
host = Socks5CommonUtils.intToIp(byteBuf.readInt());
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdRequest(cmdType, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case DOMAIN: {
|
||||
fieldLength = byteBuf.readByte();
|
||||
host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdRequest(cmdType, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case IPv6: {
|
||||
if (actualReadableBytes() < 16) {
|
||||
// Let it replay.
|
||||
byteBuf.readBytes(16);
|
||||
|
||||
// Should never reach here.
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
byte[] byteArray = new byte[16];
|
||||
byteBuf.readBytes(byteArray);
|
||||
|
||||
host = Socks5CommonUtils.ipv6toStr(byteArray);
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdRequest(cmdType, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_CMD_HEADER,
|
||||
READ_CMD_ADDRESS
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
|
||||
/**
|
||||
* A socks cmd response.
|
||||
*
|
||||
* @see Socks5CmdRequest
|
||||
* @see Socks5CmdResponseDecoder
|
||||
*/
|
||||
public final class Socks5CmdResponse extends Socks5Response {
|
||||
private final Socks5CmdStatus cmdStatus;
|
||||
|
||||
private final Socks5AddressType addressType;
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
// All arrays are initialized on construction time to 0/false/null remove array Initialization
|
||||
private static final byte[] DOMAIN_ZEROED = {0x00};
|
||||
private static final byte[] IPv4_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00};
|
||||
private static final byte[] IPv6_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
public Socks5CmdResponse(Socks5CmdStatus cmdStatus, Socks5AddressType addressType) {
|
||||
this(cmdStatus, addressType, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new response and includes provided host and port as part of it.
|
||||
*
|
||||
* @param cmdStatus status of the response
|
||||
* @param addressType type of host parameter
|
||||
* @param host host (BND.ADDR field) is address that server used when connecting to the target host.
|
||||
* When null a value of 4/8 0x00 octets will be used for IPv4/IPv6 and a single 0x00 byte will be
|
||||
* used for domain addressType. Value is converted to ASCII using {@link IDN#toASCII(String)}.
|
||||
* @param port port (BND.PORT field) that the server assigned to connect to the target host
|
||||
* @throws NullPointerException in case cmdStatus or addressType are missing
|
||||
* @throws IllegalArgumentException in case host or port cannot be validated
|
||||
* @see IDN#toASCII(String)
|
||||
*/
|
||||
public Socks5CmdResponse(Socks5CmdStatus cmdStatus, Socks5AddressType addressType, String host, int port) {
|
||||
super(Socks5ResponseType.CMD);
|
||||
if (cmdStatus == null) {
|
||||
throw new NullPointerException("cmdStatus");
|
||||
}
|
||||
if (addressType == null) {
|
||||
throw new NullPointerException("addressType");
|
||||
}
|
||||
if (host != null) {
|
||||
switch (addressType) {
|
||||
case IPv4:
|
||||
if (!NetUtil.isValidIpV4Address(host)) {
|
||||
throw new IllegalArgumentException(host + " is not a valid IPv4 address");
|
||||
}
|
||||
break;
|
||||
case DOMAIN:
|
||||
if (IDN.toASCII(host).length() > 255) {
|
||||
throw new IllegalArgumentException(host + " IDN: " +
|
||||
IDN.toASCII(host) + " exceeds 255 char limit");
|
||||
}
|
||||
break;
|
||||
case IPv6:
|
||||
if (!NetUtil.isValidIpV6Address(host)) {
|
||||
throw new IllegalArgumentException(host + " is not a valid IPv6 address");
|
||||
}
|
||||
break;
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
host = IDN.toASCII(host);
|
||||
}
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535");
|
||||
}
|
||||
this.cmdStatus = cmdStatus;
|
||||
this.addressType = addressType;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5CmdStatus} of this {@link Socks5CmdResponse}
|
||||
*
|
||||
* @return The {@link Socks5CmdStatus} of this {@link Socks5CmdResponse}
|
||||
*/
|
||||
public Socks5CmdStatus cmdStatus() {
|
||||
return cmdStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AddressType} of this {@link Socks5CmdResponse}
|
||||
*
|
||||
* @return The {@link Socks5AddressType} of this {@link Socks5CmdResponse}
|
||||
*/
|
||||
public Socks5AddressType addressType() {
|
||||
return addressType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns host that is used as a parameter in {@link Socks5CmdType}.
|
||||
* Host (BND.ADDR field in response) is address that server used when connecting to the target host.
|
||||
* This is typically different from address which client uses to connect to the SOCKS server.
|
||||
*
|
||||
* @return host that is used as a parameter in {@link Socks5CmdType}
|
||||
* or null when there was no host specified during response construction
|
||||
*/
|
||||
public String host() {
|
||||
if (host != null) {
|
||||
return IDN.toUnicode(host);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns port that is used as a parameter in {@link Socks5CmdType}.
|
||||
* Port (BND.PORT field in response) is port that the server assigned to connect to the target host.
|
||||
*
|
||||
* @return port that is used as a parameter in {@link Socks5CmdType}
|
||||
*/
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(protocolVersion().byteValue());
|
||||
byteBuf.writeByte(cmdStatus.byteValue());
|
||||
byteBuf.writeByte(0x00);
|
||||
byteBuf.writeByte(addressType.byteValue());
|
||||
switch (addressType) {
|
||||
case IPv4: {
|
||||
byte[] hostContent = host == null ?
|
||||
IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
|
||||
byteBuf.writeBytes(hostContent);
|
||||
byteBuf.writeShort(port);
|
||||
break;
|
||||
}
|
||||
case DOMAIN: {
|
||||
byte[] hostContent = host == null ?
|
||||
DOMAIN_ZEROED : host.getBytes(CharsetUtil.US_ASCII);
|
||||
byteBuf.writeByte(hostContent.length); // domain length
|
||||
byteBuf.writeBytes(hostContent); // domain value
|
||||
byteBuf.writeShort(port); // port value
|
||||
break;
|
||||
}
|
||||
case IPv6: {
|
||||
byte[] hostContent = host == null
|
||||
? IPv6_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
|
||||
byteBuf.writeBytes(hostContent);
|
||||
byteBuf.writeShort(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdResponseDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5CmdResponse}.
|
||||
* Before returning SocksResponse decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5CmdResponseDecoder extends ReplayingDecoder<State> {
|
||||
private SocksProtocolVersion version;
|
||||
private int fieldLength;
|
||||
private Socks5CmdStatus cmdStatus;
|
||||
private Socks5AddressType addressType;
|
||||
private String host;
|
||||
private int port;
|
||||
private Socks5Response msg = UnknownSocks5Response.INSTANCE;
|
||||
|
||||
public Socks5CmdResponseDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
|
||||
if (version != SocksProtocolVersion.SOCKS5) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_CMD_HEADER);
|
||||
}
|
||||
case READ_CMD_HEADER: {
|
||||
cmdStatus = Socks5CmdStatus.valueOf(byteBuf.readByte());
|
||||
byteBuf.skipBytes(1); // reserved
|
||||
addressType = Socks5AddressType.valueOf(byteBuf.readByte());
|
||||
checkpoint(State.READ_CMD_ADDRESS);
|
||||
}
|
||||
case READ_CMD_ADDRESS: {
|
||||
switch (addressType) {
|
||||
case IPv4: {
|
||||
host = Socks5CommonUtils.intToIp(byteBuf.readInt());
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdResponse(cmdStatus, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case DOMAIN: {
|
||||
fieldLength = byteBuf.readByte();
|
||||
host = byteBuf.readBytes(fieldLength).toString(CharsetUtil.US_ASCII);
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdResponse(cmdStatus, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case IPv6: {
|
||||
host = Socks5CommonUtils.ipv6toStr(byteBuf.readBytes(16).array());
|
||||
port = byteBuf.readUnsignedShort();
|
||||
msg = new Socks5CmdResponse(cmdStatus, addressType, host, port);
|
||||
break;
|
||||
}
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_CMD_HEADER,
|
||||
READ_CMD_ADDRESS
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5CmdStatus {
|
||||
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;
|
||||
|
||||
Socks5CmdStatus(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks5CmdStatus valueOf(byte b) {
|
||||
for (Socks5CmdStatus code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNASSIGNED;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5CmdType {
|
||||
CONNECT((byte) 0x01),
|
||||
BIND((byte) 0x02),
|
||||
UDP((byte) 0x03),
|
||||
UNKNOWN((byte) 0xff);
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks5CmdType(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks5CmdType valueOf(byte b) {
|
||||
for (Socks5CmdType code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -15,25 +15,29 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* An unknown socks request.
|
||||
*
|
||||
* @see Socks5InitRequestDecoder
|
||||
* @see Socks5AuthRequestDecoder
|
||||
* @see Socks5CmdRequestDecoder
|
||||
* A SOCKS5 request detail message, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1928#section-4">the section 4, RFC1928</a>.
|
||||
*/
|
||||
public final class UnknownSocks5Request extends Socks5Request {
|
||||
public interface Socks5CommandRequest extends Socks5Message {
|
||||
|
||||
public static final UnknownSocks5Request INSTANCE = new UnknownSocks5Request();
|
||||
/**
|
||||
* Returns the type of this request.
|
||||
*/
|
||||
Socks5CommandType type();
|
||||
|
||||
private UnknownSocks5Request() {
|
||||
super(Socks5RequestType.UNKNOWN);
|
||||
}
|
||||
/**
|
||||
* Returns the type of the {@code DST.ADDR} field of this request.
|
||||
*/
|
||||
Socks5AddressType dstAddrType();
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
// NOOP
|
||||
}
|
||||
/**
|
||||
* Returns the {@code DST.ADDR} field of this request.
|
||||
*/
|
||||
String dstAddr();
|
||||
|
||||
/**
|
||||
* Returns the {@code DST.PORT} field of this request.
|
||||
*/
|
||||
int dstPort();
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5CommandRequest} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5CommandRequestDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
private final Socks5AddressDecoder addressDecoder;
|
||||
|
||||
public Socks5CommandRequestDecoder() {
|
||||
this(Socks5AddressDecoder.DEFAULT);
|
||||
}
|
||||
|
||||
public Socks5CommandRequestDecoder(Socks5AddressDecoder addressDecoder) {
|
||||
super(State.INIT);
|
||||
if (addressDecoder == null) {
|
||||
throw new NullPointerException("addressDecoder");
|
||||
}
|
||||
|
||||
this.addressDecoder = addressDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final byte version = in.readByte();
|
||||
if (version != SocksVersion.SOCKS5.byteValue()) {
|
||||
throw new DecoderException(
|
||||
"unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')');
|
||||
}
|
||||
|
||||
final Socks5CommandType type = Socks5CommandType.valueOf(in.readByte());
|
||||
in.skipBytes(1); // RSV
|
||||
final Socks5AddressType dstAddrType = Socks5AddressType.valueOf(in.readByte());
|
||||
final String dstAddr = addressDecoder.decodeAddress(dstAddrType, in);
|
||||
final int dstPort = in.readUnsignedShort();
|
||||
|
||||
out.add(new DefaultSocks5CommandRequest(type, dstAddrType, dstAddr, dstPort));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5CommandRequest(
|
||||
Socks5CommandType.CONNECT, Socks5AddressType.IPv4, "0.0.0.0", 1);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -15,25 +15,29 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* An unknown socks response.
|
||||
*
|
||||
* @see Socks5InitResponseDecoder
|
||||
* @see Socks5AuthResponseDecoder
|
||||
* @see Socks5CmdResponseDecoder
|
||||
* A response to a SOCKS5 request detail message, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1928#section-6">the section 6, RFC1928</a>.
|
||||
*/
|
||||
public final class UnknownSocks5Response extends Socks5Response {
|
||||
public interface Socks5CommandResponse extends Socks5Message {
|
||||
|
||||
public static final UnknownSocks5Response INSTANCE = new UnknownSocks5Response();
|
||||
/**
|
||||
* Returns the status of this response.
|
||||
*/
|
||||
Socks5CommandStatus status();
|
||||
|
||||
private UnknownSocks5Response() {
|
||||
super(Socks5ResponseType.UNKNOWN);
|
||||
}
|
||||
/**
|
||||
* Returns the address type of the {@code BND.ADDR} field of this response.
|
||||
*/
|
||||
Socks5AddressType bndAddrType();
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
// NOOP
|
||||
}
|
||||
/**
|
||||
* Returns the {@code BND.ADDR} field of this response.
|
||||
*/
|
||||
String bndAddr();
|
||||
|
||||
/**
|
||||
* Returns the {@code BND.PORT} field of this response.
|
||||
*/
|
||||
int bndPort();
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5CommandResponse} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5CommandResponseDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
private final Socks5AddressDecoder addressDecoder;
|
||||
|
||||
public Socks5CommandResponseDecoder() {
|
||||
this(Socks5AddressDecoder.DEFAULT);
|
||||
}
|
||||
|
||||
public Socks5CommandResponseDecoder(Socks5AddressDecoder addressDecoder) {
|
||||
super(State.INIT);
|
||||
if (addressDecoder == null) {
|
||||
throw new NullPointerException("addressDecoder");
|
||||
}
|
||||
|
||||
this.addressDecoder = addressDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final byte version = in.readByte();
|
||||
if (version != SocksVersion.SOCKS5.byteValue()) {
|
||||
throw new DecoderException(
|
||||
"unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')');
|
||||
}
|
||||
final Socks5CommandStatus status = Socks5CommandStatus.valueOf(in.readByte());
|
||||
in.skipBytes(1); // Reserved
|
||||
final Socks5AddressType addrType = Socks5AddressType.valueOf(in.readByte());
|
||||
final String addr = addressDecoder.decodeAddress(addrType, in);
|
||||
final int port = in.readUnsignedShort();
|
||||
|
||||
out.add(new DefaultSocks5CommandResponse(status, addrType, addr, port));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.FAILURE, Socks5AddressType.IPv4, null, 0);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* The status of {@link Socks5CommandResponse}.
|
||||
*/
|
||||
public class Socks5CommandStatus implements Comparable<Socks5CommandStatus> {
|
||||
|
||||
public static final Socks5CommandStatus SUCCESS = new Socks5CommandStatus(0x00, "SUCCESS");
|
||||
public static final Socks5CommandStatus FAILURE = new Socks5CommandStatus(0x01, "FAILURE");
|
||||
public static final Socks5CommandStatus FORBIDDEN = new Socks5CommandStatus(0x02, "FORBIDDEN");
|
||||
public static final Socks5CommandStatus NETWORK_UNREACHABLE = new Socks5CommandStatus(0x03, "NETWORK_UNREACHABLE");
|
||||
public static final Socks5CommandStatus HOST_UNREACHABLE = new Socks5CommandStatus(0x04, "HOST_UNREACHABLE");
|
||||
public static final Socks5CommandStatus CONNECTION_REFUSED = new Socks5CommandStatus(0x05, "CONNECTION_REFUSED");
|
||||
public static final Socks5CommandStatus TTL_EXPIRED = new Socks5CommandStatus(0x06, "TTL_EXPIRED");
|
||||
public static final Socks5CommandStatus COMMAND_UNSUPPORTED = new Socks5CommandStatus(0x07, "COMMAND_UNSUPPORTED");
|
||||
public static final Socks5CommandStatus ADDRESS_UNSUPPORTED = new Socks5CommandStatus(0x08, "ADDRESS_UNSUPPORTED");
|
||||
|
||||
public static Socks5CommandStatus valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x00:
|
||||
return SUCCESS;
|
||||
case 0x01:
|
||||
return FAILURE;
|
||||
case 0x02:
|
||||
return FORBIDDEN;
|
||||
case 0x03:
|
||||
return NETWORK_UNREACHABLE;
|
||||
case 0x04:
|
||||
return HOST_UNREACHABLE;
|
||||
case 0x05:
|
||||
return CONNECTION_REFUSED;
|
||||
case 0x06:
|
||||
return TTL_EXPIRED;
|
||||
case 0x07:
|
||||
return COMMAND_UNSUPPORTED;
|
||||
case 0x08:
|
||||
return ADDRESS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return new Socks5CommandStatus(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks5CommandStatus(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks5CommandStatus(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return byteValue == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks5CommandStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks5CommandStatus) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks5CommandStatus o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* The type of {@link Socks5CommandRequest}.
|
||||
*/
|
||||
public class Socks5CommandType implements Comparable<Socks5CommandType> {
|
||||
|
||||
public static final Socks5CommandType CONNECT = new Socks5CommandType(0x01, "CONNECT");
|
||||
public static final Socks5CommandType BIND = new Socks5CommandType(0x02, "BIND");
|
||||
public static final Socks5CommandType UDP_ASSOCIATE = new Socks5CommandType(0x03, "UDP_ASSOCIATE");
|
||||
|
||||
public static Socks5CommandType valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x01:
|
||||
return CONNECT;
|
||||
case 0x02:
|
||||
return BIND;
|
||||
case 0x03:
|
||||
return UDP_ASSOCIATE;
|
||||
}
|
||||
|
||||
return new Socks5CommandType(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks5CommandType(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks5CommandType(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks5CommandType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks5CommandType) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks5CommandType o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
final class Socks5CommonUtils {
|
||||
|
||||
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 Socks5CommonUtils() {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
public static String intToIp(int i) {
|
||||
return String.valueOf(i >> FIRST_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i >> SECOND_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i >> THIRD_ADDRESS_OCTET_SHIFT & XOR_DEFAULT_VALUE) + '.' +
|
||||
(i & XOR_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
private static final char[] ipv6conseqZeroFiller = {':', ':'};
|
||||
private static final char ipv6hextetSeparator = ':';
|
||||
|
||||
/**
|
||||
* Convert numeric IPv6 to compressed format, where
|
||||
* the longest sequence of 0's (with 2 or more 0's) is replaced with "::"
|
||||
*/
|
||||
public static String ipv6toCompressedForm(byte[] src) {
|
||||
assert src.length == 16;
|
||||
//Find the longest sequence of 0's
|
||||
//start of compressed region (hextet index)
|
||||
int cmprHextet = -1;
|
||||
//length of compressed region
|
||||
int cmprSize = 0;
|
||||
for (int hextet = 0; hextet < 8;) {
|
||||
int curByte = hextet * 2;
|
||||
int size = 0;
|
||||
while (curByte < src.length && src[curByte] == 0
|
||||
&& src[curByte + 1] == 0) {
|
||||
curByte += 2;
|
||||
size++;
|
||||
}
|
||||
if (size > cmprSize) {
|
||||
cmprHextet = hextet;
|
||||
cmprSize = size;
|
||||
}
|
||||
hextet = curByte / 2 + 1;
|
||||
}
|
||||
if (cmprHextet == -1 || cmprSize < 2) {
|
||||
//No compression can be applied
|
||||
return ipv6toStr(src);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(39);
|
||||
ipv6toStr(sb, src, 0, cmprHextet);
|
||||
sb.append(ipv6conseqZeroFiller);
|
||||
ipv6toStr(sb, src, cmprHextet + cmprSize, 8);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts numeric IPv6 to standard (non-compressed) format.
|
||||
*/
|
||||
public static String ipv6toStr(byte[] src) {
|
||||
assert src.length == 16;
|
||||
StringBuilder sb = new StringBuilder(39);
|
||||
ipv6toStr(sb, src, 0, 8);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void ipv6toStr(StringBuilder sb, byte[] src, int fromHextet, int toHextet) {
|
||||
int i;
|
||||
toHextet --;
|
||||
for (i = fromHextet; i < toHextet; i++) {
|
||||
appendHextet(sb, src, i);
|
||||
sb.append(ipv6hextetSeparator);
|
||||
}
|
||||
|
||||
appendHextet(sb, src, i);
|
||||
}
|
||||
|
||||
private static void appendHextet(StringBuilder sb, byte[] src, int i) {
|
||||
StringUtil.toHexString(sb, src, i << 1, 2);
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An socks init request.
|
||||
*
|
||||
* @see Socks5InitResponse
|
||||
* @see Socks5InitRequestDecoder
|
||||
*/
|
||||
public final class Socks5InitRequest extends Socks5Request {
|
||||
private final List<Socks5AuthScheme> authSchemes;
|
||||
|
||||
public Socks5InitRequest(List<Socks5AuthScheme> authSchemes) {
|
||||
super(Socks5RequestType.INIT);
|
||||
if (authSchemes == null) {
|
||||
throw new NullPointerException("authSchemes");
|
||||
}
|
||||
this.authSchemes = authSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the List<{@link Socks5AuthScheme}> of this {@link Socks5InitRequest}
|
||||
*
|
||||
* @return The List<{@link Socks5AuthScheme}> of this {@link Socks5InitRequest}
|
||||
*/
|
||||
public List<Socks5AuthScheme> authSchemes() {
|
||||
return Collections.unmodifiableList(authSchemes);
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(protocolVersion().byteValue());
|
||||
byteBuf.writeByte(authSchemes.size());
|
||||
for (Socks5AuthScheme authScheme : authSchemes) {
|
||||
byteBuf.writeByte(authScheme.byteValue());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitRequestDecoder.State;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5InitRequest}.
|
||||
* Before returning SocksRequest decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5InitRequestDecoder extends ReplayingDecoder<State> {
|
||||
private final List<Socks5AuthScheme> authSchemes = new ArrayList<Socks5AuthScheme>();
|
||||
private SocksProtocolVersion version;
|
||||
private byte authSchemeNum;
|
||||
private Socks5Request msg = UnknownSocks5Request.INSTANCE;
|
||||
|
||||
public Socks5InitRequestDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
|
||||
if (version != SocksProtocolVersion.SOCKS5) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_AUTH_SCHEMES);
|
||||
}
|
||||
case READ_AUTH_SCHEMES: {
|
||||
authSchemes.clear();
|
||||
authSchemeNum = byteBuf.readByte();
|
||||
for (int i = 0; i < authSchemeNum; i++) {
|
||||
authSchemes.add(Socks5AuthScheme.valueOf(byteBuf.readByte()));
|
||||
}
|
||||
msg = new Socks5InitRequest(authSchemes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_AUTH_SCHEMES
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* An socks init response.
|
||||
*
|
||||
* @see Socks5InitRequest
|
||||
* @see Socks5InitResponseDecoder
|
||||
*/
|
||||
public final class Socks5InitResponse extends Socks5Response {
|
||||
private final Socks5AuthScheme authScheme;
|
||||
|
||||
public Socks5InitResponse(Socks5AuthScheme authScheme) {
|
||||
super(Socks5ResponseType.INIT);
|
||||
if (authScheme == null) {
|
||||
throw new NullPointerException("authScheme");
|
||||
}
|
||||
this.authScheme = authScheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AuthScheme} of this {@link Socks5InitResponse}
|
||||
*
|
||||
* @return The {@link Socks5AuthScheme} of this {@link Socks5InitResponse}
|
||||
*/
|
||||
public Socks5AuthScheme authScheme() {
|
||||
return authScheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
||||
byteBuf.writeByte(protocolVersion().byteValue());
|
||||
byteBuf.writeByte(authScheme.byteValue());
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ByteBuf}s into {@link Socks5InitResponse}.
|
||||
* Before returning SocksResponse decoder removes itself from pipeline.
|
||||
*/
|
||||
public class Socks5InitResponseDecoder extends ReplayingDecoder<State> {
|
||||
private SocksProtocolVersion version;
|
||||
private Socks5AuthScheme authScheme;
|
||||
|
||||
private Socks5Response msg = UnknownSocks5Response.INSTANCE;
|
||||
|
||||
public Socks5InitResponseDecoder() {
|
||||
super(State.CHECK_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case CHECK_PROTOCOL_VERSION: {
|
||||
version = SocksProtocolVersion.valueOf(byteBuf.readByte());
|
||||
if (version != SocksProtocolVersion.SOCKS5) {
|
||||
break;
|
||||
}
|
||||
checkpoint(State.READ_PREFFERED_AUTH_TYPE);
|
||||
}
|
||||
case READ_PREFFERED_AUTH_TYPE: {
|
||||
authScheme = Socks5AuthScheme.valueOf(byteBuf.readByte());
|
||||
msg = new Socks5InitResponse(authScheme);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
out.add(msg);
|
||||
}
|
||||
|
||||
enum State {
|
||||
CHECK_PROTOCOL_VERSION,
|
||||
READ_PREFFERED_AUTH_TYPE
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An initial SOCKS5 authentication method selection request, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1928#section-3">the section 3, RFC1928</a>.
|
||||
*/
|
||||
public interface Socks5InitialRequest extends Socks5Message {
|
||||
/**
|
||||
* Returns the list of desired authentication methods.
|
||||
*/
|
||||
List<Socks5AuthMethod> authMethods();
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5InitialRequest} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5InitialRequestDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
public Socks5InitialRequestDecoder() {
|
||||
super(State.INIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final byte version = in.readByte();
|
||||
if (version != SocksVersion.SOCKS5.byteValue()) {
|
||||
throw new DecoderException(
|
||||
"unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')');
|
||||
}
|
||||
|
||||
final int authMethodCnt = in.readUnsignedByte();
|
||||
if (actualReadableBytes() < authMethodCnt) {
|
||||
break;
|
||||
}
|
||||
|
||||
final Socks5AuthMethod[] authMethods = new Socks5AuthMethod[authMethodCnt];
|
||||
for (int i = 0; i < authMethodCnt; i++) {
|
||||
authMethods[i] = Socks5AuthMethod.valueOf(in.readByte());
|
||||
}
|
||||
|
||||
out.add(new DefaultSocks5InitialRequest(authMethods));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5InitialRequest(Socks5AuthMethod.NO_AUTH);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* An initial SOCKS5 authentication method selection request, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1928#section-3">the section 3, RFC1928</a>.
|
||||
*/
|
||||
public interface Socks5InitialResponse extends Socks5Message {
|
||||
|
||||
/**
|
||||
* Returns the {@code METHOD} field of this response.
|
||||
*/
|
||||
Socks5AuthMethod authMethod();
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksVersion;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitialResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5InitialResponse} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5InitialResponseDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
public Socks5InitialResponseDecoder() {
|
||||
super(State.INIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final byte version = in.readByte();
|
||||
if (version != SocksVersion.SOCKS5.byteValue()) {
|
||||
throw new DecoderException(
|
||||
"unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')');
|
||||
}
|
||||
|
||||
final Socks5AuthMethod authMethod = Socks5AuthMethod.valueOf(in.readByte());
|
||||
out.add(new DefaultSocks5InitialResponse(authMethod));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5InitialResponse(Socks5AuthMethod.UNACCEPTED);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -16,12 +16,11 @@
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
|
||||
/**
|
||||
* Type of socks request
|
||||
* A tag interface that all SOCKS5 protocol messages implement.
|
||||
*/
|
||||
public enum Socks5RequestType {
|
||||
INIT,
|
||||
AUTH,
|
||||
CMD,
|
||||
UNKNOWN
|
||||
public interface Socks5Message extends SocksMessage {
|
||||
// Tag interface
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
|
||||
/**
|
||||
* Encodes a {@link Socks5Request} and {@link Socks5Response} into a {@link ByteBuf}.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public final class Socks5MessageEncoder extends MessageToByteEncoder<SocksMessage> {
|
||||
|
||||
public static final Socks5MessageEncoder INSTANCE = new Socks5MessageEncoder();
|
||||
|
||||
private Socks5MessageEncoder() { }
|
||||
|
||||
@Override
|
||||
public boolean acceptOutboundMessage(Object msg) throws Exception {
|
||||
return super.acceptOutboundMessage(msg) &&
|
||||
((SocksMessage) msg).protocolVersion() == SocksProtocolVersion.SOCKS5;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, SocksMessage msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof Socks5Response) {
|
||||
((Socks5Response) msg).encodeAsByteBuf(out);
|
||||
} else if (msg instanceof Socks5Request) {
|
||||
((Socks5Request) msg).encodeAsByteBuf(out);
|
||||
} else {
|
||||
// Should not reach here.
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* A SOCKS5 subnegotiation request for username-password authentication, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1929#section-2">the section 2, RFC1929</a>.
|
||||
*/
|
||||
public interface Socks5PasswordAuthRequest extends Socks5Message {
|
||||
|
||||
/**
|
||||
* Returns the username of this request.
|
||||
*/
|
||||
String username();
|
||||
|
||||
/**
|
||||
* Returns the password of this request.
|
||||
*/
|
||||
String password();
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequestDecoder.State;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5PasswordAuthRequest} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5PasswordAuthRequestDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
public Socks5PasswordAuthRequestDecoder() {
|
||||
super(State.INIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final int startOffset = in.readerIndex();
|
||||
final byte version = in.getByte(startOffset);
|
||||
if (version != 1) {
|
||||
throw new DecoderException("unsupported subnegotiation version: " + version + " (expected: 1)");
|
||||
}
|
||||
|
||||
final int usernameLength = in.getUnsignedByte(startOffset + 1);
|
||||
final int passwordLength = in.getUnsignedByte(startOffset + 2 + usernameLength);
|
||||
final int totalLength = usernameLength + passwordLength + 3;
|
||||
|
||||
in.skipBytes(totalLength);
|
||||
out.add(new DefaultSocks5PasswordAuthRequest(
|
||||
in.toString(startOffset + 2, usernameLength, CharsetUtil.US_ASCII),
|
||||
in.toString(startOffset + 3 + usernameLength, passwordLength, CharsetUtil.US_ASCII)));
|
||||
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5PasswordAuthRequest("", "");
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* A SOCKS5 subnegotiation response for username-password authentication, as defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc1929#section-2">the section 2, RFC1929</a>.
|
||||
*/
|
||||
public interface Socks5PasswordAuthResponse extends Socks5Message {
|
||||
/**
|
||||
* Returns the status of this response.
|
||||
*/
|
||||
Socks5PasswordAuthStatus status();
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthResponseDecoder.State;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes a single {@link Socks5PasswordAuthResponse} from the inbound {@link ByteBuf}s.
|
||||
* On successful decode, this decoder will forward the received data to the next handler, so that
|
||||
* other handler can remove or replace this decoder later. On failed decode, this decoder will
|
||||
* discard the received data, so that other handler closes the connection later.
|
||||
*/
|
||||
public class Socks5PasswordAuthResponseDecoder extends ReplayingDecoder<State> {
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
public Socks5PasswordAuthResponseDecoder() {
|
||||
super(State.INIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try {
|
||||
switch (state()) {
|
||||
case INIT: {
|
||||
final byte version = in.readByte();
|
||||
if (version != 1) {
|
||||
throw new DecoderException("unsupported subnegotiation version: " + version + " (expected: 1)");
|
||||
}
|
||||
|
||||
out.add(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.valueOf(in.readByte())));
|
||||
checkpoint(State.SUCCESS);
|
||||
}
|
||||
case SUCCESS: {
|
||||
int readableBytes = actualReadableBytes();
|
||||
if (readableBytes > 0) {
|
||||
out.add(in.readSlice(readableBytes).retain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FAILURE: {
|
||||
in.skipBytes(actualReadableBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail(out, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(List<Object> out, Throwable cause) {
|
||||
if (!(cause instanceof DecoderException)) {
|
||||
cause = new DecoderException(cause);
|
||||
}
|
||||
|
||||
checkpoint(State.FAILURE);
|
||||
|
||||
Socks5Message m = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.FAILURE);
|
||||
m.setDecoderResult(DecoderResult.failure(cause));
|
||||
out.add(m);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
/**
|
||||
* The status of {@link Socks5PasswordAuthResponse}.
|
||||
*/
|
||||
public class Socks5PasswordAuthStatus implements Comparable<Socks5PasswordAuthStatus> {
|
||||
|
||||
public static final Socks5PasswordAuthStatus SUCCESS = new Socks5PasswordAuthStatus(0x00, "SUCCESS");
|
||||
public static final Socks5PasswordAuthStatus FAILURE = new Socks5PasswordAuthStatus(0xFF, "FAILURE");
|
||||
|
||||
public static Socks5PasswordAuthStatus valueOf(byte b) {
|
||||
switch (b) {
|
||||
case 0x00:
|
||||
return SUCCESS;
|
||||
case (byte) 0xFF:
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
return new Socks5PasswordAuthStatus(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
public Socks5PasswordAuthStatus(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public Socks5PasswordAuthStatus(int byteValue, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return byteValue == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Socks5PasswordAuthStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((Socks5PasswordAuthStatus) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Socks5PasswordAuthStatus o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.socks.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.SocksRequest;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SocksRequest, providing common properties for
|
||||
* {@link Socks5InitRequest},
|
||||
* {@link Socks5AuthRequest},
|
||||
* {@link Socks5CmdRequest} and
|
||||
* {@link UnknownSocks5Request}.
|
||||
*
|
||||
* @see Socks5InitRequest
|
||||
* @see Socks5AuthRequest
|
||||
* @see Socks5CmdRequest
|
||||
* @see UnknownSocks5Request
|
||||
*/
|
||||
public abstract class Socks5Request extends SocksRequest {
|
||||
private final Socks5RequestType requestType;
|
||||
|
||||
protected Socks5Request(Socks5RequestType requestType) {
|
||||
super(SocksProtocolVersion.SOCKS5);
|
||||
if (requestType == null) {
|
||||
throw new NullPointerException("requestType");
|
||||
}
|
||||
this.requestType = requestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns socks request type
|
||||
*
|
||||
* @return socks request type
|
||||
*/
|
||||
public Socks5RequestType requestType() {
|
||||
return requestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* We could have defined this method in {@link SocksMessage} as a protected method, but we did not,
|
||||
* because we do not want to expose this method to users.
|
||||
*/
|
||||
abstract void encodeAsByteBuf(ByteBuf byteBuf);
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.socks.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.SocksResponse;
|
||||
|
||||
/**
|
||||
* An abstract class that defines a SocksResponse, providing common properties for
|
||||
* {@link Socks5InitResponse},
|
||||
* {@link Socks5AuthResponse},
|
||||
* {@link Socks5CmdResponse}
|
||||
* and {@link UnknownSocks5Response}.
|
||||
*
|
||||
* @see Socks5InitResponse
|
||||
* @see Socks5AuthResponse
|
||||
* @see Socks5CmdResponse
|
||||
* @see UnknownSocks5Response
|
||||
*/
|
||||
public abstract class Socks5Response extends SocksResponse {
|
||||
private final Socks5ResponseType responseType;
|
||||
|
||||
protected Socks5Response(Socks5ResponseType responseType) {
|
||||
super(SocksProtocolVersion.SOCKS5);
|
||||
if (responseType == null) {
|
||||
throw new NullPointerException("responseType");
|
||||
}
|
||||
this.responseType = responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns socks response type
|
||||
*
|
||||
* @return socks response type
|
||||
*/
|
||||
public Socks5ResponseType responseType() {
|
||||
return responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* We could have defined this method in {@link SocksMessage} as a protected method, but we did not,
|
||||
* because we do not want to expose this method to users.
|
||||
*/
|
||||
abstract void encodeAsByteBuf(ByteBuf byteBuf);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* Encodes a server-side {@link Socks5Message} into a {@link ByteBuf}.
|
||||
*/
|
||||
@Sharable
|
||||
public class Socks5ServerEncoder extends MessageToByteEncoder<Socks5Message> {
|
||||
|
||||
public static final Socks5ServerEncoder DEFAULT = new Socks5ServerEncoder(Socks5AddressEncoder.DEFAULT);
|
||||
|
||||
private final Socks5AddressEncoder addressEncoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default {@link Socks5AddressEncoder}.
|
||||
*/
|
||||
protected Socks5ServerEncoder() {
|
||||
this(Socks5AddressEncoder.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified {@link Socks5AddressEncoder}.
|
||||
*/
|
||||
public Socks5ServerEncoder(Socks5AddressEncoder addressEncoder) {
|
||||
if (addressEncoder == null) {
|
||||
throw new NullPointerException("addressEncoder");
|
||||
}
|
||||
|
||||
this.addressEncoder = addressEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Socks5AddressEncoder} of this encoder.
|
||||
*/
|
||||
protected final Socks5AddressEncoder addressEncoder() {
|
||||
return addressEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof Socks5InitialResponse) {
|
||||
encodeAuthMethodResponse((Socks5InitialResponse) msg, out);
|
||||
} else if (msg instanceof Socks5PasswordAuthResponse) {
|
||||
encodePasswordAuthResponse((Socks5PasswordAuthResponse) msg, out);
|
||||
} else if (msg instanceof Socks5CommandResponse) {
|
||||
encodeCommandResponse((Socks5CommandResponse) msg, out);
|
||||
} else {
|
||||
throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg));
|
||||
}
|
||||
}
|
||||
|
||||
private static void encodeAuthMethodResponse(Socks5InitialResponse msg, ByteBuf out) {
|
||||
out.writeByte(msg.version().byteValue());
|
||||
out.writeByte(msg.authMethod().byteValue());
|
||||
}
|
||||
|
||||
private static void encodePasswordAuthResponse(Socks5PasswordAuthResponse msg, ByteBuf out) {
|
||||
out.writeByte(0x01);
|
||||
out.writeByte(msg.status().byteValue());
|
||||
}
|
||||
|
||||
private void encodeCommandResponse(Socks5CommandResponse msg, ByteBuf out) throws Exception {
|
||||
out.writeByte(msg.version().byteValue());
|
||||
out.writeByte(msg.status().byteValue());
|
||||
out.writeByte(0x00);
|
||||
|
||||
final Socks5AddressType bndAddrType = msg.bndAddrType();
|
||||
out.writeByte(bndAddrType.byteValue());
|
||||
addressEncoder.encodeAddress(bndAddrType, msg.bndAddr(), out);
|
||||
|
||||
out.writeShort(msg.bndPort());
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
public enum Socks5SubnegotiationVersion {
|
||||
AUTH_PASSWORD((byte) 0x01),
|
||||
UNKNOWN((byte) 0xff);
|
||||
|
||||
private final byte b;
|
||||
|
||||
Socks5SubnegotiationVersion(byte b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static Socks5SubnegotiationVersion valueOf(byte b) {
|
||||
for (Socks5SubnegotiationVersion code : values()) {
|
||||
if (code.b == b) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
@ -22,23 +22,21 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks4CmdResponseDecoderTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Socks4CmdResponseDecoderTest.class);
|
||||
public class Socks4ClientDecoderTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Socks4ClientDecoderTest.class);
|
||||
|
||||
private static void testSocksCmdResponseDecoderWithDifferentParams(
|
||||
Socks4CmdStatus cmdStatus, String host, int port) {
|
||||
private static void test(Socks4CommandStatus cmdStatus, String dstAddr, int dstPort) {
|
||||
logger.debug("Testing cmdStatus: " + cmdStatus);
|
||||
Socks4Response msg = new Socks4CmdResponse(cmdStatus, host, port);
|
||||
Socks4CmdResponseDecoder decoder = new Socks4CmdResponseDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
Socks4CommandResponse msg = new DefaultSocks4CommandResponse(cmdStatus, dstAddr, dstPort);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ClientDecoder());
|
||||
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
|
||||
msg = embedder.readInbound();
|
||||
assertEquals(((Socks4CmdResponse) msg).cmdStatus(), cmdStatus);
|
||||
if (host != null) {
|
||||
assertEquals(((Socks4CmdResponse) msg).host(), host);
|
||||
assertEquals(msg.status(), cmdStatus);
|
||||
if (dstAddr != null) {
|
||||
assertEquals(msg.dstAddr(), dstAddr);
|
||||
}
|
||||
assertEquals(((Socks4CmdResponse) msg).port(), port);
|
||||
assertEquals(msg.dstPort(), dstPort);
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
@ -47,8 +45,9 @@ public class Socks4CmdResponseDecoderTest {
|
||||
*/
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoder() {
|
||||
for (Socks4CmdStatus cmdStatus : Socks4CmdStatus.values()) {
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, null, 0);
|
||||
}
|
||||
test(Socks4CommandStatus.IDENTD_AUTH_FAILURE, null, 0);
|
||||
test(Socks4CommandStatus.IDENTD_UNREACHABLE, null, 0);
|
||||
test(Socks4CommandStatus.REJECTED_OR_FAILED, null, 0);
|
||||
test(Socks4CommandStatus.SUCCESS, null, 0);
|
||||
}
|
||||
}
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v4;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
|
||||
final class Socks4CommonTestUtils {
|
||||
@ -27,15 +25,15 @@ final class Socks4CommonTestUtils {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Request msg) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
msg.encodeAsByteBuf(buf);
|
||||
embedder.writeInbound(buf);
|
||||
}
|
||||
|
||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Response msg) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
msg.encodeAsByteBuf(buf);
|
||||
embedder.writeInbound(buf);
|
||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Message msg) {
|
||||
EmbeddedChannel out;
|
||||
if (msg instanceof Socks4CommandRequest) {
|
||||
out = new EmbeddedChannel(Socks4ClientEncoder.INSTANCE);
|
||||
} else {
|
||||
out = new EmbeddedChannel(Socks4ServerEncoder.INSTANCE);
|
||||
}
|
||||
out.writeOutbound(msg);
|
||||
embedder.writeInbound(out.readOutbound());
|
||||
out.finish();
|
||||
}
|
||||
}
|
||||
|
@ -20,40 +20,41 @@ import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks4CmdRequestDecoderTest {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks4CmdRequestDecoderTest.class);
|
||||
public class Socks4ServerDecoderTest {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks4ServerDecoderTest.class);
|
||||
|
||||
private static void testSocksV4CmdRequestDecoderWithDifferentParams(String userId,
|
||||
Socks4CmdType cmdType,
|
||||
String host,
|
||||
int port) {
|
||||
logger.debug("Testing cmdType: " + cmdType + " userId: " + userId + " host: " + host +
|
||||
" port: " + port);
|
||||
Socks4CmdRequest msg = new Socks4CmdRequest(userId, cmdType, host, port);
|
||||
Socks4CmdRequestDecoder decoder = new Socks4CmdRequestDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
private static void test(String userId, Socks4CommandType type, String dstAddr, int dstPort) {
|
||||
logger.debug(
|
||||
"Testing type: " + type + " dstAddr: " + dstAddr + " dstPort: " + dstPort +
|
||||
" userId: " + userId);
|
||||
|
||||
Socks4CommandRequest msg = new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ServerDecoder());
|
||||
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
Object obj = embedder.readInbound();
|
||||
msg = (Socks4CmdRequest) obj;
|
||||
assertSame(msg.cmdType(), cmdType);
|
||||
msg = embedder.readInbound();
|
||||
assertSame(msg.type(), type);
|
||||
assertEquals(msg.dstAddr(), dstAddr);
|
||||
assertEquals(msg.dstPort(), dstPort);
|
||||
assertEquals(msg.userId(), userId);
|
||||
assertEquals(msg.host(), host);
|
||||
assertEquals(msg.port(), port);
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCmdRequestDecoder() {
|
||||
String[] hosts = {"127.0.0.1", };
|
||||
String[] userIds = {"test", };
|
||||
String[] hosts = { "127.0.0.1", };
|
||||
String[] userIds = { "test", };
|
||||
int[] ports = {1, 32769, 65535};
|
||||
for (Socks4CmdType cmdType : Socks4CmdType.values()) {
|
||||
|
||||
for (Socks4CommandType cmdType : Arrays.asList(Socks4CommandType.BIND,
|
||||
Socks4CommandType.CONNECT)) {
|
||||
for (String userId : userIds) {
|
||||
for (String host : hosts) {
|
||||
for (int port : ports) {
|
||||
testSocksV4CmdRequestDecoderWithDifferentParams(userId, cmdType, host, port);
|
||||
test(userId, cmdType, host, port);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,23 +19,23 @@ import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5CmdRequestTest {
|
||||
public class DefaultSocks5CommandRequestTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
try {
|
||||
new Socks5CmdRequest(null, Socks5AddressType.UNKNOWN, "", 1);
|
||||
new DefaultSocks5CommandRequest(null, Socks5AddressType.DOMAIN, "", 1);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.UNKNOWN, null, "", 1);
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, null, "", 1);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.UNKNOWN, Socks5AddressType.UNKNOWN, null, 1);
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, null, 1);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
@ -44,7 +44,7 @@ public class Socks5CmdRequestTest {
|
||||
@Test
|
||||
public void testIPv4CorrectAddress() {
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.IPv4, "54.54.1111.253", 1);
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.IPv4, "54.54.1111.253", 1);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
@ -53,7 +53,7 @@ public class Socks5CmdRequestTest {
|
||||
@Test
|
||||
public void testIPv6CorrectAddress() {
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.IPv6, "xxx:xxx:xxx", 1);
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.IPv6, "xxx:xxx:xxx", 1);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
@ -62,7 +62,7 @@ public class Socks5CmdRequestTest {
|
||||
@Test
|
||||
public void testIDNNotExceeds255CharsLimit() {
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||
@ -75,14 +75,14 @@ public class Socks5CmdRequestTest {
|
||||
@Test
|
||||
public void testValidPortRange() {
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||
"παράδειγμα.δοκιμήπαράδει", 0);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
|
||||
try {
|
||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
||||
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||
"παράδειγμα.δοκιμήπαράδει", 65536);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
@ -16,21 +16,20 @@
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5CmdResponseTest {
|
||||
public class DefaultSocks5CommandResponseTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
try {
|
||||
new Socks5CmdResponse(null, Socks5AddressType.UNKNOWN);
|
||||
new DefaultSocks5CommandResponse(null, Socks5AddressType.DOMAIN);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
try {
|
||||
new Socks5CmdResponse(Socks5CmdStatus.UNASSIGNED, null);
|
||||
new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, null);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
@ -41,12 +40,12 @@ public class Socks5CmdResponseTest {
|
||||
*/
|
||||
@Test
|
||||
public void testEmptyDomain() {
|
||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.DOMAIN);
|
||||
assertNull(socks5CmdResponse.host());
|
||||
assertEquals(0, socks5CmdResponse.port());
|
||||
ByteBuf buffer = Unpooled.buffer(20);
|
||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
||||
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN);
|
||||
assertNull(socks5CmdResponse.bndAddr());
|
||||
assertEquals(0, socks5CmdResponse.bndPort());
|
||||
|
||||
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||
byte[] expected = {
|
||||
0x05, // version
|
||||
0x00, // success reply
|
||||
@ -65,12 +64,12 @@ public class Socks5CmdResponseTest {
|
||||
*/
|
||||
@Test
|
||||
public void testIPv4Host() {
|
||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0.1", 80);
|
||||
assertEquals("127.0.0.1", socks5CmdResponse.host());
|
||||
assertEquals(80, socks5CmdResponse.port());
|
||||
ByteBuf buffer = Unpooled.buffer(20);
|
||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
||||
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0.1", 80);
|
||||
assertEquals("127.0.0.1", socks5CmdResponse.bndAddr());
|
||||
assertEquals(80, socks5CmdResponse.bndPort());
|
||||
|
||||
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||
byte[] expected = {
|
||||
0x05, // version
|
||||
0x00, // success reply
|
||||
@ -91,12 +90,12 @@ public class Socks5CmdResponseTest {
|
||||
*/
|
||||
@Test
|
||||
public void testEmptyBoundAddress() {
|
||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.DOMAIN, "", 80);
|
||||
assertEquals("", socks5CmdResponse.host());
|
||||
assertEquals(80, socks5CmdResponse.port());
|
||||
ByteBuf buffer = Unpooled.buffer(20);
|
||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
||||
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN, "", 80);
|
||||
assertEquals("", socks5CmdResponse.bndAddr());
|
||||
assertEquals(80, socks5CmdResponse.bndPort());
|
||||
|
||||
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||
byte[] expected = {
|
||||
0x05, // version
|
||||
0x00, // success reply
|
||||
@ -114,7 +113,8 @@ public class Socks5CmdResponseTest {
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInvalidBoundAddress() {
|
||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 1000);
|
||||
new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 1000);
|
||||
}
|
||||
|
||||
private static void assertByteBufEquals(byte[] expected, ByteBuf actual) {
|
||||
@ -127,13 +127,13 @@ public class Socks5CmdResponseTest {
|
||||
@Test
|
||||
public void testValidPortRange() {
|
||||
try {
|
||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 0);
|
||||
new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 0);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
|
||||
try {
|
||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 65536);
|
||||
new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 65536);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
@ -18,13 +18,13 @@ package io.netty.handler.codec.socksx.v5;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Socks5InitRequestTest {
|
||||
public class DefaultSocks5InitialRequestTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
public void testConstructorParamsAreNotEmpty() {
|
||||
try {
|
||||
new Socks5InitRequest(null);
|
||||
new DefaultSocks5InitialRequest();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,11 +18,11 @@ package io.netty.handler.codec.socksx.v5;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Socks5InitResponseTest {
|
||||
public class DefaultSocks5InitialResponseTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
try {
|
||||
new Socks5InitResponse(null);
|
||||
new DefaultSocks5InitialResponse(null);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
@ -18,16 +18,16 @@ package io.netty.handler.codec.socksx.v5;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Socks5AuthRequestTest {
|
||||
public class DefaultSocks5PasswordAuthRequestTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
try {
|
||||
new Socks5AuthRequest(null, "");
|
||||
new DefaultSocks5PasswordAuthRequest(null, "");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
try {
|
||||
new Socks5AuthRequest("", null);
|
||||
new DefaultSocks5PasswordAuthRequest("", null);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
@ -36,12 +36,12 @@ public class Socks5AuthRequestTest {
|
||||
@Test
|
||||
public void testUsernameOrPasswordIsNotAscii() {
|
||||
try {
|
||||
new Socks5AuthRequest("παράδειγμα.δοκιμή", "password");
|
||||
new DefaultSocks5PasswordAuthRequest("παράδειγμα.δοκιμή", "password");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
try {
|
||||
new Socks5AuthRequest("username", "παράδειγμα.δοκιμή");
|
||||
new DefaultSocks5PasswordAuthRequest("username", "παράδειγμα.δοκιμή");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
@ -50,7 +50,7 @@ public class Socks5AuthRequestTest {
|
||||
@Test
|
||||
public void testUsernameOrPasswordLengthIsLessThan255Chars() {
|
||||
try {
|
||||
new Socks5AuthRequest(
|
||||
new DefaultSocks5PasswordAuthRequest(
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
@ -64,7 +64,7 @@ public class Socks5AuthRequestTest {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
try {
|
||||
new Socks5AuthRequest("password",
|
||||
new DefaultSocks5PasswordAuthRequest("password",
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||
@ -77,5 +77,4 @@ public class Socks5AuthRequestTest {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,14 +18,13 @@ package io.netty.handler.codec.socksx.v5;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Socks5AuthResponseTest {
|
||||
public class DefaultSocks5PasswordAuthResponseTest {
|
||||
@Test
|
||||
public void testConstructorParamsAreNotNull() {
|
||||
try {
|
||||
new Socks5AuthResponse(null);
|
||||
new DefaultSocks5PasswordAuthResponse(null);
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof NullPointerException);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5CmdResponseDecoderTest {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks5CmdResponseDecoderTest.class);
|
||||
|
||||
private static void testSocksCmdResponseDecoderWithDifferentParams(
|
||||
Socks5CmdStatus cmdStatus, Socks5AddressType addressType, String host, int port) {
|
||||
logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType);
|
||||
Socks5Response msg = new Socks5CmdResponse(cmdStatus, addressType, host, port);
|
||||
Socks5CmdResponseDecoder decoder = new Socks5CmdResponseDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
if (addressType == Socks5AddressType.UNKNOWN) {
|
||||
assertTrue(embedder.readInbound() instanceof UnknownSocks5Response);
|
||||
} else {
|
||||
msg = embedder.readInbound();
|
||||
assertEquals(((Socks5CmdResponse) msg).cmdStatus(), cmdStatus);
|
||||
if (host != null) {
|
||||
assertEquals(((Socks5CmdResponse) msg).host(), host);
|
||||
}
|
||||
assertEquals(((Socks5CmdResponse) msg).port(), port);
|
||||
}
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that sent socks messages are decoded correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoder() {
|
||||
for (Socks5CmdStatus cmdStatus : Socks5CmdStatus.values()) {
|
||||
for (Socks5AddressType addressType : Socks5AddressType.values()) {
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, addressType, null, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that invalid bound host will fail with IllegalArgumentException during encoding.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInvalidAddress() {
|
||||
testSocksCmdResponseDecoderWithDifferentParams(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "1", 80);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that send socks messages are decoded correctly when bound host and port are set.
|
||||
*/
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoderIncludingHost() {
|
||||
for (Socks5CmdStatus cmdStatus : Socks5CmdStatus.values()) {
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, Socks5AddressType.IPv4,
|
||||
"127.0.0.1", 80);
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, Socks5AddressType.DOMAIN,
|
||||
"testDomain.com", 80);
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, Socks5AddressType.IPv6,
|
||||
"2001:db8:85a3:42:1000:8a2e:370:7334", 80);
|
||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, Socks5AddressType.IPv6,
|
||||
"1111:111:11:1:0:0:0:1", 80);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,35 +16,36 @@
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.junit.Test;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
|
||||
import java.net.IDN;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5CmdRequestDecoderTest {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks5CmdRequestDecoderTest.class);
|
||||
public class Socks5CommandRequestDecoderTest {
|
||||
private static final InternalLogger logger =
|
||||
InternalLoggerFactory.getInstance(Socks5CommandRequestDecoderTest.class);
|
||||
|
||||
private static void testSocksCmdRequestDecoderWithDifferentParams(Socks5CmdType cmdType,
|
||||
Socks5AddressType addressType,
|
||||
String host,
|
||||
int port) {
|
||||
logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: " + host +
|
||||
" port: " + port);
|
||||
Socks5CmdRequest msg = new Socks5CmdRequest(cmdType, addressType, host, port);
|
||||
Socks5CmdRequestDecoder decoder = new Socks5CmdRequestDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
if (msg.addressType() == Socks5AddressType.UNKNOWN) {
|
||||
assertTrue(embedder.readInbound() instanceof UnknownSocks5Request);
|
||||
} else {
|
||||
msg = embedder.readInbound();
|
||||
assertSame(msg.cmdType(), cmdType);
|
||||
assertSame(msg.addressType(), addressType);
|
||||
assertEquals(msg.host(), host);
|
||||
assertEquals(msg.port(), port);
|
||||
}
|
||||
private static void test(
|
||||
Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) {
|
||||
logger.debug(
|
||||
"Testing type: " + type + " dstAddrType: " + dstAddrType +
|
||||
" dstAddr: " + dstAddr + " dstPort: " + dstPort);
|
||||
|
||||
Socks5CommandRequest msg =
|
||||
new DefaultSocks5CommandRequest(type, dstAddrType, dstAddr, dstPort);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5CommandRequestDecoder());
|
||||
Socks5CommonTestUtils.writeFromClientToServer(embedder, msg);
|
||||
msg = embedder.readInbound();
|
||||
assertSame(msg.type(), type);
|
||||
assertSame(msg.dstAddrType(), dstAddrType);
|
||||
assertEquals(msg.dstAddr(), IDN.toASCII(dstAddr));
|
||||
assertEquals(msg.dstPort(), dstPort);
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
@ -52,10 +53,12 @@ public class Socks5CmdRequestDecoderTest {
|
||||
public void testCmdRequestDecoderIPv4() {
|
||||
String[] hosts = {"127.0.0.1", };
|
||||
int[] ports = {1, 32769, 65535 };
|
||||
for (Socks5CmdType cmdType : Socks5CmdType.values()) {
|
||||
for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND,
|
||||
Socks5CommandType.CONNECT,
|
||||
Socks5CommandType.UDP_ASSOCIATE)) {
|
||||
for (String host : hosts) {
|
||||
for (int port : ports) {
|
||||
testSocksCmdRequestDecoderWithDifferentParams(cmdType, Socks5AddressType.IPv4, host, port);
|
||||
test(cmdType, Socks5AddressType.IPv4, host, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,12 +66,15 @@ public class Socks5CmdRequestDecoderTest {
|
||||
|
||||
@Test
|
||||
public void testCmdRequestDecoderIPv6() {
|
||||
String[] hosts = { Socks5CommonUtils.ipv6toStr(IPAddressUtil.textToNumericFormatV6("::1"))};
|
||||
String[] hosts = {
|
||||
NetUtil.bytesToIpAddress(IPAddressUtil.textToNumericFormatV6("::1"), 0, 16) };
|
||||
int[] ports = {1, 32769, 65535};
|
||||
for (Socks5CmdType cmdType : Socks5CmdType.values()) {
|
||||
for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND,
|
||||
Socks5CommandType.CONNECT,
|
||||
Socks5CommandType.UDP_ASSOCIATE)) {
|
||||
for (String host : hosts) {
|
||||
for (int port : ports) {
|
||||
testSocksCmdRequestDecoderWithDifferentParams(cmdType, Socks5AddressType.IPv6, host, port);
|
||||
test(cmdType, Socks5AddressType.IPv6, host, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,21 +95,14 @@ public class Socks5CmdRequestDecoderTest {
|
||||
"실례.테스트",
|
||||
"உதாரணம்.பரிட்சை"};
|
||||
int[] ports = {1, 32769, 65535};
|
||||
for (Socks5CmdType cmdType : Socks5CmdType.values()) {
|
||||
for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND,
|
||||
Socks5CommandType.CONNECT,
|
||||
Socks5CommandType.UDP_ASSOCIATE)) {
|
||||
for (String host : hosts) {
|
||||
for (int port : ports) {
|
||||
testSocksCmdRequestDecoderWithDifferentParams(cmdType, Socks5AddressType.DOMAIN, host, port);
|
||||
test(cmdType, Socks5AddressType.DOMAIN, host, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCmdRequestDecoderUnknown() {
|
||||
String host = "google.com";
|
||||
int port = 80;
|
||||
for (Socks5CmdType cmdType : Socks5CmdType.values()) {
|
||||
testSocksCmdRequestDecoderWithDifferentParams(cmdType, Socks5AddressType.UNKNOWN, host, port);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5CommandResponseDecoderTest {
|
||||
|
||||
private static final InternalLogger logger =
|
||||
InternalLoggerFactory.getInstance(Socks5CommandResponseDecoderTest.class);
|
||||
|
||||
private static final Socks5CommandStatus[] STATUSES = {
|
||||
Socks5CommandStatus.ADDRESS_UNSUPPORTED,
|
||||
Socks5CommandStatus.COMMAND_UNSUPPORTED,
|
||||
Socks5CommandStatus.CONNECTION_REFUSED,
|
||||
Socks5CommandStatus.FAILURE,
|
||||
Socks5CommandStatus.FORBIDDEN,
|
||||
Socks5CommandStatus.HOST_UNREACHABLE,
|
||||
Socks5CommandStatus.NETWORK_UNREACHABLE,
|
||||
Socks5CommandStatus.SUCCESS,
|
||||
Socks5CommandStatus.TTL_EXPIRED
|
||||
};
|
||||
|
||||
private static void test(
|
||||
Socks5CommandStatus status, Socks5AddressType bndAddrType, String bndAddr, int bndPort) {
|
||||
logger.debug("Testing status: " + status + " bndAddrType: " + bndAddrType);
|
||||
Socks5CommandResponse msg =
|
||||
new DefaultSocks5CommandResponse(status, bndAddrType, bndAddr, bndPort);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5CommandResponseDecoder());
|
||||
Socks5CommonTestUtils.writeFromServerToClient(embedder, msg);
|
||||
msg = embedder.readInbound();
|
||||
assertEquals(msg.status(), status);
|
||||
if (bndAddr != null) {
|
||||
assertEquals(msg.bndAddr(), bndAddr);
|
||||
}
|
||||
assertEquals(msg.bndPort(), bndPort);
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that sent socks messages are decoded correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoder() {
|
||||
for (Socks5CommandStatus cmdStatus: STATUSES) {
|
||||
for (Socks5AddressType addressType : Arrays.asList(Socks5AddressType.DOMAIN,
|
||||
Socks5AddressType.IPv4,
|
||||
Socks5AddressType.IPv6)) {
|
||||
test(cmdStatus, addressType, null, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that invalid bound host will fail with IllegalArgumentException during encoding.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInvalidAddress() {
|
||||
test(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "1", 80);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that send socks messages are decoded correctly when bound host and port are set.
|
||||
*/
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoderIncludingHost() {
|
||||
for (Socks5CommandStatus cmdStatus : STATUSES) {
|
||||
test(cmdStatus, Socks5AddressType.IPv4,
|
||||
"127.0.0.1", 80);
|
||||
test(cmdStatus, Socks5AddressType.DOMAIN,
|
||||
"testDomain.com", 80);
|
||||
test(cmdStatus, Socks5AddressType.IPv6,
|
||||
"2001:db8:85a3:42:1000:8a2e:370:7334", 80);
|
||||
test(cmdStatus, Socks5AddressType.IPv6,
|
||||
"1111:111:11:1:0:0:0:1", 80);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.codec.socksx.v5;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
|
||||
final class Socks5CommonTestUtils {
|
||||
@ -27,15 +26,31 @@ final class Socks5CommonTestUtils {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks5Request msg) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
msg.encodeAsByteBuf(buf);
|
||||
embedder.writeInbound(buf);
|
||||
public static void writeFromClientToServer(EmbeddedChannel embedder, Socks5Message msg) {
|
||||
embedder.writeInbound(encodeClient(msg));
|
||||
}
|
||||
|
||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks5Response msg) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
msg.encodeAsByteBuf(buf);
|
||||
embedder.writeInbound(buf);
|
||||
public static void writeFromServerToClient(EmbeddedChannel embedder, Socks5Message msg) {
|
||||
embedder.writeInbound(encodeServer(msg));
|
||||
}
|
||||
|
||||
public static ByteBuf encodeClient(Socks5Message msg) {
|
||||
EmbeddedChannel out = new EmbeddedChannel(Socks5ClientEncoder.DEFAULT);
|
||||
out.writeOutbound(msg);
|
||||
|
||||
ByteBuf encoded = out.readOutbound();
|
||||
out.finish();
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
public static ByteBuf encodeServer(Socks5Message msg) {
|
||||
EmbeddedChannel out = new EmbeddedChannel(Socks5ServerEncoder.DEFAULT);
|
||||
out.writeOutbound(msg);
|
||||
|
||||
ByteBuf encoded = out.readOutbound();
|
||||
out.finish();
|
||||
|
||||
return encoded;
|
||||
}
|
||||
}
|
||||
|
@ -20,16 +20,15 @@ import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5AuthRequestDecoderTest {
|
||||
public class Socks5PasswordAuthRequestDecoderTest {
|
||||
|
||||
@Test
|
||||
public void testAuthRequestDecoder() {
|
||||
String username = "test";
|
||||
String password = "test";
|
||||
Socks5AuthRequest msg = new Socks5AuthRequest(username, password);
|
||||
Socks5AuthRequestDecoder decoder = new Socks5AuthRequestDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
Socks5PasswordAuthRequest msg = new DefaultSocks5PasswordAuthRequest(username, password);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthRequestDecoder());
|
||||
Socks5CommonTestUtils.writeFromClientToServer(embedder, msg);
|
||||
msg = embedder.readInbound();
|
||||
assertEquals(msg.username(), username);
|
||||
assertEquals(msg.username(), password);
|
@ -22,25 +22,23 @@ import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class Socks5AuthResponseDecoderTest {
|
||||
public class Socks5PasswordAuthResponseDecoderTest {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
|
||||
Socks5AuthResponseDecoderTest.class);
|
||||
Socks5PasswordAuthResponseDecoderTest.class);
|
||||
|
||||
private static void testSocksAuthResponseDecoderWithDifferentParams(Socks5AuthStatus authStatus) {
|
||||
logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus);
|
||||
Socks5AuthResponse msg = new Socks5AuthResponse(authStatus);
|
||||
Socks5AuthResponseDecoder decoder = new Socks5AuthResponseDecoder();
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||
private static void test(Socks5PasswordAuthStatus status) {
|
||||
logger.debug("Testing Socks5PasswordAuthResponseDecoder with status: " + status);
|
||||
Socks5PasswordAuthResponse msg = new DefaultSocks5PasswordAuthResponse(status);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthResponseDecoder());
|
||||
Socks5CommonTestUtils.writeFromServerToClient(embedder, msg);
|
||||
msg = embedder.readInbound();
|
||||
assertSame(msg.authStatus(), authStatus);
|
||||
assertSame(msg.status(), status);
|
||||
assertNull(embedder.readInbound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSocksCmdResponseDecoder() {
|
||||
for (Socks5AuthStatus authStatus: Socks5AuthStatus.values()) {
|
||||
testSocksAuthResponseDecoderWithDifferentParams(authStatus);
|
||||
}
|
||||
test(Socks5PasswordAuthStatus.SUCCESS);
|
||||
test(Socks5PasswordAuthStatus.FAILURE);
|
||||
}
|
||||
}
|
@ -329,8 +329,7 @@ final class ReplayingDecoderBuffer extends ByteBuf {
|
||||
|
||||
@Override
|
||||
public int bytesBefore(int length, byte value) {
|
||||
final int readerIndex = buffer.readerIndex();
|
||||
return bytesBefore(readerIndex, buffer.writerIndex() - readerIndex, value);
|
||||
return bytesBefore(buffer.readerIndex(), length, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.util;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
@ -395,8 +396,7 @@ public final class NetUtil {
|
||||
ipByteArray[byteIndex + 1] |= charValue & 15;
|
||||
}
|
||||
|
||||
static int getIntValue(char c) {
|
||||
|
||||
private static int getIntValue(char c) {
|
||||
switch (c) {
|
||||
case '0':
|
||||
return 0;
|
||||
@ -438,6 +438,58 @@ public final class NetUtil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 32-bit integer into an IPv4 address.
|
||||
*/
|
||||
public static String intToIpAddress(int i) {
|
||||
StringBuilder buf = new StringBuilder(15);
|
||||
buf.append(i >> 24 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(i >> 16 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(i >> 8 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(i & 0xff);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts 4-byte or 16-byte data into an IPv4 or IPv6 string respectively.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code length} is not {@code 4} nor {@code 16}
|
||||
*/
|
||||
public static String bytesToIpAddress(byte[] bytes, int offset, int length) {
|
||||
if (length == 4) {
|
||||
StringBuilder buf = new StringBuilder(15);
|
||||
|
||||
buf.append(bytes[offset ++] >> 24 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(bytes[offset ++] >> 16 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(bytes[offset ++] >> 8 & 0xff);
|
||||
buf.append('.');
|
||||
buf.append(bytes[offset] & 0xff);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
if (length == 16) {
|
||||
final StringBuilder sb = new StringBuilder(39);
|
||||
final int endOffset = offset + 14;
|
||||
|
||||
for (; offset < endOffset; offset += 2) {
|
||||
StringUtil.toHexString(sb, bytes, offset, 2);
|
||||
sb.append(':');
|
||||
}
|
||||
StringUtil.toHexString(sb, bytes, offset, 2);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("length: " + length + " (expected: 4 or 16)");
|
||||
}
|
||||
|
||||
public static boolean isValidIpV6Address(String ipAddress) {
|
||||
int length = ipAddress.length();
|
||||
boolean doubleColon = false;
|
||||
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.example.socksproxy;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.socksx.SocksProtocolVersion;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequestDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4MessageEncoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitRequestDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5MessageEncoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SocksPortUnificationServerHandler extends ByteToMessageDecoder {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
ChannelPipeline p = ctx.pipeline();
|
||||
SocksProtocolVersion version = SocksProtocolVersion.valueOf(in.readByte());
|
||||
System.out.println(version);
|
||||
in.resetReaderIndex();
|
||||
switch (version) {
|
||||
case SOCKS4a:
|
||||
p.addLast(new Socks4CmdRequestDecoder());
|
||||
p.addLast(Socks4MessageEncoder.INSTANCE);
|
||||
|
||||
break;
|
||||
case SOCKS5:
|
||||
p.addLast(new Socks5InitRequestDecoder());
|
||||
p.addLast(Socks5MessageEncoder.INSTANCE);
|
||||
|
||||
break;
|
||||
case UNKNOWN:
|
||||
in.clear();
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
p.addLast(SocksServerHandler.INSTANCE);
|
||||
p.remove(this);
|
||||
}
|
||||
}
|
@ -24,46 +24,47 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.socksx.SocksRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponse;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdStatus;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdResponse;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdStatus;
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandResponse;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
|
||||
import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandStatus;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import io.netty.util.concurrent.FutureListener;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksRequest> {
|
||||
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksMessage> {
|
||||
|
||||
private final Bootstrap b = new Bootstrap();
|
||||
|
||||
@Override
|
||||
public void channelRead0(final ChannelHandlerContext ctx, final SocksRequest message) throws Exception {
|
||||
if (message instanceof Socks4CmdRequest) {
|
||||
final Socks4CmdRequest request = (Socks4CmdRequest) message;
|
||||
public void channelRead0(final ChannelHandlerContext ctx, final SocksMessage message) throws Exception {
|
||||
if (message instanceof Socks4CommandRequest) {
|
||||
final Socks4CommandRequest request = (Socks4CommandRequest) message;
|
||||
Promise<Channel> promise = ctx.executor().newPromise();
|
||||
promise.addListener(
|
||||
new GenericFutureListener<Future<Channel>>() {
|
||||
new FutureListener<Channel>() {
|
||||
@Override
|
||||
public void operationComplete(final Future<Channel> future) throws Exception {
|
||||
final Channel outboundChannel = future.getNow();
|
||||
if (future.isSuccess()) {
|
||||
ctx.channel().writeAndFlush(new Socks4CmdResponse(Socks4CmdStatus.SUCCESS))
|
||||
.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture channelFuture) {
|
||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||
}
|
||||
});
|
||||
ChannelFuture responseFuture = ctx.channel().writeAndFlush(
|
||||
new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS));
|
||||
|
||||
responseFuture.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture channelFuture) {
|
||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ctx.channel().writeAndFlush(
|
||||
new Socks4CmdResponse(Socks4CmdStatus.REJECTED_OR_FAILED)
|
||||
);
|
||||
new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED));
|
||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||
}
|
||||
}
|
||||
@ -76,7 +77,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.handler(new DirectClientHandler(promise));
|
||||
|
||||
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
|
||||
b.connect(request.dstAddr(), request.dstPort()).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
@ -84,35 +85,36 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
||||
} else {
|
||||
// Close the connection if the connection attempt has failed.
|
||||
ctx.channel().writeAndFlush(
|
||||
new Socks4CmdResponse(Socks4CmdStatus.REJECTED_OR_FAILED)
|
||||
new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED)
|
||||
);
|
||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (message instanceof Socks5CmdRequest) {
|
||||
final Socks5CmdRequest request = (Socks5CmdRequest) message;
|
||||
} else if (message instanceof Socks5CommandRequest) {
|
||||
final Socks5CommandRequest request = (Socks5CommandRequest) message;
|
||||
Promise<Channel> promise = ctx.executor().newPromise();
|
||||
promise.addListener(
|
||||
new GenericFutureListener<Future<Channel>>() {
|
||||
new FutureListener<Channel>() {
|
||||
@Override
|
||||
public void operationComplete(final Future<Channel> future) throws Exception {
|
||||
final Channel outboundChannel = future.getNow();
|
||||
if (future.isSuccess()) {
|
||||
ctx.channel().writeAndFlush(
|
||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, request.addressType())
|
||||
).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture channelFuture) {
|
||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||
}
|
||||
}
|
||||
);
|
||||
ChannelFuture responseFuture =
|
||||
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.SUCCESS, request.dstAddrType()));
|
||||
|
||||
responseFuture.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture channelFuture) {
|
||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ctx.channel().writeAndFlush(
|
||||
new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType()));
|
||||
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
||||
Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||
}
|
||||
}
|
||||
@ -125,7 +127,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.handler(new DirectClientHandler(promise));
|
||||
|
||||
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
|
||||
b.connect(request.dstAddr(), request.dstPort()).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
@ -133,7 +135,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
||||
} else {
|
||||
// Close the connection if the connection attempt has failed.
|
||||
ctx.channel().writeAndFlush(
|
||||
new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType()));
|
||||
new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||
}
|
||||
}
|
||||
|
@ -18,31 +18,32 @@ package io.netty.example.socksproxy;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.socksx.SocksRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdType;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthScheme;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequestDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitResponse;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5Request;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthResponse;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthStatus;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CmdType;
|
||||
import io.netty.handler.codec.socksx.SocksMessage;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandType;
|
||||
import io.netty.handler.codec.socksx.v5.DefaultSocks5InitialResponse;
|
||||
import io.netty.handler.codec.socksx.v5.DefaultSocks5PasswordAuthResponse;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5AuthMethod;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5InitialRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5CommandType;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequest;
|
||||
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthStatus;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksRequest> {
|
||||
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksMessage> {
|
||||
|
||||
public static final SocksServerHandler INSTANCE = new SocksServerHandler();
|
||||
|
||||
private SocksServerHandler() { }
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception {
|
||||
switch (socksRequest.protocolVersion()) {
|
||||
public void channelRead0(ChannelHandlerContext ctx, SocksMessage socksRequest) throws Exception {
|
||||
switch (socksRequest.version()) {
|
||||
case SOCKS4a:
|
||||
Socks4CmdRequest socksV4CmdRequest = (Socks4CmdRequest) socksRequest;
|
||||
if (socksV4CmdRequest.cmdType() == Socks4CmdType.CONNECT) {
|
||||
Socks4CommandRequest socksV4CmdRequest = (Socks4CommandRequest) socksRequest;
|
||||
if (socksV4CmdRequest.type() == Socks4CommandType.CONNECT) {
|
||||
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
||||
ctx.pipeline().remove(this);
|
||||
ctx.fireChannelRead(socksRequest);
|
||||
@ -51,32 +52,26 @@ public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksR
|
||||
}
|
||||
break;
|
||||
case SOCKS5:
|
||||
switch (((Socks5Request) socksRequest).requestType()) {
|
||||
case INIT: {
|
||||
// auth support example
|
||||
//ctx.pipeline().addFirst(new SocksV5AuthRequestDecoder());
|
||||
//ctx.write(new SocksV5InitResponse(SocksV5AuthScheme.AUTH_PASSWORD));
|
||||
ctx.pipeline().addFirst(new Socks5CmdRequestDecoder());
|
||||
ctx.write(new Socks5InitResponse(Socks5AuthScheme.NO_AUTH));
|
||||
break;
|
||||
}
|
||||
case AUTH:
|
||||
ctx.pipeline().addFirst(new Socks5CmdRequestDecoder());
|
||||
ctx.write(new Socks5AuthResponse(Socks5AuthStatus.SUCCESS));
|
||||
break;
|
||||
case CMD:
|
||||
Socks5CmdRequest socks5CmdRequest = (Socks5CmdRequest) socksRequest;
|
||||
if (socks5CmdRequest.cmdType() == Socks5CmdType.CONNECT) {
|
||||
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
||||
ctx.pipeline().remove(this);
|
||||
ctx.fireChannelRead(socksRequest);
|
||||
} else {
|
||||
ctx.close();
|
||||
}
|
||||
break;
|
||||
case UNKNOWN:
|
||||
if (socksRequest instanceof Socks5InitialRequest) {
|
||||
// auth support example
|
||||
//ctx.pipeline().addFirst(new Socks5PasswordAuthRequestDecoder());
|
||||
//ctx.write(new DefaultSocks5AuthMethodResponse(Socks5AuthMethod.PASSWORD));
|
||||
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
|
||||
ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH));
|
||||
} else if (socksRequest instanceof Socks5PasswordAuthRequest) {
|
||||
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
|
||||
ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS));
|
||||
} else if (socksRequest instanceof Socks5CommandRequest) {
|
||||
Socks5CommandRequest socks5CmdRequest = (Socks5CommandRequest) socksRequest;
|
||||
if (socks5CmdRequest.type() == Socks5CommandType.CONNECT) {
|
||||
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
||||
ctx.pipeline().remove(this);
|
||||
ctx.fireChannelRead(socksRequest);
|
||||
} else {
|
||||
ctx.close();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ctx.close();
|
||||
}
|
||||
break;
|
||||
case UNKNOWN:
|
||||
|
@ -16,16 +16,17 @@
|
||||
package io.netty.example.socksproxy;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.socksx.SocksPortUnificationServerHandler;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
public final class SocksServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
@Override
|
||||
public void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
ChannelPipeline p = socketChannel.pipeline();
|
||||
p.addFirst(new LoggingHandler(LogLevel.DEBUG));
|
||||
p.addLast(new SocksPortUnificationServerHandler());
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(
|
||||
new LoggingHandler(LogLevel.DEBUG),
|
||||
new SocksPortUnificationServerHandler(),
|
||||
SocksServerHandler.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ package io.netty.handler.proxy;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponse;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponseDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdStatus;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CmdType;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4MessageEncoder;
|
||||
import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandRequest;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4ClientEncoder;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandResponse;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
|
||||
import io.netty.handler.codec.socksx.v4.Socks4CommandType;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
@ -69,13 +69,13 @@ public final class Socks4ProxyHandler extends ProxyHandler {
|
||||
ChannelPipeline p = ctx.pipeline();
|
||||
String name = ctx.name();
|
||||
|
||||
Socks4CmdResponseDecoder decoder = new Socks4CmdResponseDecoder();
|
||||
Socks4ClientDecoder decoder = new Socks4ClientDecoder();
|
||||
p.addBefore(name, null, decoder);
|
||||
|
||||
decoderName = p.context(decoder).name();
|
||||
encoderName = decoderName + ".encoder";
|
||||
|
||||
p.addBefore(name, encoderName, Socks4MessageEncoder.INSTANCE);
|
||||
p.addBefore(name, encoderName, Socks4ClientEncoder.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,18 +99,18 @@ public final class Socks4ProxyHandler extends ProxyHandler {
|
||||
} else {
|
||||
rhost = raddr.getAddress().getHostAddress();
|
||||
}
|
||||
return new Socks4CmdRequest(
|
||||
username != null? username : "", Socks4CmdType.CONNECT, rhost, raddr.getPort());
|
||||
return new DefaultSocks4CommandRequest(
|
||||
Socks4CommandType.CONNECT, rhost, raddr.getPort(), username != null? username : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception {
|
||||
final Socks4CmdResponse res = (Socks4CmdResponse) response;
|
||||
final Socks4CmdStatus status = res.cmdStatus();
|
||||
if (status == Socks4CmdStatus.SUCCESS) {
|
||||
final Socks4CommandResponse res = (Socks4CommandResponse) response;
|
||||
final Socks4CommandStatus status = res.status();
|
||||
if (status == Socks4CommandStatus.SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new ProxyConnectException(exceptionMessage("cmdStatus: " + status));
|
||||
throw new ProxyConnectException(exceptionMessage("status: " + status));
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user