/*************************************************************************** * fqterm, a terminal emulator for both BBS and *nix. * * Copyright (C) 2008 fqterm development group. * * * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ #include #include #include #include "fqterm_trace.h" #include "fqterm_ssh_buffer.h" #include "fqterm_serialization.h" namespace FQTerm { //============================================================================== // FQTermSSHBuffer //============================================================================== FQTermSSHBuffer::FQTermSSHBuffer(int size) { alloc_size_ = size; buffer_size_ = 0; offset_ = 0; buffer_ = new u_char[alloc_size_]; } FQTermSSHBuffer::~FQTermSSHBuffer() { delete [] buffer_; offset_ = 0; buffer_size_ = 0; alloc_size_ = 0; } void FQTermSSHBuffer::ensure(int len) { if (len <= (alloc_size_ - (offset_ + buffer_size_))) { return ; } else { alloc_size_ = buffer_size_ + len; rebuffer(); } } void FQTermSSHBuffer::rebuffer() { u_char *newBuffer; newBuffer = new u_char[alloc_size_]; memset(newBuffer, 0, alloc_size_); memcpy(newBuffer, buffer_ + offset_, buffer_size_); delete [] buffer_; buffer_ = newBuffer; offset_ = 0; } void FQTermSSHBuffer::clear() { memset(buffer_, 0, alloc_size_); offset_ = 0; buffer_size_ = 0; } void FQTermSSHBuffer::consume(int len) { if (len > buffer_size_) { len = buffer_size_; } offset_ += len; buffer_size_ -= len; } void FQTermSSHBuffer::putRawData(const char *data, int len) { if (len < 0) { FQ_TRACE("sshbuffer", 0) << "Write data error."; } ensure(len); memcpy((buffer_ + offset_ + buffer_size_), data, len); buffer_size_ += len; } void FQTermSSHBuffer::getRawData(char *data, int len) { if (len <= buffer_size_ && len >= 0) { memcpy(data, buffer_ + offset_, len); consume(len); } else { FQ_TRACE("sshbuffer", 0) << "Read too many data: " << len << " bytes."; } } //============================================================================== // Store an BIGNUM in the buffer with a 2-byte msb first bit count, followed by // (bits+7)/8 bytes of binary data, msb first. //============================================================================== void FQTermSSHBuffer::putSSH1BN(BIGNUM *bignum) { int bits = BN_num_bits(bignum); int bin_size = (bits + 7) / 8; u_char *buf = new u_char[bin_size]; int oi; u_char msg[2]; // Get the value of in binary oi = BN_bn2bin(bignum, buf); if (oi != bin_size) { FQ_TRACE("sshbuffer", 0) << "BN_bn2bin() failed: oi = " << oi << " != bin_size." << bin_size; } // Store the number of bits in the buffer in two bytes, msb first htonu16(msg, bits); putRawData((char*)msg, 2); // Store the binary data. putRawData((char*)buf, oi); memset(buf, 0, bin_size); delete [] buf; } void FQTermSSHBuffer::putSSH2BN(BIGNUM *bignum) { // FIXME: support negative number and add error handling. FQ_VERIFY(!BN_is_negative(bignum)); // currently we don't support negative big number. if (BN_is_zero(bignum)) { this->putInt(0); } else { u_int bytes = BN_num_bytes(bignum) + 1; FQ_VERIFY(bytes >= 2); // currently we don't support big numbers so small std::vector buf(bytes); buf[0] = 0; int bin_len = BN_bn2bin(bignum, &buf[0] + 1); FQ_VERIFY(bin_len == (int)bytes - 1); u_int no_high_bit = (buf[1] & 0x80) ? 0 : 1; this->putInt(bytes - no_high_bit); this->putRawData((const char *)&buf[0] + no_high_bit, bytes - no_high_bit); } } //============================================================================== // Retrieves a BIGNUM from the buffer. //============================================================================== void FQTermSSHBuffer::getSSH1BN(BIGNUM *bignum) { int bits, bytes; u_char buf[2]; u_char *bin; // Get the number for bits. getRawData((char*)buf, 2); bits = ntohu16(buf); // Compute the number of binary bytes that follow. bytes = (bits + 7) / 8; if (bytes > 8 *1024) { FQ_TRACE("sshbuffer", 0) << "Can't handle BN of size " << bytes; return ; } if (len() < bytes) { FQ_TRACE("sshbuffer", 0) << "The input buffer is too small."; return ; } bin = data(); BN_bin2bn(bin, bytes, bignum); consume(bytes); } void FQTermSSHBuffer::getSSH2BN(BIGNUM *bignum) { // FIXME: support negative numbers and error handling int len; unsigned char *hex_data = (unsigned char *)getString(&len); FQ_VERIFY(!(len > 0 && (hex_data[0] & 0x80))); // don't support negative numbers. FQ_VERIFY(len < 10240); // don't support so large numbers. BIGNUM *res = BN_bin2bn(hex_data, len, bignum); FQ_VERIFY(res != NULL); delete hex_data; } u_short FQTermSSHBuffer::getWord() { u_char buf[2]; u_short data; getRawData((char*)buf, 2); data = ntohu16(buf); return data; } void FQTermSSHBuffer::putWord(u_short data) { u_char buf[2]; htonu16(buf, data); putRawData((char*)buf, 2); } u_int FQTermSSHBuffer::getInt() { u_char buf[4]; u_int data; getRawData((char*)buf, 4); data = ntohu32(buf); return data; } void FQTermSSHBuffer::putInt(u_int data) { u_char buf[4]; htonu32(buf, data); putRawData((char*)buf, 4); } //============================================================================== // Return a character from the buffer (0-255). //============================================================================== u_char FQTermSSHBuffer::getByte() { u_char ch; getRawData((char*) &ch, 1); return ch; } //============================================================================== // Stores a character in the buffer. //============================================================================== void FQTermSSHBuffer::putByte(int data) { u_char ch = data; putRawData((char*) &ch, 1); } //============================================================================== // Stores an arbitrary binary string in the buffer. //============================================================================== void FQTermSSHBuffer::putString(const char *str, int len) { if (str == NULL) { FQ_TRACE("sshbuffer", 0) << "Can't put a null pointer string."; return ; } if (len < 0) { len = strlen(str); } putInt(len); putRawData(str, len); } //============================================================================== // Return an arbitrary binary string from the buffer. The string cannot be // longer than 256k. The returned value points to memory allocated with new; // It is the responsibility of the calling function to free the data. //============================================================================== void *FQTermSSHBuffer::getString(int *length) { u_char *data; u_int len; // Get the length. len = getInt(); if ((long)len > buffer_size_) { FQ_TRACE("sshbuffer", 0) << "String length " << len << " is greater than buffer size " << buffer_size_; return 0; } // Allocate space for the string. Add one byte for a null character. data = new u_char[len + 1]; // Get the string. getRawData((char*)data, len); // Append a null character to make processing easier. data[len] = 0; if (length != NULL) { *length = len; } // return the length of the string. return data; } } // namespace FQTerm