You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ed...@apache.org on 2008/08/13 02:05:43 UTC
svn commit: r685389 [3/4] - in
/mina/trunk/core/src/main/java/org/apache/mina/proxy: ./ event/ filter/
handlers/ handlers/http/ handlers/http/basic/ handlers/http/digest/
handlers/http/ntlm/ handlers/socks/ session/ utils/
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.http.ntlm;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.StringTokenizer;
+
+import org.apache.mina.proxy.utils.ByteUtilities;
+
+/**
+ * NTLMUtilities.java - NTLM functions used for authentication and unit testing.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class NTLMUtilities implements NTLMConstants {
+ /**
+ * @see #writeSecurityBuffer(short, short, int, byte[], int)
+ */
+ public final static byte[] writeSecurityBuffer(short length,
+ int bufferOffset) {
+ byte[] b = new byte[8];
+ writeSecurityBuffer(length, length, bufferOffset, b, 0);
+ return b;
+ }
+
+ /**
+ * Writes a security buffer to the given array <code>b</code> at offset
+ * <code>offset</code>.
+ *
+ * @param length the length of the security buffer
+ * @param allocated the allocated space for the security buffer (should be
+ * greater or equal to <code>length</code>
+ * @param bufferOffset the offset since the beginning of the <code>b</code>
+ * buffer where the buffer iswritten
+ * @param b the buffer in which we write the security buffer
+ * @param offset the offset at which to write to the buffer
+ */
+ public final static void writeSecurityBuffer(short length, short allocated,
+ int bufferOffset, byte[] b, int offset) {
+ ByteUtilities.writeShort(length, b, offset);
+ ByteUtilities.writeShort(allocated, b, offset + 2);
+ ByteUtilities.writeInt(bufferOffset, b, offset + 4);
+ }
+
+ public final static void writeOSVersion(byte majorVersion,
+ byte minorVersion, short buildNumber, byte[] b, int offset) {
+ b[offset] = majorVersion;
+ b[offset + 1] = minorVersion;
+ b[offset + 2] = (byte) buildNumber;
+ b[offset + 3] = (byte) (buildNumber >> 8);
+ b[offset + 4] = 0;
+ b[offset + 5] = 0;
+ b[offset + 6] = 0;
+ b[offset + 7] = 0x0F;
+ }
+
+ /**
+ * Tries to return a valid Os version on windows systems.
+ */
+ public final static byte[] getOsVersion() {
+ String os = System.getProperty("os.name");
+ if (os == null || !os.toUpperCase().contains("WINDOWS")) {
+ return DEFAULT_OS_VERSION;
+ } else {
+ byte[] osVer = new byte[8];
+ try {
+ Process pr = Runtime.getRuntime().exec("cmd /C ver");
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(pr.getInputStream()));
+ pr.waitFor();
+ String line;
+ while ((line = reader.readLine()) != null && "".equals(line))
+ ;
+ int pos = line.indexOf("version");
+
+ if (pos == -1) {
+ throw new NullPointerException();
+ }
+
+ pos += 8;
+ line = line.substring(pos, line.indexOf(']'));
+ StringTokenizer tk = new StringTokenizer(line, ".");
+ if (tk.countTokens() != 3) {
+ throw new NullPointerException();
+ }
+
+ writeOSVersion(Byte.parseByte(tk.nextToken()), Byte
+ .parseByte(tk.nextToken()), Short.parseShort(tk
+ .nextToken()), osVer, 0);
+ } catch (Exception ex) {
+ try {
+ String version = System.getProperty("os.version");
+ writeOSVersion(Byte.parseByte(version.substring(0, 1)),
+ Byte.parseByte(version.substring(2, 3)), (short) 0,
+ osVer, 0);
+ } catch (Exception ex2) {
+ return DEFAULT_OS_VERSION;
+ }
+ }
+ return osVer;
+ }
+ }
+
+ /**
+ * see http://davenport.sourceforge.net/ntlm.html#theType1Message
+ *
+ * @param workStation the workstation name
+ * @param domain the domain name
+ * @param customFlags custom flags, if null then
+ * <code>NTLMConstants.DEFAULT_CONSTANTS</code> is used
+ * @param osVersion the os version of the client, if null then
+ * <code>NTLMConstants.DEFAULT_OS_VERSION</code> is used
+ * @return
+ */
+ public final static byte[] createType1Message(String workStation,
+ String domain, Integer customFlags, byte[] osVersion) {
+ byte[] msg = null;
+
+ if (osVersion != null && osVersion.length != 8) {
+ throw new IllegalArgumentException(
+ "osVersion parameter should be a 8 byte wide array");
+ }
+
+ if (workStation == null || domain == null) {
+ throw new NullPointerException(
+ "workStation and domain must be non null");
+ }
+
+ int flags = customFlags != null ? customFlags
+ | FLAG_NEGOTIATE_WORKSTATION_SUPPLIED
+ | FLAG_NEGOTIATE_DOMAIN_SUPPLIED : DEFAULT_FLAGS;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try {
+ baos.write(NTLM_SIGNATURE);
+ baos.write(ByteUtilities.writeInt(MESSAGE_TYPE_1));
+ baos.write(ByteUtilities.writeInt(flags));
+
+ byte[] domainData = ByteUtilities.getOEMStringAsByteArray(domain);
+ byte[] workStationData = ByteUtilities
+ .getOEMStringAsByteArray(workStation);
+
+ int pos = (osVersion != null) ? 40 : 32;
+ baos.write(writeSecurityBuffer((short) domainData.length, pos
+ + workStationData.length));
+ baos
+ .write(writeSecurityBuffer((short) workStationData.length,
+ pos));
+
+ if (osVersion != null) {
+ baos.write(osVersion);
+ }
+
+ // Order is not mandatory since a pointer is given in the security buffers
+ baos.write(workStationData);
+ baos.write(domainData);
+
+ msg = baos.toByteArray();
+ baos.close();
+ } catch (IOException e) {
+ return null;
+ }
+
+ return msg;
+ }
+
+ /**
+ * Write a security buffer and returns the pointer of the position
+ * where to write the next security buffer.
+ *
+ * @param baos the stream where the security buffer is written
+ * @param len the length of the security buffer
+ * @param pointer the position where the security buffer can be written
+ * @return the position where the next security buffer will be written
+ * @throws IOException
+ */
+ public final static int writeSecurityBufferAndUpdatePointer(
+ ByteArrayOutputStream baos, short len, int pointer)
+ throws IOException {
+ baos.write(writeSecurityBuffer(len, pointer));
+ return pointer + len;
+ }
+
+ public final static byte[] extractChallengeFromType2Message(byte[] msg) {
+ byte[] challenge = new byte[8];
+ System.arraycopy(msg, 24, challenge, 0, 8);
+ return challenge;
+ }
+
+ public final static int extractFlagsFromType2Message(byte[] msg) {
+ byte[] flagsBytes = new byte[4];
+
+ System.arraycopy(msg, 20, flagsBytes, 0, 4);
+ ByteUtilities.changeWordEndianess(flagsBytes, 0, 4);
+
+ return ByteUtilities.makeIntFromByte4(flagsBytes);
+ }
+
+ public final static String extractTargetNameFromType2Message(byte[] msg,
+ Integer msgFlags) throws UnsupportedEncodingException {
+ byte[] targetName = null;
+
+ // Read security buffer
+ byte[] securityBuffer = new byte[8];
+
+ System.arraycopy(msg, 12, securityBuffer, 0, 8);
+ ByteUtilities.changeWordEndianess(securityBuffer, 0, 8);
+ int length = ByteUtilities.makeIntFromByte2(securityBuffer);
+ int offset = ByteUtilities.makeIntFromByte4(securityBuffer, 4);
+
+ targetName = new byte[length];
+ System.arraycopy(msg, offset, targetName, 0, length);
+
+ int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
+ : msgFlags;
+ if (ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_UNICODE)) {
+ return new String(targetName, "UTF-16LE");
+ } else {
+ return new String(targetName, "ASCII");
+ }
+ }
+
+ public final static byte[] extractTargetInfoFromType2Message(byte[] msg,
+ Integer msgFlags) {
+ int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
+ : msgFlags;
+ byte[] targetInformationBlock = null;
+
+ if (!ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_TARGET_INFO))
+ return null;
+
+ int pos = 40; //isFlagSet(flags, FLAG_NEGOTIATE_LOCAL_CALL) ? 40 : 32;
+
+ // Read security buffer
+ byte[] securityBuffer = new byte[8];
+
+ System.arraycopy(msg, pos, securityBuffer, 0, 8);
+ ByteUtilities.changeWordEndianess(securityBuffer, 0, 8);
+ int length = ByteUtilities.makeIntFromByte2(securityBuffer);
+ int offset = ByteUtilities.makeIntFromByte4(securityBuffer, 4);
+
+ targetInformationBlock = new byte[length];
+ System.arraycopy(msg, offset, targetInformationBlock, 0, length);
+
+ return targetInformationBlock;
+ }
+
+ public final static void printTargetInformationBlockFromType2Message(
+ byte[] msg, Integer msgFlags, PrintWriter out)
+ throws UnsupportedEncodingException {
+ int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
+ : msgFlags;
+
+ byte[] infoBlock = extractTargetInfoFromType2Message(msg, flags);
+ if (infoBlock == null) {
+ out.println("No target information block found !");
+ } else {
+ int pos = 0;
+ while (infoBlock[pos] != 0) {
+ out.print("---\nType " + infoBlock[pos] + ": ");
+ switch (infoBlock[pos]) {
+ case 1:
+ out.println("Server name");
+ break;
+ case 2:
+ out.println("Domain name");
+ break;
+ case 3:
+ out.println("Fully qualified DNS hostname");
+ break;
+ case 4:
+ out.println("DNS domain name");
+ break;
+ case 5:
+ out.println("Parent DNS domain name");
+ break;
+ }
+ byte[] len = new byte[2];
+ System.arraycopy(infoBlock, pos + 2, len, 0, 2);
+ ByteUtilities.changeByteEndianess(len, 0, 2);
+
+ int length = ByteUtilities.makeIntFromByte2(len, 0);
+ out.println("Length: " + length + " bytes");
+ out.print("Data: ");
+ if (ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_UNICODE)) {
+ out.println(new String(infoBlock, pos + 4, length,
+ "UTF-16LE"));
+ } else {
+ out
+ .println(new String(infoBlock, pos + 4, length,
+ "ASCII"));
+ }
+ pos += 4 + length;
+ out.flush();
+ }
+ }
+ }
+
+ /**
+ * http://davenport.sourceforge.net/ntlm.html#theType3Message
+ */
+ public final static byte[] createType3Message(String user, String password,
+ byte[] challenge, String target, String workstation,
+ Integer serverFlags, byte[] osVersion) {
+ byte[] msg = null;
+
+ if (challenge == null || challenge.length != 8) {
+ throw new IllegalArgumentException(
+ "challenge[] should be a 8 byte wide array");
+ }
+
+ if (osVersion != null && osVersion.length != 8) {
+ throw new IllegalArgumentException(
+ "osVersion should be a 8 byte wide array");
+ }
+
+ //TOSEE breaks tests
+ /*int flags = serverFlags != null ? serverFlags |
+ FLAG_NEGOTIATE_WORKSTATION_SUPPLIED |
+ FLAG_NEGOTIATE_DOMAIN_SUPPLIED : DEFAULT_FLAGS;*/
+ int flags = serverFlags != null ? serverFlags : DEFAULT_FLAGS;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try {
+ baos.write(NTLM_SIGNATURE);
+ baos.write(ByteUtilities.writeInt(MESSAGE_TYPE_3));
+
+ byte[] dataLMResponse = NTLMResponses.getLMResponse(password,
+ challenge);
+ byte[] dataNTLMResponse = NTLMResponses.getNTLMResponse(password,
+ challenge);
+
+ boolean useUnicode = ByteUtilities.isFlagSet(flags,
+ FLAG_NEGOTIATE_UNICODE);
+ byte[] targetName = ByteUtilities.encodeString(target, useUnicode);
+ byte[] userName = ByteUtilities.encodeString(user, useUnicode);
+ byte[] workstationName = ByteUtilities.encodeString(workstation,
+ useUnicode);
+
+ int pos = osVersion != null ? 72 : 64;
+ int responsePos = pos + targetName.length + userName.length
+ + workstationName.length;
+ responsePos = writeSecurityBufferAndUpdatePointer(baos,
+ (short) dataLMResponse.length, responsePos);
+ writeSecurityBufferAndUpdatePointer(baos,
+ (short) dataNTLMResponse.length, responsePos);
+ pos = writeSecurityBufferAndUpdatePointer(baos,
+ (short) targetName.length, pos);
+ pos = writeSecurityBufferAndUpdatePointer(baos,
+ (short) userName.length, pos);
+ writeSecurityBufferAndUpdatePointer(baos,
+ (short) workstationName.length, pos);
+
+ /**
+ LM/LMv2 Response security buffer
+ 20 NTLM/NTLMv2 Response security buffer
+ 28 Target Name security buffer
+ 36 User Name security buffer
+ 44 Workstation Name security buffer
+ (52) Session Key (optional) security buffer
+ (60) Flags (optional) long
+ (64) OS Version Structure (Optional) 8 bytes
+ **/
+
+ baos.write(new byte[] { 0, 0, 0, 0, (byte) 0x9a, 0, 0, 0 }); // Session Key Security Buffer ??!
+ baos.write(ByteUtilities.writeInt(flags));
+
+ if (osVersion != null) {
+ baos.write(osVersion);
+ }
+ //else
+ // baos.write(DEFAULT_OS_VERSION);
+
+ // Order is not mandatory since a pointer is given in the security buffers
+ baos.write(targetName);
+ baos.write(userName);
+ baos.write(workstationName);
+
+ baos.write(dataLMResponse);
+ baos.write(dataNTLMResponse);
+
+ msg = baos.toByteArray();
+ baos.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return msg;
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMUtilities.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.socks;
+
+import org.apache.mina.proxy.AbstractProxyLogicHandler;
+import org.apache.mina.proxy.session.ProxyIoSession;
+
+/**
+ * AbstractSocksLogicHandler.java - Base class for SOCKS {@link AbstractProxyLogicHandler} implementations.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public abstract class AbstractSocksLogicHandler extends
+ AbstractProxyLogicHandler {
+
+ /**
+ * The request we will ask to the proxy.
+ */
+ protected final SocksProxyRequest request;
+
+ /**
+ * Creates a new {@link AbstractSocksLogicHandler}.
+ */
+ public AbstractSocksLogicHandler(final ProxyIoSession proxyIoSession) {
+ super(proxyIoSession);
+ this.request = (SocksProxyRequest) proxyIoSession.getRequest();
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/AbstractSocksLogicHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.socks;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.ByteUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Socks4LogicHandler.java - SOCKS4/SOCKS4a authentication mechanisms logic handler.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class Socks4LogicHandler extends AbstractSocksLogicHandler {
+
+ private final static Logger logger = LoggerFactory
+ .getLogger(Socks4LogicHandler.class);
+
+ public Socks4LogicHandler(final ProxyIoSession proxyIoSession) {
+ super(proxyIoSession);
+ }
+
+ /**
+ * Perform any handshaking processing.
+ */
+ public synchronized void doHandshake(final NextFilter nextFilter) {
+ logger.debug(" doHandshake()");
+
+ // Send request
+ writeRequest(nextFilter, request);
+ }
+
+ /**
+ * Encode a SOCKS4/SOCKS4a request and send it to the proxy server.
+ *
+ * @param request The request to send.
+ */
+ protected void writeRequest(final NextFilter nextFilter,
+ final SocksProxyRequest request) {
+ try {
+ boolean isV4ARequest = request.getHost() != null;
+ byte[] userID = request.getUserName().getBytes("ASCII");
+ byte[] host = isV4ARequest ? request.getHost().getBytes("ASCII")
+ : null;
+
+ int len = 9 + userID.length;
+
+ if (isV4ARequest) {
+ len += host.length + 1;
+ }
+
+ IoBuffer buf = IoBuffer.allocate(len);
+
+ buf.put(request.getProtocolVersion());
+ buf.put(request.getCommandCode());
+ buf.put(request.getPort());
+ buf.put(request.getIpAddress());
+ buf.put(userID);
+ buf.put(SocksProxyConstants.TERMINATOR);
+
+ if (isV4ARequest) {
+ buf.put(host);
+ buf.put(SocksProxyConstants.TERMINATOR);
+ }
+
+ if (isV4ARequest) {
+ logger.debug(" sending SOCKS4a request");
+ } else {
+ logger.debug(" sending SOCKS4 request");
+ }
+
+ buf.flip();
+ writeData(nextFilter, buf);
+ } catch (Exception ex) {
+ closeSession("Unable to send Socks request: ", ex);
+ }
+ }
+
+ /**
+ * Handle incoming data during the handshake process. Should consume only the
+ * handshake data from the buffer, leaving any extra data in place.
+ */
+ public synchronized void messageReceived(final NextFilter nextFilter,
+ final IoBuffer buf) {
+ try {
+ if (buf.remaining() >= 8) {
+ handleResponse(buf);
+ }
+ } catch (Exception ex) {
+ closeSession("Proxy handshake failed: ", ex);
+ }
+ }
+
+ /**
+ * Handle a SOCKS4/SOCKS4a response from the proxy server.
+ *
+ * @param response The response.
+ */
+ protected void handleResponse(final IoBuffer buf) throws Exception {
+ byte first = buf.get(0);
+
+ if (first != 0) {
+ throw new Exception("Socks response seems to be malformed");
+ }
+
+ byte status = buf.get(1);
+
+ buf.position(buf.position() + 8);
+ if (status == SocksProxyConstants.V4_REPLY_REQUEST_GRANTED) {
+ setHandshakeComplete();
+ } else {
+ throw new Exception("Proxy handshake failed - Code: 0x"
+ + ByteUtilities.asHex(new byte[] { status }) + " ("
+ + SocksProxyConstants.getReplyCodeAsString(status) + ")");
+ }
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks4LogicHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.socks;
+
+import java.io.UnsupportedEncodingException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.ByteUtilities;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Socks5LogicHandler.java - SOCKS5 authentication mechanisms logic handler.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class Socks5LogicHandler extends AbstractSocksLogicHandler {
+
+ private final static Logger logger = LoggerFactory
+ .getLogger(Socks5LogicHandler.class);
+
+ /**
+ * The selected authentication method.
+ */
+ private final static String SELECTED_AUTH_METHOD = Socks5LogicHandler.class
+ .getName()
+ + ".SelectedAuthMethod";
+
+ /**
+ * The current step in the handshake.
+ */
+ private final static String HANDSHAKE_STEP = Socks5LogicHandler.class
+ .getName()
+ + ".HandshakeStep";
+
+ /**
+ * The Java GSS-API context.
+ */
+ private final static String GSS_CONTEXT = Socks5LogicHandler.class
+ .getName()
+ + ".GSSContext";
+
+ /**
+ * Last GSS token received.
+ */
+ private final static String GSS_TOKEN = Socks5LogicHandler.class.getName()
+ + ".GSSToken";
+
+ public Socks5LogicHandler(final ProxyIoSession proxyIoSession) {
+ super(proxyIoSession);
+ getSession().setAttribute(HANDSHAKE_STEP,
+ SocksProxyConstants.SOCKS5_GREETING_STEP);
+ }
+
+ /**
+ * Perform any handshaking processing.
+ */
+ public synchronized void doHandshake(final NextFilter nextFilter) {
+ logger.debug(" doHandshake()");
+
+ // Send request
+ int step = ((Integer) getSession().getAttribute(HANDSHAKE_STEP))
+ .intValue();
+ writeRequest(nextFilter, request, step);
+ }
+
+ /**
+ * Encode the initial greeting packet.
+ *
+ * @param request the socks proxy request data
+ * @return the encoded buffer
+ */
+ private IoBuffer encodeInitialGreetingPacket(final SocksProxyRequest request) {
+ byte nbMethods = (byte) SocksProxyConstants.SUPPORTED_AUTH_METHODS.length;
+ IoBuffer buf = IoBuffer.allocate(2 + nbMethods);
+
+ buf.put(request.getProtocolVersion());
+ buf.put(nbMethods);
+ buf.put(SocksProxyConstants.SUPPORTED_AUTH_METHODS);
+
+ return buf;
+ }
+
+ /**
+ * Encode the proxy authorization request packet.
+ *
+ * @param request the socks proxy request data
+ * @return the encoded buffer
+ * @throws UnsupportedEncodingException
+ */
+ private IoBuffer encodeProxyRequestPacket(final SocksProxyRequest request)
+ throws UnsupportedEncodingException {
+ int len = 6;
+ byte[] host = request.getHost() != null ? request.getHost().getBytes(
+ "ASCII") : null;
+
+ InetSocketAddress adr = request.getEndpointAddress();
+ byte addressType = 0;
+
+ if (adr != null) {
+ if (adr.getAddress() instanceof Inet6Address) {
+ len += 16;
+ addressType = SocksProxyConstants.IPV6_ADDRESS_TYPE;
+ } else if (adr.getAddress() instanceof Inet4Address) {
+ len += 4;
+ addressType = SocksProxyConstants.IPV4_ADDRESS_TYPE;
+ }
+ } else {
+ len += 1 + host.length;
+ addressType = SocksProxyConstants.DOMAIN_NAME_ADDRESS_TYPE;
+ }
+
+ IoBuffer buf = IoBuffer.allocate(len);
+
+ buf.put(request.getProtocolVersion());
+ buf.put(request.getCommandCode());
+ buf.put((byte) 0x00); // Reserved
+ buf.put(addressType);
+
+ if (addressType == SocksProxyConstants.DOMAIN_NAME_ADDRESS_TYPE) {
+ buf.put((byte) host.length);
+ buf.put(host);
+ } else {
+ buf.put(request.getIpAddress());
+ }
+
+ buf.put(request.getPort());
+
+ return buf;
+ }
+
+ /**
+ * Encode the authentication packet for supported auth methods.
+ *
+ * @param request the socks proxy request data
+ * @return the encoded buffer
+ * @throws UnsupportedEncodingException
+ * @throws GSSException
+ */
+ private IoBuffer encodeAuthenticationPacket(final SocksProxyRequest request)
+ throws UnsupportedEncodingException, GSSException {
+ byte method = ((Byte) getSession().getAttribute(
+ Socks5LogicHandler.SELECTED_AUTH_METHOD)).byteValue();
+
+ if (method == SocksProxyConstants.NO_AUTH) {
+ getSession().setAttribute(HANDSHAKE_STEP,
+ SocksProxyConstants.SOCKS5_REQUEST_STEP);
+
+ } else if (method == SocksProxyConstants.GSSAPI_AUTH) {
+ GSSContext ctx = (GSSContext) getSession()
+ .getAttribute(GSS_CONTEXT);
+ if (ctx == null) {
+ GSSManager manager = GSSManager.getInstance();
+ GSSName serverName = manager.createName(request
+ .getServiceKerberosName(), null);
+ Oid krb5OID = new Oid(SocksProxyConstants.KERBEROS_V5_OID);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Available mechs:");
+ for (Oid o : manager.getMechs()) {
+ if (o.equals(krb5OID)) {
+ logger.debug("Found Kerberos V OID available");
+ }
+ logger.debug("{} with oid = {}", manager
+ .getNamesForMech(o), o);
+ }
+ }
+
+ ctx = manager.createContext(serverName, krb5OID, null,
+ GSSContext.DEFAULT_LIFETIME);
+
+ ctx.requestMutualAuth(true); // Mutual authentication
+ ctx.requestConf(false);
+ ctx.requestInteg(false);
+
+ getSession().setAttribute(GSS_CONTEXT, ctx);
+ }
+
+ byte[] token = (byte[]) getSession().getAttribute(GSS_TOKEN);
+ if (token != null) {
+ logger.debug(" Received Token[{}] = {}", token.length,
+ ByteUtilities.asHex(token));
+ }
+ IoBuffer buf = null;
+
+ if (!ctx.isEstablished()) {
+ // token is ignored on the first call
+ if (token == null) {
+ token = new byte[32];
+ }
+
+ token = ctx.initSecContext(token, 0, token.length);
+
+ // Send a token to the server if one was generated by
+ // initSecContext
+ if (token != null) {
+ logger.debug(" Sending Token[{}] = {}", token.length,
+ ByteUtilities.asHex(token));
+
+ getSession().setAttribute(GSS_TOKEN, token);
+ buf = IoBuffer.allocate(4 + token.length);
+ buf
+ .put(new byte[] {
+ SocksProxyConstants.GSSAPI_AUTH_SUBNEGOTIATION_VERSION,
+ SocksProxyConstants.GSSAPI_MSG_TYPE });
+
+ buf.put(ByteUtilities.intToNetworkByteOrder(token.length,
+ new byte[2], 0, 2));
+ buf.put(token);
+ }
+ }
+
+ return buf;
+
+ } else if (method == SocksProxyConstants.BASIC_AUTH) {
+ byte[] user = request.getUserName().getBytes("ASCII");
+ byte[] pwd = request.getPassword().getBytes("ASCII");
+ IoBuffer buf = IoBuffer.allocate(3 + user.length + pwd.length);
+
+ buf.put(SocksProxyConstants.BASIC_AUTH_SUBNEGOTIATION_VERSION);
+ buf.put((byte) user.length);
+ buf.put(user);
+ buf.put((byte) pwd.length);
+ buf.put(pwd);
+
+ return buf;
+ }
+
+ return null;
+ }
+
+ /**
+ * Encode a SOCKS5 request and send it to the proxy server.
+ */
+ private void writeRequest(final NextFilter nextFilter,
+ final SocksProxyRequest request, int step) {
+ try {
+ IoBuffer buf = null;
+
+ if (step == SocksProxyConstants.SOCKS5_GREETING_STEP) {
+ buf = encodeInitialGreetingPacket(request);
+ } else if (step == SocksProxyConstants.SOCKS5_AUTH_STEP) {
+ buf = encodeAuthenticationPacket(request);
+ if (buf == null) {
+ step = SocksProxyConstants.SOCKS5_REQUEST_STEP;
+ }
+ }
+
+ if (step == SocksProxyConstants.SOCKS5_REQUEST_STEP) {
+ buf = encodeProxyRequestPacket(request);
+ }
+
+ buf.flip();
+ writeData(nextFilter, buf);
+
+ } catch (Exception ex) {
+ closeSession("Unable to send Socks request: ", ex);
+ }
+ }
+
+ /**
+ * Handle incoming data during the handshake process. Should consume only the
+ * handshake data from the buffer, leaving any extra data in place.
+ */
+ public synchronized void messageReceived(final NextFilter nextFilter,
+ final IoBuffer buf) {
+ try {
+ int step = ((Integer) getSession().getAttribute(HANDSHAKE_STEP))
+ .intValue();
+
+ if (step == SocksProxyConstants.SOCKS5_GREETING_STEP
+ && buf.get(0) != SocksProxyConstants.SOCKS_VERSION_5) {
+ throw new IllegalStateException(
+ "Wrong socks version running on server");
+ }
+
+ if ((step == SocksProxyConstants.SOCKS5_GREETING_STEP || step == SocksProxyConstants.SOCKS5_AUTH_STEP)
+ && buf.remaining() >= 2) {
+ handleResponse(nextFilter, buf, step);
+ } else if (step == SocksProxyConstants.SOCKS5_REQUEST_STEP
+ && buf.remaining() >= 5) {
+ handleResponse(nextFilter, buf, step);
+ }
+ } catch (Exception ex) {
+ closeSession("Proxy handshake failed: ", ex);
+ }
+ }
+
+ /**
+ * Handle a SOCKS v5 response from the proxy server.
+ */
+ protected void handleResponse(final NextFilter nextFilter,
+ final IoBuffer buf, int step) throws Exception {
+ int len = 2;
+ if (step == SocksProxyConstants.SOCKS5_GREETING_STEP) {
+ // Send greeting message
+ byte method = buf.get(1);
+
+ if (method == SocksProxyConstants.NO_ACCEPTABLE_AUTH_METHOD) {
+ throw new IllegalStateException(
+ "No acceptable authentication method to use the socks proxy server");
+ }
+
+ getSession().setAttribute(SELECTED_AUTH_METHOD, new Byte(method));
+
+ } else if (step == SocksProxyConstants.SOCKS5_AUTH_STEP) {
+ // Authentication to the SOCKS server
+ byte method = ((Byte) getSession().getAttribute(
+ Socks5LogicHandler.SELECTED_AUTH_METHOD)).byteValue();
+
+ if (method == SocksProxyConstants.GSSAPI_AUTH) {
+ int oldPos = buf.position();
+
+ if (buf.get(0) != 0x01) {
+ throw new IllegalStateException("Authentication failed");
+ }
+ if (buf.get(1) == 0xFF) {
+ throw new IllegalStateException(
+ "Authentication failed: GSS API Security Context Failure");
+ }
+
+ if (buf.remaining() >= 2) {
+ byte[] size = new byte[2];
+ buf.get(size);
+ int s = ByteUtilities.makeIntFromByte2(size);
+ if (buf.remaining() >= s) {
+ byte[] token = new byte[s];
+ buf.get(token);
+ getSession().setAttribute(GSS_TOKEN, token);
+ len = 0;
+ } else {
+ //buf.position(oldPos);
+ return;
+ }
+ } else {
+ buf.position(oldPos);
+ return;
+ }
+ } else if (buf.get(1) != SocksProxyConstants.V5_REPLY_SUCCEEDED) {
+ throw new IllegalStateException("Authentication failed");
+ }
+
+ } else if (step == SocksProxyConstants.SOCKS5_REQUEST_STEP) {
+ // Send the request
+ byte addressType = buf.get(3);
+ len = 6;
+ if (addressType == SocksProxyConstants.IPV6_ADDRESS_TYPE) {
+ len += 16;
+ } else if (addressType == SocksProxyConstants.IPV4_ADDRESS_TYPE) {
+ len += 4;
+ } else if (addressType == SocksProxyConstants.DOMAIN_NAME_ADDRESS_TYPE) {
+ len += 1 + ((short) buf.get(4));
+ } else {
+ throw new IllegalStateException("Unknwon address type");
+ }
+
+ if (buf.remaining() >= len) {
+ // handle response
+ byte status = buf.get(1);
+ logger.debug(" response status: {}", SocksProxyConstants
+ .getReplyCodeAsString(status));
+
+ if (status == SocksProxyConstants.V5_REPLY_SUCCEEDED) {
+ buf.position(buf.position() + len);
+ setHandshakeComplete();
+ return;
+ } else
+ throw new Exception("Proxy handshake failed - Code: 0x"
+ + ByteUtilities.asHex(new byte[] { status }));
+ } else
+ return;
+ }
+
+ if (len > 0) {
+ buf.position(buf.position() + len);
+ }
+
+ // Move to the handshaking next step if not in the middle of
+ // the authentication process
+ boolean isAuthenticating = false;
+ if (step == SocksProxyConstants.SOCKS5_AUTH_STEP) {
+ byte method = ((Byte) getSession().getAttribute(
+ Socks5LogicHandler.SELECTED_AUTH_METHOD)).byteValue();
+ if (method == SocksProxyConstants.GSSAPI_AUTH) {
+ GSSContext ctx = (GSSContext) getSession().getAttribute(
+ GSS_CONTEXT);
+ if (ctx == null || !ctx.isEstablished()) {
+ isAuthenticating = true;
+ }
+ }
+ }
+
+ if (!isAuthenticating) {
+ getSession().setAttribute(HANDSHAKE_STEP, ++step);
+ }
+
+ doHandshake(nextFilter);
+ }
+
+ @Override
+ protected void closeSession(String message) {
+ GSSContext ctx = (GSSContext) getSession().getAttribute(GSS_CONTEXT);
+ if (ctx != null) {
+ try {
+ ctx.dispose();
+ } catch (GSSException e) {
+ e.printStackTrace();
+ super.closeSession(message, e);
+ return;
+ }
+ }
+ super.closeSession(message);
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/Socks5LogicHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.socks;
+
+/**
+ * SocksProxyConstants.java - SOCKS proxy constants.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class SocksProxyConstants {
+ /**
+ * SOCKS versions filed values.
+ */
+ public final static byte SOCKS_VERSION_4 = 0x04;
+
+ public final static byte SOCKS_VERSION_5 = 0x05;
+
+ public final static byte TERMINATOR = 0x00;
+
+ // Invalid IP used in SOCKS 4a protocol to specify that
+ // client can't resolve the destination host's domain name.
+ public final static byte[] FAKE_IP = new byte[] { 0, 0, 0, 10 };
+
+ /**
+ * Command code.
+ */
+ public final static byte ESTABLISH_TCPIP_STREAM = 0x01;
+
+ public final static byte ESTABLISH_TCPIP_BIND = 0x02;
+
+ public final static byte ESTABLISH_UDP_ASSOCIATE = 0x03;
+
+ /**
+ * Server reply codes of protocol v4 and v4a.
+ */
+ public final static byte V4_REPLY_REQUEST_GRANTED = 0x5a;
+
+ public final static byte V4_REPLY_REQUEST_REJECTED_OR_FAILED = 0x5b;
+
+ public final static byte V4_REPLY_REQUEST_FAILED_NO_IDENTD = 0x5c;
+
+ public final static byte V4_REPLY_REQUEST_FAILED_ID_NOT_CONFIRMED = 0x5d;
+
+ /**
+ * SOCKS v5 server reply codes.
+ */
+ public final static byte V5_REPLY_SUCCEEDED = 0x00;
+
+ public final static byte V5_REPLY_GENERAL_FAILURE = 0x01;
+
+ public final static byte V5_REPLY_NOT_ALLOWED = 0x02;
+
+ public final static byte V5_REPLY_NETWORK_UNREACHABLE = 0x03;
+
+ public final static byte V5_REPLY_HOST_UNREACHABLE = 0x04;
+
+ public final static byte V5_REPLY_CONNECTION_REFUSED = 0x05;
+
+ public final static byte V5_REPLY_TTL_EXPIRED = 0x06;
+
+ public final static byte V5_REPLY_COMMAND_NOT_SUPPORTED = 0x07;
+
+ public final static byte V5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 0x08;
+
+ /**
+ * SOCKS v5 address types.
+ */
+ public final static byte IPV4_ADDRESS_TYPE = 0x01;
+
+ public final static byte DOMAIN_NAME_ADDRESS_TYPE = 0x03;
+
+ public final static byte IPV6_ADDRESS_TYPE = 0x04;
+
+ /**
+ * SOCKS v5 handshake steps.
+ */
+ public final static int SOCKS5_GREETING_STEP = 0;
+
+ public final static int SOCKS5_AUTH_STEP = 1;
+
+ public final static int SOCKS5_REQUEST_STEP = 2;
+
+ /**
+ * SOCKS v5 authentication methods.
+ */
+ public final static byte NO_AUTH = 0x00;
+
+ public final static byte GSSAPI_AUTH = 0x01;
+
+ public final static byte BASIC_AUTH = 0x02;
+
+ public final static byte NO_ACCEPTABLE_AUTH_METHOD = (byte) 0xFF;
+
+ public final static byte[] SUPPORTED_AUTH_METHODS = new byte[] { NO_AUTH,
+ GSSAPI_AUTH, BASIC_AUTH };
+
+ public final static byte BASIC_AUTH_SUBNEGOTIATION_VERSION = 0x01;
+
+ public final static byte GSSAPI_AUTH_SUBNEGOTIATION_VERSION = 0x01;
+
+ public final static byte GSSAPI_MSG_TYPE = 0x01;
+
+ // Kerberos providers
+ public final static String KERBEROS_V5_OID = "1.2.840.113554.1.2.2";
+
+ public final static String MS_KERBEROS_V5_OID = "1.2.840.48018.1.2.2";
+
+ // Microsoft NTLM security support provider
+ public final static String NTLMSSP_OID = "1.3.6.1.4.1.311.2.2.10";
+
+ /**
+ * Return the string for the given code.
+ *
+ * @param code the received code
+ * @return
+ */
+ public final static String getReplyCodeAsString(byte code) {
+ switch (code) {
+ // v4 & v4a codes
+ case V4_REPLY_REQUEST_GRANTED:
+ return "Request granted";
+ case V4_REPLY_REQUEST_REJECTED_OR_FAILED:
+ return "Request rejected or failed";
+ case V4_REPLY_REQUEST_FAILED_NO_IDENTD:
+ return "Request failed because client is not running identd (or not reachable from the server)";
+ case V4_REPLY_REQUEST_FAILED_ID_NOT_CONFIRMED:
+ return "Request failed because client's identd could not confirm the user ID string in the request";
+
+ // v5 codes
+ case V5_REPLY_SUCCEEDED:
+ return "Request succeeded";
+ case V5_REPLY_GENERAL_FAILURE:
+ return "Request failed: general SOCKS server failure";
+ case V5_REPLY_NOT_ALLOWED:
+ return "Request failed: connection not allowed by ruleset";
+ case V5_REPLY_NETWORK_UNREACHABLE:
+ return "Request failed: network unreachable";
+ case V5_REPLY_HOST_UNREACHABLE:
+ return "Request failed: host unreachable";
+ case V5_REPLY_CONNECTION_REFUSED:
+ return "Request failed: connection refused";
+ case V5_REPLY_TTL_EXPIRED:
+ return "Request failed: TTL expired";
+ case V5_REPLY_COMMAND_NOT_SUPPORTED:
+ return "Request failed: command not supported";
+ case V5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED:
+ return "Request failed: address type not supported";
+
+ default:
+ return "Unknown code";
+ }
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyConstants.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.handlers.socks;
+
+import java.net.InetSocketAddress;
+
+import org.apache.mina.proxy.handlers.ProxyRequest;
+
+/**
+ * SocksProxyRequest.java - Wrapper class for SOCKS requests.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class SocksProxyRequest extends ProxyRequest {
+
+ private byte protocolVersion;
+
+ private byte commandCode;
+
+ private String userName;
+
+ private String password;
+
+ private String host;
+
+ private int port;
+
+ private String serviceKerberosName;
+
+ /**
+ * Constructor used when making a SOCKS4/SOCKS5 request.
+ */
+ public SocksProxyRequest(byte protocolVersion, byte commandCode,
+ InetSocketAddress endpointAddress, String userName) {
+ super(endpointAddress);
+ this.protocolVersion = protocolVersion;
+ this.commandCode = commandCode;
+ this.userName = userName;
+ }
+
+ /**
+ * Constructor used when making a SOCKS4a request.
+ */
+ public SocksProxyRequest(byte commandCode, String host, int port,
+ String userName) {
+ this.protocolVersion = SocksProxyConstants.SOCKS_VERSION_4;
+ this.commandCode = commandCode;
+ this.userName = userName;
+ this.host = host;
+ this.port = port;
+ }
+
+ public byte[] getIpAddress() {
+ if (getEndpointAddress() == null)
+ return SocksProxyConstants.FAKE_IP;
+ else
+ return getEndpointAddress().getAddress().getAddress();
+ }
+
+ public byte[] getPort() {
+ byte[] port = new byte[2];
+ int p = (int) (getEndpointAddress() == null ? this.port
+ : getEndpointAddress().getPort());
+ port[1] = (byte) p;
+ port[0] = (byte) (p >> 8);
+ return port;
+ }
+
+ public byte getCommandCode() {
+ return commandCode;
+ }
+
+ public byte getProtocolVersion() {
+ return protocolVersion;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public synchronized final String getHost() {
+ if (host == null) {
+ if (getEndpointAddress() != null) {
+ host = getEndpointAddress().getHostName();
+ }
+ }
+
+ return host;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getServiceKerberosName() {
+ return serviceKerberosName;
+ }
+
+ public void setServiceKerberosName(String serviceKerberosName) {
+ this.serviceKerberosName = serviceKerberosName;
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/socks/SocksProxyRequest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.session;
+
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.proxy.ProxyConnector;
+import org.apache.mina.proxy.ProxyLogicHandler;
+import org.apache.mina.proxy.event.IoSessionEventQueue;
+import org.apache.mina.proxy.filter.ProxyFilter;
+import org.apache.mina.proxy.handlers.ProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
+
+/**
+ * ProxyIoSession.java - Class that contains all informations for the current proxy
+ * authentication session.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class ProxyIoSession {
+
+ public final static String PROXY_SESSION = ProxyConnector.class.getName()
+ + ".ProxySession";
+
+ private final static String DEFAULT_ENCODING = "ISO-8859-1";
+
+ /**
+ * The list contains the authentication methods to use.
+ * The order in the list is revelant : if first method is available then it will be used etc ...
+ */
+ private List<HttpAuthenticationMethods> preferedOrder;
+
+ /**
+ * The request to send to the proxy.
+ */
+ private ProxyRequest request;
+
+ /**
+ * The currently selected proxy handler.
+ */
+ private ProxyLogicHandler handler;
+
+ /**
+ * Parent {@link ProxyFilter} handling the session.
+ */
+ private ProxyFilter proxyFilter;
+
+ /**
+ * The session.
+ */
+ private IoSession session;
+
+ /**
+ * The connector.
+ */
+ private ProxyConnector connector;
+
+ /**
+ * Address of the proxy server.
+ */
+ private InetSocketAddress proxyAddress = null;
+
+ /**
+ * A flag that indicates that proxy closed the connection before handshake is done. So
+ * we need to reconnect to the proxy to continue handshaking process.
+ */
+ private boolean reconnectionNeeded = false;
+
+ /**
+ * Name of the charset used for string encoding & decoding.
+ */
+ private String charsetName;
+
+ /**
+ * The session event queue.
+ */
+ private IoSessionEventQueue eventQueue;
+
+ /**
+ * Set to true when an exception has been thrown.
+ */
+ private boolean authenticationFailed;
+
+ public IoSessionEventQueue getEventQueue() {
+ return eventQueue;
+ }
+
+ public ProxyIoSession(InetSocketAddress proxyAddress, ProxyRequest request) {
+ setProxyAddress(proxyAddress);
+ setRequest(request);
+ }
+
+ public List<HttpAuthenticationMethods> getPreferedOrder() {
+ return preferedOrder;
+ }
+
+ public void setPreferedOrder(List<HttpAuthenticationMethods> preferedOrder) {
+ this.preferedOrder = preferedOrder;
+ }
+
+ public ProxyLogicHandler getHandler() {
+ return handler;
+ }
+
+ public void setHandler(ProxyLogicHandler handler) {
+ this.handler = handler;
+ }
+
+ public ProxyFilter getProxyFilter() {
+ return proxyFilter;
+ }
+
+ /**
+ * Note : Please do not call this method from your code it could result in an
+ * unexpected behaviour.
+ */
+ public void setProxyFilter(ProxyFilter proxyFilter) {
+ this.proxyFilter = proxyFilter;
+ }
+
+ public ProxyRequest getRequest() {
+ return request;
+ }
+
+ public void setRequest(ProxyRequest request) {
+ if (request == null) {
+ throw new NullPointerException("request cannot be null");
+ }
+
+ this.request = request;
+ }
+
+ public IoSession getSession() {
+ return session;
+ }
+
+ /**
+ * Note : Please do not call this method from your code it could result in an
+ * unexpected behaviour.
+ */
+ public void setSession(IoSession session) {
+ this.session = session;
+ this.eventQueue = new IoSessionEventQueue(this);
+ }
+
+ public ProxyConnector getConnector() {
+ return connector;
+ }
+
+ /**
+ * Note : Please do not call this method from your code it could result in an
+ * unexpected behaviour.
+ */
+ public void setConnector(ProxyConnector connector) {
+ this.connector = connector;
+ }
+
+ public InetSocketAddress getProxyAddress() {
+ return proxyAddress;
+ }
+
+ public void setProxyAddress(InetSocketAddress proxyAddress) {
+ if (proxyAddress == null) {
+ throw new IllegalArgumentException("proxyAddress cannot be null");
+ }
+
+ if (!(proxyAddress instanceof InetSocketAddress)) {
+ throw new NullPointerException("Unsupported proxyAddress type "
+ + proxyAddress.getClass().getName());
+ }
+
+ this.proxyAddress = proxyAddress;
+ }
+
+ public boolean isReconnectionNeeded() {
+ return reconnectionNeeded;
+ }
+
+ /**
+ * Note : Please do not call this method from your code it could result in an
+ * unexpected behaviour.
+ */
+ public void setReconnectionNeeded(boolean reconnectionNeeded) {
+ this.reconnectionNeeded = reconnectionNeeded;
+ }
+
+ public Charset getCharset() {
+ return Charset.forName(getCharsetName());
+ }
+
+ public synchronized String getCharsetName() {
+ if (charsetName == null) {
+ charsetName = DEFAULT_ENCODING;
+ }
+
+ return charsetName;
+ }
+
+ public void setCharsetName(String charsetName) {
+ this.charsetName = charsetName;
+ }
+
+ public boolean isAuthenticationFailed() {
+ return authenticationFailed;
+ }
+
+ public void setAuthenticationFailed(boolean authenticationFailed) {
+ this.authenticationFailed = authenticationFailed;
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSession.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.session;
+
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.session.IoSessionInitializer;
+
+/**
+ * ProxyIoSessionInitializer.java - {@link IoSessionInitializer} wrapper class to inject the
+ * {@link ProxyIoSession} object that contains all the attributes of the target connection
+ * into the {@link IoSession}.
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class ProxyIoSessionInitializer<T extends ConnectFuture> implements
+ IoSessionInitializer<T> {
+ private final IoSessionInitializer<T> wrappedSessionInitializer;
+
+ private final ProxyIoSession proxyIoSession;
+
+ public ProxyIoSessionInitializer(
+ final IoSessionInitializer<T> wrappedSessionInitializer,
+ final ProxyIoSession proxyIoSession) {
+ this.wrappedSessionInitializer = wrappedSessionInitializer;
+ this.proxyIoSession = proxyIoSession;
+ }
+
+ public ProxyIoSession getProxySession() {
+ return proxyIoSession;
+ }
+
+ public void initializeSession(final IoSession session, T future) {
+ if (wrappedSessionInitializer != null) {
+ wrappedSessionInitializer.initializeSession(session, future);
+ }
+
+ if (proxyIoSession != null) {
+ proxyIoSession.setSession(session);
+ session.setAttribute(ProxyIoSession.PROXY_SESSION, proxyIoSession);
+ }
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/session/ProxyIoSessionInitializer.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.utils;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * ByteUtilities.java - Byte manipulation functions.
+ *
+ * @author <a href="mailto:doe_wanted@yahoo.fr">Edouard De Oliveira</a>
+ * @version $Revision:$, $Date:$
+ */
+public class ByteUtilities {
+ /**
+ * Returns the integer represented by 4 bytes in network byte order.
+ */
+ public static int networkByteOrderToInt(byte[] buf, int start, int count) {
+ if (count > 4) {
+ throw new IllegalArgumentException(
+ "Cannot handle more than 4 bytes");
+ }
+
+ int result = 0;
+
+ for (int i = 0; i < count; i++) {
+ result <<= 8;
+ result |= ((int) buf[start + i] & 0xff);
+ }
+
+ return result;
+ }
+
+ /**
+ * Encodes an integer into 4 bytes in network byte order in the buffer
+ * supplied.
+ */
+ public static byte[] intToNetworkByteOrder(int num, byte[] buf, int start,
+ int count) {
+ if (count > 4) {
+ throw new IllegalArgumentException(
+ "Cannot handle more than 4 bytes");
+ }
+
+ for (int i = count - 1; i >= 0; i--) {
+ buf[start + i] = (byte) (num & 0xff);
+ num >>>= 8;
+ }
+
+ return buf;
+ }
+
+ /**
+ * Write a 16 bit short as LITTLE_ENDIAN.
+ *
+ * @param v the short to write
+ */
+ public final static byte[] writeShort(short v) {
+ return writeShort(v, new byte[2], 0);
+ }
+
+ /**
+ * Write a 16 bit short as LITTLE_ENDIAN to
+ * the given array <code>b</code> at offset <code>offset</code>.
+ *
+ * @param v the short to write
+ */
+ public final static byte[] writeShort(short v, byte[] b, int offset) {
+ b[offset] = (byte) v;
+ b[offset + 1] = (byte) (v >> 8);
+
+ return b;
+ }
+
+ /**
+ * Write a 32 bit int as LITTLE_ENDIAN.
+ *
+ * @param v the int to write
+ */
+ public final static byte[] writeInt(int v) {
+ return writeInt(v, new byte[4], 0);
+ }
+
+ /**
+ * Write a 32 bit int as LITTLE_ENDIAN to
+ * the given array <code>b</code> at offset <code>offset</code>.
+ *
+ * @param v the int to write
+ */
+ public final static byte[] writeInt(int v, byte[] b, int offset) {
+ b[offset] = (byte) v;
+ b[offset + 1] = (byte) (v >> 8);
+ b[offset + 2] = (byte) (v >> 16);
+ b[offset + 3] = (byte) (v >> 24);
+
+ return b;
+ }
+
+ public final static void changeWordEndianess(byte[] b, int offset,
+ int length) {
+ byte tmp;
+
+ for (int i = offset; i < offset + length; i += 4) {
+ tmp = b[i];
+ b[i] = b[i + 3];
+ b[i + 3] = tmp;
+ tmp = b[i + 1];
+ b[i + 1] = b[i + 2];
+ b[i + 2] = tmp;
+ }
+ }
+
+ public final static void changeByteEndianess(byte[] b, int offset,
+ int length) {
+ byte tmp;
+
+ for (int i = offset; i < offset + length; i += 2) {
+ tmp = b[i];
+ b[i] = b[i + 1];
+ b[i + 1] = tmp;
+ }
+ }
+
+ public final static byte[] getOEMStringAsByteArray(String s)
+ throws UnsupportedEncodingException {
+ return s.getBytes("ASCII");
+ }
+
+ public final static byte[] getUTFStringAsByteArray(String s)
+ throws UnsupportedEncodingException {
+ return s.getBytes("UTF-16LE");
+ }
+
+ public final static byte[] encodeString(String s, boolean useUnicode)
+ throws UnsupportedEncodingException {
+ if (useUnicode) {
+ return getUTFStringAsByteArray(s);
+ } else {
+ return getOEMStringAsByteArray(s);
+ }
+ }
+
+ public static String asHex(byte[] bytes) {
+ return asHex(bytes, null);
+ }
+
+ public static String asHex(byte[] bytes, String separator) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < bytes.length; i++) {
+ String code = Integer.toHexString(bytes[i] & 0xFF);
+ if ((bytes[i] & 0xFF) < 16) {
+ sb.append('0');
+ }
+
+ sb.append(code);
+
+ if (separator != null && i < bytes.length - 1) {
+ sb.append(separator);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public static byte[] asByteArray(String hex) {
+ byte[] bts = new byte[hex.length() / 2];
+ for (int i = 0; i < bts.length; i++) {
+ bts[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2),
+ 16);
+ }
+
+ return bts;
+ }
+
+ public static final int makeIntFromByte4(byte[] b) {
+ return makeIntFromByte4(b, 0);
+ }
+
+ public static final int makeIntFromByte4(byte[] b, int offset) {
+ return b[offset] << 24 | (b[offset + 1] & 0xff) << 16
+ | (b[offset + 2] & 0xff) << 8 | (b[offset + 3] & 0xff);
+ }
+
+ public static final int makeIntFromByte2(byte[] b) {
+ return makeIntFromByte2(b, 0);
+ }
+
+ public static final int makeIntFromByte2(byte[] b, int offset) {
+ return (b[offset] & 0xff) << 8 | (b[offset + 1] & 0xff);
+ }
+
+ /**
+ * Return true if the flag <code>testFlag</code> is set in the
+ * <code>flags</code> flagset.
+ *
+ * @param flagset the flagset to test
+ * @param testFlag the flag we search the presence of
+ * @return true if testFlag is present in the flagset, false otherwise.
+ */
+ public final static boolean isFlagSet(int flagSet, int testFlag) {
+ return (flagSet & testFlag) > 0;
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/ByteUtilities.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.mina.proxy.utils;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.textline.LineDelimiter;
+
+/**
+ * IoBufferDecoder.java - Handles an {@link IoBuffer} decoder which supports two methods :
+ * - dynamic delimiter decoding
+ * - fixed length content reading
+ *
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class IoBufferDecoder {
+
+ public class DecodingContext {
+
+ private IoBuffer decodedBuffer;
+
+ private IoBuffer delimiter;
+
+ private int contentLength = -1;
+
+ private int matchCount = 0;
+
+ public void clean() {
+ contentLength = -1;
+ matchCount = 0;
+ decodedBuffer = null;
+ }
+
+ public int getContentLength() {
+ return contentLength;
+ }
+
+ public void setContentLength(int contentLength) {
+ this.contentLength = contentLength;
+ }
+
+ public int getMatchCount() {
+ return matchCount;
+ }
+
+ public void setMatchCount(int matchCount) {
+ this.matchCount = matchCount;
+ }
+
+ public IoBuffer getDecodedBuffer() {
+ return decodedBuffer;
+ }
+
+ public void setDecodedBuffer(IoBuffer decodedBuffer) {
+ this.decodedBuffer = decodedBuffer;
+ }
+
+ public IoBuffer getDelimiter() {
+ return delimiter;
+ }
+
+ public void setDelimiter(IoBuffer delimiter) {
+ this.delimiter = delimiter;
+ }
+ }
+
+ private DecodingContext ctx = new DecodingContext();
+
+ /**
+ * Creates a new instance that uses specified <tt>delimiter</tt> byte array as a
+ * message delimiter.
+ */
+ public IoBufferDecoder(byte[] delimiter) {
+ setDelimiter(delimiter, true);
+ }
+
+ /**
+ * Creates a new instance that will read messages of <tt>contentLength</tt> bytes.
+ */
+ public IoBufferDecoder(int contentLength) {
+ setContentLength(contentLength, false);
+ }
+
+ /**
+ * Sets the the length of the content line to be decoded.
+ * When set, it overrides the dynamic delimiter setting. The default value is <tt>-1</tt>.
+ * Content length method will be used for decoding on the next decodeOnce call.
+ * Delimiter matching is reset only if <tt>resetMatchCount</tt> is true.
+ */
+ public void setContentLength(int contentLength, boolean resetMatchCount) {
+ if (contentLength <= 0) {
+ throw new IllegalArgumentException("contentLength: "
+ + contentLength);
+ }
+
+ ctx.setContentLength(contentLength);
+ if (resetMatchCount) {
+ ctx.setMatchCount(0);
+ }
+ }
+
+ /**
+ * Dynamically sets a new delimiter. Next time
+ * {@link IoBufferDecoder#decodeOnce(IoSession, int) } will be called it will use the new
+ * delimiter. Delimiter matching is reset only if <tt>resetMatchCount</tt> is true but
+ * decoding will continue from current position.
+ *
+ * NB : Delimiter {@link LineDelimiter#AUTO} is not allowed.
+ */
+ public void setDelimiter(byte[] delim, boolean resetMatchCount) {
+ if (delim == null) {
+ throw new NullPointerException("Null delimiter not allowed");
+ }
+
+ // Convert delimiter to IoBuffer.
+ IoBuffer delimiter = IoBuffer.allocate(delim.length);
+ delimiter.put(delim);
+ delimiter.flip();
+
+ ctx.setDelimiter(delimiter);
+ ctx.setContentLength(-1);
+ if (resetMatchCount) {
+ ctx.setMatchCount(0);
+ }
+ }
+
+ /**
+ * Will return null unless it has enough data to decode. If <code>contentLength</code>
+ * is set then it tries to retrieve <code>contentLength</code> bytes from the buffer
+ * otherwise it will scan the buffer to find the data <code>delimiter</code> and return
+ * all the data and the trailing delimiter.
+ */
+ public IoBuffer decodeFully(IoBuffer in) {
+ int contentLength = ctx.getContentLength();
+ IoBuffer decodedBuffer = ctx.getDecodedBuffer();
+
+ int oldLimit = in.limit();
+
+ // Retrieve fixed length content
+ if (contentLength > -1) {
+ if (decodedBuffer == null) {
+ decodedBuffer = IoBuffer.allocate(contentLength).setAutoExpand(
+ true);
+ }
+
+ if (in.remaining() < contentLength) {
+ int readBytes = in.remaining();
+ decodedBuffer.put(in);
+ ctx.setDecodedBuffer(decodedBuffer);
+ ctx.setContentLength(contentLength - readBytes);
+ return null;
+
+ } else {
+ int newLimit = in.position() + contentLength;
+ in.limit(newLimit);
+ decodedBuffer.put(in);
+ decodedBuffer.flip();
+ in.limit(oldLimit);
+ ctx.clean();
+
+ return decodedBuffer;
+ }
+ }
+
+ // Not a fixed length matching so try to find a delimiter match
+ int oldPos = in.position();
+ int matchCount = ctx.getMatchCount();
+ IoBuffer delimiter = ctx.getDelimiter();
+
+ while (in.hasRemaining()) {
+ byte b = in.get();
+ if (delimiter.get(matchCount) == b) {
+ matchCount++;
+ if (matchCount == delimiter.limit()) {
+ // Found a match.
+ int pos = in.position();
+ in.position(oldPos);
+
+ in.limit(pos);
+
+ if (decodedBuffer == null) {
+ decodedBuffer = IoBuffer.allocate(in.remaining())
+ .setAutoExpand(true);
+ }
+
+ decodedBuffer.put(in);
+ decodedBuffer.flip();
+
+ in.limit(oldLimit);
+ ctx.clean();
+
+ return decodedBuffer;
+ }
+ } else {
+ in.position(Math.max(0, in.position() - matchCount));
+ matchCount = 0;
+ }
+ }
+
+ // Copy remainder from buf.
+ if (in.remaining() > 0) {
+ in.position(oldPos);
+ decodedBuffer.put(in);
+ in.position(in.limit());
+ }
+
+ // Save decoding state
+ ctx.setMatchCount(matchCount);
+ ctx.setDecodedBuffer(decodedBuffer);
+
+ return decodedBuffer;
+ }
+}
\ No newline at end of file
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/utils/IoBufferDecoder.java
------------------------------------------------------------------------------
svn:mime-type = text/plain