/*
* SSHTools - Java SSH2 API
*
* Copyright (C) 2002-2003 Lee David Painter and Contributors.
*
* Contributions made by:
*
* Brett Smith
* Richard Pernavas
* Erwin Bolwidt
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 com.sshtools.j2ssh.transport;
import com.sshtools.j2ssh.configuration.ConfigurationLoader;
import com.sshtools.j2ssh.io.ByteArrayWriter;
import com.sshtools.j2ssh.transport.cipher.SshCipher;
import com.sshtools.j2ssh.transport.compression.SshCompression;
import com.sshtools.j2ssh.transport.hmac.SshHmac;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Random;
class TransportProtocolOutputStream {
//implements Runnable {
private static Log log = LogFactory.getLog(TransportProtocolOutputStream.class);
private OutputStream out;
//private Socket socket;
private TransportProtocolAlgorithmSync algorithms;
private TransportProtocolCommon transport;
private long sequenceNo = 0;
private long sequenceWrapLimit = BigInteger.valueOf(2).pow(32).longValue();
private Random rnd = ConfigurationLoader.getRND();
private long bytesTransfered = 0;
/**
* Creates a new TransportProtocolOutputStream object.
*
* @param out
* @param transport
* @param algorithms
*
* @throws TransportProtocolException
*/
public TransportProtocolOutputStream( /*Socket socket,*/
OutputStream out, TransportProtocolCommon transport,
TransportProtocolAlgorithmSync algorithms)
throws TransportProtocolException {
// try {
//this.socket = socket;
this.out = out; //socket.getOutputStream();
this.transport = transport;
this.algorithms = algorithms;
/* } catch (IOException ioe) {
throw new TransportProtocolException(
"Failed to obtain socket output stream");
}*/
}
/**
*
*
* @return
*/
protected long getNumBytesTransfered() {
return bytesTransfered;
}
/**
*
*
* @param msg
*
* @throws TransportProtocolException
*/
protected synchronized void sendMessage(SshMessage msg)
throws TransportProtocolException {
try {
// Get the algorithm objects
algorithms.lock();
SshCipher cipher = algorithms.getCipher();
SshHmac hmac = algorithms.getHmac();
SshCompression compression = algorithms.getCompression();
// Write the data into a byte array
ByteArrayWriter message = new ByteArrayWriter();
// Get the message payload data
byte[] msgdata = msg.toByteArray();
//int payload = msgdata.length;
int padding = 4;
int cipherlen = 8;
// Determine the cipher length
if (cipher != null) {
cipherlen = cipher.getBlockSize();
}
// Compress the payload if necersary
if (compression != null) {
msgdata = compression.compress(msgdata, 0, msgdata.length);
}
//Determine the padding length
padding += ((cipherlen -
((msgdata.length + 5 + padding) % cipherlen)) % cipherlen);
// Write the packet length field
message.writeInt(msgdata.length + 1 + padding);
// Write the padding length
message.write(padding);
// Write the message payload
message.write(msgdata, 0, msgdata.length);
// Create some random data for the padding
byte[] pad = new byte[padding];
rnd.nextBytes(pad);
// Write the padding
message.write(pad);
// Get the unencrypted packet data
byte[] packet = message.toByteArray();
byte[] mac = null;
// Generate the MAC
if (hmac != null) {
mac = hmac.generate(sequenceNo, packet, 0, packet.length);
}
// Perfrom encrpytion
if (cipher != null) {
packet = cipher.transform(packet);
}
// Reset the message
message.reset();
// Write the packet data
message.write(packet);
// Combine the packet and MAC
if (mac != null) {
message.write(mac);
}
bytesTransfered += message.size();
// Send!
out.write(message.toByteArray());
out.flush();
// Increment the sequence no
if (sequenceNo < sequenceWrapLimit) {
sequenceNo++;
} else {
sequenceNo = 0;
}
} catch (IOException ioe) {
if (transport.getState().getValue() != TransportProtocolState.DISCONNECTED) {
throw new TransportProtocolException("IO Error on socket: " +
ioe.getMessage());
}
}
finally {
algorithms.release();
}
}
}