summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java')
-rw-r--r--src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
new file mode 100644
index 0000000..cb09ad9
--- /dev/null
+++ b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.filetransfer;
+
+import com.isode.stroke.network.Connection;
+import com.isode.stroke.network.HostAddressPort;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Slot;
+import com.isode.stroke.signals.Slot1;
+import java.util.logging.Logger;
+
+public class SOCKS5BytestreamServerSession {
+
+ private Connection connection;
+ private SOCKS5BytestreamRegistry bytestreams;
+ private ByteArray unprocessedData;
+ private State state;
+ private int chunkSize;
+ private String streamID = "";
+ private ReadBytestream readBytestream;
+ private WriteBytestream writeBytestream;
+ private boolean waitingForData;
+
+ private SignalConnection disconnectedConnection;
+ private SignalConnection dataReadConnection;
+ private SignalConnection dataWrittenConnection;
+ private SignalConnection dataAvailableConnection;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public enum State {
+ Initial,
+ WaitingForAuthentication,
+ WaitingForRequest,
+ ReadyForTransfer,
+ ReadingData,
+ WritingData,
+ Finished
+ };
+
+ public SOCKS5BytestreamServerSession(Connection connection, SOCKS5BytestreamRegistry bytestreams) {
+ this.connection = connection;
+ this.bytestreams = bytestreams;
+ this.state = State.Initial;
+ this.chunkSize = 131072;
+ this.waitingForData = false;
+ disconnectedConnection = connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+ @Override
+ public void call(Connection.Error e) {
+ handleDisconnected(e);
+ }
+ });
+ }
+
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ public void start() {
+ logger_.fine("\n");
+ dataReadConnection = connection.onDataRead.connect(new Slot1<SafeByteArray>() {
+ @Override
+ public void call(SafeByteArray s) {
+ handleDataRead(s);
+ }
+ });
+ state = State.WaitingForAuthentication;
+ }
+
+ public void stop() {
+ finish(false);
+ }
+
+ public void startSending(ReadBytestream stream) {
+ if (!State.ReadyForTransfer.equals(state)) { logger_.fine("Not ready for transfer!\n"); return; }
+ readBytestream = stream;
+ state = State.WritingData;
+ dataAvailableConnection = readBytestream.onDataAvailable.connect(new Slot() {
+ @Override
+ public void call() {
+ handleDataAvailable();
+ }
+ });
+ dataWrittenConnection = connection.onDataWritten.connect(new Slot() {
+ @Override
+ public void call() {
+ sendData();
+ }
+ });
+ sendData();
+ }
+
+ public void startReceiving(WriteBytestream stream) {
+ if (!State.ReadyForTransfer.equals(state)) { logger_.fine("Not ready for transfer!\n"); return; }
+
+ writeBytestream = stream;
+ state = State.ReadingData;
+ writeBytestream.write(unprocessedData);
+ // onBytesReceived(unprocessedData.getSize());
+ unprocessedData.clear();
+ }
+
+ public HostAddressPort getAddressPort() {
+ return connection.getLocalAddress();
+ }
+
+ public Signal1<FileTransferError> onFinished = new Signal1<FileTransferError>();
+ public Signal1<Long> onBytesSent = new Signal1<Long>();
+ // boost::signal<void (unsigned long long)> onBytesReceived;
+
+ public String getStreamID() {
+ return streamID;
+ }
+
+ private void finish(boolean error) {
+ logger_.fine(error + " " + state + "\n");
+ if (State.Finished.equals(state)) {
+ return;
+ }
+
+ disconnectedConnection.disconnect();
+ dataReadConnection.disconnect();
+ dataWrittenConnection.disconnect();
+ dataAvailableConnection.disconnect();
+ readBytestream = null;
+ state = State.Finished;
+ if (error) {
+ onFinished.emit(new FileTransferError(FileTransferError.Type.PeerError));
+ } else {
+ onFinished.emit(null);
+ }
+ }
+
+ private void process() {
+ if (State.WaitingForAuthentication.equals(state)) {
+ if (unprocessedData.getSize() >= 2) {
+ int authCount = unprocessedData.getData()[1];
+ int i = 2;
+ while (i < 2 + authCount && i < unprocessedData.getSize()) {
+ // Skip authentication mechanism
+ ++i;
+ }
+ if (i == 2 + authCount) {
+ // Authentication message is complete
+ if (i != unprocessedData.getSize()) {
+ logger_.fine("Junk after authentication mechanism\n");
+ }
+ unprocessedData.clear();
+ connection.write(new SafeByteArray(new byte[]{0x05, 0x00}));
+ state = State.WaitingForRequest;
+ }
+ }
+ }
+ else if (State.WaitingForRequest.equals(state)) {
+ if (unprocessedData.getSize() >= 5) {
+ ByteArray requestID = new ByteArray();
+ int i = 5;
+ int hostnameSize = unprocessedData.getData()[4];
+ while (i < 5 + hostnameSize && i < unprocessedData.getSize()) {
+ requestID.append(unprocessedData.getData()[i]);
+ ++i;
+ }
+ // Skip the port: 2 byte large, one already skipped. Add one for comparison with size
+ i += 2;
+ if (i <= unprocessedData.getSize()) {
+ if (i != unprocessedData.getSize()) {
+ logger_.fine("Junk after authentication mechanism\n");
+ }
+ unprocessedData.clear();
+ streamID = requestID.toString();
+ boolean hasBytestream = bytestreams.hasBytestream(streamID);
+ SafeByteArray result = new SafeByteArray("0x05");
+ result.append(hasBytestream ? (byte)0x0 : (byte)0x4);
+ result.append(new ByteArray(new byte[]{0x00, 0x03}));
+ result.append(Integer.toString(requestID.getSize()));
+ result.append(requestID.append(new ByteArray(new byte[]{0x00, 0x00})));
+ if (!hasBytestream) {
+ logger_.fine("Readstream or Wrtiestream with ID " + streamID + " not found!\n");
+ connection.write(result);
+ finish(true);
+ }
+ else {
+ logger_.fine("Found stream. Sent OK.\n");
+ connection.write(result);
+ state = State.ReadyForTransfer;
+ }
+ }
+ }
+ }
+ }
+
+ private void handleDataRead(SafeByteArray data) {
+ if (!State.ReadingData.equals(state)) {
+ unprocessedData.append(data);
+ process();
+ } else {
+ writeBytestream.write(new ByteArray(data));
+ // onBytesReceived(data.size());
+ }
+ }
+
+ private void handleDisconnected(final Connection.Error error) {
+ logger_.fine((error != null ? (error.equals(Connection.Error.ReadError) ? "Read Error" : "Write Error") : "No Error") + "\n");
+ finish(error != null ? true : false);
+ }
+
+ private void handleDataAvailable() {
+ if (waitingForData) {
+ sendData();
+ }
+ }
+
+ private void sendData() {
+ if (!readBytestream.isFinished()) {
+ //try {
+ SafeByteArray dataToSend = new SafeByteArray(readBytestream.read((chunkSize)));
+ if (!dataToSend.isEmpty()) {
+ connection.write(dataToSend);
+ onBytesSent.emit((long)dataToSend.getSize());
+ waitingForData = false;
+ }
+ else {
+ waitingForData = true;
+ }
+ //}
+ //catch (BytestreamException e) {
+ // finish(true);
+ //}
+ }
+ else {
+ finish(false);
+ }
+ }
+} \ No newline at end of file