/**
* OnionCoffee - Anonymous Communication through TOR Network
* Copyright (C) 2005-2007 RWTH Aachen University, Informatik IV
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package TorJava;
import java.io.IOException;
import java.io.InputStream;
import TorJava.Common.Encoding;
/**
* most general form of the cells in the Tor protocol. Should not be used on its
* own.
*
* @author Lexi Pimenidis
* @author Vinh Pham
* @version unstable
*/
public class Cell {
int circID;
public byte command;
byte[] payload;
static final String[] type_to_string = { "padding", "create", "created",
"relay", "destroy", "create-fast", "created-fast" };
static final int CELL_PADDING = 0; /* Padding */
static final int CELL_CREATE = 1; /* Create a circuit */
static final int CELL_CREATED = 2; /* Acknowledge create */
public static final int CELL_RELAY = 3; /* End-to-end data */
static final int CELL_DESTROY = 4; /* Stop using a circuit */
static final int CELL_CREATE_FAST = 5; /* Create a circuit, no PK */
static final int CELL_CREATED_FAST = 6; /* Circuit created, no PK */
static final int CELL_TOTAL_SIZE = 512;
static final int CELL_CIRCID_SIZE = 2;
static final int CELL_COMMAND_SIZE = 1;
static final int CELL_PAYLOAD_SIZE = 509;
static final int CELL_CIRCID_POS = 0;
static final int CELL_COMMAND_POS = CELL_CIRCID_POS + CELL_CIRCID_SIZE;
static final int CELL_PAYLOAD_POS = CELL_COMMAND_POS + CELL_COMMAND_SIZE;
/* Circuit for sending data or circuit that needs to be created */
Circuit outCircuit;
/**
* init cell for sending.
*
* @param outCircuit
* a circuit that is used to send some data on, or that needs ot
* be created
* @param command
* the purpose of this cell
*/
Cell(Circuit outCircuit, int command) {
// payload is all zeros because java does this for us.
this.payload = new byte[Cell.CELL_PAYLOAD_SIZE];
this.circID = outCircuit.ID;
this.command = (Encoding.intToNByteArray(command, 1))[0];
this.outCircuit = outCircuit;
}
/**
* init cell from data<br>
* Attention: this.outCircuit is not set!
*
* @param data
* a raw cell. 512 bytes long.
*/
Cell(byte[] data) throws NullPointerException {
init_from_data(data);
}
/**
* init cell from stream Attention: this.outCircuit is not set!
*
* @param in
* the input stream from a TLS-line to read the data from
*/
Cell(InputStream in) throws IOException {
if (in == null)
throw new IOException("null as input stream given");
byte[] data = new byte[Cell.CELL_TOTAL_SIZE];
int filled = 0;
while (filled < data.length) {
int n = in.read(data, filled, data.length - filled);
if(n<0) throw new IOException("Cell.<init>: reached EOF");
filled += n;
Thread.yield();
};
init_from_data(data);
}
/**
* this function decodes an incoming cell from the raw bytes into the given
* data structures of the Cell-Class.
*
* @param data
* a raw cell. 512 bytes long.
*/
private void init_from_data(byte[] data) throws NullPointerException {
if (data == null)
throw new NullPointerException("no data given");
this.payload = new byte[Cell.CELL_PAYLOAD_SIZE];
this.circID = Encoding.byteArrayToInt(data, Cell.CELL_CIRCID_POS,
Cell.CELL_CIRCID_SIZE);
this.command = data[Cell.CELL_COMMAND_POS];
System.arraycopy(data, Cell.CELL_PAYLOAD_POS, this.payload, 0,
payload.length);
// ** DEBUG-START
Logger.logCell(Logger.RAW_DATA, "Cell.init_from_data: "+print("Received "));
// ** DEBUG-END
}
/** is this a padding cell? */
boolean isTypePadding() {
return this.command == CELL_PADDING;
}
/** is this a created cell? */
boolean isTypeCreated() {
return this.command == CELL_CREATED;
}
/** is this a relay cell? */
boolean isTypeRelay() {
return this.command == CELL_RELAY;
}
/** is this a destroy cell? */
boolean isTypeDestroy() {
return this.command == CELL_DESTROY;
}
/**
* @deprecated other function from the package should access the circID
* directly
*/
int getCircID() {
return circID;
}
/**
* concat all data to a single byte-array. This function is used to finally
* transmit the cell over a line.
*/
byte[] toByteArray() {
byte[] buff = new byte[Cell.CELL_TOTAL_SIZE];
// ** DEBUG-START
Logger.logCell(Logger.RAW_DATA, "Cell.toByteArray(): "+print("Sending "));
// ** DEBUG-END
System.arraycopy(Encoding.intToNByteArray(this.circID,
Cell.CELL_CIRCID_SIZE), 0, buff, Cell.CELL_CIRCID_POS,
Cell.CELL_CIRCID_SIZE);
buff[Cell.CELL_COMMAND_POS] = this.command;
System.arraycopy(this.payload, 0, buff, CELL_PAYLOAD_POS,
this.payload.length);
return buff;
}
// ** DEBUG-START
/** wrapper for print(String descr) */
String print() {
return print("");
}
public static String type(int t) {
if ((t >= 0) && (t < type_to_string.length))
return type_to_string[t];
else
return "[" + t + "]";
}
public String type() {
return type(command);
}
/** used for debugging output */
String print(String descr) {
return descr + "cell for circuit " + getCircID() + " with command "
+ type() + ". Payload:\n" + Encoding.toHexString(payload, 100);
}
// ** DEBUG-END
}