org.simplejavamail.internal.authenticatedsockssupport.socks5server.Socks5Handler Maven / Gradle / Ivy
/*
* Copyright © 2009 Benny Bottema ([email protected])
*
* Licensed 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.simplejavamail.internal.authenticatedsockssupport.socks5server;
import org.simplejavamail.api.internal.authenticatedsockssupport.common.Socks5Bridge;
import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.simplejavamail.internal.authenticatedsockssupport.socks5server.io.SocketPipe;
import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.CommandMessage;
import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.CommandResponseMessage;
import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.MethodSelectionMessage;
import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.ServerReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
public class Socks5Handler implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(Socks5Handler.class);
private static final Logger SOCKS5BRIDGE_LOGGER = LoggerFactory.getLogger("socks5bridge");
private static final byte[] METHOD_SELECTION_RESPONSE = { (byte) 0x5, (byte) 0x00 };
private static final int CONNECT_COMMAND = 0x01;
public static final int VERSION = 0x5;
private final SocksSession session;
private final Socks5Bridge socks5Bridge;
public Socks5Handler(final SocksSession session, final Socks5Bridge socks5Bridge) {
this.session = session;
this.socks5Bridge = socks5Bridge;
}
@Override
public void run() {
try {
handle(session, socks5Bridge);
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
session.close();
}
}
private void handle(final SocksSession session, final Socks5Bridge socks5Bridge)
throws IOException {
if (MethodSelectionMessage.readVersion(session.getInputStream()) != VERSION) {
throw new SocksException("Protocol error");
}
LOGGER.debug("SESSION[{}]", session.getId());
// send select method.
session.write(METHOD_SELECTION_RESPONSE);
final CommandMessage commandMessage = new CommandMessage();
commandMessage.read(session.getInputStream());
// If there is a SOCKS exception in command message, It will send a right response to client.
if (commandMessage.hasSocksException()) {
final ServerReply serverReply = commandMessage.getSocksServerReplyException().getServerReply();
session.write(CommandResponseMessage.getBytes(serverReply));
LOGGER.debug("SESSION[{}] will close, because {}", session.getId(), serverReply);
return;
}
if (commandMessage.getCommand() != CONNECT_COMMAND) {
throw new SocksException("Only CONNECT command is supported");
}
doConnect(session, commandMessage, socks5Bridge);
}
private void doConnect(final SocksSession session, final CommandMessage commandMessage, final Socks5Bridge socks5Bridge)
throws IOException {
ServerReply reply;
Socket socket = null;
int bindPort = 0;
final InetAddress targetServerAddress = commandMessage.getInetAddress();
final int targetServerPort = commandMessage.getPort();
// set default bind address.
InetAddress bindAddress = new InetSocketAddress(0).getAddress();
try {
// the magic happens here...
socket = socks5Bridge.connect(String.valueOf(this.session.getId()), targetServerAddress, targetServerPort);
bindAddress = socket.getLocalAddress();
bindPort = socket.getLocalPort();
reply = ServerReply.SUCCEEDED;
} catch (final IOException e) {
//noinspection IfCanBeSwitch
if (e.getMessage().equals("Connection refused")) {
reply = ServerReply.CONNECTION_REFUSED;
} else if (e.getMessage().equals("Operation timed out")) {
reply = ServerReply.TTL_EXPIRED;
} else if (e.getMessage().equals("Network is unreachable")) {
reply = ServerReply.NETWORK_UNREACHABLE;
} else if (e.getMessage().equals("Connection timed out")) {
reply = ServerReply.TTL_EXPIRED;
} else {
reply = ServerReply.GENERAL_SOCKS_SERVER_FAILURE;
}
final InetSocketAddress remoteAddress = new InetSocketAddress(targetServerAddress, targetServerPort);
if (e.getMessage().equals("Permission denied: connect")) {
final String msg = "Permission denied - unable to establish outbound connection to proxy. Perhaps blocked by a firewall?";
LOGGER.info("connect {} [{}] exception: {}", session.getId(), remoteAddress, msg);
SOCKS5BRIDGE_LOGGER.error("connecting to {}: {}", remoteAddress, msg);
} else {
LOGGER.info("SESSION[{}] connect {} [{}] exception: {}", session.getId(), remoteAddress, reply, e.getMessage());
}
}
session.write(CommandResponseMessage.getBytes(reply, bindAddress, bindPort));
if (reply != ServerReply.SUCCEEDED) {
session.close();
return;
}
final SocketPipe pipe = new SocketPipe(session.getSocket(), socket);
pipe.setName("SESSION[" + session.getId() + "]");
pipe.start(); // This method will build tow thread to run tow internal pipes.
// wait for pipe exit.
while (pipe.isRunning()) {
try {
Thread.sleep(500);
} catch (final InterruptedException e) {
pipe.stop();
session.close();
LOGGER.info("SESSION[{}] closed from {}", session.getId(), session.getClientAddress());
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy