001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.proxy.handlers.socks; 021 022import java.util.Arrays; 023 024import org.apache.mina.core.buffer.IoBuffer; 025import org.apache.mina.core.filterchain.IoFilter.NextFilter; 026import org.apache.mina.proxy.session.ProxyIoSession; 027import org.apache.mina.proxy.utils.ByteUtilities; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Socks4LogicHandler.java - SOCKS4/SOCKS4a authentication mechanisms logic handler. 033 * 034 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 035 * @since MINA 2.0.0-M3 036 */ 037public class Socks4LogicHandler extends AbstractSocksLogicHandler { 038 039 private final static Logger logger = LoggerFactory.getLogger(Socks4LogicHandler.class); 040 041 /** 042 * @see AbstractSocksLogicHandler#AbstractSocksLogicHandler(ProxyIoSession) 043 * 044 * @param proxyIoSession The original session 045 */ 046 public Socks4LogicHandler(final ProxyIoSession proxyIoSession) { 047 super(proxyIoSession); 048 } 049 050 /** 051 * Perform the handshake. 052 * 053 * @param nextFilter the next filter 054 */ 055 public void doHandshake(final NextFilter nextFilter) { 056 logger.debug(" doHandshake()"); 057 058 // Send request 059 writeRequest(nextFilter, request); 060 } 061 062 /** 063 * Encode a SOCKS4/SOCKS4a request and writes it to the next filter 064 * so it can be sent to the proxy server. 065 * 066 * @param nextFilter the next filter 067 * @param request the request to send. 068 */ 069 protected void writeRequest(final NextFilter nextFilter, final SocksProxyRequest request) { 070 try { 071 boolean isV4ARequest = Arrays.equals(request.getIpAddress(), SocksProxyConstants.FAKE_IP); 072 byte[] userID = request.getUserName().getBytes("ASCII"); 073 byte[] host = isV4ARequest ? request.getHost().getBytes("ASCII") : null; 074 075 int len = 9 + userID.length; 076 077 if (isV4ARequest) { 078 len += host.length + 1; 079 } 080 081 IoBuffer buf = IoBuffer.allocate(len); 082 083 buf.put(request.getProtocolVersion()); 084 buf.put(request.getCommandCode()); 085 buf.put(request.getPort()); 086 buf.put(request.getIpAddress()); 087 buf.put(userID); 088 buf.put(SocksProxyConstants.TERMINATOR); 089 090 if (isV4ARequest) { 091 buf.put(host); 092 buf.put(SocksProxyConstants.TERMINATOR); 093 } 094 095 if (isV4ARequest) { 096 logger.debug(" sending SOCKS4a request"); 097 } else { 098 logger.debug(" sending SOCKS4 request"); 099 } 100 101 buf.flip(); 102 writeData(nextFilter, buf); 103 } catch (Exception ex) { 104 closeSession("Unable to send Socks request: ", ex); 105 } 106 } 107 108 /** 109 * Handle incoming data during the handshake process. Should consume only the 110 * handshake data from the buffer, leaving any extra data in place. 111 * 112 * @param nextFilter the next filter 113 * @param buf the server response data buffer 114 */ 115 public void messageReceived(final NextFilter nextFilter, final IoBuffer buf) { 116 try { 117 if (buf.remaining() >= SocksProxyConstants.SOCKS_4_RESPONSE_SIZE) { 118 handleResponse(buf); 119 } 120 } catch (Exception ex) { 121 closeSession("Proxy handshake failed: ", ex); 122 } 123 } 124 125 /** 126 * Handle a SOCKS4/SOCKS4a response from the proxy server. Test 127 * the response buffer reply code and call {@link #setHandshakeComplete()} 128 * if access is granted. 129 * 130 * @param buf the buffer holding the server response data. 131 * @throws Exception if server response is malformed or if request is rejected 132 * by the proxy server. 133 */ 134 protected void handleResponse(final IoBuffer buf) throws Exception { 135 byte first = buf.get(0); 136 137 if (first != 0) { 138 throw new Exception("Socks response seems to be malformed"); 139 } 140 141 byte status = buf.get(1); 142 143 // Consumes all the response data from the buffer 144 buf.position(buf.position() + SocksProxyConstants.SOCKS_4_RESPONSE_SIZE); 145 146 if (status == SocksProxyConstants.V4_REPLY_REQUEST_GRANTED) { 147 setHandshakeComplete(); 148 } else { 149 throw new Exception("Proxy handshake failed - Code: 0x" + ByteUtilities.asHex(new byte[] { status }) + " (" 150 + SocksProxyConstants.getReplyCodeAsString(status) + ")"); 151 } 152 } 153}