/*
* Copyright 2015-2025 the original author or authors.
*
* 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 sockslib.client;
import sockslib.common.SocksException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
/**
* The class <code>SocksServerSocket</code> is server socket that can bind a port at SOCKS server
* and accept a connection. This server socket can only accept one connection from specified IP
* address and port.<br>
* <p>
* You can build a TCP server in SOCKS server easily by using following codes:
* </p>
* <pre>
* SocksProxy proxy = new Socks5(new InetSocketAddress("foo.com", 1080));
* //Create SOCKS5 proxy.
*
* ServerSocket serverSocket = new SocksServerSocket(proxy, inetAddress, 8080);
* // Get bind address in SOCKS server.
* InetAddress bindAddress = ((SocksServerSocket) serverSocket).getBindAddress();
* // Get bind port in SOKCS server.
* int bindPort = ((SocksServerSocket) serverSocket).getBindPort();
* Socket socket = serverSocket.accept();
* </pre>
*
* @author Youchao Feng
* @version 1.0
* @date Mar 25, 2015 11:40:36 AM
*/
public class SocksServerSocket extends ServerSocket {
/**
* Logger.
*/
protected static final Logger logger = LoggerFactory.getLogger(SocksServerSocket.class);
/**
* SOCKS proxy.
*/
private SocksProxy proxy;
/**
* The remote host's IP address that will connect the server.
*/
private InetAddress incomeAddress;
/**
* The remote host's port that will connect the server.
*/
private int incomePort;
/**
* Server's IP address.
*/
private InetAddress bindAddress;
/**
* Server's port.
*/
private int bindPort;
/**
* If {@link #accept()} is called, it will be <code>true</code>.
*/
private boolean alreadyAccepted = false;
/**
* Constructs a server socket. This server socket will established in SOCKS server.
*
* @param proxy SOCKS proxy.
* @param inetAddress The IP address that server socket will accept.
* @param port The port that server socket will accept.
* @throws SocksException If any error about SOCKS protocol occurs.
* @throws IOException If any I/O error occurs.
*/
public SocksServerSocket(SocksProxy proxy, InetAddress inetAddress, int port) throws
SocksException, IOException {
this.proxy = proxy.copy();
this.incomePort = port;
this.incomeAddress = inetAddress;
this.proxy.buildConnection();
// Send BIND command to SOCKS server.
CommandReplyMessage replyMesasge = this.proxy.requestBind(incomeAddress, incomePort);
// Get a bind IP and port in proxy server.
bindAddress = replyMesasge.getIp();
bindPort = replyMesasge.getPort();
logger.debug("Bind at {}:{}", bindAddress, bindPort);
}
/**
* Accepts a connection.<br>
* <b>Notice:</b> This method can be called only once. It will throw SocksException if this method
* is called more than once.
*/
@Override
public synchronized Socket accept() throws SocksException, IOException {
if (alreadyAccepted) {
throw new SocksException("SOCKS4/SOCKS5 protocol only allows one income connection");
}
alreadyAccepted = true;
return proxy.accept();
}
public InetAddress getBindAddress() {
return bindAddress;
}
public void setBindAddress(InetAddress bindAddress) {
this.bindAddress = bindAddress;
}
public int getBindPort() {
return bindPort;
}
public void setBindPort(int bindPort) {
this.bindPort = bindPort;
}
public SocketAddress getBindSocketAddress() {
return new InetSocketAddress(bindAddress, bindPort);
}
}