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
32
33
34
35 public final class Socks4ProxyHandler extends ProxyHandler {
36
37 private static final String PROTOCOL = "socks4";
38 private static final String AUTH_USERNAME = "username";
39
40 private final String username;
41
42 private String decoderName;
43 private String encoderName;
44
45 public Socks4ProxyHandler(SocketAddress proxyAddress) {
46 this(proxyAddress, null);
47 }
48
49 public Socks4ProxyHandler(SocketAddress proxyAddress, String username) {
50 super(proxyAddress);
51 if (username != null && username.isEmpty()) {
52 username = null;
53 }
54 this.username = username;
55 }
56
57 @Override
58 public String protocol() {
59 return PROTOCOL;
60 }
61
62 @Override
63 public String authScheme() {
64 return username != null? AUTH_USERNAME : AUTH_NONE;
65 }
66
67 public String username() {
68 return username;
69 }
70
71 @Override
72 protected void addCodec(ChannelHandlerContext ctx) throws Exception {
73 ChannelPipeline p = ctx.pipeline();
74 String name = ctx.name();
75
76 Socks4ClientDecoder decoder = new Socks4ClientDecoder();
77 p.addBefore(name, null, decoder);
78
79 decoderName = p.context(decoder).name();
80 encoderName = decoderName + ".encoder";
81
82 p.addBefore(name, encoderName, Socks4ClientEncoder.INSTANCE);
83 }
84
85 @Override
86 protected void removeEncoder(ChannelHandlerContext ctx) throws Exception {
87 ChannelPipeline p = ctx.pipeline();
88 p.remove(encoderName);
89 }
90
91 @Override
92 protected void removeDecoder(ChannelHandlerContext ctx) throws Exception {
93 ChannelPipeline p = ctx.pipeline();
94 p.remove(decoderName);
95 }
96
97 @Override
98 protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception {
99 InetSocketAddress raddr = destinationAddress();
100 String rhost;
101 if (raddr.isUnresolved()) {
102 rhost = raddr.getHostString();
103 } else {
104 rhost = raddr.getAddress().getHostAddress();
105 }
106 return new DefaultSocks4CommandRequest(
107 Socks4CommandType.CONNECT, rhost, raddr.getPort(), username != null? username : "");
108 }
109
110 @Override
111 protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception {
112 final Socks4CommandResponse res = (Socks4CommandResponse) response;
113 final Socks4CommandStatus status = res.status();
114 if (status == Socks4CommandStatus.SUCCESS) {
115 return true;
116 }
117
118 throw new ProxyConnectException(exceptionMessage("status: " + status));
119 }
120 }