1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.proxy.handlers.socks;
21
22 import org.apache.mina.core.buffer.IoBuffer;
23 import org.apache.mina.core.filterchain.IoFilter.NextFilter;
24 import org.apache.mina.proxy.session.ProxyIoSession;
25 import org.apache.mina.proxy.utils.ByteUtilities;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30 * Socks4LogicHandler.java - SOCKS4/SOCKS4a authentication mechanisms logic handler.
31 *
32 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
33 * @since MINA 2.0.0-M3
34 */
35 public class Socks4LogicHandler extends AbstractSocksLogicHandler {
36
37 private final static Logger logger = LoggerFactory
38 .getLogger(Socks4LogicHandler.class);
39
40 /**
41 * {@inheritDoc}
42 */
43 public Socks4LogicHandler(final ProxyIoSession proxyIoSession) {
44 super(proxyIoSession);
45 }
46
47 /**
48 * Perform the handshake.
49 *
50 * @param nextFilter the next filter
51 */
52 public void doHandshake(final NextFilter nextFilter) {
53 logger.debug(" doHandshake()");
54
55 // Send request
56 writeRequest(nextFilter, request);
57 }
58
59 /**
60 * Encode a SOCKS4/SOCKS4a request and writes it to the next filter
61 * so it can be sent to the proxy server.
62 *
63 * @param nextFilter the next filter
64 * @param request the request to send.
65 */
66 protected void writeRequest(final NextFilter nextFilter,
67 final SocksProxyRequest request) {
68 try {
69 boolean isV4ARequest = request.getHost() != null;
70 byte[] userID = request.getUserName().getBytes("ASCII");
71 byte[] host = isV4ARequest ? request.getHost().getBytes("ASCII")
72 : null;
73
74 int len = 9 + userID.length;
75
76 if (isV4ARequest) {
77 len += host.length + 1;
78 }
79
80 IoBuffer buf = IoBuffer.allocate(len);
81
82 buf.put(request.getProtocolVersion());
83 buf.put(request.getCommandCode());
84 buf.put(request.getPort());
85 buf.put(request.getIpAddress());
86 buf.put(userID);
87 buf.put(SocksProxyConstants.TERMINATOR);
88
89 if (isV4ARequest) {
90 buf.put(host);
91 buf.put(SocksProxyConstants.TERMINATOR);
92 }
93
94 if (isV4ARequest) {
95 logger.debug(" sending SOCKS4a request");
96 } else {
97 logger.debug(" sending SOCKS4 request");
98 }
99
100 buf.flip();
101 writeData(nextFilter, buf);
102 } catch (Exception ex) {
103 closeSession("Unable to send Socks request: ", ex);
104 }
105 }
106
107 /**
108 * Handle incoming data during the handshake process. Should consume only the
109 * handshake data from the buffer, leaving any extra data in place.
110 *
111 * @param nextFilter the next filter
112 * @param buf the server response data buffer
113 */
114 public void messageReceived(final NextFilter nextFilter,
115 final IoBuffer buf) {
116 try {
117 if (buf.remaining() >= SocksProxyConstants.SOCKS_4_RESPONSE_SIZE) {
118 handleResponse(buf);
119 }
120 } catch (Exception ex) {
121 closeSession("Proxy handshake failed: ", ex);
122 }
123 }
124
125 /**
126 * Handle a SOCKS4/SOCKS4a response from the proxy server. Test
127 * the response buffer reply code and call {@link #setHandshakeComplete()}
128 * if access is granted.
129 *
130 * @param buf the buffer holding the server response data.
131 * @throws exception if server response is malformed or if request is rejected
132 * by the proxy server.
133 */
134 protected void handleResponse(final IoBuffer buf) throws Exception {
135 byte first = buf.get(0);
136
137 if (first != 0) {
138 throw new Exception("Socks response seems to be malformed");
139 }
140
141 byte status = buf.get(1);
142
143 // Consumes all the response data from the buffer
144 buf.position(buf.position() + SocksProxyConstants.SOCKS_4_RESPONSE_SIZE);
145
146 if (status == SocksProxyConstants.V4_REPLY_REQUEST_GRANTED) {
147 setHandshakeComplete();
148 } else {
149 throw new Exception("Proxy handshake failed - Code: 0x"
150 + ByteUtilities.asHex(new byte[] { status }) + " ("
151 + SocksProxyConstants.getReplyCodeAsString(status) + ")");
152 }
153 }
154 }