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