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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -16,27 +16,25 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.socksx;
|
package io.netty.handler.codec.socksx;
|
||||||
|
|
||||||
public enum SocksProtocolVersion {
|
import io.netty.handler.codec.DecoderResult;
|
||||||
SOCKS4a((byte) 0x04),
|
|
||||||
SOCKS5((byte) 0x05),
|
|
||||||
UNKNOWN((byte) 0xff);
|
|
||||||
|
|
||||||
private final byte b;
|
/**
|
||||||
|
* An abstract {@link SocksMessage}.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSocksMessage implements SocksMessage {
|
||||||
|
|
||||||
SocksProtocolVersion(byte b) {
|
private DecoderResult decoderResult = DecoderResult.SUCCESS;
|
||||||
this.b = b;
|
|
||||||
|
@Override
|
||||||
|
public DecoderResult decoderResult() {
|
||||||
|
return decoderResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SocksProtocolVersion valueOf(byte b) {
|
@Override
|
||||||
for (SocksProtocolVersion code : values()) {
|
public void setDecoderResult(DecoderResult decoderResult) {
|
||||||
if (code.b == b) {
|
if (decoderResult == null) {
|
||||||
return code;
|
throw new NullPointerException("decoderResult");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return UNKNOWN;
|
this.decoderResult = decoderResult;
|
||||||
}
|
|
||||||
|
|
||||||
public byte byteValue() {
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,40 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.socksx;
|
package io.netty.handler.codec.socksx;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.DecoderResultProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class that defines a SocksMessage, providing common properties for
|
* An interface that all SOCKS protocol messages implement.
|
||||||
* {@link SocksRequest} and {@link SocksResponse}.
|
|
||||||
*/
|
*/
|
||||||
public abstract class SocksMessage {
|
public interface SocksMessage extends DecoderResultProvider {
|
||||||
private final SocksMessageType type;
|
|
||||||
private final SocksProtocolVersion protocolVersion;
|
|
||||||
|
|
||||||
protected SocksMessage(SocksProtocolVersion protocolVersion, SocksMessageType type) {
|
|
||||||
if (protocolVersion == null) {
|
|
||||||
throw new NullPointerException("protocolVersion");
|
|
||||||
}
|
|
||||||
if (type == null) {
|
|
||||||
throw new NullPointerException("type");
|
|
||||||
}
|
|
||||||
this.protocolVersion = protocolVersion;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link SocksMessageType} of this {@link SocksMessage}
|
* Returns the protocol version of this message.
|
||||||
*
|
|
||||||
* @return The {@link SocksMessageType} of this {@link SocksMessage}
|
|
||||||
*/
|
*/
|
||||||
public SocksMessageType type() {
|
SocksVersion version();
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link SocksProtocolVersion} of this {@link SocksMessage}
|
|
||||||
*
|
|
||||||
* @return The {@link SocksProtocolVersion} of this {@link SocksMessage}
|
|
||||||
*/
|
|
||||||
public SocksProtocolVersion protocolVersion() {
|
|
||||||
return protocolVersion;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
* 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
|
* 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
|
* License for the specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.netty.handler.codec.socksx.v4;
|
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.
|
* An abstract {@link Socks4Message}.
|
||||||
*
|
|
||||||
* @see Socks4CmdResponseDecoder
|
|
||||||
*/
|
*/
|
||||||
public final class UnknownSocks4Response extends Socks4Response {
|
public abstract class AbstractSocks4Message extends AbstractSocksMessage implements Socks4Message {
|
||||||
|
|
||||||
public static final UnknownSocks4Response INSTANCE = new UnknownSocks4Response();
|
|
||||||
|
|
||||||
private UnknownSocks4Response() { }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void encodeAsByteBuf(ByteBuf byteBuf) {
|
public final SocksVersion version() {
|
||||||
// NOOP
|
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;
|
package io.netty.handler.codec.socksx.v4;
|
||||||
|
|
||||||
public enum Socks4CmdType {
|
/**
|
||||||
CONNECT((byte) 0x01),
|
* A SOCKS4a {@code CONNECT} or {@code BIND} request.
|
||||||
BIND((byte) 0x02),
|
*/
|
||||||
UNKNOWN((byte) 0xff);
|
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()) {
|
* Returns the {@code DSTIP} field of this request.
|
||||||
if (code.b == b) {
|
*/
|
||||||
return code;
|
String dstAddr();
|
||||||
}
|
|
||||||
}
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
package io.netty.handler.codec.socksx.v4;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unknown socks request.
|
* A SOCKS4a response.
|
||||||
*
|
|
||||||
* @see Socks4CmdRequestDecoder
|
|
||||||
*/
|
*/
|
||||||
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) {
|
* Returns the {@code DSTPORT} field of this response.
|
||||||
// NOOP
|
*/
|
||||||
}
|
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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -14,14 +14,13 @@
|
|||||||
* under the License.
|
* 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 {
|
public interface Socks4Message extends SocksMessage {
|
||||||
INIT,
|
// Tag interface
|
||||||
AUTH,
|
|
||||||
CMD,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
}
|
@ -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;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
public enum Socks5AddressType {
|
/**
|
||||||
IPv4((byte) 0x01),
|
* The type of address in {@link Socks5CommandRequest} and {@link Socks5CommandResponse}.
|
||||||
DOMAIN((byte) 0x03),
|
*/
|
||||||
IPv6((byte) 0x04),
|
public class Socks5AddressType implements Comparable<Socks5AddressType> {
|
||||||
UNKNOWN((byte) 0xff);
|
|
||||||
|
|
||||||
private final byte b;
|
public static final Socks5AddressType IPv4 = new Socks5AddressType(0x01, "IPv4");
|
||||||
|
public static final Socks5AddressType DOMAIN = new Socks5AddressType(0x03, "DOMAIN");
|
||||||
Socks5AddressType(byte b) {
|
public static final Socks5AddressType IPv6 = new Socks5AddressType(0x04, "IPv6");
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Socks5AddressType valueOf(byte b) {
|
public static Socks5AddressType valueOf(byte b) {
|
||||||
for (Socks5AddressType code : values()) {
|
switch (b) {
|
||||||
if (code.b == b) {
|
case 0x01:
|
||||||
return code;
|
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() {
|
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;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unknown socks request.
|
* A SOCKS5 request detail message, as defined in
|
||||||
*
|
* <a href="http://tools.ietf.org/html/rfc1928#section-4">the section 4, RFC1928</a>.
|
||||||
* @see Socks5InitRequestDecoder
|
|
||||||
* @see Socks5AuthRequestDecoder
|
|
||||||
* @see Socks5CmdRequestDecoder
|
|
||||||
*/
|
*/
|
||||||
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) {
|
* Returns the {@code DST.ADDR} field of this request.
|
||||||
// NOOP
|
*/
|
||||||
}
|
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;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unknown socks response.
|
* 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>.
|
||||||
* @see Socks5InitResponseDecoder
|
|
||||||
* @see Socks5AuthResponseDecoder
|
|
||||||
* @see Socks5CmdResponseDecoder
|
|
||||||
*/
|
*/
|
||||||
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) {
|
* Returns the {@code BND.ADDR} field of this response.
|
||||||
// NOOP
|
*/
|
||||||
}
|
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,
|
* 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
|
* 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;
|
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 {
|
public interface Socks5Message extends SocksMessage {
|
||||||
INIT,
|
// Tag interface
|
||||||
AUTH,
|
|
||||||
CMD,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
}
|
@ -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.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks4CmdResponseDecoderTest {
|
public class Socks4ClientDecoderTest {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Socks4CmdResponseDecoderTest.class);
|
private static final Logger logger = LoggerFactory.getLogger(Socks4ClientDecoderTest.class);
|
||||||
|
|
||||||
private static void testSocksCmdResponseDecoderWithDifferentParams(
|
private static void test(Socks4CommandStatus cmdStatus, String dstAddr, int dstPort) {
|
||||||
Socks4CmdStatus cmdStatus, String host, int port) {
|
|
||||||
logger.debug("Testing cmdStatus: " + cmdStatus);
|
logger.debug("Testing cmdStatus: " + cmdStatus);
|
||||||
Socks4Response msg = new Socks4CmdResponse(cmdStatus, host, port);
|
Socks4CommandResponse msg = new DefaultSocks4CommandResponse(cmdStatus, dstAddr, dstPort);
|
||||||
Socks4CmdResponseDecoder decoder = new Socks4CmdResponseDecoder();
|
EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ClientDecoder());
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
|
||||||
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||||
|
|
||||||
msg = embedder.readInbound();
|
msg = embedder.readInbound();
|
||||||
assertEquals(((Socks4CmdResponse) msg).cmdStatus(), cmdStatus);
|
assertEquals(msg.status(), cmdStatus);
|
||||||
if (host != null) {
|
if (dstAddr != null) {
|
||||||
assertEquals(((Socks4CmdResponse) msg).host(), host);
|
assertEquals(msg.dstAddr(), dstAddr);
|
||||||
}
|
}
|
||||||
assertEquals(((Socks4CmdResponse) msg).port(), port);
|
assertEquals(msg.dstPort(), dstPort);
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +45,9 @@ public class Socks4CmdResponseDecoderTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSocksCmdResponseDecoder() {
|
public void testSocksCmdResponseDecoder() {
|
||||||
for (Socks4CmdStatus cmdStatus : Socks4CmdStatus.values()) {
|
test(Socks4CommandStatus.IDENTD_AUTH_FAILURE, null, 0);
|
||||||
testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, 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;
|
package io.netty.handler.codec.socksx.v4;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
|
||||||
final class Socks4CommonTestUtils {
|
final class Socks4CommonTestUtils {
|
||||||
@ -27,15 +25,15 @@ final class Socks4CommonTestUtils {
|
|||||||
//NOOP
|
//NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Request msg) {
|
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Message msg) {
|
||||||
ByteBuf buf = Unpooled.buffer();
|
EmbeddedChannel out;
|
||||||
msg.encodeAsByteBuf(buf);
|
if (msg instanceof Socks4CommandRequest) {
|
||||||
embedder.writeInbound(buf);
|
out = new EmbeddedChannel(Socks4ClientEncoder.INSTANCE);
|
||||||
}
|
} else {
|
||||||
|
out = new EmbeddedChannel(Socks4ServerEncoder.INSTANCE);
|
||||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Response msg) {
|
}
|
||||||
ByteBuf buf = Unpooled.buffer();
|
out.writeOutbound(msg);
|
||||||
msg.encodeAsByteBuf(buf);
|
embedder.writeInbound(out.readOutbound());
|
||||||
embedder.writeInbound(buf);
|
out.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,40 +20,41 @@ import io.netty.util.internal.logging.InternalLogger;
|
|||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks4CmdRequestDecoderTest {
|
public class Socks4ServerDecoderTest {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks4CmdRequestDecoderTest.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks4ServerDecoderTest.class);
|
||||||
|
|
||||||
private static void testSocksV4CmdRequestDecoderWithDifferentParams(String userId,
|
private static void test(String userId, Socks4CommandType type, String dstAddr, int dstPort) {
|
||||||
Socks4CmdType cmdType,
|
logger.debug(
|
||||||
String host,
|
"Testing type: " + type + " dstAddr: " + dstAddr + " dstPort: " + dstPort +
|
||||||
int port) {
|
" userId: " + userId);
|
||||||
logger.debug("Testing cmdType: " + cmdType + " userId: " + userId + " host: " + host +
|
|
||||||
" port: " + port);
|
Socks4CommandRequest msg = new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId);
|
||||||
Socks4CmdRequest msg = new Socks4CmdRequest(userId, cmdType, host, port);
|
EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ServerDecoder());
|
||||||
Socks4CmdRequestDecoder decoder = new Socks4CmdRequestDecoder();
|
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
|
||||||
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
||||||
Object obj = embedder.readInbound();
|
msg = embedder.readInbound();
|
||||||
msg = (Socks4CmdRequest) obj;
|
assertSame(msg.type(), type);
|
||||||
assertSame(msg.cmdType(), cmdType);
|
assertEquals(msg.dstAddr(), dstAddr);
|
||||||
|
assertEquals(msg.dstPort(), dstPort);
|
||||||
assertEquals(msg.userId(), userId);
|
assertEquals(msg.userId(), userId);
|
||||||
assertEquals(msg.host(), host);
|
|
||||||
assertEquals(msg.port(), port);
|
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCmdRequestDecoder() {
|
public void testCmdRequestDecoder() {
|
||||||
String[] hosts = {"127.0.0.1", };
|
String[] hosts = { "127.0.0.1", };
|
||||||
String[] userIds = {"test", };
|
String[] userIds = { "test", };
|
||||||
int[] ports = {1, 32769, 65535};
|
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 userId : userIds) {
|
||||||
for (String host : hosts) {
|
for (String host : hosts) {
|
||||||
for (int port : ports) {
|
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.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks5CmdRequestTest {
|
public class DefaultSocks5CommandRequestTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotNull() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(null, Socks5AddressType.UNKNOWN, "", 1);
|
new DefaultSocks5CommandRequest(null, Socks5AddressType.DOMAIN, "", 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.UNKNOWN, null, "", 1);
|
new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, null, "", 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.UNKNOWN, Socks5AddressType.UNKNOWN, null, 1);
|
new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, null, 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ public class Socks5CmdRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testIPv4CorrectAddress() {
|
public void testIPv4CorrectAddress() {
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ public class Socks5CmdRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testIPv6CorrectAddress() {
|
public void testIPv6CorrectAddress() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.IPv6, "xxx:xxx:xxx", 1);
|
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.IPv6, "xxx:xxx:xxx", 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ public class Socks5CmdRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testIDNNotExceeds255CharsLimit() {
|
public void testIDNNotExceeds255CharsLimit() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||||
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
"παράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμήπαράδειγμα.δοκιμή" +
|
||||||
@ -75,14 +75,14 @@ public class Socks5CmdRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testValidPortRange() {
|
public void testValidPortRange() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||||
"παράδειγμα.δοκιμήπαράδει", 0);
|
"παράδειγμα.δοκιμήπαράδει", 0);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new Socks5CmdRequest(Socks5CmdType.BIND, Socks5AddressType.DOMAIN,
|
new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN,
|
||||||
"παράδειγμα.δοκιμήπαράδει", 65536);
|
"παράδειγμα.δοκιμήπαράδει", 65536);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
@ -16,21 +16,20 @@
|
|||||||
package io.netty.handler.codec.socksx.v5;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks5CmdResponseTest {
|
public class DefaultSocks5CommandResponseTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotNull() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdResponse(null, Socks5AddressType.UNKNOWN);
|
new DefaultSocks5CommandResponse(null, Socks5AddressType.DOMAIN);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.UNASSIGNED, null);
|
new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
@ -41,12 +40,12 @@ public class Socks5CmdResponseTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyDomain() {
|
public void testEmptyDomain() {
|
||||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.DOMAIN);
|
Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN);
|
||||||
assertNull(socks5CmdResponse.host());
|
assertNull(socks5CmdResponse.bndAddr());
|
||||||
assertEquals(0, socks5CmdResponse.port());
|
assertEquals(0, socks5CmdResponse.bndPort());
|
||||||
ByteBuf buffer = Unpooled.buffer(20);
|
|
||||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||||
byte[] expected = {
|
byte[] expected = {
|
||||||
0x05, // version
|
0x05, // version
|
||||||
0x00, // success reply
|
0x00, // success reply
|
||||||
@ -65,12 +64,12 @@ public class Socks5CmdResponseTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testIPv4Host() {
|
public void testIPv4Host() {
|
||||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0.1", 80);
|
Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0.1", 80);
|
||||||
assertEquals("127.0.0.1", socks5CmdResponse.host());
|
assertEquals("127.0.0.1", socks5CmdResponse.bndAddr());
|
||||||
assertEquals(80, socks5CmdResponse.port());
|
assertEquals(80, socks5CmdResponse.bndPort());
|
||||||
ByteBuf buffer = Unpooled.buffer(20);
|
|
||||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||||
byte[] expected = {
|
byte[] expected = {
|
||||||
0x05, // version
|
0x05, // version
|
||||||
0x00, // success reply
|
0x00, // success reply
|
||||||
@ -91,12 +90,12 @@ public class Socks5CmdResponseTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyBoundAddress() {
|
public void testEmptyBoundAddress() {
|
||||||
Socks5CmdResponse socks5CmdResponse = new Socks5CmdResponse(
|
Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse(
|
||||||
Socks5CmdStatus.SUCCESS, Socks5AddressType.DOMAIN, "", 80);
|
Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN, "", 80);
|
||||||
assertEquals("", socks5CmdResponse.host());
|
assertEquals("", socks5CmdResponse.bndAddr());
|
||||||
assertEquals(80, socks5CmdResponse.port());
|
assertEquals(80, socks5CmdResponse.bndPort());
|
||||||
ByteBuf buffer = Unpooled.buffer(20);
|
|
||||||
socks5CmdResponse.encodeAsByteBuf(buffer);
|
ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse);
|
||||||
byte[] expected = {
|
byte[] expected = {
|
||||||
0x05, // version
|
0x05, // version
|
||||||
0x00, // success reply
|
0x00, // success reply
|
||||||
@ -114,7 +113,8 @@ public class Socks5CmdResponseTest {
|
|||||||
*/
|
*/
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testInvalidBoundAddress() {
|
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) {
|
private static void assertByteBufEquals(byte[] expected, ByteBuf actual) {
|
||||||
@ -127,13 +127,13 @@ public class Socks5CmdResponseTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testValidPortRange() {
|
public void testValidPortRange() {
|
||||||
try {
|
try {
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 0);
|
new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 0);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 65536);
|
new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 65536);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
@ -18,13 +18,13 @@ package io.netty.handler.codec.socksx.v5;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class Socks5InitRequestTest {
|
public class DefaultSocks5InitialRequestTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotEmpty() {
|
||||||
try {
|
try {
|
||||||
new Socks5InitRequest(null);
|
new DefaultSocks5InitialRequest();
|
||||||
} catch (Exception e) {
|
} 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 org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class Socks5InitResponseTest {
|
public class DefaultSocks5InitialResponseTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotNull() {
|
||||||
try {
|
try {
|
||||||
new Socks5InitResponse(null);
|
new DefaultSocks5InitialResponse(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
@ -18,16 +18,16 @@ package io.netty.handler.codec.socksx.v5;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class Socks5AuthRequestTest {
|
public class DefaultSocks5PasswordAuthRequestTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotNull() {
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest(null, "");
|
new DefaultSocks5PasswordAuthRequest(null, "");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest("", null);
|
new DefaultSocks5PasswordAuthRequest("", null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
assertTrue(e instanceof NullPointerException);
|
||||||
}
|
}
|
||||||
@ -36,12 +36,12 @@ public class Socks5AuthRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testUsernameOrPasswordIsNotAscii() {
|
public void testUsernameOrPasswordIsNotAscii() {
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest("παράδειγμα.δοκιμή", "password");
|
new DefaultSocks5PasswordAuthRequest("παράδειγμα.δοκιμή", "password");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest("username", "παράδειγμα.δοκιμή");
|
new DefaultSocks5PasswordAuthRequest("username", "παράδειγμα.δοκιμή");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ public class Socks5AuthRequestTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testUsernameOrPasswordLengthIsLessThan255Chars() {
|
public void testUsernameOrPasswordLengthIsLessThan255Chars() {
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest(
|
new DefaultSocks5PasswordAuthRequest(
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
@ -64,7 +64,7 @@ public class Socks5AuthRequestTest {
|
|||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Socks5AuthRequest("password",
|
new DefaultSocks5PasswordAuthRequest("password",
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
"passwordpasswordpasswordpasswordpasswordpasswordpassword" +
|
||||||
@ -77,5 +77,4 @@ public class Socks5AuthRequestTest {
|
|||||||
assertTrue(e instanceof IllegalArgumentException);
|
assertTrue(e instanceof IllegalArgumentException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -18,14 +18,13 @@ package io.netty.handler.codec.socksx.v5;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class Socks5AuthResponseTest {
|
public class DefaultSocks5PasswordAuthResponseTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParamsAreNotNull() {
|
public void testConstructorParamsAreNotNull() {
|
||||||
try {
|
try {
|
||||||
new Socks5AuthResponse(null);
|
new DefaultSocks5PasswordAuthResponse(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e instanceof NullPointerException);
|
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;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
import io.netty.util.NetUtil;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sun.net.util.IPAddressUtil;
|
import sun.net.util.IPAddressUtil;
|
||||||
|
|
||||||
|
import java.net.IDN;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks5CmdRequestDecoderTest {
|
public class Socks5CommandRequestDecoderTest {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks5CmdRequestDecoderTest.class);
|
private static final InternalLogger logger =
|
||||||
|
InternalLoggerFactory.getInstance(Socks5CommandRequestDecoderTest.class);
|
||||||
|
|
||||||
private static void testSocksCmdRequestDecoderWithDifferentParams(Socks5CmdType cmdType,
|
private static void test(
|
||||||
Socks5AddressType addressType,
|
Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) {
|
||||||
String host,
|
logger.debug(
|
||||||
int port) {
|
"Testing type: " + type + " dstAddrType: " + dstAddrType +
|
||||||
logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: " + host +
|
" dstAddr: " + dstAddr + " dstPort: " + dstPort);
|
||||||
" port: " + port);
|
|
||||||
Socks5CmdRequest msg = new Socks5CmdRequest(cmdType, addressType, host, port);
|
Socks5CommandRequest msg =
|
||||||
Socks5CmdRequestDecoder decoder = new Socks5CmdRequestDecoder();
|
new DefaultSocks5CommandRequest(type, dstAddrType, dstAddr, dstPort);
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5CommandRequestDecoder());
|
||||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
Socks5CommonTestUtils.writeFromClientToServer(embedder, msg);
|
||||||
if (msg.addressType() == Socks5AddressType.UNKNOWN) {
|
msg = embedder.readInbound();
|
||||||
assertTrue(embedder.readInbound() instanceof UnknownSocks5Request);
|
assertSame(msg.type(), type);
|
||||||
} else {
|
assertSame(msg.dstAddrType(), dstAddrType);
|
||||||
msg = embedder.readInbound();
|
assertEquals(msg.dstAddr(), IDN.toASCII(dstAddr));
|
||||||
assertSame(msg.cmdType(), cmdType);
|
assertEquals(msg.dstPort(), dstPort);
|
||||||
assertSame(msg.addressType(), addressType);
|
|
||||||
assertEquals(msg.host(), host);
|
|
||||||
assertEquals(msg.port(), port);
|
|
||||||
}
|
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,10 +53,12 @@ public class Socks5CmdRequestDecoderTest {
|
|||||||
public void testCmdRequestDecoderIPv4() {
|
public void testCmdRequestDecoderIPv4() {
|
||||||
String[] hosts = {"127.0.0.1", };
|
String[] hosts = {"127.0.0.1", };
|
||||||
int[] ports = {1, 32769, 65535 };
|
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 (String host : hosts) {
|
||||||
for (int port : ports) {
|
for (int port : ports) {
|
||||||
testSocksCmdRequestDecoderWithDifferentParams(cmdType, Socks5AddressType.IPv4, host, port);
|
test(cmdType, Socks5AddressType.IPv4, host, port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,12 +66,15 @@ public class Socks5CmdRequestDecoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCmdRequestDecoderIPv6() {
|
public void testCmdRequestDecoderIPv6() {
|
||||||
String[] hosts = { Socks5CommonUtils.ipv6toStr(IPAddressUtil.textToNumericFormatV6("::1"))};
|
String[] hosts = {
|
||||||
|
NetUtil.bytesToIpAddress(IPAddressUtil.textToNumericFormatV6("::1"), 0, 16) };
|
||||||
int[] ports = {1, 32769, 65535};
|
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 (String host : hosts) {
|
||||||
for (int port : ports) {
|
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};
|
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 (String host : hosts) {
|
||||||
for (int port : ports) {
|
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;
|
package io.netty.handler.codec.socksx.v5;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
|
||||||
final class Socks5CommonTestUtils {
|
final class Socks5CommonTestUtils {
|
||||||
@ -27,15 +26,31 @@ final class Socks5CommonTestUtils {
|
|||||||
//NOOP
|
//NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks5Request msg) {
|
public static void writeFromClientToServer(EmbeddedChannel embedder, Socks5Message msg) {
|
||||||
ByteBuf buf = Unpooled.buffer();
|
embedder.writeInbound(encodeClient(msg));
|
||||||
msg.encodeAsByteBuf(buf);
|
|
||||||
embedder.writeInbound(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks5Response msg) {
|
public static void writeFromServerToClient(EmbeddedChannel embedder, Socks5Message msg) {
|
||||||
ByteBuf buf = Unpooled.buffer();
|
embedder.writeInbound(encodeServer(msg));
|
||||||
msg.encodeAsByteBuf(buf);
|
}
|
||||||
embedder.writeInbound(buf);
|
|
||||||
|
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.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks5AuthRequestDecoderTest {
|
public class Socks5PasswordAuthRequestDecoderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAuthRequestDecoder() {
|
public void testAuthRequestDecoder() {
|
||||||
String username = "test";
|
String username = "test";
|
||||||
String password = "test";
|
String password = "test";
|
||||||
Socks5AuthRequest msg = new Socks5AuthRequest(username, password);
|
Socks5PasswordAuthRequest msg = new DefaultSocks5PasswordAuthRequest(username, password);
|
||||||
Socks5AuthRequestDecoder decoder = new Socks5AuthRequestDecoder();
|
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthRequestDecoder());
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
Socks5CommonTestUtils.writeFromClientToServer(embedder, msg);
|
||||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
|
||||||
msg = embedder.readInbound();
|
msg = embedder.readInbound();
|
||||||
assertEquals(msg.username(), username);
|
assertEquals(msg.username(), username);
|
||||||
assertEquals(msg.username(), password);
|
assertEquals(msg.username(), password);
|
@ -22,25 +22,23 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class Socks5AuthResponseDecoderTest {
|
public class Socks5PasswordAuthResponseDecoderTest {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
|
||||||
Socks5AuthResponseDecoderTest.class);
|
Socks5PasswordAuthResponseDecoderTest.class);
|
||||||
|
|
||||||
private static void testSocksAuthResponseDecoderWithDifferentParams(Socks5AuthStatus authStatus) {
|
private static void test(Socks5PasswordAuthStatus status) {
|
||||||
logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus);
|
logger.debug("Testing Socks5PasswordAuthResponseDecoder with status: " + status);
|
||||||
Socks5AuthResponse msg = new Socks5AuthResponse(authStatus);
|
Socks5PasswordAuthResponse msg = new DefaultSocks5PasswordAuthResponse(status);
|
||||||
Socks5AuthResponseDecoder decoder = new Socks5AuthResponseDecoder();
|
EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthResponseDecoder());
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(decoder);
|
Socks5CommonTestUtils.writeFromServerToClient(embedder, msg);
|
||||||
Socks5CommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
|
|
||||||
msg = embedder.readInbound();
|
msg = embedder.readInbound();
|
||||||
assertSame(msg.authStatus(), authStatus);
|
assertSame(msg.status(), status);
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSocksCmdResponseDecoder() {
|
public void testSocksCmdResponseDecoder() {
|
||||||
for (Socks5AuthStatus authStatus: Socks5AuthStatus.values()) {
|
test(Socks5PasswordAuthStatus.SUCCESS);
|
||||||
testSocksAuthResponseDecoderWithDifferentParams(authStatus);
|
test(Socks5PasswordAuthStatus.FAILURE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -329,8 +329,7 @@ final class ReplayingDecoderBuffer extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bytesBefore(int length, byte value) {
|
public int bytesBefore(int length, byte value) {
|
||||||
final int readerIndex = buffer.readerIndex();
|
return bytesBefore(buffer.readerIndex(), length, value);
|
||||||
return bytesBefore(readerIndex, buffer.writerIndex() - readerIndex, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.util;
|
package io.netty.util;
|
||||||
|
|
||||||
import io.netty.util.internal.PlatformDependent;
|
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.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
@ -395,8 +396,7 @@ public final class NetUtil {
|
|||||||
ipByteArray[byteIndex + 1] |= charValue & 15;
|
ipByteArray[byteIndex + 1] |= charValue & 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getIntValue(char c) {
|
private static int getIntValue(char c) {
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '0':
|
case '0':
|
||||||
return 0;
|
return 0;
|
||||||
@ -438,6 +438,58 @@ public final class NetUtil {
|
|||||||
return 0;
|
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) {
|
public static boolean isValidIpV6Address(String ipAddress) {
|
||||||
int length = ipAddress.length();
|
int length = ipAddress.length();
|
||||||
boolean doubleColon = false;
|
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.ChannelOption;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
import io.netty.handler.codec.socksx.SocksRequest;
|
import io.netty.handler.codec.socksx.SocksMessage;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandResponse;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponse;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandRequest;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdStatus;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequest;
|
import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdResponse;
|
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdStatus;
|
import io.netty.handler.codec.socksx.v5.Socks5CommandStatus;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.FutureListener;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksRequest> {
|
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksMessage> {
|
||||||
|
|
||||||
private final Bootstrap b = new Bootstrap();
|
private final Bootstrap b = new Bootstrap();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead0(final ChannelHandlerContext ctx, final SocksRequest message) throws Exception {
|
public void channelRead0(final ChannelHandlerContext ctx, final SocksMessage message) throws Exception {
|
||||||
if (message instanceof Socks4CmdRequest) {
|
if (message instanceof Socks4CommandRequest) {
|
||||||
final Socks4CmdRequest request = (Socks4CmdRequest) message;
|
final Socks4CommandRequest request = (Socks4CommandRequest) message;
|
||||||
Promise<Channel> promise = ctx.executor().newPromise();
|
Promise<Channel> promise = ctx.executor().newPromise();
|
||||||
promise.addListener(
|
promise.addListener(
|
||||||
new GenericFutureListener<Future<Channel>>() {
|
new FutureListener<Channel>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(final Future<Channel> future) throws Exception {
|
public void operationComplete(final Future<Channel> future) throws Exception {
|
||||||
final Channel outboundChannel = future.getNow();
|
final Channel outboundChannel = future.getNow();
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ctx.channel().writeAndFlush(new Socks4CmdResponse(Socks4CmdStatus.SUCCESS))
|
ChannelFuture responseFuture = ctx.channel().writeAndFlush(
|
||||||
.addListener(new ChannelFutureListener() {
|
new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS));
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture channelFuture) {
|
responseFuture.addListener(new ChannelFutureListener() {
|
||||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
@Override
|
||||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
public void operationComplete(ChannelFuture channelFuture) {
|
||||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||||
}
|
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||||
});
|
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ctx.channel().writeAndFlush(
|
ctx.channel().writeAndFlush(
|
||||||
new Socks4CmdResponse(Socks4CmdStatus.REJECTED_OR_FAILED)
|
new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED));
|
||||||
);
|
|
||||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +77,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
|||||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||||
.handler(new DirectClientHandler(promise));
|
.handler(new DirectClientHandler(promise));
|
||||||
|
|
||||||
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
|
b.connect(request.dstAddr(), request.dstPort()).addListener(new ChannelFutureListener() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
public void operationComplete(ChannelFuture future) throws Exception {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
@ -84,35 +85,36 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
|||||||
} else {
|
} else {
|
||||||
// Close the connection if the connection attempt has failed.
|
// Close the connection if the connection attempt has failed.
|
||||||
ctx.channel().writeAndFlush(
|
ctx.channel().writeAndFlush(
|
||||||
new Socks4CmdResponse(Socks4CmdStatus.REJECTED_OR_FAILED)
|
new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED)
|
||||||
);
|
);
|
||||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (message instanceof Socks5CmdRequest) {
|
} else if (message instanceof Socks5CommandRequest) {
|
||||||
final Socks5CmdRequest request = (Socks5CmdRequest) message;
|
final Socks5CommandRequest request = (Socks5CommandRequest) message;
|
||||||
Promise<Channel> promise = ctx.executor().newPromise();
|
Promise<Channel> promise = ctx.executor().newPromise();
|
||||||
promise.addListener(
|
promise.addListener(
|
||||||
new GenericFutureListener<Future<Channel>>() {
|
new FutureListener<Channel>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(final Future<Channel> future) throws Exception {
|
public void operationComplete(final Future<Channel> future) throws Exception {
|
||||||
final Channel outboundChannel = future.getNow();
|
final Channel outboundChannel = future.getNow();
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ctx.channel().writeAndFlush(
|
ChannelFuture responseFuture =
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, request.addressType())
|
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
||||||
).addListener(new ChannelFutureListener() {
|
Socks5CommandStatus.SUCCESS, request.dstAddrType()));
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture channelFuture) {
|
responseFuture.addListener(new ChannelFutureListener() {
|
||||||
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
@Override
|
||||||
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
public void operationComplete(ChannelFuture channelFuture) {
|
||||||
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
||||||
}
|
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
||||||
}
|
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
||||||
);
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ctx.channel().writeAndFlush(
|
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType()));
|
Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
||||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +127,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
|||||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||||
.handler(new DirectClientHandler(promise));
|
.handler(new DirectClientHandler(promise));
|
||||||
|
|
||||||
b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
|
b.connect(request.dstAddr(), request.dstPort()).addListener(new ChannelFutureListener() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
public void operationComplete(ChannelFuture future) throws Exception {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
@ -133,7 +135,7 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
|
|||||||
} else {
|
} else {
|
||||||
// Close the connection if the connection attempt has failed.
|
// Close the connection if the connection attempt has failed.
|
||||||
ctx.channel().writeAndFlush(
|
ctx.channel().writeAndFlush(
|
||||||
new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType()));
|
new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
||||||
SocksServerUtils.closeOnFlush(ctx.channel());
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,31 +18,32 @@ package io.netty.example.socksproxy;
|
|||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.handler.codec.socksx.SocksRequest;
|
import io.netty.handler.codec.socksx.SocksMessage;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandRequest;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdType;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandType;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5AuthScheme;
|
import io.netty.handler.codec.socksx.v5.DefaultSocks5InitialResponse;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequestDecoder;
|
import io.netty.handler.codec.socksx.v5.DefaultSocks5PasswordAuthResponse;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5InitResponse;
|
import io.netty.handler.codec.socksx.v5.Socks5AuthMethod;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5Request;
|
import io.netty.handler.codec.socksx.v5.Socks5InitialRequest;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5AuthResponse;
|
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5AuthStatus;
|
import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdRequest;
|
import io.netty.handler.codec.socksx.v5.Socks5CommandType;
|
||||||
import io.netty.handler.codec.socksx.v5.Socks5CmdType;
|
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequest;
|
||||||
|
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthStatus;
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksRequest> {
|
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksMessage> {
|
||||||
|
|
||||||
public static final SocksServerHandler INSTANCE = new SocksServerHandler();
|
public static final SocksServerHandler INSTANCE = new SocksServerHandler();
|
||||||
|
|
||||||
private SocksServerHandler() { }
|
private SocksServerHandler() { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead0(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception {
|
public void channelRead0(ChannelHandlerContext ctx, SocksMessage socksRequest) throws Exception {
|
||||||
switch (socksRequest.protocolVersion()) {
|
switch (socksRequest.version()) {
|
||||||
case SOCKS4a:
|
case SOCKS4a:
|
||||||
Socks4CmdRequest socksV4CmdRequest = (Socks4CmdRequest) socksRequest;
|
Socks4CommandRequest socksV4CmdRequest = (Socks4CommandRequest) socksRequest;
|
||||||
if (socksV4CmdRequest.cmdType() == Socks4CmdType.CONNECT) {
|
if (socksV4CmdRequest.type() == Socks4CommandType.CONNECT) {
|
||||||
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
||||||
ctx.pipeline().remove(this);
|
ctx.pipeline().remove(this);
|
||||||
ctx.fireChannelRead(socksRequest);
|
ctx.fireChannelRead(socksRequest);
|
||||||
@ -51,32 +52,26 @@ public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksR
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SOCKS5:
|
case SOCKS5:
|
||||||
switch (((Socks5Request) socksRequest).requestType()) {
|
if (socksRequest instanceof Socks5InitialRequest) {
|
||||||
case INIT: {
|
// auth support example
|
||||||
// auth support example
|
//ctx.pipeline().addFirst(new Socks5PasswordAuthRequestDecoder());
|
||||||
//ctx.pipeline().addFirst(new SocksV5AuthRequestDecoder());
|
//ctx.write(new DefaultSocks5AuthMethodResponse(Socks5AuthMethod.PASSWORD));
|
||||||
//ctx.write(new SocksV5InitResponse(SocksV5AuthScheme.AUTH_PASSWORD));
|
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
|
||||||
ctx.pipeline().addFirst(new Socks5CmdRequestDecoder());
|
ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH));
|
||||||
ctx.write(new Socks5InitResponse(Socks5AuthScheme.NO_AUTH));
|
} else if (socksRequest instanceof Socks5PasswordAuthRequest) {
|
||||||
break;
|
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
|
||||||
}
|
ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS));
|
||||||
case AUTH:
|
} else if (socksRequest instanceof Socks5CommandRequest) {
|
||||||
ctx.pipeline().addFirst(new Socks5CmdRequestDecoder());
|
Socks5CommandRequest socks5CmdRequest = (Socks5CommandRequest) socksRequest;
|
||||||
ctx.write(new Socks5AuthResponse(Socks5AuthStatus.SUCCESS));
|
if (socks5CmdRequest.type() == Socks5CommandType.CONNECT) {
|
||||||
break;
|
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
||||||
case CMD:
|
ctx.pipeline().remove(this);
|
||||||
Socks5CmdRequest socks5CmdRequest = (Socks5CmdRequest) socksRequest;
|
ctx.fireChannelRead(socksRequest);
|
||||||
if (socks5CmdRequest.cmdType() == Socks5CmdType.CONNECT) {
|
} else {
|
||||||
ctx.pipeline().addLast(new SocksServerConnectHandler());
|
|
||||||
ctx.pipeline().remove(this);
|
|
||||||
ctx.fireChannelRead(socksRequest);
|
|
||||||
} else {
|
|
||||||
ctx.close();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case UNKNOWN:
|
|
||||||
ctx.close();
|
ctx.close();
|
||||||
break;
|
}
|
||||||
|
} else {
|
||||||
|
ctx.close();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
|
@ -16,16 +16,17 @@
|
|||||||
package io.netty.example.socksproxy;
|
package io.netty.example.socksproxy;
|
||||||
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.handler.codec.socksx.SocksPortUnificationServerHandler;
|
||||||
import io.netty.handler.logging.LogLevel;
|
import io.netty.handler.logging.LogLevel;
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
|
||||||
public final class SocksServerInitializer extends ChannelInitializer<SocketChannel> {
|
public final class SocksServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel socketChannel) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline p = socketChannel.pipeline();
|
ch.pipeline().addLast(
|
||||||
p.addFirst(new LoggingHandler(LogLevel.DEBUG));
|
new LoggingHandler(LogLevel.DEBUG),
|
||||||
p.addLast(new SocksPortUnificationServerHandler());
|
new SocksPortUnificationServerHandler(),
|
||||||
|
SocksServerHandler.INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,12 @@ package io.netty.handler.proxy;
|
|||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdRequest;
|
import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandRequest;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponse;
|
import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdResponseDecoder;
|
import io.netty.handler.codec.socksx.v4.Socks4ClientEncoder;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdStatus;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandResponse;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4CmdType;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
|
||||||
import io.netty.handler.codec.socksx.v4.Socks4MessageEncoder;
|
import io.netty.handler.codec.socksx.v4.Socks4CommandType;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@ -69,13 +69,13 @@ public final class Socks4ProxyHandler extends ProxyHandler {
|
|||||||
ChannelPipeline p = ctx.pipeline();
|
ChannelPipeline p = ctx.pipeline();
|
||||||
String name = ctx.name();
|
String name = ctx.name();
|
||||||
|
|
||||||
Socks4CmdResponseDecoder decoder = new Socks4CmdResponseDecoder();
|
Socks4ClientDecoder decoder = new Socks4ClientDecoder();
|
||||||
p.addBefore(name, null, decoder);
|
p.addBefore(name, null, decoder);
|
||||||
|
|
||||||
decoderName = p.context(decoder).name();
|
decoderName = p.context(decoder).name();
|
||||||
encoderName = decoderName + ".encoder";
|
encoderName = decoderName + ".encoder";
|
||||||
|
|
||||||
p.addBefore(name, encoderName, Socks4MessageEncoder.INSTANCE);
|
p.addBefore(name, encoderName, Socks4ClientEncoder.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -99,18 +99,18 @@ public final class Socks4ProxyHandler extends ProxyHandler {
|
|||||||
} else {
|
} else {
|
||||||
rhost = raddr.getAddress().getHostAddress();
|
rhost = raddr.getAddress().getHostAddress();
|
||||||
}
|
}
|
||||||
return new Socks4CmdRequest(
|
return new DefaultSocks4CommandRequest(
|
||||||
username != null? username : "", Socks4CmdType.CONNECT, rhost, raddr.getPort());
|
Socks4CommandType.CONNECT, rhost, raddr.getPort(), username != null? username : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception {
|
protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception {
|
||||||
final Socks4CmdResponse res = (Socks4CmdResponse) response;
|
final Socks4CommandResponse res = (Socks4CommandResponse) response;
|
||||||
final Socks4CmdStatus status = res.cmdStatus();
|
final Socks4CommandStatus status = res.status();
|
||||||
if (status == Socks4CmdStatus.SUCCESS) {
|
if (status == Socks4CommandStatus.SUCCESS) {
|
||||||
return true;
|
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