/*
* 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.server;
import sockslib.common.Credentials;
import sockslib.common.SocksException;
import sockslib.common.net.MonitorSocketWrapper;
import sockslib.common.net.NetworkMonitor;
import sockslib.server.msg.ReadableMessage;
import sockslib.server.msg.WritableMessage;
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 java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
/**
* The class <code>SocksSession</code> represents
*
* @author Youchao Feng
* @version 1.0
* @date Apr 5, 2015 10:21:36 AM
*/
public class SocksSession implements Session {
private static final Logger logger = LoggerFactory.getLogger(SocksSession.class);
private Socket socket;
private long id;
private InputStream inputStream;
private OutputStream outputStream;
private Map<Long, Session> sessions;
private SocketAddress clientAddress;
private Map<Object, Object> attributes;
private NetworkMonitor networkMonitor;
private Credentials credentials;
public SocksSession() {
}
public SocksSession(Socket socket) {
this(0, socket, null);
}
public SocksSession(long id, Socket socket, Map<Long, Session> sessions) {
if (!socket.isConnected()) {
throw new IllegalArgumentException("Socket should be a connected socket");
}
if (socket instanceof MonitorSocketWrapper) {
networkMonitor = new NetworkMonitor();
((MonitorSocketWrapper) socket).addMonitor(networkMonitor);
}
this.id = id;
this.socket = socket;
this.sessions = sessions;
try {
this.inputStream = this.socket.getInputStream();
this.outputStream = this.socket.getOutputStream();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
clientAddress = socket.getRemoteSocketAddress();
attributes = new HashMap<Object, Object>();
}
@Override
public Socket getSocket() {
return socket;
}
@Override
public void write(byte[] bytes) throws SocksException, IOException {
write(bytes, 0, bytes.length);
}
@Override
public void write(WritableMessage message) throws SocksException, IOException {
write(message.getBytes());
}
@Override
public int read(byte[] bytes) throws SocksException, IOException {
return inputStream.read(bytes);
}
@Override
public int read(ReadableMessage message) throws SocksException, IOException {
message.read(inputStream);
return message.getLength();
}
@Override
public long getId() {
return id;
}
@Override
public void close() {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
} finally {
sessions.remove(id);
}
}
@Override
public InputStream getInputStream() {
return inputStream;
}
@Override
public OutputStream getOutputStream() {
return outputStream;
}
@Override
public void write(byte[] bytes, int offset, int length) throws SocksException, IOException {
outputStream.write(bytes, offset, length);
outputStream.flush();
}
@Override
public Map<Long, Session> getManagedSessions() {
return sessions;
}
@Override
public SocketAddress getClientAddress() {
return clientAddress;
}
@Override
public void setAttribute(Object key, Object value) {
attributes.put(key, value);
}
@Override
public Object getAttribute(Object key) {
return attributes.get(key);
}
@Override
public Map<Object, Object> getAttributes() {
return attributes;
}
@Override
public void clearAllAttributes() {
attributes.clear();
}
@Override
public boolean isClose() {
try {
socket.sendUrgentData(0);
return false;
} catch (IOException expected) {
return true;
}
}
@Override
public boolean isConnected() {
return socket.isConnected();
}
@Override
public NetworkMonitor getNetworkMonitor() {
return networkMonitor;
}
@Override
public String toString() {
return "SESSION[" + id + "]" + "@" + clientAddress;
}
}