All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simplejavamail.internal.authenticatedsockssupport.socks5client.SocksAuthenticationHelper Maven / Gradle / Ivy

There is a newer version: 8.12.2
Show newest version
/*
 * 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.socks5client;

import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.simplejavamail.internal.util.MiscUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

final class SocksAuthenticationHelper {
	private static final Logger LOGGER = LoggerFactory.getLogger(SocksAuthenticationHelper.class);

	private static final byte SOCKS_VERSION = 0x05;
	private static final int ACCEPTABLE_METHODS = 2; // anonymous & user / password

	private static final int NO_AUTHENTICATION_REQUIRED_METHOD = 0x00;
	private static final int USERNAME_PASSWORD_METHOD = 0x02;

	/**
	 * Performs an authentication method request to see how the proxy server wants to authenticate. GSSAPI is not supported, only anonymous
	 * and user / password authentication.
	 */
	public static boolean shouldAuthenticate(final Socket socket)
			throws IOException {
		// send data
		final byte[] bufferSent = new byte[4];
		bufferSent[0] = SOCKS_VERSION;
		bufferSent[1] = (byte) ACCEPTABLE_METHODS;
		bufferSent[2] = (byte) NO_AUTHENTICATION_REQUIRED_METHOD;
		bufferSent[3] = (byte) USERNAME_PASSWORD_METHOD;

		final OutputStream outputStream = socket.getOutputStream();
		outputStream.write(bufferSent);
		outputStream.flush();

		LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));

		// Received data.
		final InputStream inputStream = socket.getInputStream();
		final byte[] receivedData = read2Bytes(inputStream);
		LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(receivedData, true));
		if (receivedData[0] != (int) SOCKS_VERSION) {
			throw new SocksException("Remote server don't support SOCKS5");
		}
		final byte command = receivedData[1];
		if (command != NO_AUTHENTICATION_REQUIRED_METHOD && command != USERNAME_PASSWORD_METHOD) {
			throw new SocksException("requested authentication method not supported: " + command);
		}
		return command == USERNAME_PASSWORD_METHOD;
	}

	public static void performUserPasswordAuthentication(final Socks5 socksProxy)
			throws IOException {
        requireNonNull(socksProxy, "Argument [socksProxy] may not be null");
        final ProxyCredentials credentials = socksProxy.getCredentials();
		if (credentials == null) {
			throw new SocksException("Need Username/Password authentication");
		}

		final String username = credentials.getUsername();
		final String password = credentials.getPassword();
		final InputStream inputStream = socksProxy.getInputStream();
		final OutputStream outputStream = socksProxy.getOutputStream();

		final int USERNAME_LENGTH = username.getBytes(UTF_8).length;
		final int PASSWORD_LENGTH = password.getBytes(UTF_8).length;
		final byte[] bytesOfUsername = username.getBytes(UTF_8);
		final byte[] bytesOfPassword = password.getBytes(UTF_8);
		final byte[] bufferSent = new byte[3 + USERNAME_LENGTH + PASSWORD_LENGTH];

		bufferSent[0] = 0x01; // VER
		bufferSent[1] = (byte) USERNAME_LENGTH; // ULEN
		System.arraycopy(bytesOfUsername, 0, bufferSent, 2, USERNAME_LENGTH);// UNAME
		bufferSent[2 + USERNAME_LENGTH] = (byte) PASSWORD_LENGTH; // PLEN
		System.arraycopy(bytesOfPassword, 0, bufferSent, 3 + USERNAME_LENGTH, PASSWORD_LENGTH); // PASSWD
		outputStream.write(bufferSent);
		outputStream.flush();
		// logger send bytes
		LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));

		final byte[] authenticationResult = new byte[2];
		checkEnd(inputStream.read(authenticationResult));
		// logger
		LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(authenticationResult, true));

		if (authenticationResult[1] != Socks5.AUTHENTICATION_SUCCEEDED) {
			// Close connection if authentication is failed.
			outputStream.close();
			inputStream.close();
			socksProxy.getProxySocket().close();
			throw new SocksException("Username or password error");
		}
	}

	private static byte[] read2Bytes(final InputStream inputStream)
			throws IOException {
		final byte[] bytes = new byte[2];
		bytes[0] = (byte) checkEnd(inputStream.read());
		bytes[1] = (byte) checkEnd(inputStream.read());
		return bytes;
	}

	private static int checkEnd(final int b)
			throws IOException {
		if (b < 0) {
			throw new IOException("End of stream");
		} else {
			return b;
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy