1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.proxy;
18
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelPipeline;
21 import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandRequest;
22 import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder;
23 import io.netty.handler.codec.socksx.v4.Socks4ClientEncoder;
24 import io.netty.handler.codec.socksx.v4.Socks4CommandResponse;
25 import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
26 import io.netty.handler.codec.socksx.v4.Socks4CommandType;
27
28 import java.net.InetSocketAddress;
29 import java.net.SocketAddress;
30
31 public final class Socks4ProxyHandler extends ProxyHandler {
32
33 private static final String PROTOCOL = "socks4";
34 private static final String AUTH_USERNAME = "username";
35
36 private final String username;
37
38 private String decoderName;
39 private String encoderName;
40
41 public Socks4ProxyHandler(SocketAddress proxyAddress) {
42 this(proxyAddress, null);
43 }
44
45 public Socks4ProxyHandler(SocketAddress proxyAddress, String username) {
46 super(proxyAddress);
47 if (username != null && username.isEmpty()) {
48 username = null;
49 }
50 this.username = username;
51 }
52
53 @Override
54 public String protocol() {
55 return PROTOCOL;
56 }
57
58 @Override
59 public String authScheme() {
60 return username != null? AUTH_USERNAME : AUTH_NONE;
61 }
62
63 public String username() {
64 return username;
65 }
66
67 @Override
68 protected void addCodec(ChannelHandlerContext ctx) throws Exception {
69 ChannelPipeline p = ctx.pipeline();
70 String name = ctx.name();
71
72 Socks4ClientDecoder decoder = new Socks4ClientDecoder();
73 p.addBefore(name, null, decoder);
74
75 decoderName = p.context(decoder).name();
76 encoderName = decoderName + ".encoder";
77
78 p.addBefore(name, encoderName, Socks4ClientEncoder.INSTANCE);
79 }
80
81 @Override
82 protected void removeEncoder(ChannelHandlerContext ctx) throws Exception {
83 ChannelPipeline p = ctx.pipeline();
84 p.remove(encoderName);
85 }
86
87 @Override
88 protected void removeDecoder(ChannelHandlerContext ctx) throws Exception {
89 ChannelPipeline p = ctx.pipeline();
90 p.remove(decoderName);
91 }
92
93 @Override
94 protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception {
95 InetSocketAddress raddr = destinationAddress();
96 String rhost;
97 if (raddr.isUnresolved()) {
98 rhost = raddr.getHostString();
99 } else {
100 rhost = raddr.getAddress().getHostAddress();
101 }
102 return new DefaultSocks4CommandRequest(
103 Socks4CommandType.CONNECT, rhost, raddr.getPort(), username != null? username : "");
104 }
105
106 @Override
107 protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception {
108 final Socks4CommandResponse res = (Socks4CommandResponse) response;
109 final Socks4CommandStatus status = res.status();
110 if (status == Socks4CommandStatus.SUCCESS) {
111 return true;
112 }
113
114 throw new ProxyConnectException(exceptionMessage("status: " + status));
115 }
116 }